Kernel - Slight reworks to timer code
[tpg/acess2.git] / Modules / Storage / FDD / fdd.c
index e54e774..9cf808d 100644 (file)
-/*\r
- * AcessOS 0.1\r
- * Floppy Disk Access Code\r
- */\r
-#define DEBUG  0\r
-#include <acess.h>\r
-#include <modules.h>\r
-#include <fs_devfs.h>\r
-#include <tpl_drv_disk.h>\r
-#include <dma.h>\r
-#include <iocache.h>\r
-\r
-#define WARN   0\r
-\r
-// === CONSTANTS ===\r
-// --- Current Version\r
-#define FDD_VERSION     ((0<<8)|(75))\r
-\r
-// --- Options\r
-#define FDD_SEEK_TIMEOUT       10      // Timeout for a seek operation\r
-#define MOTOR_ON_DELAY 500             // Miliseconds\r
-#define MOTOR_OFF_DELAY        2000    // Miliseconds\r
-#define        FDD_MAX_READWRITE_ATTEMPTS      16\r
-\r
-// === TYPEDEFS ===\r
-/**\r
- * \brief Representation of a floppy drive\r
- */\r
-typedef struct sFloppyDrive\r
-{\r
-        int    type;\r
-       volatile int    motorState;     //2 - On, 1 - Spinup, 0 - Off\r
-        int    track[2];\r
-        int    timer;\r
-       tVFS_Node       Node;\r
-       #if !USE_CACHE\r
-       tIOCache        *CacheHandle;\r
-       #endif\r
-} t_floppyDevice;\r
-\r
-/**\r
- * \brief Cached Sector\r
- */\r
-typedef struct {\r
-       Uint64  timestamp;\r
-       Uint16  disk;\r
-       Uint16  sector; // Allows 32Mb of addressable space (Plenty for FDD)\r
-       Uint8   data[512];\r
-} t_floppySector;\r
-\r
-// === CONSTANTS ===\r
-static const char      *cFDD_TYPES[] = {"None", "360kB 5.25\"", "1.2MB 5.25\"", "720kB 3.5\"", "1.44MB 3.5\"", "2.88MB 3.5\"" };\r
-static const int       cFDD_SIZES[] = { 0, 360*1024, 1200*1024, 720*1024, 1440*1024, 2880*1024 };\r
-static const short     cPORTBASE[] = { 0x3F0, 0x370 };\r
-#if DEBUG\r
-static const char      *cFDD_STATUSES[] = {NULL, "Error", "Invalid command", "Drive not ready"};\r
-#endif\r
-\r
-enum FloppyPorts {\r
-       PORT_STATUSA    = 0x0,\r
-       PORT_STATUSB    = 0x1,\r
-       PORT_DIGOUTPUT  = 0x2,\r
-       PORT_MAINSTATUS = 0x4,\r
-       PORT_DATARATE   = 0x4,\r
-       PORT_DATA               = 0x5,\r
-       PORT_DIGINPUT   = 0x7,\r
-       PORT_CONFIGCTRL = 0x7\r
-};\r
-\r
-enum FloppyCommands {\r
-       FIX_DRIVE_DATA  = 0x03,\r
-       HECK_DRIVE_STATUS       = 0x04,\r
-       CALIBRATE_DRIVE = 0x07,\r
-       CHECK_INTERRUPT_STATUS = 0x08,\r
-       SEEK_TRACK              = 0x0F,\r
-       READ_SECTOR_ID  = 0x4A,\r
-       FORMAT_TRACK    = 0x4D,\r
-       READ_TRACK              = 0x42,\r
-       READ_SECTOR             = 0x66,\r
-       WRITE_SECTOR    = 0xC5,\r
-       WRITE_DELETE_SECTOR     = 0xC9,\r
-       READ_DELETE_SECTOR      = 0xCC,\r
-};\r
-\r
-// === PROTOTYPES ===\r
-// --- Filesystem\r
- int   FDD_Install(char **Arguments);\r
-void   FDD_UnloadModule();\r
-// --- VFS Methods\r
-char   *FDD_ReadDir(tVFS_Node *Node, int pos);\r
-tVFS_Node      *FDD_FindDir(tVFS_Node *dirNode, char *Name);\r
- int   FDD_IOCtl(tVFS_Node *Node, int ID, void *Data);\r
-Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
-// --- Functions for IOCache/DrvUtil\r
-Uint   FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk);\r
-// --- Raw Disk Access\r
- int   FDD_ReadSector(Uint32 disk, Uint64 lba, void *Buffer);\r
- int   FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer);\r
-// --- Helpers\r
-void   FDD_IRQHandler(int Num);\r
-inline void    FDD_WaitIRQ();\r
-void   FDD_SensInt(int base, Uint8 *sr0, Uint8 *cyl);\r
-void   FDD_int_SendByte(int base, char byte);\r
- int   FDD_int_GetByte(int base);\r
-void   FDD_Reset(int id);\r
-void   FDD_Recalibrate(int disk);\r
- int   FDD_int_SeekTrack(int disk, int head, int track);\r
-void   FDD_int_TimerCallback(int arg);\r
-void   FDD_int_StopMotor(int disk);\r
-void   FDD_int_StartMotor(int disk);\r
- int   FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt);\r
-\r
-// === GLOBALS ===\r
-MODULE_DEFINE(0, FDD_VERSION, FDD, FDD_Install, NULL, "ISADMA", NULL);\r
-t_floppyDevice gFDD_Devices[2];\r
-tSpinlock      glFDD;\r
-volatile int   gbFDD_IrqFired = 0;\r
-tDevFS_Driver  gFDD_DriverInfo = {\r
-       NULL, "fdd",\r
-       {\r
-       .Size = -1,\r
-       .NumACLs = 1,\r
-       .ACLs = &gVFS_ACL_EveryoneRX,\r
-       .Flags = VFS_FFLAG_DIRECTORY,\r
-       .ReadDir = FDD_ReadDir,\r
-       .FindDir = FDD_FindDir,\r
-       .IOCtl = FDD_IOCtl\r
-       }\r
-};\r
-\r
-// === CODE ===\r
-/**\r
- * \fn int FDD_Install(char **Arguments)\r
- * \brief Installs floppy driver\r
- */\r
-int FDD_Install(char **Arguments)\r
-{\r
-       Uint8 data;\r
-       \r
-       // Determine Floppy Types (From CMOS)\r
-       outb(0x70, 0x10);\r
-       data = inb(0x71);\r
-       gFDD_Devices[0].type = data >> 4;\r
-       gFDD_Devices[1].type = data & 0xF;\r
-       gFDD_Devices[0].track[0] = -1;\r
-       gFDD_Devices[1].track[1] = -1;\r
-       \r
-       Log("[FDD ] Detected Disk 0: %s and Disk 1: %s", cFDD_TYPES[data>>4], cFDD_TYPES[data&0xF]);\r
-       \r
-       if( data == 0 ) {\r
-               return MODULE_ERR_NOTNEEDED;\r
-       }\r
-       \r
-       // Clear FDD IRQ Flag\r
-       FDD_SensInt(0x3F0, NULL, NULL);\r
-       // Install IRQ6 Handler\r
-       IRQ_AddHandler(6, FDD_IRQHandler);\r
-       // Reset Primary FDD Controller\r
-       FDD_Reset(0);\r
-       \r
-       // Initialise Root Node\r
-       gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime\r
-               = gFDD_DriverInfo.RootNode.ATime = now();\r
-       \r
-       // Initialise Child Nodes\r
-       gFDD_Devices[0].Node.Inode = 0;\r
-       gFDD_Devices[0].Node.Flags = 0;\r
-       gFDD_Devices[0].Node.NumACLs = 0;\r
-       gFDD_Devices[0].Node.Read = FDD_ReadFS;\r
-       gFDD_Devices[0].Node.Write = NULL;//FDD_WriteFS;\r
-       memcpy(&gFDD_Devices[1].Node, &gFDD_Devices[0].Node, sizeof(tVFS_Node));\r
-       \r
-       gFDD_Devices[1].Node.Inode = 1;\r
-       \r
-       // Set Lengths\r
-       gFDD_Devices[0].Node.Size = cFDD_SIZES[data >> 4];\r
-       gFDD_Devices[1].Node.Size = cFDD_SIZES[data & 0xF];\r
-       \r
-       // Create Sector Cache\r
-       if( cFDD_SIZES[data >> 4] )\r
-       {\r
-               gFDD_Devices[0].CacheHandle = IOCache_Create(\r
-                       FDD_WriteSector, 0, 512,\r
-                       gFDD_Devices[0].Node.Size / (512*4)\r
-                       );      // Cache is 1/4 the size of the disk\r
-       }\r
-       if( cFDD_SIZES[data & 15] )\r
-       {\r
-               gFDD_Devices[1].CacheHandle = IOCache_Create(\r
-                       FDD_WriteSector, 0, 512,\r
-                       gFDD_Devices[1].Node.Size / (512*4)\r
-                       );      // Cache is 1/4 the size of the disk\r
-       }\r
-       \r
-       // Register with devfs\r
-       DevFS_AddDevice(&gFDD_DriverInfo);\r
-       \r
-       return MODULE_ERR_OK;\r
-}\r
-\r
-/**\r
- * \brief Prepare the module for removal\r
- */\r
-void FDD_UnloadModule()\r
-{\r
-        int    i;\r
-       //DevFS_DelDevice( &gFDD_DriverInfo );\r
-       LOCK(&glFDD);\r
-       for(i=0;i<4;i++) {\r
-               Time_RemoveTimer(gFDD_Devices[i].timer);\r
-               FDD_int_StopMotor(i);\r
-       }\r
-       RELEASE(&glFDD);\r
-       //IRQ_Clear(6);\r
-}\r
-\r
-/**\r
- * \fn char *FDD_ReadDir(tVFS_Node *Node, int pos)\r
- * \brief Read Directory\r
- */\r
-char *FDD_ReadDir(tVFS_Node *Node, int Pos)\r
-{\r
-       char    name[2] = "0\0";\r
-\r
-       if(Pos >= 2 || Pos < 0) return NULL;\r
-       \r
-       if(gFDD_Devices[Pos].type == 0) return VFS_SKIP;\r
-       \r
-       name[0] += Pos;\r
-       \r
-       return strdup(name);\r
-}\r
-\r
-/**\r
- * \fn tVFS_Node *FDD_FindDir(tVFS_Node *Node, char *filename);\r
- * \brief Find File Routine (for vfs_node)\r
- */\r
-tVFS_Node *FDD_FindDir(tVFS_Node *Node, char *Filename)\r
-{\r
-        int    i;\r
-       \r
-       ENTER("sFilename", Filename);\r
-       \r
-       // Sanity check string\r
-       if(Filename == NULL) {\r
-               LEAVE('n');\r
-               return NULL;\r
-       }\r
-       \r
-       // Check string length (should be 1)\r
-       if(Filename[0] == '\0' || Filename[1] != '\0') {\r
-               LEAVE('n');\r
-               return NULL;\r
-       }\r
-       \r
-       // Get First character\r
-       i = Filename[0] - '0';\r
-       \r
-       // Check for 1st disk and if it is present return\r
-       if(i == 0 && gFDD_Devices[0].type != 0) {\r
-               LEAVE('p', &gFDD_Devices[0].Node);\r
-               return &gFDD_Devices[0].Node;\r
-       }\r
-       \r
-       // Check for 2nd disk and if it is present return\r
-       if(i == 1 && gFDD_Devices[1].type != 0) {\r
-               LEAVE('p', &gFDD_Devices[1].Node);\r
-               return &gFDD_Devices[1].Node;\r
-       }\r
-       \r
-       // Else return null\r
-       LEAVE('n');\r
-       return NULL;\r
-}\r
-\r
-static const char      *casIOCTLS[] = {DRV_IOCTLNAMES,DRV_DISK_IOCTLNAMES,NULL};\r
-/**\r
- * \fn int FDD_IOCtl(tVFS_Node *Node, int id, void *data)\r
- * \brief Stub ioctl function\r
- */\r
-int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
-{\r
-       switch(ID)\r
-       {\r
-       case DRV_IOCTL_TYPE:    return DRV_TYPE_DISK;\r
-       case DRV_IOCTL_IDENT:   return ModUtil_SetIdent(Data, "FDD");\r
-       case DRV_IOCTL_VERSION: return FDD_VERSION;\r
-       case DRV_IOCTL_LOOKUP:  return ModUtil_LookupString((char**)casIOCTLS, Data);\r
-       \r
-       case DISK_IOCTL_GETBLOCKSIZE:   return 512;     \r
-       \r
-       default:\r
-               return 0;\r
-       }\r
-}\r
-\r
-/**\r
- * \fn Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
- * \brief Read Data from a disk\r
-*/\r
-Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
-{\r
-        int    ret;\r
-       \r
-       ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);\r
-       \r
-       if(Node == NULL) {\r
-               LEAVE('i', -1);\r
-               return -1;\r
-       }\r
-       \r
-       if(Node->Inode != 0 && Node->Inode != 1) {\r
-               LEAVE('i', -1);\r
-               return -1;\r
-       }\r
-       \r
-       ret = DrvUtil_ReadBlock(Offset, Length, Buffer, FDD_ReadSectors, 512, Node->Inode);\r
-       LEAVE('i', ret);\r
-       return ret;\r
-}\r
-\r
-/**\r
- * \brief Reads \a Count contiguous sectors from a disk\r
- * \param SectorAddr   Address of the first sector\r
- * \param Count        Number of sectors to read\r
- * \param Buffer       Destination Buffer\r
- * \param Disk Disk Number\r
- * \return Number of sectors read\r
- * \note Used as a ::DrvUtil_ReadBlock helper\r
- */\r
-Uint FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk)\r
-{\r
-       Uint    ret = 0;\r
-       while(Count --)\r
-       {\r
-               if( FDD_ReadSector(Disk, SectorAddr, Buffer) != 1 )\r
-                       return ret;\r
-               \r
-               Buffer = (void*)( (tVAddr)Buffer + 512 );\r
-               SectorAddr ++;\r
-               ret ++;\r
-       }\r
-       return ret;\r
-}\r
-\r
-int FDD_int_ReadWriteSector(Uint32 Disk, Uint64 SectorAddr, int Write, void *Buffer)\r
-{\r
-        int    cyl, head, sec;\r
-        int    spt, base;\r
-        int    i;\r
-        int    lba = SectorAddr;\r
-       Uint8   st0, st1, st2, rcy, rhe, rse, bps;      //      Status Values\r
-       \r
-       ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer);\r
-       \r
-       base = cPORTBASE[Disk >> 1];\r
-       \r
-       LOG("Calculating Disk Dimensions");\r
-       // Get CHS position\r
-       if(FDD_int_GetDims(gFDD_Devices[Disk].type, lba, &cyl, &head, &sec, &spt) != 1)\r
-       {\r
-               LEAVE('i', -1);\r
-               return -1;\r
-       }\r
-       LOG("Cyl=%i, Head=%i, Sector=%i", cyl, head, sec);\r
-       \r
-       LOCK(&glFDD);   // Lock to stop the motor stopping on us\r
-       Time_RemoveTimer(gFDD_Devices[Disk].timer);     // Remove Old Timer\r
-       // Start motor if needed\r
-       if(gFDD_Devices[Disk].motorState != 2)  FDD_int_StartMotor(Disk);\r
-       RELEASE(&glFDD);\r
-       \r
-       LOG("Wait for the motor to spin up");\r
-       \r
-       // Wait for spinup\r
-       while(gFDD_Devices[Disk].motorState == 1)       Threads_Yield();\r
-       \r
-       LOG("Acquire Spinlock");\r
-       LOCK(&glFDD);\r
-       \r
-       // Seek to track\r
-       outb(base + CALIBRATE_DRIVE, 0);\r
-       i = 0;\r
-       while(FDD_int_SeekTrack(Disk, head, (Uint8)cyl) == 0 && i++ < FDD_SEEK_TIMEOUT )\r
-               Threads_Yield();\r
-       if( i > FDD_SEEK_TIMEOUT ) {\r
-               RELEASE(&glFDD);\r
-               LEAVE('i', 0);\r
-               return 0;\r
-       }\r
-       //FDD_SensInt(base, NULL, NULL);        // Wait for IRQ\r
-               \r
-       // Read Data from DMA\r
-       LOG("Setting DMA for read");\r
-       DMA_SetChannel(2, 512, !Write); // Read 512 Bytes from channel 2\r
-       \r
-       LOG("Sending command");\r
-       \r
-       //Threads_Wait(100);    // Wait for Head to settle\r
-       Time_Delay(100);\r
-       \r
-       for( i = 0; i < FDD_MAX_READWRITE_ATTEMPTS; i ++ )\r
-       {\r
-               if( Write )\r
-                       FDD_int_SendByte(base, READ_SECTOR);    // Was 0xE6\r
-               else\r
-                       FDD_int_SendByte(base, READ_SECTOR);    // Was 0xE6\r
-               FDD_int_SendByte(base, (head << 2) | (Disk&1));\r
-               FDD_int_SendByte(base, (Uint8)cyl);\r
-               FDD_int_SendByte(base, (Uint8)head);\r
-               FDD_int_SendByte(base, (Uint8)sec);\r
-               FDD_int_SendByte(base, 0x02);   // Bytes Per Sector (Real BPS=128*2^{val})\r
-               FDD_int_SendByte(base, spt);    // SPT\r
-               FDD_int_SendByte(base, 0x1B);   // Gap Length (27 is default)\r
-               FDD_int_SendByte(base, 0xFF);   // Data Length\r
-               \r
-               // Wait for IRQ\r
-               if( Write ) {\r
-                       LOG("Writing Data");\r
-                       DMA_WriteData(2, 512, Buffer);\r
-                       LOG("Waiting for Data to be written");\r
-                       FDD_WaitIRQ();\r
-               }\r
-               else {\r
-                       LOG("Waiting for data to be read");\r
-                       FDD_WaitIRQ();\r
-                       LOG("Reading Data");\r
-                       DMA_ReadData(2, 512, Buffer);\r
-               }\r
-               \r
-               // Clear Input Buffer\r
-               LOG("Clearing Input Buffer");\r
-               // Status Values\r
-               st0 = FDD_int_GetByte(base);\r
-               st1 = FDD_int_GetByte(base);\r
-               st2 = FDD_int_GetByte(base);\r
-               \r
-               // Cylinder, Head and Sector (mutilated in some way\r
-               rcy = FDD_int_GetByte(base);\r
-               rhe = FDD_int_GetByte(base);\r
-               rse = FDD_int_GetByte(base);\r
-               // Should be the BPS set above (0x02)\r
-               bps = FDD_int_GetByte(base);\r
-               \r
-               // Check Status\r
-               // - Error Code\r
-               if(st0 & 0xC0) {\r
-                       LOG("Error (st0 & 0xC0) \"%s\"", cFDD_STATUSES[st0 >> 6]);\r
-                       continue;\r
-        }\r
-        // - Status Flags\r
-        if(st0 & 0x08) {       LOG("Drive not ready"); continue;       }\r
-        if(st1 & 0x80) {       LOG("End of Cylinder"); continue;       }\r
-               if(st1 & 0x20) {        LOG("CRC Error");       continue;       }\r
-               if(st1 & 0x10) {        LOG("Controller Timeout");      continue;       }\r
-               if(st1 & 0x04) {        LOG("No Data Found");   continue;       }\r
-               if(st1 & 0x01 || st2 & 0x01) {\r
-                       LOG("No Address mark found");\r
-                       continue;\r
-               }\r
-        if(st2 & 0x40) {       LOG("Deleted address mark");    continue;       }\r
-               if(st2 & 0x20) {        LOG("CRC error in data");       continue;       }\r
-               if(st2 & 0x10) {        LOG("Wrong Cylinder");  continue;       }\r
-               if(st2 & 0x04) {        LOG("uPD765 sector not found"); continue;       }\r
-               if(st2 & 0x02) {        LOG("Bad Cylinder");    continue;       }\r
-               \r
-               if(bps != 0x2) {\r
-                       LOG("Returned BPS = 0x%02x, not 0x02", bps);\r
-                       continue;\r
-               }\r
-               \r
-               if(st1 & 0x02) {\r
-                       LOG("Floppy not writable");\r
-                       i = FDD_MAX_READWRITE_ATTEMPTS;\r
-                       break;\r
-               }\r
-       }\r
-       \r
-       // Release Spinlock\r
-       LOG("Realeasing Spinlock and setting motor to stop");\r
-       RELEASE(&glFDD);\r
-       \r
-       // Don't turn the motor off now, wait for a while\r
-       gFDD_Devices[Disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotor, (void*)Disk);\r
-\r
-       if( i < FDD_MAX_READWRITE_ATTEMPTS ) {\r
-               LEAVE('i', 0);\r
-               return 0;\r
-       }\r
-       else {\r
-               LEAVE('i', 1);\r
-               return 1;\r
-       }\r
-}\r
-\r
-/**\r
- * \fn int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer)\r
- * \brief Read a sector from disk\r
- * \todo Make real-hardware safe (account for read errors)\r
-*/\r
-int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer)\r
-{\r
-        int    ret;\r
-       \r
-       ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer);\r
-       \r
-       if( IOCache_Read( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer ) == 1 ) {\r
-               LEAVE('i', 1);\r
-               return 1;\r
-       }\r
-       \r
-       // Pass to general function\r
+/*
+ * AcessOS 0.1
+ * Floppy Disk Access Code
+ */
+#define DEBUG  0
+#include <acess.h>
+#include <modules.h>
+#include <fs_devfs.h>
+#include <api_drv_disk.h>
+#include <dma.h>
+#include <iocache.h>
+
+#define WARN   0
+
+// === CONSTANTS ===
+// --- Current Version
+#define FDD_VERSION     ((0<<8)|(75))
+
+// --- Options
+#define FDD_SEEK_TIMEOUT       10      // Timeout for a seek operation
+#define MOTOR_ON_DELAY 500             // Miliseconds
+#define MOTOR_OFF_DELAY        2000    // Miliseconds
+#define        FDD_MAX_READWRITE_ATTEMPTS      16
+
+// === TYPEDEFS ===
+/**
+ * \brief Representation of a floppy drive
+ */
+typedef struct sFloppyDrive
+{
+        int    type;
+       volatile int    motorState;     //2 - On, 1 - Spinup, 0 - Off
+        int    track[2];
+        int    timer;
+       tVFS_Node       Node;
+       #if !USE_CACHE
+       tIOCache        *CacheHandle;
+       #endif
+} t_floppyDevice;
+
+/**
+ * \brief Cached Sector
+ */
+typedef struct {
+       Uint64  timestamp;
+       Uint16  disk;
+       Uint16  sector; // Allows 32Mb of addressable space (Plenty for FDD)
+       Uint8   data[512];
+} t_floppySector;
+
+// === CONSTANTS ===
+static const char      *cFDD_TYPES[] = {"None", "360kB 5.25\"", "1.2MB 5.25\"", "720kB 3.5\"", "1.44MB 3.5\"", "2.88MB 3.5\"" };
+static const int       cFDD_SIZES[] = { 0, 360*1024, 1200*1024, 720*1024, 1440*1024, 2880*1024 };
+static const short     cPORTBASE[] = { 0x3F0, 0x370 };
+#if DEBUG
+static const char      *cFDD_STATUSES[] = {NULL, "Error", "Invalid command", "Drive not ready"};
+#endif
+
+enum FloppyPorts {
+       PORT_STATUSA    = 0x0,
+       PORT_STATUSB    = 0x1,
+       PORT_DIGOUTPUT  = 0x2,
+       PORT_MAINSTATUS = 0x4,
+       PORT_DATARATE   = 0x4,
+       PORT_DATA       = 0x5,
+       PORT_DIGINPUT   = 0x7,
+       PORT_CONFIGCTRL = 0x7
+};
+
+#define CMD_FLAG_MULTI_TRACK   0x80    //!< Multitrack
+#define CMD_FLAG_MFM_ENCODING  0x40    //!< MFM Encoding Mode (Always set for read/write/format/verify)
+#define CMD_FLAG_SKIP_MODE     0x20    //!< Skip Mode (don't use)
+enum FloppyCommands {
+       CMD_READ_TRACK      = 0x02,
+       CMD_SPECIFY         = 0x03,
+       CMD_SENSE_STATUS    = 0x04,
+       CMD_WRITE_DATA      = 0x05,
+       CMD_READ_DATA       = 0x06,
+       CMD_RECALIBRATE     = 0x07,
+       CMD_SENSE_INTERRUPT = 0x08,
+       CMD_WRITE_DEL_DATA  = 0x09,
+       CMD_READ_SECTOR_ID  = 0x0A,
+       // 0x0B - ?
+       CMD_READ_DEL_DATA       = 0x0C,
+       CMD_FORMAT_TRACK    = 0x0D,
+       // 0x0E - ?
+       CMD_SEEK_TRACK      = 0x0F,
+       CMD_VERSION         = 0x10,
+       
+       CMD_LOCK            = 0x14,     //!< Save controller parameters
+       
+       CMD_CONFIGURE       = 0x13
+};
+
+// === PROTOTYPES ===
+// --- Filesystem
+ int   FDD_Install(char **Arguments);
+void   FDD_UnloadModule();
+// --- VFS Methods
+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);
+// --- Functions for IOCache/DrvUtil
+Uint   FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk);
+// --- Raw Disk Access
+ int   FDD_ReadSector(Uint32 disk, Uint64 lba, void *Buffer);
+ int   FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer);
+// --- Helpers
+ int   FDD_WaitIRQ();
+void   FDD_IRQHandler(int Num, void *Ptr);
+void   FDD_SenseInt(int base, Uint8 *sr0, Uint8 *cyl);
+
+ int   FDD_Reset(int id);
+void   FDD_Recalibrate(int disk);
+ int   FDD_Reconfigure(int ID);
+
+ int   FDD_int_SeekTrack(int disk, int head, int track);
+void   FDD_int_TimerCallback(void *Arg);
+void   FDD_int_StopMotor(int Disk);
+void   FDD_int_StopMotorCallback(void *Arg);
+void   FDD_int_StartMotor(int Disk);
+ int   FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt);
+
+ int   FDD_int_SendByte(int base, Uint8 Byte);
+ int   FDD_int_GetByte(int base, Uint8 *Byte);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, FDD_VERSION, FDD, FDD_Install, NULL, "x86_ISADMA", NULL);
+t_floppyDevice gFDD_Devices[2];
+tMutex glFDD;
+volatile int   gbFDD_IrqFired = 0;
+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 ===
+/**
+ * \fn int FDD_Install(char **Arguments)
+ * \brief Installs floppy driver
+ */
+int FDD_Install(char **Arguments)
+{
+       Uint8 data;
+       char    **args = Arguments;
+       
+       // Determine Floppy Types (From CMOS)
+       outb(0x70, 0x10);
+       data = inb(0x71);
+       gFDD_Devices[0].type = data >> 4;
+       gFDD_Devices[1].type = data & 0xF;
+       gFDD_Devices[0].track[0] = -1;
+       gFDD_Devices[1].track[1] = -1;
+       
+       Log_Log("FDD", "Detected Disk 0: %s and Disk 1: %s", cFDD_TYPES[data>>4], cFDD_TYPES[data&0xF]);
+       
+       if( data == 0 ) {
+               return MODULE_ERR_NOTNEEDED;
+       }
+       
+       // Handle arguments
+       if(args) {
+               for(;*args;args++)
+               {
+                       if(strcmp(*args, "disable")==0)
+                               return MODULE_ERR_NOTNEEDED;
+               }
+       }
+       
+       // Install IRQ6 Handler
+       IRQ_AddHandler(6, FDD_IRQHandler, NULL);
+
+       // Ensure the FDD version is 0x90
+       {
+               Uint8   tmp = 0;
+               FDD_int_SendByte(cPORTBASE[0], CMD_VERSION);
+               FDD_int_GetByte(cPORTBASE[0], &tmp);
+               if( tmp != 0x90 ) {
+                       Log_Error("FDD", "Version(0x%2x) != 0x90", tmp);
+                       return MODULE_ERR_NOTNEEDED;
+               }
+       }
+
+       // Configure
+       FDD_Reconfigure(0);
+
+       // Reset Primary FDD Controller
+       if( FDD_Reset(0) != 0 ) {
+               return MODULE_ERR_MISC;
+       }
+
+       #if 0
+       {
+                int    retries;
+               // Recalibrate disks
+               LOG("Recalibrate disks (16x seek)");
+               retries = 16;
+               while(FDD_int_SeekTrack(0, 0, 1) == 0 && retries --)
+                       Threads_Yield();        // set track
+               if(retries < 0) LEAVE_RET('i', -1);
+       
+               retries = 16;
+               while(FDD_int_SeekTrack(0, 1, 1) == 0 && retries --)
+                       Threads_Yield();        // set track
+               if(retries < 0) LEAVE_RET('i', -1);
+       }
+       #endif
+       
+       
+       // Initialise Root Node
+       gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime
+               = gFDD_DriverInfo.RootNode.ATime = now();
+       
+       // Initialise Child Nodes
+       gFDD_Devices[0].Node.Inode = 0;
+       gFDD_Devices[0].Node.Flags = 0;
+       gFDD_Devices[0].Node.NumACLs = 0;
+       gFDD_Devices[0].Node.Read = FDD_ReadFS;
+       gFDD_Devices[0].Node.Write = NULL;//FDD_WriteFS;
+       memcpy(&gFDD_Devices[1].Node, &gFDD_Devices[0].Node, sizeof(tVFS_Node));
+       
+       gFDD_Devices[1].Node.Inode = 1;
+       
+       // Set Lengths
+       gFDD_Devices[0].Node.Size = cFDD_SIZES[data >> 4];
+       gFDD_Devices[1].Node.Size = cFDD_SIZES[data & 0xF];
+       
+       // Create Sector Cache
+       if( cFDD_SIZES[data >> 4] )
+       {
+               gFDD_Devices[0].CacheHandle = IOCache_Create(
+                       FDD_WriteSector, 0, 512,
+                       gFDD_Devices[0].Node.Size / (512*4)
+                       );      // Cache is 1/4 the size of the disk
+       }
+       if( cFDD_SIZES[data & 15] )
+       {
+               gFDD_Devices[1].CacheHandle = IOCache_Create(
+                       FDD_WriteSector, 0, 512,
+                       gFDD_Devices[1].Node.Size / (512*4)
+                       );      // Cache is 1/4 the size of the disk
+       }
+       
+       // Register with devfs
+       DevFS_AddDevice(&gFDD_DriverInfo);
+       
+       return MODULE_ERR_OK;
+}
+
+/**
+ * \brief Prepare the module for removal
+ */
+void FDD_UnloadModule()
+{
+        int    i;
+       DevFS_DelDevice( &gFDD_DriverInfo );
+       Mutex_Acquire(&glFDD);
+       for(i=0;i<4;i++) {
+               Time_RemoveTimer(gFDD_Devices[i].timer);
+               FDD_int_StopMotor(i);
+       }
+       Mutex_Release(&glFDD);
+       //IRQ_Clear(6);
+}
+
+/**
+ * \fn char *FDD_ReadDir(tVFS_Node *Node, int pos)
+ * \brief Read Directory
+ */
+char *FDD_ReadDir(tVFS_Node *UNUSED(Node), int Pos)
+{
+       char    name[2] = "0\0";
+
+       if(Pos >= 2 || Pos < 0) return NULL;
+       
+       if(gFDD_Devices[Pos].type == 0) return VFS_SKIP;
+       
+       name[0] += Pos;
+       
+       return strdup(name);
+}
+
+/**
+ * \fn tVFS_Node *FDD_FindDir(tVFS_Node *Node, const char *filename);
+ * \brief Find File Routine (for vfs_node)
+ */
+tVFS_Node *FDD_FindDir(tVFS_Node *UNUSED(Node), const char *Filename)
+{
+        int    i;
+       
+       ENTER("sFilename", Filename);
+       
+       // Sanity check string
+       if(Filename == NULL) {
+               LEAVE('n');
+               return NULL;
+       }
+       
+       // Check string length (should be 1)
+       if(Filename[0] == '\0' || Filename[1] != '\0') {
+               LEAVE('n');
+               return NULL;
+       }
+       
+       // Get First character
+       i = Filename[0] - '0';
+       
+       // Check for 1st disk and if it is present return
+       if(i == 0 && gFDD_Devices[0].type != 0) {
+               LEAVE('p', &gFDD_Devices[0].Node);
+               return &gFDD_Devices[0].Node;
+       }
+       
+       // Check for 2nd disk and if it is present return
+       if(i == 1 && gFDD_Devices[1].type != 0) {
+               LEAVE('p', &gFDD_Devices[1].Node);
+               return &gFDD_Devices[1].Node;
+       }
+       
+       // Else return null
+       LEAVE('n');
+       return NULL;
+}
+
+static const char      *casIOCTLS[] = {DRV_IOCTLNAMES,DRV_DISK_IOCTLNAMES,NULL};
+/**
+ * \fn int FDD_IOCtl(tVFS_Node *Node, int id, void *data)
+ * \brief Stub ioctl function
+ */
+int FDD_IOCtl(tVFS_Node *UNUSED(Node), int ID, void *Data)
+{
+       switch(ID)
+       {
+       BASE_IOCTLS(DRV_TYPE_DISK, "FDD", FDD_VERSION, casIOCTLS);
+       
+       case DISK_IOCTL_GETBLOCKSIZE:   return 512;     
+       
+       default:
+               return 0;
+       }
+}
+
+/**
+ * \fn Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Read Data from a disk
+*/
+Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+        int    ret;
+       
+       ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+       
+       if(Node == NULL) {
+               LEAVE('i', -1);
+               return -1;
+       }
+       
+       if(Node->Inode != 0 && Node->Inode != 1) {
+               LEAVE('i', -1);
+               return -1;
+       }
+       
+       ret = DrvUtil_ReadBlock(Offset, Length, Buffer, FDD_ReadSectors, 512, Node->Inode);
+       LEAVE('i', ret);
+       return ret;
+}
+
+/**
+ * \brief Reads \a Count contiguous sectors from a disk
+ * \param SectorAddr   Address of the first sector
+ * \param Count        Number of sectors to read
+ * \param Buffer       Destination Buffer
+ * \param Disk Disk Number
+ * \return Number of sectors read
+ * \note Used as a ::DrvUtil_ReadBlock helper
+ */
+Uint FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk)
+{
+       Uint    ret = 0;
+       while(Count --)
+       {
+               if( FDD_ReadSector(Disk, SectorAddr, Buffer) != 1 )
+                       return ret;
+               
+               Buffer = (void*)( (tVAddr)Buffer + 512 );
+               SectorAddr ++;
+               ret ++;
+       }
+       return ret;
+}
+
+int FDD_int_ReadWriteSector(Uint32 Disk, Uint64 SectorAddr, int Write, void *Buffer)
+{
+        int    cyl, head, sec;
+        int    spt, base;
+        int    i;
+        int    lba = SectorAddr;
+       Uint8   st0=0, st1=0, st2=0, bps=0;     // Status Values
+       
+       ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer);
+       
+       base = cPORTBASE[Disk >> 1];
+       
+       LOG("Calculating Disk Dimensions");
+       // Get CHS position
+       if(FDD_int_GetDims(gFDD_Devices[Disk].type, lba, &cyl, &head, &sec, &spt) != 1)
+       {
+               LEAVE('i', -1);
+               return -1;
+       }
+       LOG("Cyl=%i, Head=%i, Sector=%i", cyl, head, sec);
+
+       // Start the motor      
+       Mutex_Acquire(&glFDD);  // Lock to stop the motor stopping on us
+       Time_RemoveTimer(gFDD_Devices[Disk].timer);     // Remove Old Timer
+       // Start motor if needed
+       if(gFDD_Devices[Disk].motorState != 2)  FDD_int_StartMotor(Disk);
+       Mutex_Release(&glFDD);
+       
+       // Wait for spinup
+       LOG("Wait for the motor to spin up");
+       while(gFDD_Devices[Disk].motorState == 1)       Threads_Yield();
+       
+       LOG("Acquire Spinlock");
+       Mutex_Acquire(&glFDD);
+       
+       // Read Data from DMA
+       LOG("Setting DMA for read");
+       DMA_SetChannel(2, 512, !Write); // Read/Write 512 Bytes from channel 2
+       
+       LOG("Sending command");
+       
+       #define SENDB(__data)   if(FDD_int_SendByte(base, __data)) { FDD_Reset(Disk >> 1); continue; }
+
+       for( i = 0; i < FDD_MAX_READWRITE_ATTEMPTS; i ++ )
+       {
+               FDD_int_SeekTrack(Disk, head, cyl);
+               if( Write ) {
+                       SENDB(CMD_WRITE_DATA|CMD_FLAG_MFM_ENCODING);
+               }
+               else {
+                       SENDB(CMD_READ_DATA|CMD_FLAG_MFM_ENCODING);
+               }
+               SENDB( (head << 2) | (Disk&1) );
+               SENDB(cyl & 0xFF);
+               SENDB(head & 0xFF);
+               SENDB(sec & 0xFF);
+               SENDB(0x02);    // Bytes Per Sector (Real BPS=128*2^{val})
+               SENDB(spt);     // SPT
+               SENDB(0x1B);    // Gap Length (27 is default)
+               SENDB(0xFF);    // Data Length
+               
+               // Wait for IRQ
+               if( Write ) {
+                       LOG("Writing Data");
+                       DMA_WriteData(2, 512, Buffer);
+                       LOG("Waiting for Data to be written");
+                       if( FDD_WaitIRQ() ) { FDD_Reset(Disk>>1); continue; }
+               }
+               else {
+                       LOG("Waiting for data to be read");
+                       if( FDD_WaitIRQ() ) { FDD_Reset(Disk>>1); continue; }
+                       LOG("Reading Data");
+                       DMA_ReadData(2, 512, Buffer);
+               }
+               
+               // Clear Input Buffer
+               LOG("Clearing Input Buffer");
+               // Status Values
+               FDD_int_GetByte(base, &st0);
+               FDD_int_GetByte(base, &st1);
+               FDD_int_GetByte(base, &st2);
+               
+               // Cylinder, Head and Sector (mutilated in some way)
+               FDD_int_GetByte(base, NULL);    // Cylinder
+               FDD_int_GetByte(base, NULL);    // Head
+               FDD_int_GetByte(base, NULL);    // Sector
+               // Should be the BPS set above (0x02)
+               FDD_int_GetByte(base, &bps);
+               
+               // Check Status
+               // - Error Code
+               if(st0 & 0xC0) {
+                       LOG("Error (st0 & 0xC0) \"%s\"", cFDD_STATUSES[st0 >> 6]);
+                       continue;
+               }
+               // - Status Flags
+               if(st0 & 0x08) {        LOG("Drive not ready"); continue;       }
+               if(st1 & 0x80) {        LOG("End of Cylinder"); continue;       }
+               if(st1 & 0x20) {        LOG("CRC Error");       continue;       }
+               if(st1 & 0x10) {        LOG("Controller Timeout");      continue;       }
+               if(st1 & 0x04) {        LOG("No Data Found");   continue;       }
+               if(st1 & 0x01 || st2 & 0x01) {
+                       LOG("No Address mark found");
+                       continue;
+               }
+               if(st2 & 0x40) {        LOG("Deleted address mark");    continue;       }
+               if(st2 & 0x20) {        LOG("CRC error in data");       continue;       }
+               if(st2 & 0x10) {        LOG("Wrong Cylinder");  continue;       }
+               if(st2 & 0x04) {        LOG("uPD765 sector not found"); continue;       }
+               if(st2 & 0x02) {        LOG("Bad Cylinder");    continue;       }
+               
+               if(bps != 0x2) {
+                       LOG("Returned BPS = 0x%02x, not 0x02", bps);
+                       continue;
+               }
+               
+               if(st1 & 0x02) {
+                       LOG("Floppy not writable");
+                       // Return error without triggering the attempt count check
+                       i = FDD_MAX_READWRITE_ATTEMPTS+1;
+                       break;
+               }
+               
+               // Success!
+               break;
+       }
+       #undef SENDB
+       
+       // Release Spinlock
+       LOG("Realeasing Spinlock and setting motor to stop");
+       Mutex_Release(&glFDD);
+       
+       if(i == FDD_MAX_READWRITE_ATTEMPTS) {
+               Log_Warning("FDD", "Exceeded %i attempts in %s the disk",
+                       FDD_MAX_READWRITE_ATTEMPTS,
+                       (Write ? "writing to" : "reading from")
+                       );
+       }
+       
+       // Don't turn the motor off now, wait for a while
+       FDD_int_StopMotor(Disk);
+
+       // Error check
+       if( i < FDD_MAX_READWRITE_ATTEMPTS ) {
+               LEAVE('i', 0);
+               return 0;
+       }
+       else {
+               LEAVE('i', 1);
+               return 1;
+       }
+}
+
+/**
+ * \fn int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer)
+ * \brief Read a sector from disk
+ * \todo Make real-hardware safe (account for read errors)
+ */
+int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer)
+{
+        int    ret;
+       
+       ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer);
+       
+       if( IOCache_Read( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer ) == 1 ) {
+               LEAVE('i', 1);
+               return 1;
+       }
+       
+       // Pass to general function
        ret = FDD_int_ReadWriteSector(Disk, SectorAddr, 0, Buffer);
