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

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