3 * - By John Hodge (thePowersGang)
15 #define MOTOR_ON_DELAY 500
16 #define MOTOR_OFF_DELAY 2000
26 FDC_DOR = 0x02, // Digital Output Register
27 FDC_MSR = 0x04, // Master Status Register (Read Only)
28 FDC_FIFO = 0x05, // FIFO port
29 FDC_CCR = 0x07 // Configuration Control Register (write only)
34 CMD_SPECIFY = 3, // Specify parameters
35 CMD_WRITE_DATA = 5, // Write Data
36 CMD_READ_DATA = 6, // Read Data
37 CMD_RECALIBRATE = 7, // Recalibrate a drive
38 CMD_SENSE_INTERRUPT = 8, // Sense (Ack) an interrupt
39 CMD_SEEK = 15, // Seek to a track
43 int FDD_SetupIO(void);
44 int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer);
45 int FDD_int_SeekToTrack(int Disk, int Track);
46 int FDD_int_Calibrate(int Disk);
47 int FDD_int_Reset(int Disk);
49 int FDD_int_WriteData(Uint16 Base, Uint8 Data);
50 int FDD_int_ReadData(Uint16 Base, Uint8 *Data);
51 void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl);
53 int FDD_int_StartMotor(int Disk);
54 int FDD_int_StopMotor(int Disk);
55 void FDD_int_StopMotorCallback(void *Ptr);
57 int FDD_int_HandleST0Error(const char *Fcn, int Disk, Uint8 ST0);
58 Uint16 FDD_int_GetBase(int Disk, int *Drive);
60 void FDD_int_ClearIRQ(void);
61 int FDD_int_WaitIRQ(void);
62 void FDD_int_IRQHandler(int IRQ, void *Ptr);
66 * \brief Marker for IRQ6
67 * \todo Convert into a semaphore?
71 * \brief Protector for DMA and IRQ6
77 * \brief Set up FDC IO
78 * \return Boolean failure
80 * Registers the IRQ handler and resets the controller
84 // Install IRQ6 Handler
85 IRQ_AddHandler(6, FDD_int_IRQHandler, NULL);
87 for( int i = 0; i < 2; i ++ )
89 if( !gaFDD_Disks[i].bValid ) continue ;
91 gaFDD_Disks[i].Timer = Time_AllocateTimer(FDD_int_StopMotorCallback, (void*)(tVAddr)i);
96 // TODO: All controllers
102 * \brief Read/Write data from/to a disk
103 * \param Disk Global disk number
104 * \param Track Track number (Cyl*2+Head)
105 * \param bWrite Toggle write mode
106 * \param Buffer Destination/Source buffer
107 * \return Boolean failure
109 int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer)
113 Uint16 base = FDD_int_GetBase(Disk, &_disk);
114 int cyl = Track >> 1, head = Track & 1;
116 ENTER("iDisk iTrack ibWrite pBuffer", Disk, Track, bWrite, Buffer);
118 Mutex_Acquire( &gFDD_IOMutex );
120 // Initialise DMA for read/write
121 // TODO: Support non 1.44MiB FDs
122 DMA_SetChannel(2, BYTES_PER_TRACK, !bWrite);
126 cmd = CMD_WRITE_DATA | 0xC0;
128 cmd = CMD_READ_DATA | 0xC0;
130 LOG("cmd = 0x%x", cmd);
133 if( FDD_int_SeekToTrack(Disk, Track) ) {
134 Mutex_Release( &gFDD_IOMutex );
138 LOG("Track seek done");
140 for( i = 0; i < 20; i ++ )
142 LOG("Starting motor");
143 FDD_int_StartMotor(Disk);
147 DMA_WriteData(2, BYTES_PER_TRACK, Buffer);
149 LOG("Sending command stream");
150 FDD_int_WriteData(base, cmd);
151 FDD_int_WriteData(base, (head << 2) | _disk);
152 FDD_int_WriteData(base, cyl);
153 FDD_int_WriteData(base, head);
154 FDD_int_WriteData(base, 1); // First Sector
155 FDD_int_WriteData(base, 2); // Bytes per sector (128*2^n)
156 FDD_int_WriteData(base, 18); // 18 tracks (full disk) - TODO: Non 1.44
157 FDD_int_WriteData(base, 0x1B); // Gap length - TODO: again
158 FDD_int_WriteData(base, 0xFF); // Data length - ?
160 LOG("Waiting for IRQ");
163 // No Sense Interrupt
165 LOG("Reading result");
166 Uint8 st0=0, st1=0, st2=0, bps=0;
167 FDD_int_ReadData(base, &st0);
168 FDD_int_ReadData(base, &st1); // st1
169 FDD_int_ReadData(base, &st2); // st2
170 FDD_int_ReadData(base, NULL); // rcy - Mutilated Cyl
171 FDD_int_ReadData(base, NULL); // rhe - Mutilated Head
172 FDD_int_ReadData(base, NULL); // rse - Mutilated sector
173 FDD_int_ReadData(base, &bps); // bps - Should be the same as above
176 FDD_int_HandleST0Error(__func__, Disk, st0);
181 Log_Debug("FDD", "Disk %i is not writable", Disk);
182 Mutex_Release( &gFDD_IOMutex );
188 Log_Debug("FDD", "FDD_int_ReadWriteTrack: Drive not ready");
194 Log_Debug("FDD", "FDD_int_ReadWriteTrack: End of cylinder");
198 if( st1 & (0x20|0x10|0x04|0x01) ) {
199 Log_Debug("FDD", "FDD_int_ReadWriteTrack: st1 = 0x%x", st1);
203 if( st2 & (0x40|0x20|0x10|0x04|0x01) ) {
204 Log_Debug("FDD", "FDD_int_ReadWriteTrack: st2 = 0x%x", st2);
209 Log_Debug("FDD", "Wanted bps = 2 (512), got %i", bps);
215 DMA_ReadData(2, BYTES_PER_TRACK, Buffer);
217 LOG("All data done");
218 FDD_int_StopMotor(Disk);
219 Mutex_Release( &gFDD_IOMutex );
224 Log_Debug("FDD", "%i retries exhausted", i);
225 FDD_int_StopMotor(Disk);
226 Mutex_Release( &gFDD_IOMutex );
232 * \brief Seek to a specific track
233 * \param Disk Global disk number
234 * \param Track Track number (Cyl*2+Head)
235 * \return Boolean failure
237 int FDD_int_SeekToTrack(int Disk, int Track)
239 Uint8 st0=0, res_cyl=0;
242 Uint16 base = FDD_int_GetBase(Disk, &_disk);;
244 ENTER("iDisk iTrack", Disk, Track);
249 LOG("cyl = %i, head = %i", cyl, head);
251 FDD_int_StartMotor(Disk);
253 for( int i = 0; i < 10; i ++ )
255 LOG("Sending command");
257 FDD_int_WriteData(base, CMD_SEEK);
258 FDD_int_WriteData(base, (head << 2) + _disk);
259 FDD_int_WriteData(base, cyl);
261 LOG("Waiting for IRQ");
263 FDD_int_SenseInterrupt(base, &st0, &res_cyl);
267 FDD_int_HandleST0Error(__func__, Disk, st0);
271 if( res_cyl == cyl ) {
272 FDD_int_StopMotor(Disk);
278 Log_Error("FDD", "FDD_int_SeekToTrack: 10 retries exhausted\n");
279 FDD_int_StopMotor(Disk);
285 * \brief Calibrate a drive
286 * \param Disk Global disk number
288 int FDD_int_Calibrate(int Disk)
291 Uint16 base = FDD_int_GetBase(Disk, &_disk);
292 FDD_int_StartMotor(Disk);
294 for( int i = 0; i < 10; i ++ )
296 Uint8 st0=0, cyl = -1;
299 FDD_int_WriteData(base, CMD_RECALIBRATE);
300 FDD_int_WriteData(base, _disk);
304 FDD_int_SenseInterrupt(base, &st0, &cyl);
307 FDD_int_HandleST0Error(__func__, Disk, st0);
313 FDD_int_StopMotor(Disk);
318 Log_Error("FDD", "FDD_int_Calibrate: Retries exhausted");
324 * \brief Reset a controller
325 * \param Base Controller base address
327 int FDD_int_Reset(int Disk)
331 Uint16 base = FDD_int_GetBase(Disk, &_disk);
333 tmp = inb(base + FDC_DOR) & 0xF0;
334 outb( base + FDC_DOR, 0x00 );
336 outb( base + FDC_DOR, tmp | 0x0C );
338 FDD_int_SenseInterrupt(base, NULL, NULL);
340 outb(base + FDC_CCR, 0x00); // 500KB/s
342 FDD_int_WriteData(base, CMD_SPECIFY); // Step and Head Load Times
343 FDD_int_WriteData(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each)
344 FDD_int_WriteData(base, 0x02); // Head Load Time >> 1
346 // TODO: Recalibrate all present disks
347 FDD_int_Calibrate(Disk);
352 * \brief Write a byte to the FIFO
354 int FDD_int_WriteData(Uint16 Base, Uint8 Data)
356 for( int i = 0; i < 100; i ++ )
358 if( inb(Base + FDC_MSR) & 0x80 )
360 outb(Base + FDC_FIFO, Data);
365 Log_Error("FDD", "Write timeout");
370 * \brief Read a byte from the FIFO
372 int FDD_int_ReadData(Uint16 Base, Uint8 *Data)
374 for( int i = 0; i < 100; i ++ )
376 if( inb(Base + FDC_MSR) & 0x80 )
378 Uint8 tmp = inb(Base + FDC_FIFO);
379 if(Data) *Data = tmp;
384 Log_Error("FDD", "Read timeout");
389 * \brief Acknowledge an interrupt
390 * \param Base Controller base address
391 * \param ST0 Location to store the ST0 value
392 * \param Cyl Current cylinder
394 void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl)
396 FDD_int_WriteData(Base, CMD_SENSE_INTERRUPT);
397 FDD_int_ReadData(Base, ST0);
398 FDD_int_ReadData(Base, Cyl);
402 * \brief Start the motor on a disk
404 int FDD_int_StartMotor(int Disk)
407 Uint16 base = FDD_int_GetBase(Disk, &_disk);
409 // Clear the motor off timer
410 Time_RemoveTimer(gaFDD_Disks[Disk].Timer);
412 // Check if the motor is already on
413 if( gaFDD_Disks[Disk].MotorState == MOTOR_ATSPEED )
417 outb(base + FDC_DOR, inb(base+FDC_DOR) | (1 << (_disk + 4)));
419 // Wait for it to reach speed
420 Time_Delay(MOTOR_ON_DELAY);
422 gaFDD_Disks[Disk].MotorState = MOTOR_ATSPEED;
428 * \brief Schedule the motor to stop
430 int FDD_int_StopMotor(int Disk)
432 if( gaFDD_Disks[Disk].MotorState != MOTOR_ATSPEED )
435 Time_ScheduleTimer(gaFDD_Disks[Disk].Timer, MOTOR_OFF_DELAY);
441 * \brief Actually stop the motor
442 * \param Ptr Actaully the global disk number
444 void FDD_int_StopMotorCallback(void *Ptr)
446 int Disk = (tVAddr)Ptr;
448 Uint16 base = FDD_int_GetBase(Disk, &_disk);
450 gaFDD_Disks[Disk].MotorState = MOTOR_OFF;
452 outb(base + FDC_DOR, inb(base+FDC_DOR) & ~(1 << (_disk + 4)));
458 * \brief Converts a global disk number into a controller and drive
459 * \param Disk Global disk number
460 * \param Drive Destination for controller disk number
461 * \return Controller base address
463 Uint16 FDD_int_GetBase(int Disk, int *Drive)
465 if(Drive) *Drive = Disk & 3;
468 case 0: return 0x3F0;
469 case 1: return 0x370;
476 * \brief Convert a ST0 error value into a message
477 * \param Fcn Calling function name
478 * \parma Disk Global disk number
479 * \param ST0 ST0 Value
480 * \return Boolean failure
482 int FDD_int_HandleST0Error(const char *Fcn, int Disk, Uint8 ST0)
484 static const char *status_type[] = {
485 0, "Error", "Invalid", "Drive Error"
488 Log_Debug("FDD", "%s: Disk %i ST0 Status = %s (0x%x & 0xC0 = 0x%x)",
489 Fcn, Disk, status_type[ST0 >> 6], ST0, ST0 & 0xC0
495 * \brief Clear the IRQ fired flag
497 void FDD_int_ClearIRQ(void)
503 * \brief Wait for an IRQ to fire
505 int FDD_int_WaitIRQ(void)
507 while(gbFDD_IRQ6Fired == 0)
514 * \param IRQ IRQ Number (unused)
515 * \param Ptr Data Pointer (unused)
517 void FDD_int_IRQHandler(int IRQ, void *Ptr)