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

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