3 * Floppy Disk Access Code
9 #include <tpl_drv_disk.h>
16 // --- Current Version
17 #define FDD_VERSION ((0<<8)|(75))
20 #define FDD_SEEK_TIMEOUT 10 // Timeout for a seek operation
21 #define MOTOR_ON_DELAY 500 // Miliseconds
22 #define MOTOR_OFF_DELAY 2000 // Miliseconds
23 #define FDD_MAX_READWRITE_ATTEMPTS 16
27 * \brief Representation of a floppy drive
29 typedef struct sFloppyDrive
32 volatile int motorState; //2 - On, 1 - Spinup, 0 - Off
37 tIOCache *CacheHandle;
42 * \brief Cached Sector
47 Uint16 sector; // Allows 32Mb of addressable space (Plenty for FDD)
52 static const char *cFDD_TYPES[] = {"None", "360kB 5.25\"", "1.2MB 5.25\"", "720kB 3.5\"", "1.44MB 3.5\"", "2.88MB 3.5\"" };
53 static const int cFDD_SIZES[] = { 0, 360*1024, 1200*1024, 720*1024, 1440*1024, 2880*1024 };
54 static const short cPORTBASE[] = { 0x3F0, 0x370 };
56 static const char *cFDD_STATUSES[] = {NULL, "Error", "Invalid command", "Drive not ready"};
63 PORT_MAINSTATUS = 0x4,
71 FIX_DRIVE_DATA = 0x03,
72 CHECK_DRIVE_STATUS = 0x04,
73 CALIBRATE_DRIVE = 0x07,
74 CHECK_INTERRUPT_STATUS = 0x08,
76 READ_SECTOR_ID = 0x4A,
81 WRITE_DELETE_SECTOR = 0xC9,
82 READ_DELETE_SECTOR = 0xCC,
87 int FDD_Install(char **Arguments);
88 void FDD_UnloadModule();
90 char *FDD_ReadDir(tVFS_Node *Node, int pos);
91 tVFS_Node *FDD_FindDir(tVFS_Node *dirNode, const char *Name);
92 int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data);
93 Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
94 // --- Functions for IOCache/DrvUtil
95 Uint FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk);
96 // --- Raw Disk Access
97 int FDD_ReadSector(Uint32 disk, Uint64 lba, void *Buffer);
98 int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer);
100 void FDD_IRQHandler(int Num);
101 inline void FDD_WaitIRQ();
102 void FDD_SensInt(int base, Uint8 *sr0, Uint8 *cyl);
103 void FDD_int_SendByte(int base, char byte);
104 int FDD_int_GetByte(int base);
105 void FDD_Reset(int id);
106 void FDD_Recalibrate(int disk);
107 int FDD_int_SeekTrack(int disk, int head, int track);
108 void FDD_int_TimerCallback(void *Arg);
109 void FDD_int_StopMotor(void *Arg);
110 void FDD_int_StartMotor(int Disk);
111 int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt);
114 MODULE_DEFINE(0, FDD_VERSION, FDD, FDD_Install, NULL, "ISADMA", NULL);
115 t_floppyDevice gFDD_Devices[2];
117 volatile int gbFDD_IrqFired = 0;
118 tDevFS_Driver gFDD_DriverInfo = {
123 .ACLs = &gVFS_ACL_EveryoneRX,
124 .Flags = VFS_FFLAG_DIRECTORY,
125 .ReadDir = FDD_ReadDir,
126 .FindDir = FDD_FindDir,
133 * \fn int FDD_Install(char **Arguments)
134 * \brief Installs floppy driver
136 int FDD_Install(char **Arguments)
139 char **args = Arguments;
141 // Determine Floppy Types (From CMOS)
144 gFDD_Devices[0].type = data >> 4;
145 gFDD_Devices[1].type = data & 0xF;
146 gFDD_Devices[0].track[0] = -1;
147 gFDD_Devices[1].track[1] = -1;
152 if(strcmp(*args, "disable")==0)
153 return MODULE_ERR_NOTNEEDED;
157 Log_Log("FDD", "Detected Disk 0: %s and Disk 1: %s", cFDD_TYPES[data>>4], cFDD_TYPES[data&0xF]);
160 return MODULE_ERR_NOTNEEDED;
163 // Clear FDD IRQ Flag
164 FDD_SensInt(0x3F0, NULL, NULL);
165 // Install IRQ6 Handler
166 IRQ_AddHandler(6, FDD_IRQHandler);
167 // Reset Primary FDD Controller
170 // Initialise Root Node
171 gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime
172 = gFDD_DriverInfo.RootNode.ATime = now();
174 // Initialise Child Nodes
175 gFDD_Devices[0].Node.Inode = 0;
176 gFDD_Devices[0].Node.Flags = 0;
177 gFDD_Devices[0].Node.NumACLs = 0;
178 gFDD_Devices[0].Node.Read = FDD_ReadFS;
179 gFDD_Devices[0].Node.Write = NULL;//FDD_WriteFS;
180 memcpy(&gFDD_Devices[1].Node, &gFDD_Devices[0].Node, sizeof(tVFS_Node));
182 gFDD_Devices[1].Node.Inode = 1;
185 gFDD_Devices[0].Node.Size = cFDD_SIZES[data >> 4];
186 gFDD_Devices[1].Node.Size = cFDD_SIZES[data & 0xF];
188 // Create Sector Cache
189 if( cFDD_SIZES[data >> 4] )
191 gFDD_Devices[0].CacheHandle = IOCache_Create(
192 FDD_WriteSector, 0, 512,
193 gFDD_Devices[0].Node.Size / (512*4)
194 ); // Cache is 1/4 the size of the disk
196 if( cFDD_SIZES[data & 15] )
198 gFDD_Devices[1].CacheHandle = IOCache_Create(
199 FDD_WriteSector, 0, 512,
200 gFDD_Devices[1].Node.Size / (512*4)
201 ); // Cache is 1/4 the size of the disk
204 // Register with devfs
205 DevFS_AddDevice(&gFDD_DriverInfo);
207 return MODULE_ERR_OK;
211 * \brief Prepare the module for removal
213 void FDD_UnloadModule()
216 DevFS_DelDevice( &gFDD_DriverInfo );
217 Mutex_Acquire(&glFDD);
219 Time_RemoveTimer(gFDD_Devices[i].timer);
220 FDD_int_StopMotor((void *)(Uint)i);
222 Mutex_Release(&glFDD);
227 * \fn char *FDD_ReadDir(tVFS_Node *Node, int pos)
228 * \brief Read Directory
230 char *FDD_ReadDir(tVFS_Node *UNUSED(Node), int Pos)
232 char name[2] = "0\0";
234 if(Pos >= 2 || Pos < 0) return NULL;
236 if(gFDD_Devices[Pos].type == 0) return VFS_SKIP;
244 * \fn tVFS_Node *FDD_FindDir(tVFS_Node *Node, const char *filename);
245 * \brief Find File Routine (for vfs_node)
247 tVFS_Node *FDD_FindDir(tVFS_Node *UNUSED(Node), const char *Filename)
251 ENTER("sFilename", Filename);
253 // Sanity check string
254 if(Filename == NULL) {
259 // Check string length (should be 1)
260 if(Filename[0] == '\0' || Filename[1] != '\0') {
265 // Get First character
266 i = Filename[0] - '0';
268 // Check for 1st disk and if it is present return
269 if(i == 0 && gFDD_Devices[0].type != 0) {
270 LEAVE('p', &gFDD_Devices[0].Node);
271 return &gFDD_Devices[0].Node;
274 // Check for 2nd disk and if it is present return
275 if(i == 1 && gFDD_Devices[1].type != 0) {
276 LEAVE('p', &gFDD_Devices[1].Node);
277 return &gFDD_Devices[1].Node;
285 static const char *casIOCTLS[] = {DRV_IOCTLNAMES,DRV_DISK_IOCTLNAMES,NULL};
287 * \fn int FDD_IOCtl(tVFS_Node *Node, int id, void *data)
288 * \brief Stub ioctl function
290 int FDD_IOCtl(tVFS_Node *UNUSED(Node), int ID, void *Data)
294 BASE_IOCTLS(DRV_TYPE_DISK, "FDD", FDD_VERSION, casIOCTLS);
296 case DISK_IOCTL_GETBLOCKSIZE: return 512;
304 * \fn Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
305 * \brief Read Data from a disk
307 Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
311 ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
318 if(Node->Inode != 0 && Node->Inode != 1) {
323 ret = DrvUtil_ReadBlock(Offset, Length, Buffer, FDD_ReadSectors, 512, Node->Inode);
329 * \brief Reads \a Count contiguous sectors from a disk
330 * \param SectorAddr Address of the first sector
331 * \param Count Number of sectors to read
332 * \param Buffer Destination Buffer
333 * \param Disk Disk Number
334 * \return Number of sectors read
335 * \note Used as a ::DrvUtil_ReadBlock helper
337 Uint FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk)
342 if( FDD_ReadSector(Disk, SectorAddr, Buffer) != 1 )
345 Buffer = (void*)( (tVAddr)Buffer + 512 );
352 int FDD_int_ReadWriteSector(Uint32 Disk, Uint64 SectorAddr, int Write, void *Buffer)
357 int lba = SectorAddr;
358 Uint8 st0, st1, st2, rcy, rhe, rse, bps; // Status Values
360 ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer);
362 base = cPORTBASE[Disk >> 1];
364 LOG("Calculating Disk Dimensions");
366 if(FDD_int_GetDims(gFDD_Devices[Disk].type, lba, &cyl, &head, &sec, &spt) != 1)
371 LOG("Cyl=%i, Head=%i, Sector=%i", cyl, head, sec);
373 Mutex_Acquire(&glFDD); // Lock to stop the motor stopping on us
374 Time_RemoveTimer(gFDD_Devices[Disk].timer); // Remove Old Timer
375 // Start motor if needed
376 if(gFDD_Devices[Disk].motorState != 2) FDD_int_StartMotor(Disk);
377 Mutex_Release(&glFDD);
379 LOG("Wait for the motor to spin up");
382 while(gFDD_Devices[Disk].motorState == 1) Threads_Yield();
384 LOG("Acquire Spinlock");
385 Mutex_Acquire(&glFDD);
388 outb(base + CALIBRATE_DRIVE, 0);
390 while(FDD_int_SeekTrack(Disk, head, (Uint8)cyl) == 0 && i++ < FDD_SEEK_TIMEOUT )
392 if( i > FDD_SEEK_TIMEOUT ) {
393 Mutex_Release(&glFDD);
397 //FDD_SensInt(base, NULL, NULL); // Wait for IRQ
399 // Read Data from DMA
400 LOG("Setting DMA for read");
401 DMA_SetChannel(2, 512, !Write); // Read/Write 512 Bytes from channel 2
403 LOG("Sending command");
405 for( i = 0; i < FDD_MAX_READWRITE_ATTEMPTS; i ++ )
408 FDD_int_SendByte(base, READ_SECTOR); // Was 0xE6
410 FDD_int_SendByte(base, READ_SECTOR); // Was 0xE6
411 FDD_int_SendByte(base, (head << 2) | (Disk&1));
412 FDD_int_SendByte(base, (Uint8)cyl);
413 FDD_int_SendByte(base, (Uint8)head);
414 FDD_int_SendByte(base, (Uint8)sec);
415 FDD_int_SendByte(base, 0x02); // Bytes Per Sector (Real BPS=128*2^{val})
416 FDD_int_SendByte(base, spt); // SPT
417 FDD_int_SendByte(base, 0x1B); // Gap Length (27 is default)
418 FDD_int_SendByte(base, 0xFF); // Data Length
423 DMA_WriteData(2, 512, Buffer);
424 LOG("Waiting for Data to be written");
428 LOG("Waiting for data to be read");
431 DMA_ReadData(2, 512, Buffer);
434 // Clear Input Buffer
435 LOG("Clearing Input Buffer");
437 st0 = FDD_int_GetByte(base);
438 st1 = FDD_int_GetByte(base);
439 st2 = FDD_int_GetByte(base);
441 // Cylinder, Head and Sector (mutilated in some way)
442 rcy = FDD_int_GetByte(base);
443 rhe = FDD_int_GetByte(base);
444 rse = FDD_int_GetByte(base);
445 // Should be the BPS set above (0x02)
446 bps = FDD_int_GetByte(base);
451 LOG("Error (st0 & 0xC0) \"%s\"", cFDD_STATUSES[st0 >> 6]);
455 if(st0 & 0x08) { LOG("Drive not ready"); continue; }
456 if(st1 & 0x80) { LOG("End of Cylinder"); continue; }
457 if(st1 & 0x20) { LOG("CRC Error"); continue; }
458 if(st1 & 0x10) { LOG("Controller Timeout"); continue; }
459 if(st1 & 0x04) { LOG("No Data Found"); continue; }
460 if(st1 & 0x01 || st2 & 0x01) {
461 LOG("No Address mark found");
464 if(st2 & 0x40) { LOG("Deleted address mark"); continue; }
465 if(st2 & 0x20) { LOG("CRC error in data"); continue; }
466 if(st2 & 0x10) { LOG("Wrong Cylinder"); continue; }
467 if(st2 & 0x04) { LOG("uPD765 sector not found"); continue; }
468 if(st2 & 0x02) { LOG("Bad Cylinder"); continue; }
471 LOG("Returned BPS = 0x%02x, not 0x02", bps);
476 LOG("Floppy not writable");
477 i = FDD_MAX_READWRITE_ATTEMPTS+1;
486 LOG("Realeasing Spinlock and setting motor to stop");
487 Mutex_Release(&glFDD);
489 if(i == FDD_MAX_READWRITE_ATTEMPTS) {
490 Log_Warning("FDD", "Exceeded %i attempts in %s the disk",
491 FDD_MAX_READWRITE_ATTEMPTS,
492 (Write ? "writing to" : "reading from")
496 // Don't turn the motor off now, wait for a while
497 gFDD_Devices[Disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotor, (void*)(tVAddr)Disk);
500 if( i < FDD_MAX_READWRITE_ATTEMPTS ) {
511 * \fn int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer)
512 * \brief Read a sector from disk
513 * \todo Make real-hardware safe (account for read errors)
515 int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer)
519 ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer);
521 if( IOCache_Read( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer ) == 1 ) {
526 // Pass to general function
527 ret = FDD_int_ReadWriteSector(Disk, SectorAddr, 0, Buffer);
530 IOCache_Add( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer );
535 LOG("Reading failed");
542 * \fn int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer)
543 * \brief Write a sector to the floppy disk
544 * \note Not Implemented
546 int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer)
548 Warning("[FDD ] Read Only at the moment");
553 * \fn int FDD_int_SeekTrack(int disk, int track)
554 * \brief Seek disk to selected track
556 int FDD_int_SeekTrack(int disk, int head, int track)
561 base = cPORTBASE[disk>>1];
563 // Check if seeking is needed
564 if(gFDD_Devices[disk].track[head] == track)
568 FDD_int_SendByte(base, SEEK_TRACK);
569 FDD_int_SendByte(base, (head<<2)|(disk&1));
570 FDD_int_SendByte(base, track); // Send Seek command
572 FDD_SensInt(base, &sr0, &cyl); // Wait for IRQ
573 if((sr0 & 0xF0) != 0x20) {
574 LOG("sr0 = 0x%x", sr0);
575 return 0; //Check Status
577 if(cyl != track) return 0;
579 // Set Track in structure
580 gFDD_Devices[disk].track[head] = track;
582 // Wait for Head to settle
589 * \fn int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt)
590 * \brief Get Dimensions of a disk
592 int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt)
628 //Log("1440k - lba=%i(0x%x), *s=%i,*c=%i,*h=%i", lba, lba, *s, *c, *h);
646 * \fn void FDD_IRQHandler(int Num)
647 * \brief Handles IRQ6
649 void FDD_IRQHandler(int Num)
656 * \brief Wait for an IRQ6
658 inline void FDD_WaitIRQ()
661 while(!gbFDD_IrqFired) Threads_Yield();
665 void FDD_SensInt(int base, Uint8 *sr0, Uint8 *cyl)
668 FDD_int_SendByte(base, CHECK_INTERRUPT_STATUS);
669 byte = FDD_int_GetByte(base);
671 byte = FDD_int_GetByte(base);
676 * void FDD_int_SendByte(int base, char byte)
677 * \brief Sends a command to the controller
679 void FDD_int_SendByte(int base, char byte)
683 while( (inb(base + PORT_MAINSTATUS) & 0xC0) != 0x80 && timeout-- )
689 static int totalTimeout = 0;
690 static int totalCount = 0;
691 totalTimeout += timeout;
693 LOG("timeout = %i, average %i", timeout, totalTimeout/totalCount);
695 outb(base + PORT_DATA, byte);
699 Log_Warning("FDD", "FDD_int_SendByte: Timeout sending byte 0x%x to base 0x%x\n", byte, base);
704 * int FDD_int_GetByte(int base, char byte)
705 * \brief Receive data from fdd controller
707 int FDD_int_GetByte(int base)
711 while( (inb(base + PORT_MAINSTATUS) & 0xd0) != 0xd0 && timeout-- )
717 static int totalTimeout = 0;
718 static int totalCount = 0;
719 totalTimeout += timeout;
721 LOG("timeout = %i, average %i", timeout, totalTimeout/totalCount);
723 return inb(base + PORT_DATA);
727 Log_Warning("FDD", "FDD_int_GetByte: Timeout reading byte from base 0x%x\n", base);
733 * \brief Recalibrate the specified disk
735 void FDD_Recalibrate(int disk)
737 ENTER("idisk", disk);
739 LOG("Starting Motor");
740 FDD_int_StartMotor(disk);
742 while(gFDD_Devices[disk].motorState == 1) Threads_Yield();
744 LOG("Sending Calibrate Command");
745 FDD_int_SendByte(cPORTBASE[disk>>1], CALIBRATE_DRIVE);
746 FDD_int_SendByte(cPORTBASE[disk>>1], disk&1);
748 LOG("Waiting for IRQ");
750 FDD_SensInt(cPORTBASE[disk>>1], NULL, NULL);
752 LOG("Stopping Motor");
753 gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotor, (void*)(Uint)disk);
758 * \brief Reset the specified FDD controller
760 void FDD_Reset(int id)
762 int base = cPORTBASE[id];
766 outb(base + PORT_DIGOUTPUT, 0); // Stop Motors & Disable FDC
767 outb(base + PORT_DIGOUTPUT, 0x0C); // Re-enable FDC (DMA and Enable)
772 FDD_SensInt(base, NULL, NULL);
774 LOG("Setting Driver Info");
775 outb(base + PORT_DATARATE, 0); // Set data rate to 500K/s
776 FDD_int_SendByte(base, FIX_DRIVE_DATA); // Step and Head Load Times
777 FDD_int_SendByte(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each)
778 FDD_int_SendByte(base, 0x02); // Head Load Time >> 1
779 while(FDD_int_SeekTrack(0, 0, 1) == 0); // set track
780 while(FDD_int_SeekTrack(0, 1, 1) == 0); // set track
782 LOG("Recalibrating Disk");
783 FDD_Recalibrate((id<<1)|0);
784 FDD_Recalibrate((id<<1)|1);
790 * \fn void FDD_int_TimerCallback()
791 * \brief Called by timer
793 void FDD_int_TimerCallback(void *Arg)
795 int disk = (Uint)Arg;
797 if(gFDD_Devices[disk].motorState == 1)
798 gFDD_Devices[disk].motorState = 2;
799 Time_RemoveTimer(gFDD_Devices[disk].timer);
800 gFDD_Devices[disk].timer = -1;
805 * \fn void FDD_int_StartMotor(char disk)
806 * \brief Starts FDD Motor
808 void FDD_int_StartMotor(int disk)
811 state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );
812 state |= 1 << (4+disk);
813 outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );
814 gFDD_Devices[disk].motorState = 1;
815 gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_ON_DELAY, FDD_int_TimerCallback, (void*)(Uint)disk);
819 * \fn void FDD_int_StopMotor(int disk)
820 * \brief Stops FDD Motor
822 void FDD_int_StopMotor(void *Arg)
824 Uint8 state, disk = (Uint)Arg;
825 if( Mutex_IsLocked(&glFDD) ) return ;
826 ENTER("iDisk", disk);
828 state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );
829 state &= ~( 1 << (4+disk) );
830 outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );
831 gFDD_Devices[disk].motorState = 0;