3 * Floppy Disk Access Code
\r
8 #include <fs_devfs.h>
\r
9 #include <tpl_drv_common.h>
\r
14 // Version Information
\r
15 #define FDD_VER_MAJ 0
\r
16 #define FDD_VER_MIN 75
\r
18 #define USE_CACHE 1 // Use Sector Cache
\r
19 #define CACHE_SIZE 32 // Number of cachable sectors
\r
20 #define FDD_SEEK_TIMEOUT 10 // Timeout for a seek operation
\r
21 #define MOTOR_ON_DELAY 500 // Miliseconds
\r
22 #define MOTOR_OFF_DELAY 2000 // Miliseconds
\r
27 volatile int motorState; //2 - On, 1 - Spinup, 0 - Off
\r
37 Uint16 sector; // Allows 32Mb of addressable space (Plenty for FDD)
\r
41 // === CONSTANTS ===
\r
42 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
43 static const int cFDD_SIZES[] = { 0, 360*1024, 1200*1024, 720*1024, 1440*1024, 2880*1024 };
\r
44 static const short cPORTBASE[] = {0x3F0, 0x370 };
\r
49 PORT_DIGOUTPUT = 0x2,
\r
50 PORT_MAINSTATUS = 0x4,
\r
51 PORT_DATARATE = 0x4,
\r
53 PORT_DIGINPUT = 0x7,
\r
54 PORT_CONFIGCTRL = 0x7
\r
57 enum FloppyCommands {
\r
58 FIX_DRIVE_DATA = 0x03,
\r
59 HECK_DRIVE_STATUS = 0x04,
\r
60 CALIBRATE_DRIVE = 0x07,
\r
61 CHECK_INTERRUPT_STATUS = 0x08,
\r
63 READ_SECTOR_ID = 0x4A,
\r
64 FORMAT_TRACK = 0x4D,
\r
67 WRITE_SECTOR = 0xC5,
\r
68 WRITE_DELETE_SECTOR = 0xC9,
\r
69 READ_DELETE_SECTOR = 0xCC,
\r
72 // === PROTOTYPES ===
\r
73 char *FDD_ReadDir(tVFS_Node *Node, int pos);
\r
74 tVFS_Node *FDD_FindDir(tVFS_Node *dirNode, char *Name);
\r
75 static int fdd_readSector(int disk, int lba, void *buf);
\r
77 void FDD_SensInt(int base, Uint8 *sr0, Uint8 *cyl);
\r
78 static void FDD_AquireSpinlock();
\r
79 static void inline FDD_FreeSpinlock();
\r
81 static inline void FDD_AquireCacheSpinlock();
\r
82 static inline void FDD_FreeCacheSpinlock();
\r
84 static void sendbyte(int base, char byte);
\r
85 static int getbyte(int base);
\r
86 static int seekTrack(int disk, int head, int track);
\r
87 static void stop_motor(int disk);
\r
88 static void start_motor(int disk);
\r
89 static int get_dims(int type, int lba, int *c, int *h, int *s, int *spt);
\r
90 int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data);
\r
91 Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
\r
92 int FDD_Install(char **Arguments);
\r
95 //MODULE_DEFINE(0, 0x004B, FDD, FDD_Install, NULL, NULL);
\r
96 static t_floppyDevice fdd_devices[2];
\r
97 static volatile int fdd_inUse = 0;
\r
98 static volatile int fdd_irq6 = 0;
\r
99 tDevFS_Driver gFDD_DriverInfo = {
\r
104 .ACLs = &gVFS_ACL_EveryoneRX,
\r
105 .Flags = VFS_FFLAG_DIRECTORY,
\r
106 .ReadDir = FDD_ReadDir,
\r
107 .FindDir = FDD_FindDir,
\r
112 static int siFDD_CacheInUse = 0;
\r
113 static int siFDD_SectorCacheSize = CACHE_SIZE;
\r
114 static t_floppySector sFDD_SectorCache[CACHE_SIZE];
\r
119 * \fn char *FDD_ReadDir(tVFS_Node *Node, int pos)
\r
120 * \brief Read Directory
\r
122 char *FDD_ReadDir(tVFS_Node *Node, int pos)
\r
124 //Update Accessed Time
\r
125 //gFDD_DrvInfo.rootNode.atime = now();
\r
128 if(pos >= 2 || pos < 0)
\r
131 if(fdd_devices[pos].type == 0)
\r
135 return fdd_devices[pos].Name;
\r
139 * \fn tVFS_Node *FDD_FindDir(tVFS_Node *Node, char *filename);
\r
140 * \brief Find File Routine (for vfs_node)
\r
142 tVFS_Node *FDD_FindDir(tVFS_Node *Node, char *filename)
\r
146 ENTER("sfilename", filename);
\r
148 if(filename == NULL) return NULL;
\r
150 //Check string length (should be 1)
\r
151 if(filename[0] == '\0') return NULL;
\r
152 if(filename[1] != '\0') return NULL;
\r
155 i = filename[0] - '0';
\r
157 // Check for 1st disk and if it is present return
\r
158 if(i == 0 && fdd_devices[0].type != 0)
\r
159 return &fdd_devices[0].Node;
\r
161 // Check for 2nd disk and if it is present return
\r
162 if(i == 1 && fdd_devices[1].type != 0)
\r
163 return &fdd_devices[1].Node;
\r
165 // Else return null
\r
170 * \fn Uint64 fdd_readFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)
\r
171 * \brief Read Data from a disk
\r
173 Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)
\r
179 ENTER("xoff xlen pbuffer", off, len, buffer)
\r
186 if(node->Inode != 0 && node->Inode != 1) {
\r
191 disk = node->Inode;
\r
193 // Update Accessed Time
\r
194 node->ATime = now();
\r
196 if((off & 0x1FF) || (len & 0x1FF))
\r
198 // Un-Aligned Offset/Length
\r
199 int startOff = off>>9;
\r
200 int sectOff = off&0x1FF;
\r
201 int sectors = (len+0x1FF)>>9;
\r
203 LOG("Non-aligned Read");
\r
205 //Read Starting Sector
\r
206 if(!fdd_readSector(disk, startOff, buf))
\r
208 memcpy(buffer, (char*)(buf+sectOff), len>512-sectOff?512-sectOff:len);
\r
210 //If the data size is one sector or less
\r
211 if(len <= 512-sectOff) {
\r
213 return len; //Return
\r
215 buffer += 512-sectOff;
\r
217 //Read Middle Sectors
\r
218 for(i=1;i<sectors-1;i++) {
\r
219 if(!fdd_readSector(disk, startOff+i, buf)) {
\r
223 memcpy(buffer, buf, 512);
\r
228 if(!fdd_readSector(disk, startOff+i, buf))
\r
230 memcpy(buffer, buf, (len&0x1FF)-sectOff);
\r
237 int count = len >> 9;
\r
238 int sector = off >> 9;
\r
239 LOG("Aligned Read");
\r
240 //Aligned Offset and Length - Simple Code
\r
241 for(i=0;i<count;i++)
\r
243 fdd_readSector(disk, sector, buf);
\r
244 memcpy(buffer, buf, 512);
\r
254 * \fn static int fdd_readSector(int disk, int lba, void *buf)
\r
255 * \fn Read a sector from disk
\r
257 int fdd_readSector(int disk, int lba, void *buf)
\r
259 int cyl, head, sec;
\r
263 ENTER("idisk xlba pbuf", disk, lba, buf);
\r
266 FDD_AquireCacheSpinlock();
\r
267 for(i=0;i<siFDD_SectorCacheSize;i++)
\r
269 if(sFDD_SectorCache[i].timestamp == 0) continue;
\r
270 if(sFDD_SectorCache[i].disk == disk
\r
271 && sFDD_SectorCache[i].sector == lba) {
\r
272 LOG("Found %i in cache %i", lba, i);
\r
273 memcpy(buf, sFDD_SectorCache[i].data, 512);
\r
274 sFDD_SectorCache[i].timestamp = now();
\r
275 FDD_FreeCacheSpinlock();
\r
280 LOG("Read %i from Disk", lba);
\r
281 FDD_FreeCacheSpinlock();
\r
284 base = cPORTBASE[disk>>1];
\r
286 LOG("Calculating Disk Dimensions");
\r
288 if(get_dims(fdd_devices[disk].type, lba, &cyl, &head, &sec, &spt) != 1) {
\r
293 // Remove Old Timer
\r
294 Time_RemoveTimer(fdd_devices[disk].timer);
\r
295 // Check if Motor is on
\r
296 if(fdd_devices[disk].motorState == 0) {
\r
300 LOG("Wait for Motor Spinup");
\r
303 while(fdd_devices[disk].motorState == 1) Threads_Yield();
\r
305 LOG("C:%i,H:%i,S:%i", cyl, head, sec);
\r
306 LOG("Acquire Spinlock");
\r
308 FDD_AquireSpinlock();
\r
311 outb(base+CALIBRATE_DRIVE, 0);
\r
313 while(seekTrack(disk, head, (Uint8)cyl) == 0 && i++ < FDD_SEEK_TIMEOUT ) Threads_Yield();
\r
314 //FDD_SensInt(base, NULL, NULL); // Wait for IRQ
\r
316 LOG("Setting DMA for read");
\r
318 //Read Data from DMA
\r
319 DMA_SetChannel(2, 512, 1); // Read 512 Bytes
\r
321 LOG("Sending read command");
\r
323 //Threads_Wait(100); // Wait for Head to settle
\r
325 sendbyte(base, READ_SECTOR); // Was 0xE6
\r
326 sendbyte(base, (head << 2) | (disk&1));
\r
327 sendbyte(base, (Uint8)cyl);
\r
328 sendbyte(base, (Uint8)head);
\r
329 sendbyte(base, (Uint8)sec);
\r
330 sendbyte(base, 0x02); // Bytes Per Sector (Real BPS=128*2^{val})
\r
331 sendbyte(base, spt); // SPT
\r
332 sendbyte(base, 0x1B); // Gap Length (27 is default)
\r
333 sendbyte(base, 0xFF); // Data Length
\r
336 LOG("Waiting for Data to be read");
\r
339 // Read Data from DMA
\r
340 LOG(" fdd_readSector: Reading Data");
\r
341 DMA_ReadData(2, 512, buf);
\r
343 // Clear Input Buffer
\r
344 LOG("Clearing Input Buffer");
\r
345 getbyte(base); getbyte(base); getbyte(base);
\r
346 getbyte(base); getbyte(base); getbyte(base); getbyte(base);
\r
348 LOG("Realeasing Spinlock and Setting motor to stop");
\r
349 // Release Spinlock
\r
350 FDD_FreeSpinlock();
\r
352 //Set timer to turn off motor affter a gap
\r
353 fdd_devices[disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, stop_motor, (void*)disk); //One Shot Timer
357 FDD_AquireCacheSpinlock();
\r
359 for(i=0;i<siFDD_SectorCacheSize;i++)
\r
361 if(sFDD_SectorCache[i].timestamp == 0) {
\r
365 if(sFDD_SectorCache[i].timestamp < sFDD_SectorCache[oldest].timestamp)
\r
368 sFDD_SectorCache[oldest].timestamp = now();
\r
369 sFDD_SectorCache[oldest].disk = disk;
\r
370 sFDD_SectorCache[oldest].sector = lba;
\r
371 memcpy(sFDD_SectorCache[oldest].data, buf, 512);
\r
372 FDD_FreeCacheSpinlock();
\r
381 * \fn static int seekTrack(int disk, int track)
\r
382 * \brief Seek disk to selected track
\r
384 static int seekTrack(int disk, int head, int track)
\r
389 base = cPORTBASE[disk>>1];
\r
391 // Check if seeking is needed
\r
392 if(fdd_devices[disk].track[head] == track)
\r
396 sendbyte(base, SEEK_TRACK);
\r
397 sendbyte(base, (head<<2)|(disk&1));
\r
398 sendbyte(base, track); // Send Seek command
\r
400 FDD_SensInt(base, &sr0, &cyl); // Wait for IRQ
\r
401 if((sr0 & 0xF0) != 0x20) {
402 LOG("sr0 = 0x%x", sr0);
403 return 0; //Check Status
405 if(cyl != track) return 0;
\r
407 // Set Track in structure
\r
408 fdd_devices[disk].track[head] = track;
\r
413 * \fn static int get_dims(int type, int lba, int *c, int *h, int *s, int *spt)
\r
414 * \brief Get Dimensions of a disk
\r
416 static int get_dims(int type, int lba, int *c, int *h, int *s, int *spt)
\r
425 *s = (lba % 9) + 1;
\r
427 *h = (lba / 9) & 1;
\r
433 *s = (lba % 15) + 1;
\r
435 *h = (lba / 15) & 1;
\r
441 *s = (lba % 9) + 1;
\r
443 *h = (lba / 9) & 1;
\r
449 *s = (lba % 18) + 1;
\r
451 *h = (lba / 18) & 1;
\r
457 *s = (lba % 36) + 1;
\r
459 *h = (lba / 32) & 1;
\r
469 * \fn int FDD_IOCtl(tVFS_Node *node, int id, void *data)
\r
470 * \brief Stub ioctl function
\r
472 int FDD_IOCtl(tVFS_Node *node, int id, void *data)
\r
476 case DRV_IOCTL_TYPE: return DRV_TYPE_DISK;
\r
477 case DRV_IOCTL_IDENT: memcpy(data, "FDD\0", 4); return 1;
\r
478 case DRV_IOCTL_VERSION: return (FDD_VER_MAJ<<8)|FDD_VER_MIN;
\r
484 * \fn void fdd_handler(void)
\r
485 * \brief Handles IRQ6
\r
487 void fdd_handler(void)
\r
493 * \fn FDD_WaitIRQ()
\r
494 * \brief Wait for an IRQ6
\r
499 while(!fdd_irq6) Threads_Yield();
\r
503 void FDD_SensInt(int base, Uint8 *sr0, Uint8 *cyl)
\r
505 sendbyte(base, CHECK_INTERRUPT_STATUS);
\r
506 if(sr0) *sr0 = getbyte(base);
\r
507 else getbyte(base);
\r
508 if(cyl) *cyl = getbyte(base);
\r
509 else getbyte(base);
\r
512 void FDD_AquireSpinlock()
\r
519 inline void FDD_FreeSpinlock()
\r
525 inline void FDD_AquireCacheSpinlock()
\r
527 while(siFDD_CacheInUse) Threads_Yield();
\r
528 siFDD_CacheInUse = 1;
\r
530 inline void FDD_FreeCacheSpinlock()
\r
532 siFDD_CacheInUse = 0;
\r
537 * void sendbyte(int base, char byte)
\r
538 * \brief Sends a command to the controller
\r
540 static void sendbyte(int base, char byte)
\r
542 volatile int state;
\r
544 for( ; timeout--; )
\r
546 state = inb(base + PORT_MAINSTATUS);
\r
547 if ((state & 0xC0) == 0x80)
\r
549 outb(base + PORT_DATA, byte);
\r
555 Warning("FDD_SendByte - Timeout sending byte 0x%x to base 0x%x\n", byte, base);
\r
560 * int getbyte(int base, char byte)
\r
561 * \brief Receive data from fdd controller
\r
563 static int getbyte(int base)
\r
565 volatile int state;
\r
567 for( timeout = 128; timeout--; )
\r
569 state = inb((base + PORT_MAINSTATUS));
\r
570 if ((state & 0xd0) == 0xd0)
\r
571 return inb(base + PORT_DATA);
\r
577 void FDD_Recalibrate(int disk)
\r
579 ENTER("idisk", disk);
\r
581 LOG("Starting Motor");
\r
584 while(fdd_devices[disk].motorState == 1) Threads_Yield();
\r
586 LOG("Sending Calibrate Command");
\r
587 sendbyte(cPORTBASE[disk>>1], CALIBRATE_DRIVE);
\r
588 sendbyte(cPORTBASE[disk>>1], disk&1);
\r
590 LOG("Waiting for IRQ");
\r
592 FDD_SensInt(cPORTBASE[disk>>1], NULL, NULL);
\r
594 LOG("Stopping Motor");
\r
599 void FDD_Reset(int id)
\r
601 int base = cPORTBASE[id];
\r
605 outb(base + PORT_DIGOUTPUT, 0); // Stop Motors & Disable FDC
\r
606 outb(base + PORT_DIGOUTPUT, 0x0C); // Re-enable FDC (DMA and Enable)
\r
608 LOG("Awaiting IRQ");
\r
611 FDD_SensInt(base, NULL, NULL);
\r
613 LOG("Setting Driver Info");
\r
614 outb(base + PORT_DATARATE, 0); // Set data rate to 500K/s
\r
615 sendbyte(base, FIX_DRIVE_DATA); // Step and Head Load Times
\r
616 sendbyte(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each)
\r
617 sendbyte(base, 0x02); // Head Load Time >> 1
\r
618 while(seekTrack(0, 0, 1) == 0); // set track
\r
619 while(seekTrack(0, 1, 1) == 0); // set track
\r
621 LOG("Recalibrating Disk");
\r
622 FDD_Recalibrate((id<<1)|0);
\r
623 FDD_Recalibrate((id<<1)|1);
629 * \fn void fdd_timer()
\r
630 * \brief Called by timer
\r
632 static void fdd_timer(int arg)
\r
634 ENTER("iarg", arg);
\r
635 if(fdd_devices[arg].motorState == 1)
\r
636 fdd_devices[arg].motorState = 2;
\r
637 Time_RemoveTimer(fdd_devices[arg].timer);
\r
638 fdd_devices[arg].timer = -1;
\r
643 * \fn void start_motor(char disk)
\r
644 * \brief Starts FDD Motor
\r
646 static void start_motor(int disk)
\r
649 state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );
\r
650 state |= 1 << (4+disk);
\r
651 outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );
\r
652 fdd_devices[disk].motorState = 1;
\r
653 fdd_devices[disk].timer = Time_CreateTimer(MOTOR_ON_DELAY, fdd_timer, (void*)disk); //One Shot Timer
\r
657 * \fn void stop_motor(int disk)
\r
658 * \brief Stops FDD Motor
\r
660 static void stop_motor(int disk)
\r
663 state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );
\r
664 state &= ~( 1 << (4+disk) );
\r
665 outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );
\r
666 fdd_devices[disk].motorState = 0;
\r
670 * \fn int FDD_Install(char **Arguments)
\r
671 * \brief Installs floppy driver
\r
673 int FDD_Install(char **Arguments)
\r
677 // Determine Floppy Types (From CMOS)
\r
680 fdd_devices[0].type = data >> 4;
\r
681 fdd_devices[1].type = data & 0xF;
\r
682 fdd_devices[0].track[0] = -1;
\r
683 fdd_devices[1].track[1] = -1;
\r
685 // Clear FDD IRQ Flag
\r
686 FDD_SensInt(0x3F0, NULL, NULL);
\r
687 // Install IRQ6 Handler
\r
688 IRQ_AddHandler(6, fdd_handler);
\r
689 // Reset Primary FDD Controller
\r
692 Log("[FDD ] Detected Disk 0: %s and Disk 1: %s\n", cFDD_TYPES[data>>4], cFDD_TYPES[data&0xF]);
\r
694 // Initialise Root Node
\r
695 gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime
\r
696 = gFDD_DriverInfo.RootNode.ATime = now();
\r
698 // Initialise Child Nodes
\r
699 fdd_devices[0].Name[0] = '0'; fdd_devices[0].Name[1] = '\0';
\r
700 fdd_devices[0].Node.Inode = 0;
\r
701 fdd_devices[0].Node.Flags = 0;
\r
702 fdd_devices[0].Node.NumACLs = 0;
\r
703 fdd_devices[0].Node.Read = FDD_ReadFS;
\r
704 fdd_devices[0].Node.Write = NULL;//fdd_writeFS;
\r
705 memcpy(&fdd_devices[1].Node, &fdd_devices[0].Node, sizeof(tVFS_Node));
\r
706 fdd_devices[1].Name[0] = '1';
\r
707 fdd_devices[1].Node.Inode = 1;
\r
710 fdd_devices[0].Node.Size = cFDD_SIZES[data >> 4];
\r
711 fdd_devices[1].Node.Size = cFDD_SIZES[data & 0xF];
\r
713 // Create Sector Cache
\r
715 //sFDD_SectorCache = malloc(sizeof(*sFDD_SectorCache)*CACHE_SIZE);
\r
716 //siFDD_SectorCacheSize = CACHE_SIZE;
\r
719 // Register with devfs
\r
720 DevFS_AddDevice(&gFDD_DriverInfo);
\r
726 * \fn void ModuleUnload()
\r
727 * \brief Prepare the module for removal
\r
729 void ModuleUnload()
\r
732 FDD_AquireSpinlock();
\r
734 Time_RemoveTimer(fdd_devices[i].timer);
\r