X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=Modules%2FStorage%2FFDDv2%2Ffdc.c;h=aeaebb55d59bf8552b646b007c5ee952fabb90a3;hb=1fa20812c8fba3ba938dda91d4ec01b02541bde6;hp=81dc00cc861784103ea21531f20aa3f9b6bf5074;hpb=bb8e7d800960fe262c8e76ec45870c7202dcef21;p=tpg%2Facess2.git diff --git a/Modules/Storage/FDDv2/fdc.c b/Modules/Storage/FDDv2/fdc.c index 81dc00cc..aeaebb55 100644 --- a/Modules/Storage/FDDv2/fdc.c +++ b/Modules/Storage/FDDv2/fdc.c @@ -7,6 +7,7 @@ */ #include #include "common.h" +#include // === CONSTANTS === #define MOTOR_ON_DELAY 500 @@ -37,88 +38,216 @@ enum eFDC_Commands }; // === PROTOTYPES === + int FDD_SetupIO(void); int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer); - + int FDD_int_SeekToTrack(int Disk, int Track); + int FDD_int_Calibrate(int Disk); + int FDD_int_Reset(int Disk); +// --- FIFO int FDD_int_WriteData(Uint16 Base, Uint8 Data); int FDD_int_ReadData(Uint16 Base, Uint8 *Data); void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl); - int FDD_int_Calibrate(int Disk); - int FDD_int_Reset(Uint16 Base); +// --- Motor Control int FDD_int_StartMotor(int Disk); int FDD_int_StopMotor(int Disk); void FDD_int_StopMotorCallback(void *Ptr); +// --- Helpers + int FDD_int_HandleST0Error(const char *Fcn, int Disk, Uint8 ST0); Uint16 FDD_int_GetBase(int Disk, int *Drive); +// --- Interrupt void FDD_int_ClearIRQ(void); int FDD_int_WaitIRQ(void); void FDD_int_IRQHandler(int IRQ, void *Ptr); // === GLOBALS === +/** + * \brief Marker for IRQ6 + * \todo Convert into a semaphore? + */ int gbFDD_IRQ6Fired; +/** + * \brief Protector for DMA and IRQ6 + */ +tMutex gFDD_IOMutex; // === CODE === +/** + * \brief Set up FDC IO + * \return Boolean failure + * + * Registers the IRQ handler and resets the controller + */ +int FDD_SetupIO(void) +{ + // Install IRQ6 Handler + IRQ_AddHandler(6, FDD_int_IRQHandler, NULL); + + // Reset controller + FDD_int_Reset(0); + // TODO: All controllers +} + /** * \brief Read/Write data from/to a disk * \param Disk Global disk number - * \param Track Track number + * \param Track Track number (Cyl*2+Head) * \param bWrite Toggle write mode * \param Buffer Destination/Source buffer * \return Boolean failure */ int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer) { - return -1; -} + Uint8 cmd; + int i, _disk; + Uint16 base = FDD_int_GetBase(Disk, &_disk); + int cyl = Track >> 1, head = Track & 1; -/** - * \brief Write a byte to the FIFO - */ -int FDD_int_WriteData(Uint16 Base, Uint8 Data) -{ - for( int i = 0; i < 100; i ++ ) + Mutex_Acquire( &gFDD_IOMutex ); + + // Initialise DMA for read/write + // TODO: Support non 1.44MiB FDs + DMA_SetChannel(2, BYTES_PER_TRACK, !bWrite); + + // Select command + if( bWrite ) + cmd = CMD_WRITE_DATA | 0xC0; + else + cmd = CMD_READ_DATA | 0xC0; + + // Seek + if( FDD_int_SeekToTrack(Disk, Track) ) { + Mutex_Release( &gFDD_IOMutex ); + return -1; + } + + for( i = 0; i < 20; i ++ ) { - if( inb(Base + FDC_MSR) & 0x80 ) - { - outb(Base + FDC_FIFO, Data); - return 0; + FDD_int_StartMotor(Disk); + + // Write data + if( bWrite ) + DMA_WriteData(2, BYTES_PER_TRACK, Buffer); + + FDD_int_WriteData(base, cmd); + FDD_int_WriteData(base, (head << 2) | _disk); + FDD_int_WriteData(base, cyl); + FDD_int_WriteData(base, head); + FDD_int_WriteData(base, 1); // First Sector + FDD_int_WriteData(base, 2); // Bytes per sector (128*2^n) + FDD_int_WriteData(base, 18); // 18 tracks (full disk) - TODO: Non 1.44 + FDD_int_WriteData(base, 0x1B); // Gap length - TODO: again + FDD_int_WriteData(base, 0xFF); // Data length - ? + + FDD_int_WaitIRQ(); + + // No Sense Interrupt + + Uint8 st0=0, st1=0, st2=0, bps=0; + FDD_int_ReadData(base, &st0); + FDD_int_ReadData(base, &st1); // st1 + FDD_int_ReadData(base, &st2); // st2 + FDD_int_ReadData(base, NULL); // rcy - Mutilated Cyl + FDD_int_ReadData(base, NULL); // rhe - Mutilated Head + FDD_int_ReadData(base, NULL); // rse - Mutilated sector + FDD_int_ReadData(base, &bps); // bps - Should be the same as above + + if( st0 & 0xc0 ) { + FDD_int_HandleST0Error(__func__, Disk, st0); + continue ; } - Time_Delay(10); + + if( st2 & 0x02 ) { + Log_Debug("FDD", "Disk %i is not writable", Disk); + Mutex_Release( &gFDD_IOMutex ); + return 2; + } + + if( st0 & 0x08 ) { + Log_Debug("FDD", "FDD_int_ReadWriteTrack: Drive not ready"); + continue ; + } + + + if( st1 & 0x80 ) { + Log_Debug("FDD", "FDD_int_ReadWriteTrack: End of cylinder"); + continue ; + } + + if( st1 & (0x20|0x10|0x04|0x01) ) { + Log_Debug("FDD", "FDD_int_ReadWriteTrack: st1 = 0x%x", st1); + continue; + } + + if( st2 & (0x40|0x20|0x10|0x04|0x01) ) { + Log_Debug("FDD", "FDD_int_ReadWriteTrack: st2 = 0x%x", st2); + continue ; + } + + if( bps != 0x2 ) { + Log_Debug("FDD", "Wanted bps = 2 (512), got %i", bps); + continue ; + } + + // Read back data + if( !bWrite ) + DMA_ReadData(2, BYTES_PER_TRACK, Buffer); + + FDD_int_StopMotor(Disk); + Mutex_Release( &gFDD_IOMutex ); + return 0; } - Log_Error("FDD", "Write timeout"); + + Log_Debug("FDD", "%i retries exhausted", i); + FDD_int_StopMotor(Disk); + Mutex_Release( &gFDD_IOMutex ); return 1; } /** - * \brief Read a byte from the FIFO + * \brief Seek to a specific track + * \param Disk Global disk number + * \param Track Track number (Cyl*2+Head) + * \return Boolean failure */ -int FDD_int_ReadData(Uint16 Base, Uint8 *Data) +int FDD_int_SeekToTrack(int Disk, int Track) { - for( int i = 0; i < 100; i ++ ) + Uint8 st0=0, res_cyl=0; + int cyl, head; + int _disk; + Uint16 base = FDD_int_GetBase(Disk, &_disk);; + + cyl = Track / 2; + head = Track % 1; + + FDD_int_StartMotor(Disk); + + for( int i = 0; i < 10; i ++ ) { - if( inb(Base + FDC_MSR) & 0x80 ) + FDD_int_ClearIRQ(); + FDD_int_WriteData(base, CMD_SEEK); + FDD_int_WriteData(base, (head << 2) + _disk); + FDD_int_WriteData(base, cyl); + + FDD_int_WaitIRQ(); + FDD_int_SenseInterrupt(base, &st0, &res_cyl); + + if( st0 & 0xC0 ) { - Uint8 tmp = inb(Base + FDC_FIFO); - if(Data) *Data = tmp; + FDD_int_HandleST0Error(__func__, Disk, st0); + continue ; + } + + if( res_cyl == cyl ) { + FDD_int_StopMotor(Disk); return 0; } - Time_Delay(10); } - Log_Error("FDD", "Read timeout"); + + Log_Error("FDD", "FDD_int_SeekToTrack: 10 retries exhausted\n"); + FDD_int_StopMotor(Disk); return 1; } -/** - * \brief Acknowledge an interrupt - * \param Base Controller base address - * \param ST0 Location to store the ST0 value - * \param Cyl Current cylinder - */ -void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl) -{ - FDD_int_WriteData(Base, CMD_SENSE_INTERRUPT); - FDD_int_ReadData(Base, ST0); - FDD_int_ReadData(Base, Cyl); -} - /** * \brief Calibrate a drive * \param Disk Global disk number @@ -142,12 +271,7 @@ int FDD_int_Calibrate(int Disk) FDD_int_SenseInterrupt(base, &st0, NULL); if( st0 & 0xC0 ) { - static const char *status_type[] = { - 0, "Error", "Invalid", "Drive Error" - }; - Log_Debug("FDD", "FDD_int_Calibrate: st0 & 0xC0 = 0x%x, %s", - st0 & 0xC0, status_type[st0 >> 6] - ); + FDD_int_HandleST0Error(__func__, Disk, st0); continue ; } @@ -167,28 +291,80 @@ int FDD_int_Calibrate(int Disk) * \brief Reset a controller * \param Base Controller base address */ -int FDD_int_Reset(Uint16 Base) +int FDD_int_Reset(int Disk) { Uint8 tmp; - - tmp = inb(Base + FDC_DOR) & 0xF0; - outb( Base + FDC_DOR, 0x00 ); + int _disk; + Uint16 base = FDD_int_Reset(Disk, &_disk); + + tmp = inb(base + FDC_DOR) & 0xF0; + outb( base + FDC_DOR, 0x00 ); Time_Delay(1); - outb( Base + FDC_DOR, tmp | 0x0C ); + outb( base + FDC_DOR, tmp | 0x0C ); - FDD_int_SenseInterrupt(Base, NULL, NULL); + FDD_int_SenseInterrupt(base, NULL, NULL); - outb(Base + FDC_CCR, 0x00); // 500KB/s + outb(base + FDC_CCR, 0x00); // 500KB/s - FDD_int_WriteData(Base, CMD_SPECIFY); // Step and Head Load Times - FDD_int_WriteData(Base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each) - FDD_int_WriteData(Base, 0x02); // Head Load Time >> 1 + FDD_int_WriteData(base, CMD_SPECIFY); // Step and Head Load Times + FDD_int_WriteData(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each) + FDD_int_WriteData(base, 0x02); // Head Load Time >> 1 // TODO: Recalibrate all present disks - FDD_int_Calibrate(0); + FDD_int_Calibrate(Disk); return 0; } +/** + * \brief Write a byte to the FIFO + */ +int FDD_int_WriteData(Uint16 Base, Uint8 Data) +{ + for( int i = 0; i < 100; i ++ ) + { + if( inb(Base + FDC_MSR) & 0x80 ) + { + outb(Base + FDC_FIFO, Data); + return 0; + } + Time_Delay(10); + } + Log_Error("FDD", "Write timeout"); + return 1; +} + +/** + * \brief Read a byte from the FIFO + */ +int FDD_int_ReadData(Uint16 Base, Uint8 *Data) +{ + for( int i = 0; i < 100; i ++ ) + { + if( inb(Base + FDC_MSR) & 0x80 ) + { + Uint8 tmp = inb(Base + FDC_FIFO); + if(Data) *Data = tmp; + return 0; + } + Time_Delay(10); + } + Log_Error("FDD", "Read timeout"); + return 1; +} + +/** + * \brief Acknowledge an interrupt + * \param Base Controller base address + * \param ST0 Location to store the ST0 value + * \param Cyl Current cylinder + */ +void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl) +{ + FDD_int_WriteData(Base, CMD_SENSE_INTERRUPT); + FDD_int_ReadData(Base, ST0); + FDD_int_ReadData(Base, Cyl); +} + /** * \brief Start the motor on a disk */ @@ -266,6 +442,25 @@ Uint16 FDD_int_GetBase(int Disk, int *Drive) } } +/** + * \brief Convert a ST0 error value into a message + * \param Fcn Calling function name + * \parma Disk Global disk number + * \param ST0 ST0 Value + * \return Boolean failure + */ +int FDD_int_HandleST0Error(const char *Fcn, int Disk, Uint8 ST0) +{ + static const char *status_type[] = { + 0, "Error", "Invalid", "Drive Error" + }; + + Log_Debug("FDD", "%s: Disk %i ST0 Status = %s (0x%x & 0xC0 = 0x%x)", + Fcn, Disk, status_type[ST0 >> 6], ST0, ST0 & 0xC0 + ); + return 0; +} + /** * \brief Clear the IRQ fired flag */