-\r
-       if( ret == 0 ) {\r
+
+       if( ret == 0 ) {
                IOCache_Add( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer );
-               LEAVE('i', 1);\r
-               return 1;\r
-       }\r
-       else {\r
-               LOG("Reading failed");\r
-               LEAVE('i', 0);\r
-               return 0;\r
-       }\r
-}\r
-\r
-/**\r
- * \fn int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer)\r
- * \brief Write a sector to the floppy disk\r
- * \note Not Implemented\r
- */\r
-int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer)\r
-{\r
-       Warning("[FDD  ] Read Only at the moment");\r
-       return -1;\r
-}\r
-\r
-/**\r
- * \fn int FDD_int_SeekTrack(int disk, int track)\r
- * \brief Seek disk to selected track\r
- */\r
-int FDD_int_SeekTrack(int disk, int head, int track)\r
-{\r
-       Uint8   sr0, cyl;\r
-        int    base;\r
-       \r
-       base = cPORTBASE[disk>>1];\r
-       \r
-       // Check if seeking is needed\r
-       if(gFDD_Devices[disk].track[head] == track)\r
-               return 1;\r
-       \r
-       // - Seek Head 0\r
-       FDD_int_SendByte(base, SEEK_TRACK);\r
-       FDD_int_SendByte(base, (head<<2)|(disk&1));\r
-       FDD_int_SendByte(base, track);  // Send Seek command\r
-       FDD_WaitIRQ();\r
-       FDD_SensInt(base, &sr0, &cyl);  // Wait for IRQ\r
-       if((sr0 & 0xF0) != 0x20) {
-               LOG("sr0 = 0x%x", sr0);
-               return 0;       //Check Status
-       }\r
-       if(cyl != track)        return 0;\r
-       \r
-       // Set Track in structure\r
-       gFDD_Devices[disk].track[head] = track;\r
-       return 1;\r
-}\r
-\r
-/**\r
- * \fn int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt)\r
- * \brief Get Dimensions of a disk\r
- */\r
-int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt)\r
-{\r
-       switch(type) {\r
-       case 0:\r
-               return 0;\r
-       \r
-       // 360Kb 5.25"\r
-       case 1:\r
-               *spt = 9;\r
-               *s = (lba % 9) + 1;\r
-               *c = lba / 18;\r
-               *h = (lba / 9) & 1;\r
-               break;\r
-       \r
-       // 1220Kb 5.25"\r
-       case 2:\r
-               *spt = 15;\r
-               *s = (lba % 15) + 1;\r
-               *c = lba / 30;\r
-               *h = (lba / 15) & 1;\r
-               break;\r
-       \r
-       // 720Kb 3.5"\r
-       case 3:\r
-               *spt = 9;\r
-               *s = (lba % 9) + 1;\r
-               *c = lba / 18;\r
-               *h = (lba / 9) & 1;\r
-               break;\r
-       \r
-       // 1440Kb 3.5"\r
-       case 4:\r
-               *spt = 18;\r
-               *s = (lba % 18) + 1;\r
-               *c = lba / 36;\r
-               *h = (lba / 18) & 1;\r
-               //Log("1440k - lba=%i(0x%x), *s=%i,*c=%i,*h=%i", lba, lba, *s, *c, *h);\r
-               break;\r
-               \r
-       // 2880Kb 3.5"\r
-       case 5:\r
-               *spt = 36;\r
-               *s = (lba % 36) + 1;\r
-               *c = lba / 72;\r
-               *h = (lba / 32) & 1;\r
-               break;\r
-               \r
-       default:\r
-               return -2;\r
-       }\r
-       return 1;\r
-}\r
-\r
-/**\r
- * \fn void FDD_IRQHandler(int Num)\r
- * \brief Handles IRQ6\r
- */\r
-void FDD_IRQHandler(int Num)\r
-{\r
-    gbFDD_IrqFired = 1;\r
-}\r
-\r
-/**\r
- * \fn FDD_WaitIRQ()\r
- * \brief Wait for an IRQ6\r
- */\r
-inline void FDD_WaitIRQ()\r
-{\r
-       // Wait for IRQ\r
-       while(!gbFDD_IrqFired)  Threads_Yield();\r
-       gbFDD_IrqFired = 0;\r
-}\r
-\r
-void FDD_SensInt(int base, Uint8 *sr0, Uint8 *cyl)\r
-{\r
-       FDD_int_SendByte(base, CHECK_INTERRUPT_STATUS);\r
-       if(sr0) *sr0 = FDD_int_GetByte(base);\r
-       else    FDD_int_GetByte(base);\r
-       if(cyl) *cyl = FDD_int_GetByte(base);\r
-       else    FDD_int_GetByte(base);\r
-}\r
-\r
-/**\r
- * void FDD_int_SendByte(int base, char byte)\r
- * \brief Sends a command to the controller\r
- */\r
-void FDD_int_SendByte(int base, char byte)\r
-{\r
-    volatile int state;\r
-    int timeout = 128;\r
-    for( ; timeout--; )\r
-    {\r
-        state = inb(base + PORT_MAINSTATUS);\r
-        if ((state & 0xC0) == 0x80)\r
-        {\r
-            outb(base + PORT_DATA, byte);\r
-            return;\r
-        }\r
-        inb(0x80);     //Delay\r
-    }\r
-       \r
-       #if WARN\r
-       Warning("FDD_int_SendByte - Timeout sending byte 0x%x to base 0x%x\n", byte, base);\r
-       #endif\r
-}\r
-\r
-/**\r
- * int FDD_int_GetByte(int base, char byte)\r
- * \brief Receive data from fdd controller\r
- */\r
-int FDD_int_GetByte(int base)\r
-{\r
-    volatile int state;\r
-    int timeout;\r
-    for( timeout = 128; timeout--; )\r
-    {\r
-        state = inb((base + PORT_MAINSTATUS));\r
-        if ((state & 0xd0) == 0xd0)\r
-               return inb(base + PORT_DATA);\r
-        inb(0x80);\r
-    }\r
-    return -1;\r
-}\r
-\r
-/**\r
- * \brief Recalibrate the specified disk\r
- */\r
-void FDD_Recalibrate(int disk)\r
-{\r
-       ENTER("idisk", disk);\r
-       \r
-       LOG("Starting Motor");\r
-       FDD_int_StartMotor(disk);\r
-       // Wait for Spinup\r
-       while(gFDD_Devices[disk].motorState == 1)       Threads_Yield();\r
-       \r
-       LOG("Sending Calibrate Command");\r
-       FDD_int_SendByte(cPORTBASE[disk>>1], CALIBRATE_DRIVE);\r
-       FDD_int_SendByte(cPORTBASE[disk>>1], disk&1);\r
-       \r
-       LOG("Waiting for IRQ");\r
-       FDD_WaitIRQ();\r
-       FDD_SensInt(cPORTBASE[disk>>1], NULL, NULL);\r
-       \r
-       LOG("Stopping Motor");\r
-       FDD_int_StopMotor(disk);\r
-       LEAVE('-');\r
-}\r
-\r
-/**\r
- * \brief Reset the specified FDD controller\r
- */\r
-void FDD_Reset(int id)\r
-{\r
-       int base = cPORTBASE[id];\r
-       \r
-       ENTER("iID", id);\r
-       \r
-       outb(base + PORT_DIGOUTPUT, 0); // Stop Motors & Disable FDC\r
-       outb(base + PORT_DIGOUTPUT, 0x0C);      // Re-enable FDC (DMA and Enable)\r
-       \r
-       LOG("Awaiting IRQ");\r
-       \r
-       FDD_WaitIRQ();\r
-       FDD_SensInt(base, NULL, NULL);\r
-       \r
-       LOG("Setting Driver Info");\r
-       outb(base + PORT_DATARATE, 0);  // Set data rate to 500K/s\r
-       FDD_int_SendByte(base, FIX_DRIVE_DATA); // Step and Head Load Times\r
-       FDD_int_SendByte(base, 0xDF);   // Step Rate Time, Head Unload Time (Nibble each)\r
-       FDD_int_SendByte(base, 0x02);   // Head Load Time >> 1\r
-       while(FDD_int_SeekTrack(0, 0, 1) == 0); // set track\r
-       while(FDD_int_SeekTrack(0, 1, 1) == 0); // set track\r
-       \r
-       LOG("Recalibrating Disk");\r
-       FDD_Recalibrate((id<<1)|0);\r
+               LEAVE('i', 1);
+               return 1;
+       }
+       else {
+               LOG("Reading failed");
+               LEAVE('i', 0);
+               return 0;
+       }
+}
+
+/**
+ * \fn int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer)
+ * \brief Write a sector to the floppy disk
+ * \note Not Implemented
+ */
+int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer)
+{
+       Log_Warning("FDD", "Read Only at the moment");
+       return -1;
+}
+
+/**
+ * \brief Seek disk to selected track
+ */
+int FDD_int_SeekTrack(int disk, int head, int track)
+{
+       Uint8   sr0=0, cyl=0;
+        int    base, i, bUnclean;
+       
+       base = cPORTBASE[disk>>1];
+       
+       // Check if seeking is needed
+       if(gFDD_Devices[disk].track[head] == track)
+               return 1;
+       
+       // - Seek Head 0
+       for( i = 0; i < FDD_MAX_READWRITE_ATTEMPTS; i ++ )
+       {
+               if(i && bUnclean)       FDD_Reset(disk >> 1);
+               bUnclean = 1;
+               if(FDD_int_SendByte(base, CMD_SEEK_TRACK))      continue;
+               if(FDD_int_SendByte(base, (head<<2)|(disk&1)))  continue;
+               if(FDD_int_SendByte(base, track))       continue;
+               FDD_WaitIRQ();
+               FDD_SenseInt(base, &sr0, &cyl); // Wait for IRQ
+
+               bUnclean = 0;
+               if( cyl != track )
+                       continue;       // Try again
+               if( (sr0 & 0xF0) != 0x20 ) {
+                       LOG("sr0 = 0x%x", sr0);
+                       continue ;
+               }
+       
+               break;
+       }
+
+       if( i == FDD_MAX_READWRITE_ATTEMPTS ) {
+               Log_Warning("FDD", "Unable to seek to track %i on disk %i",
+                       track, disk);
+               return 0;
+       }       
+
+       // Set Track in structure
+       gFDD_Devices[disk].track[head] = track;
+
+       LOG("Time_Delay(100)"); 
+       // Wait for Head to settle
+       Time_Delay(100);
+       
+       return 1;
+}
+
+/**
+ * \fn int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt)
+ * \brief Get Dimensions of a disk
+ */
+int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt)
+{
+       switch(type) {
+       case 0:
+               return 0;
+       
+       // 360Kb 5.25"
+       case 1:
+               *spt = 9;
+               *s = (lba % 9) + 1;
+               *c = lba / 18;
+               *h = (lba / 9) & 1;
+               break;
+       
+       // 1220Kb 5.25"
+       case 2:
+               *spt = 15;
+               *s = (lba % 15) + 1;
+               *c = lba / 30;
+               *h = (lba / 15) & 1;
+               break;
+       
+       // 720Kb 3.5"
+       case 3:
+               *spt = 9;
+               *s = (lba % 9) + 1;
+               *c = lba / 18;
+               *h = (lba / 9) & 1;
+               break;
+       
+       // 1440Kb 3.5"
+       case 4:
+               *spt = 18;
+               *s = (lba % 18) + 1;
+               *c = lba / 36;
+               *h = (lba / 18) & 1;
+               //Log("1440k - lba=%i(0x%x), *s=%i,*c=%i,*h=%i", lba, lba, *s, *c, *h);
+               break;
+               
+       // 2880Kb 3.5"
+       case 5:
+               *spt = 36;
+               *s = (lba % 36) + 1;
+               *c = lba / 72;
+               *h = (lba / 32) & 1;
+               break;
+               
+       default:
+               return -2;
+       }
+       return 1;
+}
+
+/**
+ * \fn void FDD_IRQHandler(int Num)
+ * \brief Handles IRQ6
+ */
+void FDD_IRQHandler(int Num, void *Ptr)
+{
+       gbFDD_IrqFired = 1;
+}
+
+/**
+ * \brief Wait for the FDD IRQ to fire
+ * \return Boolean failure (1 for timeout)
+ */
+inline int FDD_WaitIRQ()
+{
+       tTime   end = now() + 2000;
+       
+       // Wait for IRQ
+       while(!gbFDD_IrqFired && now() < end)
+               Threads_Yield();
+
+       if( !gbFDD_IrqFired ) {
+               Log_Warning("FDD", "FDD_WaitIRQ - Timeout");
+               return 1;
+       }       
+
+       gbFDD_IrqFired = 0;
+       return 0;
+}
+
+void FDD_SenseInt(int base, Uint8 *sr0, Uint8 *cyl)
+{
+       FDD_int_SendByte(base, CMD_SENSE_INTERRUPT);
+       FDD_int_GetByte(base, sr0);
+       FDD_int_GetByte(base, cyl);
+}
+
+/**
+ * void FDD_int_SendByte(int base, char byte)
+ * \brief Sends a command to the controller
+ */
+int FDD_int_SendByte(int base, Uint8 byte)
+{
+       tTime   end = now() + 1000;     // 1s
+       
+       while( (inb(base + PORT_MAINSTATUS) & 0x80) != 0x80 && now() < end )
+               Threads_Yield();        //Delay
+//             Time_Delay(10); //Delay
+
+       if( inb(base + PORT_MAINSTATUS) & 0x40 ) {
+               Log_Warning("FDD", "FDD_int_SendByte: DIO set, is this ok?");
+               return -2;
+       }
+       
+       if( now() > end )
+       {
+               Log_Warning("FDD", "FDD_int_SendByte: Timeout sending byte 0x%x to base 0x%x", byte, base);
+               return 1;
+       }
+       outb(base + PORT_DATA, byte);
+//     Log_Debug("FDD", "FDD_int_SendByte: Sent 0x%02x to 0x%x", byte, base);
+       return 0;
+}
+
+/**
+ * int FDD_int_GetByte(int base, char byte)
+ * \brief Receive data from fdd controller
+ */
+int FDD_int_GetByte(int base, Uint8 *value)
+{
+       tTime   end = now() + 1000;     // 1s
+       
+       while( (inb(base + PORT_MAINSTATUS) & 0x80) != 0x80 && now() < end )
+               Time_Delay(10);
+       
+       if( !(inb(base + PORT_MAINSTATUS) & 0x40) ) {
+               Log_Warning("FDD", "FDD_int_GetByte: DIO unset, is this ok?");
+               return -2;
+       }
+
+       if( now() > end )
+       {
+               Log_Warning("FDD", "FDD_int_GetByte: Timeout reading byte from base 0x%x", base);
+               return -1;
+       }
+       
+       if(value)
+               *value = inb(base + PORT_DATA);
+       else
+               inb(base + PORT_DATA);
+       return 0;
+}
+
+/**
+ * \brief Recalibrate the specified disk
+ */
+void FDD_Recalibrate(int disk)
+{
+       ENTER("idisk", disk);
+       
+       LOG("Starting Motor");
+       FDD_int_StartMotor(disk);
+       // Wait for Spinup
+       while(gFDD_Devices[disk].motorState <= 1)       Threads_Yield();
+       
+       LOG("Sending Calibrate Command");
+       FDD_int_SendByte(cPORTBASE[disk>>1], CMD_RECALIBRATE);
+       FDD_int_SendByte(cPORTBASE[disk>>1], disk&1);
+       
+       LOG("Waiting for IRQ");
+       FDD_WaitIRQ();
+       FDD_SenseInt(cPORTBASE[disk>>1], NULL, NULL);
+       
+       LOG("Stopping Motor");
+       FDD_int_StopMotor(disk);
+       LEAVE('-');
+}
+
+/**
+ * \brief Reconfigure the controller
+ */
+int FDD_Reconfigure(int ID)
+{
+       Uint16  base = cPORTBASE[ID];
+       
+       ENTER("iID", ID);
+       
+       FDD_int_SendByte(base, CMD_CONFIGURE);
+       FDD_int_SendByte(base, 0);
+       // Implied seek enabled, FIFO Enabled, Drive Polling Disabled, data buffer threshold 8 bytes
+       FDD_int_SendByte(base, (1 << 6) | (0 << 5) | (0 << 4) | 7);
+       FDD_int_SendByte(base, 0);      // Precompensation - use default
+       
+       // Commit
+       FDD_int_SendByte(base, CMD_LOCK|CMD_FLAG_MULTI_TRACK);
+       FDD_int_GetByte(base, NULL);
+       
+       LEAVE('i', 0);
+       return 0;
+}
+
+/**
+ * \brief Reset the specified FDD controller
+ */
+int FDD_Reset(int id)
+{
+       Uint16  base = cPORTBASE[id];
+       Uint8   motor_state;
+       
+       ENTER("iID", id);
+
+       // Reset the card
+       motor_state = inb(base + PORT_DIGOUTPUT) & 0xF0;
+       outb(base + PORT_DIGOUTPUT, motor_state|0);     // Disable FDC
+       Time_Delay(1);
+       outb(base + PORT_DIGOUTPUT, motor_state|8|4);   // Re-enable FDC (DMA and Enable)
+       
+       // Set the data rate
+       outb(base + PORT_DATARATE, 0);  // Set data rate to 500K/s
+
+       // Wait for IRQ
+       LOG("Awaiting IRQ");
+       
+       FDD_WaitIRQ();
+
+       FDD_SenseInt(base, NULL, NULL);
+       
+       // Specify
+       FDD_int_SendByte(base, CMD_SPECIFY);    // Step and Head Load Times
+       FDD_int_SendByte(base, 0xDF);   // Step Rate Time, Head Unload Time (Nibble each)
+       FDD_int_SendByte(base, 0x02);   // Head Load Time >> 1
+
+       LOG("Recalibrating Disk");
+       FDD_Recalibrate((id<<1)|0);
        FDD_Recalibrate((id<<1)|1);
-\r
-       LEAVE('-');\r
-}\r
-\r
-/**\r
- * \fn void FDD_int_TimerCallback()\r
- * \brief Called by timer\r
- */\r
-void FDD_int_TimerCallback(int arg)\r
-{\r
-       ENTER("iarg", arg);\r
-       if(gFDD_Devices[arg].motorState == 1)\r
-               gFDD_Devices[arg].motorState = 2;\r
-       Time_RemoveTimer(gFDD_Devices[arg].timer);\r
-       gFDD_Devices[arg].timer = -1;\r
-       LEAVE('-');\r
-}\r
-\r
-/**\r
- * \fn void FDD_int_StartMotor(char disk)\r
- * \brief Starts FDD Motor\r
- */\r
-void FDD_int_StartMotor(int disk)\r
-{\r
-       Uint8   state;\r
-       state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );\r
-       state |= 1 << (4+disk);\r
-       outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );\r
-       gFDD_Devices[disk].motorState = 1;\r
-       gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_ON_DELAY, FDD_int_TimerCallback, (void*)disk);\r
-}\r
-\r
-/**\r
- * \fn void FDD_int_StopMotor(int disk)\r
- * \brief Stops FDD Motor\r
- */\r
-void FDD_int_StopMotor(int disk)\r
-{\r
-       Uint8   state;\r
-       if( IS_LOCKED(&glFDD) ) return ;\r
-       ENTER("iDisk", disk);\r
-       \r
-       state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );\r
-       state &= ~( 1 << (4+disk) );\r
-       outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );\r
-    gFDD_Devices[disk].motorState = 0;\r
-    LEAVE('-');\r
-}\r
+
+       LEAVE_RET('i', 0);
+}
+
+/**
+ * \fn void FDD_int_TimerCallback()
+ * \brief Called by timer
+ */
+void FDD_int_TimerCallback(void *Arg)
+{
+        int    disk = (Uint)Arg;
+       ENTER("iarg", disk);
+       if(gFDD_Devices[disk].motorState == 1)
+               gFDD_Devices[disk].motorState = 2;
+       Time_RemoveTimer(gFDD_Devices[disk].timer);
+       gFDD_Devices[disk].timer = -1;
+       LEAVE('-');
+}
+
+/**
+ * \fn void FDD_int_StartMotor(char disk)
+ * \brief Starts FDD Motor
+ */
+void FDD_int_StartMotor(int disk)
+{
+       Uint8   state;
+       if( gFDD_Devices[disk].motorState != 0 )        return ;
+       // Set motor ON bit
+       state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );
+       state |= 1 << (4+disk);
+       outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );
+       // Mark as spinning up
+       gFDD_Devices[disk].motorState = 1;
+       // Schedule a timer for when it's up to speed
+       gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_ON_DELAY, FDD_int_TimerCallback, (void*)(Uint)disk);
+}
+
+/**
+ * \brief Schedule the drive motor to stop
+ * \param Disk Disk number to stop
+ */
+void FDD_int_StopMotor(int Disk)
+{
+       // Ignore if the motor is aready off
+       if( gFDD_Devices[Disk].motorState == 0 )        return ;
+       
+       // Don't double-schedule timer
+       if( gFDD_Devices[Disk].timer != -1 )
+       {
+               Time_RemoveTimer( gFDD_Devices[Disk].timer );
+       }
+       
+       gFDD_Devices[Disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotorCallback, (void*)(Uint)Disk);
+}
+
+/**
+ * \brief Stops FDD Motor
+ */
+void FDD_int_StopMotorCallback(void *Arg)
+{
+       Uint8   state, disk = (Uint)Arg;
+
+       // Mutex is only locked if disk is in use
+       if( Mutex_IsLocked(&glFDD) )    return ;
+
+       ENTER("iDisk", disk);
+
+       // Clear motor on bit   
+       state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );
+       state &= ~( 1 << (4+disk) );
+       outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );
+       
+       // Mark as off
+       gFDD_Devices[disk].motorState = 0;
+
+       LEAVE('-');
+}
+

UCC git Repository :: git.ucc.asn.au