3 * - By John Hodge (thePowersGang)
12 #define MOTOR_ON_DELAY 500
13 #define MOTOR_OFF_DELAY 2000
23 FDC_DOR = 0x02, // Digital Output Register
24 FDC_MSR = 0x04, // Master Status Register (Read Only)
25 FDC_FIFO = 0x05, // FIFO port
26 FDC_CCR = 0x07 // Configuration Control Register (write only)
31 CMD_SPECIFY = 3, // Specify parameters
32 CMD_WRITE_DATA = 5, // Write Data
33 CMD_READ_DATA = 6, // Read Data
34 CMD_RECALIBRATE = 7, // Recalibrate a drive
35 CMD_SENSE_INTERRUPT = 8, // Sense (Ack) an interrupt
36 CMD_SEEK = 15, // Seek to a track
40 int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer);
42 int FDD_int_WriteData(Uint16 Base, Uint8 Data);
43 int FDD_int_ReadData(Uint16 Base, Uint8 *Data);
44 void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl);
45 int FDD_int_Calibrate(int Disk);
46 int FDD_int_Reset(Uint16 Base);
47 int FDD_int_StartMotor(int Disk);
48 int FDD_int_StopMotor(int Disk);
49 void FDD_int_StopMotorCallback(void *Ptr);
50 Uint16 FDD_int_GetBase(int Disk, int *Drive);
51 void FDD_int_ClearIRQ(void);
52 int FDD_int_WaitIRQ(void);
53 void FDD_int_IRQHandler(int IRQ, void *Ptr);
60 * \brief Read/Write data from/to a disk
61 * \param Disk Global disk number
62 * \param Track Track number
63 * \param bWrite Toggle write mode
64 * \param Buffer Destination/Source buffer
65 * \return Boolean failure
67 int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer)
73 * \brief Write a byte to the FIFO
75 int FDD_int_WriteData(Uint16 Base, Uint8 Data)
77 for( int i = 0; i < 100; i ++ )
79 if( inb(Base + FDC_MSR) & 0x80 )
81 outb(Base + FDC_FIFO, Data);
86 Log_Error("FDD", "Write timeout");
91 * \brief Read a byte from the FIFO
93 int FDD_int_ReadData(Uint16 Base, Uint8 *Data)
95 for( int i = 0; i < 100; i ++ )
97 if( inb(Base + FDC_MSR) & 0x80 )
99 Uint8 tmp = inb(Base + FDC_FIFO);
100 if(Data) *Data = tmp;
105 Log_Error("FDD", "Read timeout");
110 * \brief Acknowledge an interrupt
111 * \param Base Controller base address
112 * \param ST0 Location to store the ST0 value
113 * \param Cyl Current cylinder
115 void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl)
117 FDD_int_WriteData(Base, CMD_SENSE_INTERRUPT);
118 FDD_int_ReadData(Base, ST0);
119 FDD_int_ReadData(Base, Cyl);
123 * \brief Calibrate a drive
124 * \param Disk Global disk number
126 int FDD_int_Calibrate(int Disk)
129 Uint16 base = FDD_int_GetBase(Disk, &_disk);
130 FDD_int_StartMotor(Disk);
132 for( int i = 0; i < 10; i ++ )
134 Uint8 st0=0, cyl = -1;
137 FDD_int_WriteData(base, CMD_RECALIBRATE);
138 FDD_int_WriteData(base, _disk);
142 FDD_int_SenseInterrupt(base, &st0, NULL);
145 static const char *status_type[] = {
146 0, "Error", "Invalid", "Drive Error"
148 Log_Debug("FDD", "FDD_int_Calibrate: st0 & 0xC0 = 0x%x, %s",
149 st0 & 0xC0, status_type[st0 >> 6]
156 FDD_int_StopMotor(Disk);
161 Log_Error("FDD", "FDD_int_Calibrate: Retries exhausted");
167 * \brief Reset a controller
168 * \param Base Controller base address
170 int FDD_int_Reset(Uint16 Base)
174 tmp = inb(Base + FDC_DOR) & 0xF0;
175 outb( Base + FDC_DOR, 0x00 );
177 outb( Base + FDC_DOR, tmp | 0x0C );
179 FDD_int_SenseInterrupt(Base, NULL, NULL);
181 outb(Base + FDC_CCR, 0x00); // 500KB/s
183 FDD_int_WriteData(Base, CMD_SPECIFY); // Step and Head Load Times
184 FDD_int_WriteData(Base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each)
185 FDD_int_WriteData(Base, 0x02); // Head Load Time >> 1
187 // TODO: Recalibrate all present disks
188 FDD_int_Calibrate(0);
193 * \brief Start the motor on a disk
195 int FDD_int_StartMotor(int Disk)
198 Uint16 base = FDD_int_GetBase(Disk, &_disk);
200 if( gaFDD_Disks[Disk].MotorState == MOTOR_ATSPEED )
203 // Clear the motor off timer
204 Time_RemoveTimer(gaFDD_Disks[Disk].Timer);
205 gaFDD_Disks[Disk].Timer = -1;
208 outb(base + FDC_DOR, inb(base+FDC_DOR) | (1 << (_disk + 4)));
210 // Wait for it to reach speed
211 Time_Delay(MOTOR_ON_DELAY);
213 gaFDD_Disks[Disk].MotorState = MOTOR_ATSPEED;
219 * \brief Schedule the motor to stop
221 int FDD_int_StopMotor(int Disk)
223 if( gaFDD_Disks[Disk].MotorState != MOTOR_ATSPEED )
225 if( gaFDD_Disks[Disk].Timer != -1 )
228 gaFDD_Disks[Disk].Timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotorCallback, (void*)(tVAddr)Disk);
234 * \brief Actually stop the motor
235 * \param Ptr Actaully the global disk number
237 void FDD_int_StopMotorCallback(void *Ptr)
239 int Disk = (tVAddr)Ptr;
241 Uint16 base = FDD_int_GetBase(Disk, &_disk);
243 gaFDD_Disks[Disk].Timer = -1;
244 gaFDD_Disks[Disk].MotorState = MOTOR_OFF;
246 outb(base + FDC_DOR, inb(base+FDC_DOR) & ~(1 << (_disk + 4)));
252 * \brief Converts a global disk number into a controller and drive
253 * \param Disk Global disk number
254 * \param Drive Destination for controller disk number
255 * \return Controller base address
257 Uint16 FDD_int_GetBase(int Disk, int *Drive)
259 if(Drive) *Drive = Disk & 3;
262 case 0: return 0x3F0;
263 case 1: return 0x370;
270 * \brief Clear the IRQ fired flag
272 void FDD_int_ClearIRQ(void)
278 * \brief Wait for an IRQ to fire
280 int FDD_int_WaitIRQ(void)
282 while(gbFDD_IRQ6Fired == 0)
289 * \param IRQ IRQ Number (unused)
290 * \param Ptr Data Pointer (unused)
292 void FDD_int_IRQHandler(int IRQ, void *Ptr)