From 1fa20812c8fba3ba938dda91d4ec01b02541bde6 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 9 Oct 2011 11:01:59 +0800 Subject: [PATCH] Modules/FDDv2 - VFS interface almost done, Caching added --- Modules/Storage/FDDv2/common.h | 8 +- Modules/Storage/FDDv2/fdc.c | 307 +++++++++++++++++++++++++++------ Modules/Storage/FDDv2/main.c | 187 +++++++++++++++++++- 3 files changed, 444 insertions(+), 58 deletions(-) diff --git a/Modules/Storage/FDDv2/common.h b/Modules/Storage/FDDv2/common.h index 2f35c7bc..f426716f 100644 --- a/Modules/Storage/FDDv2/common.h +++ b/Modules/Storage/FDDv2/common.h @@ -8,6 +8,8 @@ #ifndef _FDC_COMMON_H_ #define _FDC_COMMON_H_ +#include + // === CONSTANTS === #define MAX_DISKS 8 // 4 per controller, 2 controllers #define TRACKS_PER_DISK (1440*2/18) @@ -19,15 +21,19 @@ typedef struct sFDD_Drive tDrive; // === STRUCTURES === struct sFDD_Drive { + int bValid; int bInserted; int MotorState; int Timer; + + tMutex Mutex; void *TrackData[TRACKS_PER_DISK]; // Whole tracks are read }; // === FUNCTIONS === -extern void FDD_int_IRQHandler(int IRQ, void *Ptr); +extern int FDD_SetupIO(void); +extern int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer); // === GLOBALS === extern tDrive gaFDD_Disks[MAX_DISKS]; 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 */ diff --git a/Modules/Storage/FDDv2/main.c b/Modules/Storage/FDDv2/main.c index 191c50c5..6ffcfdb7 100644 --- a/Modules/Storage/FDDv2/main.c +++ b/Modules/Storage/FDDv2/main.c @@ -9,21 +9,206 @@ #include #include #include "common.h" +#include // === CONSTANTS === +#define FDD_VERSION VER2(1,10) // === STRUCTURES === // === PROTOTYPES === int FDD_Install(char **Arguments); + int FDD_RegisterFS(void); +// --- VFS +char *FDD_ReadDir(tVFS_Node *Node, int pos); +tVFS_Node *FDD_FindDir(tVFS_Node *dirNode, const char *Name); + int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data); +Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); // === GLOBALS === -MODULE_DEFINE(0, 0x110, Storage_FDDv2, FDD_Install, NULL, "x86_ISADMA", NULL); +MODULE_DEFINE(0, FDD_VERSION, Storage_FDDv2, FDD_Install, NULL, "x86_ISADMA", NULL); tDrive gaFDD_Disks[MAX_DISKS]; +tVFS_Node gaFDD_DiskNodes[MAX_DISKS]; +tDevFS_Driver gFDD_DriverInfo = { + NULL, "fdd", + { + .Size = -1, + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Flags = VFS_FFLAG_DIRECTORY, + .ReadDir = FDD_ReadDir, + .FindDir = FDD_FindDir, + .IOCtl = FDD_IOCtl + } + }; // === CODE === int FDD_Install(char **Arguments) { + // Query CMOS memory + { + Uint8 data; + outb(0x70, 0x10); + data = inb(0x71); + + // NOTE: CMOS only reports 2 disks + if( (data & 0xF0) == 0x40 ) + gaFDD_Disks[0].bValid = gaFDD_Disks[0].bInserted = 1; + if( (data & 0x0F) == 0x04 ) + gaFDD_Disks[1].bValid = gaFDD_Disks[1].bInserted = 1; + + if( gaFDD_Disks[0].bValid == 0 && gaFDD_Disks[1].bValid == 0 ) + return MODULE_ERR_NOTNEEDED; + } + + // Initialise controller + FDD_SetupIO(); + + FDD_RegisterFS(); + return 0; } +/** + * \brief Register the FDD driver with DevFS + */ +int FDD_RegisterFS(void) +{ + gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime + = gFDD_DriverInfo.RootNode.ATime = now(); + + for( int i = 0; i < MAX_DISKS; i ++ ) + { + if( !gaFDD_Disks[i].bValid ) continue ; + + // Initialise Child Nodes + gaFDD_DiskNodes[i].Inode = i; + gaFDD_DiskNodes[i].Flags = 0; + gaFDD_DiskNodes[i].NumACLs = 0; + gaFDD_DiskNodes[i].Read = FDD_ReadFS; + gaFDD_DiskNodes[i].Write = NULL;//FDD_WriteFS; + gaFDD_DiskNodes[i].Size = 1440*1024; // TODO: Non 1.44 disks + } + + DevFS_AddDevice( &gFDD_DriverInfo ); +} + +/** + * \brief Get the name of the \a Pos th item in the driver root + * \param Node Root node (unused) + * \param Pos Position + * \return Heap string of node name + */ +char *FDD_ReadDir(tVFS_Node *Node, int Pos) +{ + char ret_tpl[2]; + if(Pos < 0 || Pos > MAX_DISKS ) + return NULL; + if(gaFDD_Disks[Pos].bValid) + return VFS_SKIP; + + ret_tpl[0] = '0' + Pos; + ret_tpl[1] = '\0'; + return strdup(ret_tpl); +} + +/** + * \brief Get a node by name + * \param Node Root node (unused) + * \param Name Drive name + * \return Pointer to node structure + */ +tVFS_Node *FDD_FindDir(tVFS_Node *Node, const char *Name) +{ + int pos; + if( '0' > Name[0] || Name[0] > '9' ) return NULL; + if( Name[1] != '\0' ) return NULL; + + pos = Name[0] - '0'; + + return &gaFDD_DiskNodes[pos]; +} + +static const char *casIOCTLS[] = {DRV_IOCTLNAMES,DRV_DISK_IOCTLNAMES,NULL}; +/** + * \brief Driver root IOCtl Handler + * \param Node Root node (unused) + * \param ID IOCtl ID + * \param Data IOCtl specific data pointer + */ +int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_DISK, "FDDv2", FDD_VERSION, casIOCTLS); + + case DISK_IOCTL_GETBLOCKSIZE: return 512; + + default: + return -1; + } +} + +/** + * \brief Read from a disk + * \param Node Disk node + * \param Offset Byte offset in disk + * \param Length Number of bytes to read + * \param Buffer Destination buffer + * \return Number of bytes read + */ +Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + int disk = Node->Inode; + int track; + + track = Offset / BYTES_PER_TRACK; + + if( Offset % BYTES_PER_TRACK ) + { + + } +} + +/** + * \brief Read from a track + */ +int FDD_int_ReadWriteWithinTrack(int Disk, int Track, int bWrite, size_t Offset, size_t Length, void *Buffer) +{ + if( Offset > BYTES_PER_TRACK || Length > BYTES_PER_TRACK ) + return 1; + if( Offset + Length > BYTES_PER_TRACK ) + return 1; + + Mutex_Acquire( &gaFDD_Disks[Disk].Mutex ); + + // If the cache doesn't exist, create it + if( !gaFDD_Disks[Disk].TrackData[Track] ) + { + gaFDD_Disks[Disk].TrackData[Track] = malloc( BYTES_PER_TRACK ); + // Don't bother reading if this is a whole track write + if( !(bWrite && Offset == 0 && Length == BYTES_PER_TRACK) ) + { + FDD_int_ReadWriteTrack(Disk, Track, 0, gaFDD_Disks[Disk].TrackData[Track]); + } + } + + // Read/Write + if( bWrite ) + { + // Write to cache then commit cache to disk + char *dest = gaFDD_Disks[Disk].TrackData[Track]; + memcpy( dest + Offset, Buffer, Length ); + FDD_int_ReadWriteTrack(Disk, Track, 1, gaFDD_Disks[Disk].TrackData[Track]); + } + else + { + // Read from cache + char *src = gaFDD_Disks[Disk].TrackData[Track]; + memcpy(Buffer, src + Offset, Length); + } + + Mutex_Release( &gaFDD_Disks[Disk].Mutex ); + + return 0; +} -- 2.20.1