More MP work (now APs start and use the LAPIC timer)
[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 enum FloppyCommands {
71         FIX_DRIVE_DATA  = 0x03,
72         HECK_DRIVE_STATUS       = 0x04,
73         CALIBRATE_DRIVE = 0x07,
74         CHECK_INTERRUPT_STATUS = 0x08,
75         SEEK_TRACK              = 0x0F,
76         READ_SECTOR_ID  = 0x4A,
77         FORMAT_TRACK    = 0x4D,
78         READ_TRACK              = 0x42,
79         READ_SECTOR             = 0x66,
80         WRITE_SECTOR    = 0xC5,
81         WRITE_DELETE_SECTOR     = 0xC9,
82         READ_DELETE_SECTOR      = 0xCC,
83 };
84
85 // === PROTOTYPES ===
86 // --- Filesystem
87  int    FDD_Install(char **Arguments);
88 void    FDD_UnloadModule();
89 // --- VFS Methods
90 char    *FDD_ReadDir(tVFS_Node *Node, int pos);
91 tVFS_Node       *FDD_FindDir(tVFS_Node *dirNode, 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);
99 // --- Helpers
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);
112
113 // === GLOBALS ===
114 MODULE_DEFINE(0, FDD_VERSION, FDD, FDD_Install, NULL, "ISADMA", NULL);
115 t_floppyDevice  gFDD_Devices[2];
116 tSpinlock       glFDD;
117 volatile int    gbFDD_IrqFired = 0;
118 tDevFS_Driver   gFDD_DriverInfo = {
119         NULL, "fdd",
120         {
121         .Size = -1,
122         .NumACLs = 1,
123         .ACLs = &gVFS_ACL_EveryoneRX,
124         .Flags = VFS_FFLAG_DIRECTORY,
125         .ReadDir = FDD_ReadDir,
126         .FindDir = FDD_FindDir,
127         .IOCtl = FDD_IOCtl
128         }
129 };
130
131 // === CODE ===
132 /**
133  * \fn int FDD_Install(char **Arguments)
134  * \brief Installs floppy driver
135  */
136 int FDD_Install(char **Arguments)
137 {
138         Uint8 data;
139         char    **args = Arguments;
140         
141         // Determine Floppy Types (From CMOS)
142         outb(0x70, 0x10);
143         data = inb(0x71);
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;
148         
149         if(args) {
150                 for(;*args;args++)
151                 {
152                         if(strcmp(*args, "disable")==0)
153                                 return MODULE_ERR_NOTNEEDED;
154                 }
155         }
156         
157         Log_Log("FDD", "Detected Disk 0: %s and Disk 1: %s", cFDD_TYPES[data>>4], cFDD_TYPES[data&0xF]);
158         
159         if( data == 0 ) {
160                 return MODULE_ERR_NOTNEEDED;
161         }
162         
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
168         FDD_Reset(0);
169         
170         // Initialise Root Node
171         gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime
172                 = gFDD_DriverInfo.RootNode.ATime = now();
173         
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));
181         
182         gFDD_Devices[1].Node.Inode = 1;
183         
184         // Set Lengths
185         gFDD_Devices[0].Node.Size = cFDD_SIZES[data >> 4];
186         gFDD_Devices[1].Node.Size = cFDD_SIZES[data & 0xF];
187         
188         // Create Sector Cache
189         if( cFDD_SIZES[data >> 4] )
190         {
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
195         }
196         if( cFDD_SIZES[data & 15] )
197         {
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
202         }
203         
204         // Register with devfs
205         DevFS_AddDevice(&gFDD_DriverInfo);
206         
207         return MODULE_ERR_OK;
208 }
209
210 /**
211  * \brief Prepare the module for removal
212  */
213 void FDD_UnloadModule()
214 {
215          int    i;
216         //DevFS_DelDevice( &gFDD_DriverInfo );
217         LOCK(&glFDD);
218         for(i=0;i<4;i++) {
219                 Time_RemoveTimer(gFDD_Devices[i].timer);
220                 FDD_int_StopMotor((void *)(Uint)i);
221         }
222         RELEASE(&glFDD);
223         //IRQ_Clear(6);
224 }
225
226 /**
227  * \fn char *FDD_ReadDir(tVFS_Node *Node, int pos)
228  * \brief Read Directory
229  */
230 char *FDD_ReadDir(tVFS_Node *Node, int Pos)
231 {
232         char    name[2] = "0\0";
233
234         if(Pos >= 2 || Pos < 0) return NULL;
235         
236         if(gFDD_Devices[Pos].type == 0) return VFS_SKIP;
237         
238         name[0] += Pos;
239         
240         return strdup(name);
241 }
242
243 /**
244  * \fn tVFS_Node *FDD_FindDir(tVFS_Node *Node, char *filename);
245  * \brief Find File Routine (for vfs_node)
246  */
247 tVFS_Node *FDD_FindDir(tVFS_Node *Node, char *Filename)
248 {
249          int    i;
250         
251         ENTER("sFilename", Filename);
252         
253         // Sanity check string
254         if(Filename == NULL) {
255                 LEAVE('n');
256                 return NULL;
257         }
258         
259         // Check string length (should be 1)
260         if(Filename[0] == '\0' || Filename[1] != '\0') {
261                 LEAVE('n');
262                 return NULL;
263         }
264         
265         // Get First character
266         i = Filename[0] - '0';
267         
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;
272         }
273         
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;
278         }
279         
280         // Else return null
281         LEAVE('n');
282         return NULL;
283 }
284
285 static const char       *casIOCTLS[] = {DRV_IOCTLNAMES,DRV_DISK_IOCTLNAMES,NULL};
286 /**
287  * \fn int FDD_IOCtl(tVFS_Node *Node, int id, void *data)
288  * \brief Stub ioctl function
289  */
290 int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data)
291 {
292         switch(ID)
293         {
294         case DRV_IOCTL_TYPE:    return DRV_TYPE_DISK;
295         case DRV_IOCTL_IDENT:   return ModUtil_SetIdent(Data, "FDD");
296         case DRV_IOCTL_VERSION: return FDD_VERSION;
297         case DRV_IOCTL_LOOKUP:  return ModUtil_LookupString((char**)casIOCTLS, Data);
298         
299         case DISK_IOCTL_GETBLOCKSIZE:   return 512;     
300         
301         default:
302                 return 0;
303         }
304 }
305
306 /**
307  * \fn Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
308  * \brief Read Data from a disk
309 */
310 Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
311 {
312          int    ret;
313         
314         ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
315         
316         if(Node == NULL) {
317                 LEAVE('i', -1);
318                 return -1;
319         }
320         
321         if(Node->Inode != 0 && Node->Inode != 1) {
322                 LEAVE('i', -1);
323                 return -1;
324         }
325         
326         ret = DrvUtil_ReadBlock(Offset, Length, Buffer, FDD_ReadSectors, 512, Node->Inode);
327         LEAVE('i', ret);
328         return ret;
329 }
330
331 /**
332  * \brief Reads \a Count contiguous sectors from a disk
333  * \param SectorAddr    Address of the first sector
334  * \param Count Number of sectors to read
335  * \param Buffer        Destination Buffer
336  * \param Disk  Disk Number
337  * \return Number of sectors read
338  * \note Used as a ::DrvUtil_ReadBlock helper
339  */
340 Uint FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk)
341 {
342         Uint    ret = 0;
343         while(Count --)
344         {
345                 if( FDD_ReadSector(Disk, SectorAddr, Buffer) != 1 )
346                         return ret;
347                 
348                 Buffer = (void*)( (tVAddr)Buffer + 512 );
349                 SectorAddr ++;
350                 ret ++;
351         }
352         return ret;
353 }
354
355 int FDD_int_ReadWriteSector(Uint32 Disk, Uint64 SectorAddr, int Write, void *Buffer)
356 {
357          int    cyl, head, sec;
358          int    spt, base;
359          int    i;
360          int    lba = SectorAddr;
361         Uint8   st0, st1, st2, rcy, rhe, rse, bps;      //      Status Values
362         
363         ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer);
364         
365         base = cPORTBASE[Disk >> 1];
366         
367         LOG("Calculating Disk Dimensions");
368         // Get CHS position
369         if(FDD_int_GetDims(gFDD_Devices[Disk].type, lba, &cyl, &head, &sec, &spt) != 1)
370         {
371                 LEAVE('i', -1);
372                 return -1;
373         }
374         LOG("Cyl=%i, Head=%i, Sector=%i", cyl, head, sec);
375         
376         LOCK(&glFDD);   // Lock to stop the motor stopping on us
377         Time_RemoveTimer(gFDD_Devices[Disk].timer);     // Remove Old Timer
378         // Start motor if needed
379         if(gFDD_Devices[Disk].motorState != 2)  FDD_int_StartMotor(Disk);
380         RELEASE(&glFDD);
381         
382         LOG("Wait for the motor to spin up");
383         
384         // Wait for spinup
385         while(gFDD_Devices[Disk].motorState == 1)       Threads_Yield();
386         
387         LOG("Acquire Spinlock");
388         LOCK(&glFDD);
389         
390         // Seek to track
391         outb(base + CALIBRATE_DRIVE, 0);
392         i = 0;
393         while(FDD_int_SeekTrack(Disk, head, (Uint8)cyl) == 0 && i++ < FDD_SEEK_TIMEOUT )
394                 Threads_Yield();
395         if( i > FDD_SEEK_TIMEOUT ) {
396                 RELEASE(&glFDD);
397                 LEAVE('i', 0);
398                 return 0;
399         }
400         //FDD_SensInt(base, NULL, NULL);        // Wait for IRQ
401                 
402         // Read Data from DMA
403         LOG("Setting DMA for read");
404         DMA_SetChannel(2, 512, !Write); // Read 512 Bytes from channel 2
405         
406         LOG("Sending command");
407         
408         //Threads_Wait(100);    // Wait for Head to settle
409         Time_Delay(100);
410         
411         for( i = 0; i < FDD_MAX_READWRITE_ATTEMPTS; i ++ )
412         {
413                 if( Write )
414                         FDD_int_SendByte(base, READ_SECTOR);    // Was 0xE6
415                 else
416                         FDD_int_SendByte(base, READ_SECTOR);    // Was 0xE6
417                 FDD_int_SendByte(base, (head << 2) | (Disk&1));
418                 FDD_int_SendByte(base, (Uint8)cyl);
419                 FDD_int_SendByte(base, (Uint8)head);
420                 FDD_int_SendByte(base, (Uint8)sec);
421                 FDD_int_SendByte(base, 0x02);   // Bytes Per Sector (Real BPS=128*2^{val})
422                 FDD_int_SendByte(base, spt);    // SPT
423                 FDD_int_SendByte(base, 0x1B);   // Gap Length (27 is default)
424                 FDD_int_SendByte(base, 0xFF);   // Data Length
425                 
426                 // Wait for IRQ
427                 if( Write ) {
428                         LOG("Writing Data");
429                         DMA_WriteData(2, 512, Buffer);
430                         LOG("Waiting for Data to be written");
431                         FDD_WaitIRQ();
432                 }
433                 else {
434                         LOG("Waiting for data to be read");
435                         FDD_WaitIRQ();
436                         LOG("Reading Data");
437                         DMA_ReadData(2, 512, Buffer);
438                 }
439                 
440                 // Clear Input Buffer
441                 LOG("Clearing Input Buffer");
442                 // Status Values
443                 st0 = FDD_int_GetByte(base);
444                 st1 = FDD_int_GetByte(base);
445                 st2 = FDD_int_GetByte(base);
446                 
447                 // Cylinder, Head and Sector (mutilated in some way
448                 rcy = FDD_int_GetByte(base);
449                 rhe = FDD_int_GetByte(base);
450                 rse = FDD_int_GetByte(base);
451                 // Should be the BPS set above (0x02)
452                 bps = FDD_int_GetByte(base);
453                 
454                 // Check Status
455                 // - Error Code
456                 if(st0 & 0xC0) {
457                         LOG("Error (st0 & 0xC0) \"%s\"", cFDD_STATUSES[st0 >> 6]);
458                         continue;
459             }
460             // - Status Flags
461             if(st0 & 0x08) {    LOG("Drive not ready"); continue;       }
462             if(st1 & 0x80) {    LOG("End of Cylinder"); continue;       }
463                 if(st1 & 0x20) {        LOG("CRC Error");       continue;       }
464                 if(st1 & 0x10) {        LOG("Controller Timeout");      continue;       }
465                 if(st1 & 0x04) {        LOG("No Data Found");   continue;       }
466                 if(st1 & 0x01 || st2 & 0x01) {
467                         LOG("No Address mark found");
468                         continue;
469                 }
470             if(st2 & 0x40) {    LOG("Deleted address mark");    continue;       }
471                 if(st2 & 0x20) {        LOG("CRC error in data");       continue;       }
472                 if(st2 & 0x10) {        LOG("Wrong Cylinder");  continue;       }
473                 if(st2 & 0x04) {        LOG("uPD765 sector not found"); continue;       }
474                 if(st2 & 0x02) {        LOG("Bad Cylinder");    continue;       }
475                 
476                 if(bps != 0x2) {
477                         LOG("Returned BPS = 0x%02x, not 0x02", bps);
478                         continue;
479                 }
480                 
481                 if(st1 & 0x02) {
482                         LOG("Floppy not writable");
483                         i = FDD_MAX_READWRITE_ATTEMPTS+1;
484                         break;
485                 }
486                 
487                 // Success!
488                 break;
489         }
490         
491         // Release Spinlock
492         LOG("Realeasing Spinlock and setting motor to stop");
493         RELEASE(&glFDD);
494         
495         if(i == FDD_MAX_READWRITE_ATTEMPTS) {
496                 Log_Warning("FDD", "Exceeded %i attempts in %s the disk",
497                         FDD_MAX_READWRITE_ATTEMPTS,
498                         (Write ? "writing to" : "reading from")
499                         );
500         }
501         
502         // Don't turn the motor off now, wait for a while
503         gFDD_Devices[Disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotor, (void*)(tVAddr)Disk);
504
505         if( i < FDD_MAX_READWRITE_ATTEMPTS ) {
506                 LEAVE('i', 0);
507                 return 0;
508         }
509         else {
510                 LEAVE('i', 1);
511                 return 1;
512         }
513 }
514
515 /**
516  * \fn int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer)
517  * \brief Read a sector from disk
518  * \todo Make real-hardware safe (account for read errors)
519 */
520 int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer)
521 {
522          int    ret;
523         
524         ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer);
525         
526         if( IOCache_Read( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer ) == 1 ) {
527                 LEAVE('i', 1);
528                 return 1;
529         }
530         
531         // Pass to general function
532         ret = FDD_int_ReadWriteSector(Disk, SectorAddr, 0, Buffer);
533
534         if( ret == 0 ) {
535                 IOCache_Add( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer );
536                 LEAVE('i', 1);
537                 return 1;
538         }
539         else {
540                 LOG("Reading failed");
541                 LEAVE('i', 0);
542                 return 0;
543         }
544 }
545
546 /**
547  * \fn int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer)
548  * \brief Write a sector to the floppy disk
549  * \note Not Implemented
550  */
551 int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer)
552 {
553         Warning("[FDD  ] Read Only at the moment");
554         return -1;
555 }
556
557 /**
558  * \fn int FDD_int_SeekTrack(int disk, int track)
559  * \brief Seek disk to selected track
560  */
561 int FDD_int_SeekTrack(int disk, int head, int track)
562 {
563         Uint8   sr0, cyl;
564          int    base;
565         
566         base = cPORTBASE[disk>>1];
567         
568         // Check if seeking is needed
569         if(gFDD_Devices[disk].track[head] == track)
570                 return 1;
571         
572         // - Seek Head 0
573         FDD_int_SendByte(base, SEEK_TRACK);
574         FDD_int_SendByte(base, (head<<2)|(disk&1));
575         FDD_int_SendByte(base, track);  // Send Seek command
576         FDD_WaitIRQ();
577         FDD_SensInt(base, &sr0, &cyl);  // Wait for IRQ
578         if((sr0 & 0xF0) != 0x20) {
579                 LOG("sr0 = 0x%x", sr0);
580                 return 0;       //Check Status
581         }
582         if(cyl != track)        return 0;
583         
584         // Set Track in structure
585         gFDD_Devices[disk].track[head] = track;
586         return 1;
587 }
588
589 /**
590  * \fn int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt)
591  * \brief Get Dimensions of a disk
592  */
593 int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt)
594 {
595         switch(type) {
596         case 0:
597                 return 0;
598         
599         // 360Kb 5.25"
600         case 1:
601                 *spt = 9;
602                 *s = (lba % 9) + 1;
603                 *c = lba / 18;
604                 *h = (lba / 9) & 1;
605                 break;
606         
607         // 1220Kb 5.25"
608         case 2:
609                 *spt = 15;
610                 *s = (lba % 15) + 1;
611                 *c = lba / 30;
612                 *h = (lba / 15) & 1;
613                 break;
614         
615         // 720Kb 3.5"
616         case 3:
617                 *spt = 9;
618                 *s = (lba % 9) + 1;
619                 *c = lba / 18;
620                 *h = (lba / 9) & 1;
621                 break;
622         
623         // 1440Kb 3.5"
624         case 4:
625                 *spt = 18;
626                 *s = (lba % 18) + 1;
627                 *c = lba / 36;
628                 *h = (lba / 18) & 1;
629                 //Log("1440k - lba=%i(0x%x), *s=%i,*c=%i,*h=%i", lba, lba, *s, *c, *h);
630                 break;
631                 
632         // 2880Kb 3.5"
633         case 5:
634                 *spt = 36;
635                 *s = (lba % 36) + 1;
636                 *c = lba / 72;
637                 *h = (lba / 32) & 1;
638                 break;
639                 
640         default:
641                 return -2;
642         }
643         return 1;
644 }
645
646 /**
647  * \fn void FDD_IRQHandler(int Num)
648  * \brief Handles IRQ6
649  */
650 void FDD_IRQHandler(int Num)
651 {
652         gbFDD_IrqFired = 1;
653 }
654
655 /**
656  * \fn FDD_WaitIRQ()
657  * \brief Wait for an IRQ6
658  */
659 inline void FDD_WaitIRQ()
660 {
661         // Wait for IRQ
662         while(!gbFDD_IrqFired)  Threads_Yield();
663         gbFDD_IrqFired = 0;
664 }
665
666 void FDD_SensInt(int base, Uint8 *sr0, Uint8 *cyl)
667 {
668         FDD_int_SendByte(base, CHECK_INTERRUPT_STATUS);
669         if(sr0) *sr0 = FDD_int_GetByte(base);
670         else    FDD_int_GetByte(base);
671         if(cyl) *cyl = FDD_int_GetByte(base);
672         else    FDD_int_GetByte(base);
673 }
674
675 /**
676  * void FDD_int_SendByte(int base, char byte)
677  * \brief Sends a command to the controller
678  */
679 void FDD_int_SendByte(int base, char byte)
680 {
681         volatile int state;
682         int timeout = 128;
683         for( ; timeout--; )
684         {
685             state = inb(base + PORT_MAINSTATUS);
686             if ((state & 0xC0) == 0x80)
687             {
688                 outb(base + PORT_DATA, byte);
689                 return;
690             }
691             inb(0x80);  //Delay
692         }
693         
694         #if WARN
695         Warning("FDD_int_SendByte - Timeout sending byte 0x%x to base 0x%x\n", byte, base);
696         #endif
697 }
698
699 /**
700  * int FDD_int_GetByte(int base, char byte)
701  * \brief Receive data from fdd controller
702  */
703 int FDD_int_GetByte(int base)
704 {
705         volatile int state;
706         int timeout;
707         for( timeout = 128; timeout--; )
708         {
709             state = inb((base + PORT_MAINSTATUS));
710             if ((state & 0xd0) == 0xd0)
711                     return inb(base + PORT_DATA);
712             inb(0x80);
713         }
714         return -1;
715 }
716
717 /**
718  * \brief Recalibrate the specified disk
719  */
720 void FDD_Recalibrate(int disk)
721 {
722         ENTER("idisk", disk);
723         
724         LOG("Starting Motor");
725         FDD_int_StartMotor(disk);
726         // Wait for Spinup
727         while(gFDD_Devices[disk].motorState == 1)       Threads_Yield();
728         
729         LOG("Sending Calibrate Command");
730         FDD_int_SendByte(cPORTBASE[disk>>1], CALIBRATE_DRIVE);
731         FDD_int_SendByte(cPORTBASE[disk>>1], disk&1);
732         
733         LOG("Waiting for IRQ");
734         FDD_WaitIRQ();
735         FDD_SensInt(cPORTBASE[disk>>1], NULL, NULL);
736         
737         LOG("Stopping Motor");
738         gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotor, (void*)(Uint)disk);
739         LEAVE('-');
740 }
741
742 /**
743  * \brief Reset the specified FDD controller
744  */
745 void FDD_Reset(int id)
746 {
747         int base = cPORTBASE[id];
748         
749         ENTER("iID", id);
750         
751         outb(base + PORT_DIGOUTPUT, 0); // Stop Motors & Disable FDC
752         outb(base + PORT_DIGOUTPUT, 0x0C);      // Re-enable FDC (DMA and Enable)
753         
754         LOG("Awaiting IRQ");
755         
756         FDD_WaitIRQ();
757         FDD_SensInt(base, NULL, NULL);
758         
759         LOG("Setting Driver Info");
760         outb(base + PORT_DATARATE, 0);  // Set data rate to 500K/s
761         FDD_int_SendByte(base, FIX_DRIVE_DATA); // Step and Head Load Times
762         FDD_int_SendByte(base, 0xDF);   // Step Rate Time, Head Unload Time (Nibble each)
763         FDD_int_SendByte(base, 0x02);   // Head Load Time >> 1
764         while(FDD_int_SeekTrack(0, 0, 1) == 0); // set track
765         while(FDD_int_SeekTrack(0, 1, 1) == 0); // set track
766         
767         LOG("Recalibrating Disk");
768         FDD_Recalibrate((id<<1)|0);
769         FDD_Recalibrate((id<<1)|1);
770
771         LEAVE('-');
772 }
773
774 /**
775  * \fn void FDD_int_TimerCallback()
776  * \brief Called by timer
777  */
778 void FDD_int_TimerCallback(void *Arg)
779 {
780          int    disk = (Uint)Arg;
781         ENTER("iarg", disk);
782         if(gFDD_Devices[disk].motorState == 1)
783                 gFDD_Devices[disk].motorState = 2;
784         Time_RemoveTimer(gFDD_Devices[disk].timer);
785         gFDD_Devices[disk].timer = -1;
786         LEAVE('-');
787 }
788
789 /**
790  * \fn void FDD_int_StartMotor(char disk)
791  * \brief Starts FDD Motor
792  */
793 void FDD_int_StartMotor(int disk)
794 {
795         Uint8   state;
796         state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );
797         state |= 1 << (4+disk);
798         outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );
799         gFDD_Devices[disk].motorState = 1;
800         gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_ON_DELAY, FDD_int_TimerCallback, (void*)(Uint)disk);
801 }
802
803 /**
804  * \fn void FDD_int_StopMotor(int disk)
805  * \brief Stops FDD Motor
806  */
807 void FDD_int_StopMotor(void *Arg)
808 {
809         Uint8   state, disk = (Uint)Arg;
810         if( IS_LOCKED(&glFDD) ) return ;
811         ENTER("iDisk", disk);
812         
813         state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );
814         state &= ~( 1 << (4+disk) );
815         outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );
816         gFDD_Devices[disk].motorState = 0;
817         LEAVE('-');
818 }

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