FDD Driver - Fixing bugs exposed in x86_64 testing
[tpg/acess2.git] / Modules / Storage / FDD / fdd.c
1 /*
2  * AcessOS 0.1
3  * Floppy Disk Access Code
4  */
5 #define DEBUG   0
6 #include <acess.h>
7 #include <modules.h>
8 #include <fs_devfs.h>
9 #include <tpl_drv_disk.h>
10 #include <dma.h>
11 #include <iocache.h>
12
13 #define WARN    0
14
15 // === CONSTANTS ===
16 // --- Current Version
17 #define FDD_VERSION      ((0<<8)|(75))
18
19 // --- Options
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
24
25 // === TYPEDEFS ===
26 /**
27  * \brief Representation of a floppy drive
28  */
29 typedef struct sFloppyDrive
30 {
31          int    type;
32         volatile int    motorState;     //2 - On, 1 - Spinup, 0 - Off
33          int    track[2];
34          int    timer;
35         tVFS_Node       Node;
36         #if !USE_CACHE
37         tIOCache        *CacheHandle;
38         #endif
39 } t_floppyDevice;
40
41 /**
42  * \brief Cached Sector
43  */
44 typedef struct {
45         Uint64  timestamp;
46         Uint16  disk;
47         Uint16  sector; // Allows 32Mb of addressable space (Plenty for FDD)
48         Uint8   data[512];
49 } t_floppySector;
50
51 // === CONSTANTS ===
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 };
55 #if DEBUG
56 static const char       *cFDD_STATUSES[] = {NULL, "Error", "Invalid command", "Drive not ready"};
57 #endif
58
59 enum FloppyPorts {
60         PORT_STATUSA    = 0x0,
61         PORT_STATUSB    = 0x1,
62         PORT_DIGOUTPUT  = 0x2,
63         PORT_MAINSTATUS = 0x4,
64         PORT_DATARATE   = 0x4,
65         PORT_DATA               = 0x5,
66         PORT_DIGINPUT   = 0x7,
67         PORT_CONFIGCTRL = 0x7
68 };
69
70 #define CMD_FLAG_MULTI_TRACK    0x80    //!< Multitrack
71 #define CMD_FLAG_MFM_ENCODING   0x40    //!< MFM Encoding Mode (Always set for read/write/format/verify)
72 #define CMD_FLAG_SKIP_MODE      0x20    //!< Skip Mode (don't use)
73 enum FloppyCommands {
74         CMD_READ_TRACK      = 0x02,
75         CMD_SPECIFY         = 0x03,
76         CMD_SENSE_STATUS    = 0x04,
77         CMD_WRITE_DATA      = 0x05,
78         CMD_READ_DATA       = 0x06,
79         CMD_RECALIBRATE     = 0x07,
80         CMD_SENSE_INTERRUPT = 0x08,
81         CMD_WRITE_DEL_DATA  = 0x09,
82         CMD_READ_SECTOR_ID  = 0x0A,
83         // 0x0B - ?
84         CMD_READ_DEL_DATA       = 0x0C,
85         CMD_FORMAT_TRACK    = 0x0D,
86         // 0x0E - ?
87         CMD_SEEK_TRACK      = 0x0F,
88         CMD_VERSION         = 0x10,
89         
90         CMD_LOCK            = 0x14,     //!< Save controller parameters
91         
92         CMD_CONFIGURE       = 0x13
93 };
94
95 // === PROTOTYPES ===
96 // --- Filesystem
97  int    FDD_Install(char **Arguments);
98 void    FDD_UnloadModule();
99 // --- VFS Methods
100 char    *FDD_ReadDir(tVFS_Node *Node, int pos);
101 tVFS_Node       *FDD_FindDir(tVFS_Node *dirNode, const char *Name);
102  int    FDD_IOCtl(tVFS_Node *Node, int ID, void *Data);
103 Uint64  FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
104 // --- Functions for IOCache/DrvUtil
105 Uint    FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk);
106 // --- Raw Disk Access
107  int    FDD_ReadSector(Uint32 disk, Uint64 lba, void *Buffer);
108  int    FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer);
109 // --- Helpers
110 void    FDD_IRQHandler(int Num);
111 inline void     FDD_WaitIRQ();
112 void    FDD_SensInt(int base, Uint8 *sr0, Uint8 *cyl);
113  int    FDD_int_SendByte(int base, Uint8 Byte);
114  int    FDD_int_GetByte(int base, Uint8 *Byte);
115  int    FDD_Reset(int id);
116 void    FDD_Recalibrate(int disk);
117  int    FDD_int_SeekTrack(int disk, int head, int track);
118 void    FDD_int_TimerCallback(void *Arg);
119 void    FDD_int_StopMotor(void *Arg);
120 void    FDD_int_StartMotor(int Disk);
121  int    FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt);
122
123 // === GLOBALS ===
124 MODULE_DEFINE(0, FDD_VERSION, FDD, FDD_Install, NULL, "x86_ISADMA", NULL);
125 t_floppyDevice  gFDD_Devices[2];
126 tMutex  glFDD;
127 volatile int    gbFDD_IrqFired = 0;
128 tDevFS_Driver   gFDD_DriverInfo = {
129         NULL, "fdd",
130         {
131         .Size = -1,
132         .NumACLs = 1,
133         .ACLs = &gVFS_ACL_EveryoneRX,
134         .Flags = VFS_FFLAG_DIRECTORY,
135         .ReadDir = FDD_ReadDir,
136         .FindDir = FDD_FindDir,
137         .IOCtl = FDD_IOCtl
138         }
139 };
140
141 // === CODE ===
142 /**
143  * \fn int FDD_Install(char **Arguments)
144  * \brief Installs floppy driver
145  */
146 int FDD_Install(char **Arguments)
147 {
148         Uint8 data;
149         char    **args = Arguments;
150         
151         // Determine Floppy Types (From CMOS)
152         outb(0x70, 0x10);
153         data = inb(0x71);
154         gFDD_Devices[0].type = data >> 4;
155         gFDD_Devices[1].type = data & 0xF;
156         gFDD_Devices[0].track[0] = -1;
157         gFDD_Devices[1].track[1] = -1;
158         
159         if(args) {
160                 for(;*args;args++)
161                 {
162                         if(strcmp(*args, "disable")==0)
163                                 return MODULE_ERR_NOTNEEDED;
164                 }
165         }
166         
167         Log_Log("FDD", "Detected Disk 0: %s and Disk 1: %s", cFDD_TYPES[data>>4], cFDD_TYPES[data&0xF]);
168         
169         if( data == 0 ) {
170                 return MODULE_ERR_NOTNEEDED;
171         }
172         
173         // Install IRQ6 Handler
174         IRQ_AddHandler(6, FDD_IRQHandler);
175         // Reset Primary FDD Controller
176         if( FDD_Reset(0) != 0 ) {
177                 return MODULE_ERR_MISC;
178         }
179         
180         // Initialise Root Node
181         gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime
182                 = gFDD_DriverInfo.RootNode.ATime = now();
183         
184         // Initialise Child Nodes
185         gFDD_Devices[0].Node.Inode = 0;
186         gFDD_Devices[0].Node.Flags = 0;
187         gFDD_Devices[0].Node.NumACLs = 0;
188         gFDD_Devices[0].Node.Read = FDD_ReadFS;
189         gFDD_Devices[0].Node.Write = NULL;//FDD_WriteFS;
190         memcpy(&gFDD_Devices[1].Node, &gFDD_Devices[0].Node, sizeof(tVFS_Node));
191         
192         gFDD_Devices[1].Node.Inode = 1;
193         
194         // Set Lengths
195         gFDD_Devices[0].Node.Size = cFDD_SIZES[data >> 4];
196         gFDD_Devices[1].Node.Size = cFDD_SIZES[data & 0xF];
197         
198         // Create Sector Cache
199         if( cFDD_SIZES[data >> 4] )
200         {
201                 gFDD_Devices[0].CacheHandle = IOCache_Create(
202                         FDD_WriteSector, 0, 512,
203                         gFDD_Devices[0].Node.Size / (512*4)
204                         );      // Cache is 1/4 the size of the disk
205         }
206         if( cFDD_SIZES[data & 15] )
207         {
208                 gFDD_Devices[1].CacheHandle = IOCache_Create(
209                         FDD_WriteSector, 0, 512,
210                         gFDD_Devices[1].Node.Size / (512*4)
211                         );      // Cache is 1/4 the size of the disk
212         }
213         
214         // Register with devfs
215         DevFS_AddDevice(&gFDD_DriverInfo);
216         
217         return MODULE_ERR_OK;
218 }
219
220 /**
221  * \brief Prepare the module for removal
222  */
223 void FDD_UnloadModule()
224 {
225          int    i;
226         DevFS_DelDevice( &gFDD_DriverInfo );
227         Mutex_Acquire(&glFDD);
228         for(i=0;i<4;i++) {
229                 Time_RemoveTimer(gFDD_Devices[i].timer);
230                 FDD_int_StopMotor((void *)(Uint)i);
231         }
232         Mutex_Release(&glFDD);
233         //IRQ_Clear(6);
234 }
235
236 /**
237  * \fn char *FDD_ReadDir(tVFS_Node *Node, int pos)
238  * \brief Read Directory
239  */
240 char *FDD_ReadDir(tVFS_Node *UNUSED(Node), int Pos)
241 {
242         char    name[2] = "0\0";
243
244         if(Pos >= 2 || Pos < 0) return NULL;
245         
246         if(gFDD_Devices[Pos].type == 0) return VFS_SKIP;
247         
248         name[0] += Pos;
249         
250         return strdup(name);
251 }
252
253 /**
254  * \fn tVFS_Node *FDD_FindDir(tVFS_Node *Node, const char *filename);
255  * \brief Find File Routine (for vfs_node)
256  */
257 tVFS_Node *FDD_FindDir(tVFS_Node *UNUSED(Node), const char *Filename)
258 {
259          int    i;
260         
261         ENTER("sFilename", Filename);
262         
263         // Sanity check string
264         if(Filename == NULL) {
265                 LEAVE('n');
266                 return NULL;
267         }
268         
269         // Check string length (should be 1)
270         if(Filename[0] == '\0' || Filename[1] != '\0') {
271                 LEAVE('n');
272                 return NULL;
273         }
274         
275         // Get First character
276         i = Filename[0] - '0';
277         
278         // Check for 1st disk and if it is present return
279         if(i == 0 && gFDD_Devices[0].type != 0) {
280                 LEAVE('p', &gFDD_Devices[0].Node);
281                 return &gFDD_Devices[0].Node;
282         }
283         
284         // Check for 2nd disk and if it is present return
285         if(i == 1 && gFDD_Devices[1].type != 0) {
286                 LEAVE('p', &gFDD_Devices[1].Node);
287                 return &gFDD_Devices[1].Node;
288         }
289         
290         // Else return null
291         LEAVE('n');
292         return NULL;
293 }
294
295 static const char       *casIOCTLS[] = {DRV_IOCTLNAMES,DRV_DISK_IOCTLNAMES,NULL};
296 /**
297  * \fn int FDD_IOCtl(tVFS_Node *Node, int id, void *data)
298  * \brief Stub ioctl function
299  */
300 int FDD_IOCtl(tVFS_Node *UNUSED(Node), int ID, void *Data)
301 {
302         switch(ID)
303         {
304         BASE_IOCTLS(DRV_TYPE_DISK, "FDD", FDD_VERSION, casIOCTLS);
305         
306         case DISK_IOCTL_GETBLOCKSIZE:   return 512;     
307         
308         default:
309                 return 0;
310         }
311 }
312
313 /**
314  * \fn Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
315  * \brief Read Data from a disk
316 */
317 Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
318 {
319          int    ret;
320         
321         ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
322         
323         if(Node == NULL) {
324                 LEAVE('i', -1);
325                 return -1;
326         }
327         
328         if(Node->Inode != 0 && Node->Inode != 1) {
329                 LEAVE('i', -1);
330                 return -1;
331         }
332         
333         ret = DrvUtil_ReadBlock(Offset, Length, Buffer, FDD_ReadSectors, 512, Node->Inode);
334         LEAVE('i', ret);
335         return ret;
336 }
337
338 /**
339  * \brief Reads \a Count contiguous sectors from a disk
340  * \param SectorAddr    Address of the first sector
341  * \param Count Number of sectors to read
342  * \param Buffer        Destination Buffer
343  * \param Disk  Disk Number
344  * \return Number of sectors read
345  * \note Used as a ::DrvUtil_ReadBlock helper
346  */
347 Uint FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk)
348 {
349         Uint    ret = 0;
350         while(Count --)
351         {
352                 if( FDD_ReadSector(Disk, SectorAddr, Buffer) != 1 )
353                         return ret;
354                 
355                 Buffer = (void*)( (tVAddr)Buffer + 512 );
356                 SectorAddr ++;
357                 ret ++;
358         }
359         return ret;
360 }
361
362 int FDD_int_ReadWriteSector(Uint32 Disk, Uint64 SectorAddr, int Write, void *Buffer)
363 {
364          int    cyl, head, sec;
365          int    spt, base;
366          int    i;
367          int    lba = SectorAddr;
368         Uint8   st0=0, st1=0, st2=0, bps=0;     // Status Values
369         
370         ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer);
371         
372         base = cPORTBASE[Disk >> 1];
373         
374         LOG("Calculating Disk Dimensions");
375         // Get CHS position
376         if(FDD_int_GetDims(gFDD_Devices[Disk].type, lba, &cyl, &head, &sec, &spt) != 1)
377         {
378                 LEAVE('i', -1);
379                 return -1;
380         }
381         LOG("Cyl=%i, Head=%i, Sector=%i", cyl, head, sec);
382         
383         Mutex_Acquire(&glFDD);  // Lock to stop the motor stopping on us
384         Time_RemoveTimer(gFDD_Devices[Disk].timer);     // Remove Old Timer
385         // Start motor if needed
386         if(gFDD_Devices[Disk].motorState != 2)  FDD_int_StartMotor(Disk);
387         Mutex_Release(&glFDD);
388         
389         LOG("Wait for the motor to spin up");
390         
391         // Wait for spinup
392         while(gFDD_Devices[Disk].motorState == 1)       Threads_Yield();
393         
394         LOG("Acquire Spinlock");
395         Mutex_Acquire(&glFDD);
396         
397         #if 0
398         // Seek to track
399         outb(base + CALIBRATE_DRIVE, 0);
400         i = 0;
401         while(FDD_int_SeekTrack(Disk, head, (Uint8)cyl) == 0 && i++ < FDD_SEEK_TIMEOUT )
402                 Threads_Yield();
403         if( i > FDD_SEEK_TIMEOUT ) {
404                 Mutex_Release(&glFDD);
405                 LEAVE('i', 0);
406                 return 0;
407         }
408         //FDD_SensInt(base, NULL, NULL);        // Wait for IRQ
409         #endif
410                 
411         // Read Data from DMA
412         LOG("Setting DMA for read");
413         DMA_SetChannel(2, 512, !Write); // Read/Write 512 Bytes from channel 2
414         
415         LOG("Sending command");
416         
417         for( i = 0; i < FDD_MAX_READWRITE_ATTEMPTS; i ++ )
418         {
419                 if( Write )
420                         FDD_int_SendByte(base, CMD_WRITE_DATA|CMD_FLAG_MFM_ENCODING);
421                 else
422                         FDD_int_SendByte(base, CMD_READ_DATA|CMD_FLAG_MFM_ENCODING);
423                 FDD_int_SendByte(base, (head << 2) | (Disk&1));
424                 FDD_int_SendByte(base, (Uint8)cyl);
425                 FDD_int_SendByte(base, (Uint8)head);
426                 FDD_int_SendByte(base, (Uint8)sec);
427                 FDD_int_SendByte(base, 0x02);   // Bytes Per Sector (Real BPS=128*2^{val})
428                 FDD_int_SendByte(base, spt);    // SPT
429                 FDD_int_SendByte(base, 0x1B);   // Gap Length (27 is default)
430                 FDD_int_SendByte(base, 0xFF);   // Data Length
431                 
432                 // Wait for IRQ
433                 if( Write ) {
434                         LOG("Writing Data");
435                         DMA_WriteData(2, 512, Buffer);
436                         LOG("Waiting for Data to be written");
437                         FDD_WaitIRQ();
438                 }
439                 else {
440                         LOG("Waiting for data to be read");
441                         FDD_WaitIRQ();
442                         LOG("Reading Data");
443                         DMA_ReadData(2, 512, Buffer);
444                 }
445                 
446                 // Clear Input Buffer
447                 LOG("Clearing Input Buffer");
448                 // Status Values
449                 FDD_int_GetByte(base, &st0);
450                 FDD_int_GetByte(base, &st1);
451                 FDD_int_GetByte(base, &st2);
452                 
453                 // Cylinder, Head and Sector (mutilated in some way)
454                 FDD_int_GetByte(base, NULL);    // Cylinder
455                 FDD_int_GetByte(base, NULL);    // Head
456                 FDD_int_GetByte(base, NULL);    // Sector
457                 // Should be the BPS set above (0x02)
458                 FDD_int_GetByte(base, &bps);
459                 
460                 // Check Status
461                 // - Error Code
462                 if(st0 & 0xC0) {
463                         LOG("Error (st0 & 0xC0) \"%s\"", cFDD_STATUSES[st0 >> 6]);
464                         continue;
465                 }
466                 // - Status Flags
467                 if(st0 & 0x08) {        LOG("Drive not ready"); continue;       }
468                 if(st1 & 0x80) {        LOG("End of Cylinder"); continue;       }
469                 if(st1 & 0x20) {        LOG("CRC Error");       continue;       }
470                 if(st1 & 0x10) {        LOG("Controller Timeout");      continue;       }
471                 if(st1 & 0x04) {        LOG("No Data Found");   continue;       }
472                 if(st1 & 0x01 || st2 & 0x01) {
473                         LOG("No Address mark found");
474                         continue;
475                 }
476                 if(st2 & 0x40) {        LOG("Deleted address mark");    continue;       }
477                 if(st2 & 0x20) {        LOG("CRC error in data");       continue;       }
478                 if(st2 & 0x10) {        LOG("Wrong Cylinder");  continue;       }
479                 if(st2 & 0x04) {        LOG("uPD765 sector not found"); continue;       }
480                 if(st2 & 0x02) {        LOG("Bad Cylinder");    continue;       }
481                 
482                 if(bps != 0x2) {
483                         LOG("Returned BPS = 0x%02x, not 0x02", bps);
484                         continue;
485                 }
486                 
487                 if(st1 & 0x02) {
488                         LOG("Floppy not writable");
489                         // Return error without triggering the attempt count check
490                         i = FDD_MAX_READWRITE_ATTEMPTS+1;
491                         break;
492                 }
493                 
494                 // Success!
495                 break;
496         }
497         
498         // Release Spinlock
499         LOG("Realeasing Spinlock and setting motor to stop");
500         Mutex_Release(&glFDD);
501         
502         if(i == FDD_MAX_READWRITE_ATTEMPTS) {
503                 Log_Warning("FDD", "Exceeded %i attempts in %s the disk",
504                         FDD_MAX_READWRITE_ATTEMPTS,
505                         (Write ? "writing to" : "reading from")
506                         );
507         }
508         
509         // Don't turn the motor off now, wait for a while
510         gFDD_Devices[Disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotor, (void*)(tVAddr)Disk);
511
512         // Error check
513         if( i < FDD_MAX_READWRITE_ATTEMPTS ) {
514                 LEAVE('i', 0);
515                 return 0;
516         }
517         else {
518                 LEAVE('i', 1);
519                 return 1;
520         }
521 }
522
523 /**
524  * \fn int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer)
525  * \brief Read a sector from disk
526  * \todo Make real-hardware safe (account for read errors)
527 */
528 int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer)
529 {
530          int    ret;
531         
532         ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer);
533         
534         if( IOCache_Read( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer ) == 1 ) {
535                 LEAVE('i', 1);
536                 return 1;
537         }
538         
539         // Pass to general function
540         ret = FDD_int_ReadWriteSector(Disk, SectorAddr, 0, Buffer);
541
542         if( ret == 0 ) {
543                 IOCache_Add( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer );
544                 LEAVE('i', 1);
545                 return 1;
546         }
547         else {
548                 LOG("Reading failed");
549                 LEAVE('i', 0);
550                 return 0;
551         }
552 }
553
554 /**
555  * \fn int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer)
556  * \brief Write a sector to the floppy disk
557  * \note Not Implemented
558  */
559 int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer)
560 {
561         Log_Warning("FDD", "Read Only at the moment");
562         return -1;
563 }
564
565 /**
566  * \fn int FDD_int_SeekTrack(int disk, int track)
567  * \brief Seek disk to selected track
568  */
569 int FDD_int_SeekTrack(int disk, int head, int track)
570 {
571         Uint8   sr0, cyl;
572          int    base;
573         
574         base = cPORTBASE[disk>>1];
575         
576         // Check if seeking is needed
577         if(gFDD_Devices[disk].track[head] == track)
578                 return 1;
579         
580         // - Seek Head 0
581         FDD_int_SendByte(base, CMD_SEEK_TRACK);
582         FDD_int_SendByte(base, (head<<2)|(disk&1));
583         FDD_int_SendByte(base, track);  // Send Seek command
584         FDD_WaitIRQ();
585         FDD_SensInt(base, &sr0, &cyl);  // Wait for IRQ
586         if((sr0 & 0xF0) != 0x20) {
587                 LOG("sr0 = 0x%x", sr0);
588                 return 0;       //Check Status
589         }
590         if(cyl != track)        return 0;
591         
592         // Set Track in structure
593         gFDD_Devices[disk].track[head] = track;
594         
595         // Wait for Head to settle
596         Time_Delay(100);
597         
598         return 1;
599 }
600
601 /**
602  * \fn int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt)
603  * \brief Get Dimensions of a disk
604  */
605 int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt)
606 {
607         switch(type) {
608         case 0:
609                 return 0;
610         
611         // 360Kb 5.25"
612         case 1:
613                 *spt = 9;
614                 *s = (lba % 9) + 1;
615                 *c = lba / 18;
616                 *h = (lba / 9) & 1;
617                 break;
618         
619         // 1220Kb 5.25"
620         case 2:
621                 *spt = 15;
622                 *s = (lba % 15) + 1;
623                 *c = lba / 30;
624                 *h = (lba / 15) & 1;
625                 break;
626         
627         // 720Kb 3.5"
628         case 3:
629                 *spt = 9;
630                 *s = (lba % 9) + 1;
631                 *c = lba / 18;
632                 *h = (lba / 9) & 1;
633                 break;
634         
635         // 1440Kb 3.5"
636         case 4:
637                 *spt = 18;
638                 *s = (lba % 18) + 1;
639                 *c = lba / 36;
640                 *h = (lba / 18) & 1;
641                 //Log("1440k - lba=%i(0x%x), *s=%i,*c=%i,*h=%i", lba, lba, *s, *c, *h);
642                 break;
643                 
644         // 2880Kb 3.5"
645         case 5:
646                 *spt = 36;
647                 *s = (lba % 36) + 1;
648                 *c = lba / 72;
649                 *h = (lba / 32) & 1;
650                 break;
651                 
652         default:
653                 return -2;
654         }
655         return 1;
656 }
657
658 /**
659  * \fn void FDD_IRQHandler(int Num)
660  * \brief Handles IRQ6
661  */
662 void FDD_IRQHandler(int Num)
663 {
664         gbFDD_IrqFired = 1;
665 }
666
667 /**
668  * \fn FDD_WaitIRQ()
669  * \brief Wait for an IRQ6
670  */
671 inline void FDD_WaitIRQ()
672 {
673         // Wait for IRQ
674         while(!gbFDD_IrqFired)  Threads_Yield();
675         gbFDD_IrqFired = 0;
676 }
677
678 void FDD_SensInt(int base, Uint8 *sr0, Uint8 *cyl)
679 {
680         FDD_int_SendByte(base, CMD_SENSE_INTERRUPT);
681         FDD_int_GetByte(base, sr0);
682         FDD_int_GetByte(base, cyl);
683 }
684
685 /**
686  * void FDD_int_SendByte(int base, char byte)
687  * \brief Sends a command to the controller
688  */
689 int FDD_int_SendByte(int base, Uint8 byte)
690 {
691         tTime   end = now() + 1000;     // 1s
692         
693         while( (inb(base + PORT_MAINSTATUS) & 0xC0) != 0x80 && now() < end )
694                 Threads_Yield();        //Delay
695         
696         if( now() < end )
697         {
698                 outb(base + PORT_DATA, byte);
699 //              Log_Debug("FDD", "FDD_int_SendByte: Sent 0x%02x to 0x%x", byte, base);
700                 return 0;
701         }
702         else
703         {
704                 Log_Warning("FDD", "FDD_int_SendByte: Timeout sending byte 0x%x to base 0x%x", byte, base);
705                 return 1;
706         }
707 }
708
709 /**
710  * int FDD_int_GetByte(int base, char byte)
711  * \brief Receive data from fdd controller
712  */
713 int FDD_int_GetByte(int base, Uint8 *value)
714 {
715         tTime   end = now() + 1000;     // 1s
716         
717         while( (inb(base + PORT_MAINSTATUS) & 0xd0) != 0xd0 && now() < end )
718                 Threads_Yield();
719         
720         if( now() < end )
721         {
722                 Uint8   tmp = inb(base + PORT_DATA);
723                 if(value)       *value = tmp;
724 //              Log_Debug("FDD", "FDD_int_GetByte: Read 0x%02x from 0x%x", *value, base);
725                 return 0;
726         }
727         else
728         {
729                 Log_Warning("FDD", "FDD_int_GetByte: Timeout reading byte from base 0x%x", base);
730                 return -1;
731         }
732 }
733
734 /**
735  * \brief Recalibrate the specified disk
736  */
737 void FDD_Recalibrate(int disk)
738 {
739         ENTER("idisk", disk);
740         
741         LOG("Starting Motor");
742         FDD_int_StartMotor(disk);
743         // Wait for Spinup
744         while(gFDD_Devices[disk].motorState == 1)       Threads_Yield();
745         
746         LOG("Sending Calibrate Command");
747         FDD_int_SendByte(cPORTBASE[disk>>1], CMD_RECALIBRATE);
748         FDD_int_SendByte(cPORTBASE[disk>>1], disk&1);
749         
750         LOG("Waiting for IRQ");
751         FDD_WaitIRQ();
752         FDD_SensInt(cPORTBASE[disk>>1], NULL, NULL);
753         
754         LOG("Stopping Motor");
755         gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotor, (void*)(Uint)disk);
756         LEAVE('-');
757 }
758
759 /**
760  * \brief Reconfigure the controller
761  */
762 int FDD_Reconfigure(int ID)
763 {
764         Uint16  base = cPORTBASE[ID];
765         
766         ENTER("iID", ID);
767         
768         FDD_int_SendByte(base, CMD_CONFIGURE);
769         FDD_int_SendByte(base, 0);
770         // Implied seek enabled, FIFO Enabled, Drive Polling Disabled, data buffer threshold 8 bytes
771         FDD_int_SendByte(base, (1 << 6) | (0 << 5) | (0 << 4) | 7);
772         FDD_int_SendByte(base, 0);      // Precompensation - use default
773         
774         // Commit
775         FDD_int_SendByte(base, CMD_LOCK|CMD_FLAG_MULTI_TRACK);
776         FDD_int_GetByte(base, NULL);
777         
778         LEAVE('i', 0);
779         return 0;
780 }
781
782 /**
783  * \brief Reset the specified FDD controller
784  */
785 int FDD_Reset(int id)
786 {
787         Uint16  base = cPORTBASE[id];
788          int    retries;
789         
790         ENTER("iID", id);
791
792         // Reset the card       
793         outb(base + PORT_DIGOUTPUT, 0); // Disable FDC
794         // Wait 4 microseconds - or use 1 thread delay
795         Threads_Yield();
796         outb(base + PORT_DIGOUTPUT, 0x0C);      // Re-enable FDC (DMA and Enable)
797         
798         LOG("Awaiting IRQ");
799         
800         FDD_WaitIRQ();
801         FDD_SensInt(base, NULL, NULL);
802         FDD_SensInt(base, NULL, NULL);
803         FDD_SensInt(base, NULL, NULL);
804         FDD_SensInt(base, NULL, NULL);
805         
806         // Set the data rate
807         outb(base + PORT_DATARATE, 0);  // Set data rate to 500K/s
808
809         // Configure
810         FDD_int_SendByte(base, CMD_SPECIFY);    // Step and Head Load Times
811         FDD_int_SendByte(base, 0xDF);   // Step Rate Time, Head Unload Time (Nibble each)
812         FDD_int_SendByte(base, 0x02);   // Head Load Time >> 1
813
814         retries = 16;
815         while(FDD_int_SeekTrack(0, 0, 1) == 0 && retries --);   // set track
816         if(retries < 0) return -1;
817
818         retries = 16;
819         while(FDD_int_SeekTrack(0, 1, 1) == 0); // set track
820         if(retries < 0) return -1;
821         
822         LOG("Recalibrating Disk");
823         FDD_Recalibrate((id<<1)|0);
824         FDD_Recalibrate((id<<1)|1);
825
826         LEAVE('i',0);
827         return 0;
828 }
829
830 /**
831  * \fn void FDD_int_TimerCallback()
832  * \brief Called by timer
833  */
834 void FDD_int_TimerCallback(void *Arg)
835 {
836          int    disk = (Uint)Arg;
837         ENTER("iarg", disk);
838         if(gFDD_Devices[disk].motorState == 1)
839                 gFDD_Devices[disk].motorState = 2;
840         Time_RemoveTimer(gFDD_Devices[disk].timer);
841         gFDD_Devices[disk].timer = -1;
842         LEAVE('-');
843 }
844
845 /**
846  * \fn void FDD_int_StartMotor(char disk)
847  * \brief Starts FDD Motor
848  */
849 void FDD_int_StartMotor(int disk)
850 {
851         Uint8   state;
852         state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );
853         state |= 1 << (4+disk);
854         outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );
855         gFDD_Devices[disk].motorState = 1;
856         gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_ON_DELAY, FDD_int_TimerCallback, (void*)(Uint)disk);
857 }
858
859 /**
860  * \fn void FDD_int_StopMotor(int disk)
861  * \brief Stops FDD Motor
862  */
863 void FDD_int_StopMotor(void *Arg)
864 {
865         Uint8   state, disk = (Uint)Arg;
866         if( Mutex_IsLocked(&glFDD) )    return ;
867         ENTER("iDisk", disk);
868         
869         state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );
870         state &= ~( 1 << (4+disk) );
871         outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );
872         gFDD_Devices[disk].motorState = 0;
873         LEAVE('-');
874 }
875

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