1 /*
2  *  ReactOS Floppy Driver
3  *  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * PROJECT:         ReactOS Floppy Driver
20  * FILE:            hardware.c
21  * PURPOSE:         FDC Hardware control routines
22  * PROGRAMMER:      Vizzini (vizzini@plasmic.com)
23  * REVISIONS:
24  *                  15-Feb-2004 vizzini - Created
25  * NOTES:
26  *     - Many of these functions are based directly on information from the
27  *       Intel datasheet for their enhanced floppy controller.  Send_Byte and
28  *       Get_Byte are direct C implementations of their flowcharts, and the
29  *       read/write routine and others are loose adaptations of their charts.
30  *     - These routines are generally designed to be small, atomic operations.  They
31  *       do not wait for interrupts, deal with DMA, or do any other Windows-
32  *       specific things, unless they have to.
33  *     - If you compare this to Microsoft samples or to the old ReactOS driver,
34  *       or even to the linux driver, you will notice a big difference:  we use
35  *       a system thread to drain the queue.  This is because it's illegal to block
36  *       in a dispatch routine, unless you're a top-level driver (which we absolutely
37  *       are not).  One big reason is that we may be called at raised IRQL, at which
38  *       it's illegal to block.  The floppy controller is a *dumb* piece of hardware,
39  *       too - it is slow and difficult to deal with.  The solution is to do all
40  *       of the blocking and servicing of the controller in a dedicated worker
41  *       thread.
42  *     - Some information taken from Intel 82077AA data sheet (order #290166-007)
43  *
44  * TODO: ATM the constants defined in hardware.h *might* be shifted to line up
45  *       with the bit position in the register, or they *might not*.  This should
46  *       all be converted to standardize on absolute values or shifts.
47  *       I prefer bit fields, but they break endianness.
48  */
49 
50 #include "precomp.h"
51 
52 #include <debug.h>
53 
54 /*
55  * Hardware Support Routines
56  */
57 
58 static BOOLEAN NTAPI
ReadyForWrite(PCONTROLLER_INFO ControllerInfo)59 ReadyForWrite(PCONTROLLER_INFO ControllerInfo)
60 /*
61  * FUNCTION: Determine of the controller is ready to accept a byte on the FIFO
62  * ARGUMENTS:
63  *     ControllerInfo: Info structure for the FDC we're testing
64  * RETURNS:
65  *     TRUE if the controller can accept a byte right now
66  *     FALSE otherwise
67  * NOTES:
68  *     - it is necessary to check both that the FIFO is set to "outbound"
69  *       and that the "ready for i/o" bit is set.
70  */
71 {
72     UCHAR Status = READ_PORT_UCHAR(ControllerInfo->BaseAddress + MAIN_STATUS_REGISTER);
73 
74     if(Status & MSR_IO_DIRECTION) /* 0 for out */
75         return FALSE;
76 
77     if(!(Status & MSR_DATA_REG_READY_FOR_IO))
78         return FALSE;
79 
80     return TRUE;
81 }
82 
83 
84 static BOOLEAN NTAPI
ReadyForRead(PCONTROLLER_INFO ControllerInfo)85 ReadyForRead(PCONTROLLER_INFO ControllerInfo)
86 /*
87  * FUNCTION: Determine of the controller is ready to read a byte on the FIFO
88  * ARGUMENTS:
89  *     ControllerInfo: Info structure for the FDC we're testing
90  * RETURNS:
91  *     TRUE if the controller can read a byte right now
92  *     FALSE otherwise
93  * NOTES:
94  *     - it is necessary to check both that the FIFO is set to "inbound"
95  *       and that the "ready for i/o" bit is set.
96  */
97 {
98     UCHAR Status = READ_PORT_UCHAR(ControllerInfo->BaseAddress + MAIN_STATUS_REGISTER);
99 
100     if(!(Status & MSR_IO_DIRECTION)) /* Read = 1 */
101         return FALSE;
102 
103     if(!(Status & MSR_DATA_REG_READY_FOR_IO))
104         return FALSE;
105 
106     return TRUE;
107 }
108 
109 
110 static NTSTATUS NTAPI
Send_Byte(PCONTROLLER_INFO ControllerInfo,UCHAR Byte)111 Send_Byte(PCONTROLLER_INFO ControllerInfo, UCHAR Byte)
112 /*
113  * FUNCTION: Send a byte from the host to the controller's FIFO
114  * ARGUMENTS:
115  *     ControllerInfo: Info structure for the controller we're writing to
116  *     Offset: Offset over the controller's base address that we're writing to
117  *     Byte: Byte to write to the bus
118  * RETURNS:
119  *     STATUS_SUCCESS if the byte was written successfully
120  *     STATUS_UNSUCCESSFUL if not
121  * NOTES:
122  *     - Function designed after flowchart in intel datasheet
123  *     - 250us max delay.  Note that this is exactly 5 times longer
124  *       than Microsoft recommends stalling the processor
125  *     - PAGED_CODE, because we spin for more than the Microsoft-recommended
126  *       maximum.
127  *     - This function is necessary because sometimes the FIFO reacts slowly
128  *       and isn't yet ready to read or write the next byte
129  */
130 {
131     int i;
132 
133     PAGED_CODE();
134 
135     for(i = 0; i < 5; i++)
136     {
137         if(ReadyForWrite(ControllerInfo))
138             break;
139 
140         KeStallExecutionProcessor(50);
141     }
142 
143     if (i < 5)
144     {
145         WRITE_PORT_UCHAR(ControllerInfo->BaseAddress + FIFO, Byte);
146         return STATUS_SUCCESS;
147     }
148     else
149     {
150         INFO_(FLOPPY, "Send_Byte: timed out trying to write\n");
151         HwDumpRegisters(ControllerInfo);
152         return STATUS_UNSUCCESSFUL;
153     }
154 }
155 
156 
157 static NTSTATUS NTAPI
Get_Byte(PCONTROLLER_INFO ControllerInfo,PUCHAR Byte)158 Get_Byte(PCONTROLLER_INFO ControllerInfo, PUCHAR Byte)
159 /*
160  * FUNCTION: Read a byte from the controller to the host
161  * ARGUMENTS:
162  *     ControllerInfo: Info structure for the controller we're reading from
163  *     Offset: Offset over the controller's base address  that we're reading from
164  *     Byte: Byte to read from the bus
165  * RETURNS:
166  *     STATUS_SUCCESS if the byte was read successfully
167  *     STATUS_UNSUCCESSFUL if not
168  * NOTES:
169  *     - Function designed after flowchart in intel datasheet
170  *     - 250us max delay.  Note that this is exactly 5 times longer
171  *       than Microsoft recommends stalling the processor
172  *     - Remember that we can be interrupted here, so this might
173  *       take much more wall clock time than 250us
174  *     - PAGED_CODE because we spin for longer than Microsoft recommends
175  */
176 {
177     int i;
178 
179     PAGED_CODE();
180 
181     for(i = 0; i < 5; i++)
182     {
183         if(ReadyForRead(ControllerInfo))
184             break;
185 
186         KeStallExecutionProcessor(50);
187     }
188 
189     if (i < 5)
190     {
191         *Byte = READ_PORT_UCHAR(ControllerInfo->BaseAddress + FIFO);
192         return STATUS_SUCCESS;
193     }
194     else
195     {
196         INFO_(FLOPPY, "Get_Byte: timed out trying to write\n");
197         HwDumpRegisters(ControllerInfo);
198         return STATUS_UNSUCCESSFUL;
199     }
200 }
201 
202 
203 NTSTATUS NTAPI
HwSetDataRate(PCONTROLLER_INFO ControllerInfo,UCHAR DataRate)204 HwSetDataRate(PCONTROLLER_INFO ControllerInfo, UCHAR DataRate)
205 /*
206  * FUNCTION: Set the data rte on a controller
207  * ARGUMENTS:
208  *     ControllerInfo: Controller whose rate is being set
209  *     DataRate: Data rate code to set the controller to
210  * RETURNS:
211  *     STATUS_SUCCESS
212  */
213 {
214     TRACE_(FLOPPY, "HwSetDataRate called; writing rate code 0x%x to offset 0x%x\n", DataRate, DATA_RATE_SELECT_REGISTER);
215 
216     WRITE_PORT_UCHAR(ControllerInfo->BaseAddress + DATA_RATE_SELECT_REGISTER, DataRate);
217 
218     return STATUS_SUCCESS;
219 }
220 
221 
222 NTSTATUS NTAPI
HwTurnOffMotor(PCONTROLLER_INFO ControllerInfo)223 HwTurnOffMotor(PCONTROLLER_INFO ControllerInfo)
224 /*
225  * FUNCTION: Turn off all motors
226  * ARGUMENTS:
227  *     DriveInfo: drive to turn off
228  * RETURNS:
229  *     STATUS_SUCCESS if the motor is successfully turned off
230  * NOTES:
231  *     - Don't call this routine directly unless you've thought about it
232  *       and read the source to StartMotor() and StopMotor().
233  *     - Called at DISPATCH_LEVEL
234  */
235 {
236     TRACE_(FLOPPY, "HwTurnOffMotor: writing byte 0x%x to offset 0x%x\n", DOR_FDC_ENABLE|DOR_DMA_IO_INTERFACE_ENABLE, DIGITAL_OUTPUT_REGISTER);
237 
238     WRITE_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_OUTPUT_REGISTER, DOR_FDC_ENABLE|DOR_DMA_IO_INTERFACE_ENABLE);
239 
240     return STATUS_SUCCESS;
241 }
242 
243 
244 NTSTATUS NTAPI
HwTurnOnMotor(PDRIVE_INFO DriveInfo)245 HwTurnOnMotor(PDRIVE_INFO DriveInfo)
246 /*
247  * FUNCTION: Turn on the motor on the selected drive
248  * ARGUMENTS:
249  *     DriveInfo: drive to turn on
250  * RETURNS:
251  *     STATUS_SUCCESS if the motor is successfully turned on
252  *     STATUS_UNSUCCESSFUL otherwise
253  * NOTES:
254  *     - Doesn't interrupt
255  *     - Currently cannot fail
256  */
257 {
258     PCONTROLLER_INFO ControllerInfo = DriveInfo->ControllerInfo;
259     UCHAR Unit = DriveInfo->UnitNumber;
260     UCHAR Buffer;
261 
262     PAGED_CODE();
263 
264     /* turn on motor */
265     Buffer = Unit;
266 
267     Buffer |= DOR_FDC_ENABLE;
268     Buffer |= DOR_DMA_IO_INTERFACE_ENABLE;
269 
270     if(Unit == 0)
271         Buffer |= DOR_FLOPPY_MOTOR_ON_A;
272     else if (Unit == 1)
273         Buffer |= DOR_FLOPPY_MOTOR_ON_B;
274     else if (Unit == 2)
275         Buffer |= DOR_FLOPPY_MOTOR_ON_C;
276     else if (Unit == 3)
277         Buffer |= DOR_FLOPPY_MOTOR_ON_D;
278 
279     TRACE_(FLOPPY, "HwTurnOnMotor: writing byte 0x%x to offset 0x%x\n", Buffer, DIGITAL_OUTPUT_REGISTER);
280     WRITE_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_OUTPUT_REGISTER, Buffer);
281 
282     return STATUS_SUCCESS;
283 }
284 
285 
286 NTSTATUS NTAPI
HwSenseDriveStatus(PDRIVE_INFO DriveInfo)287 HwSenseDriveStatus(PDRIVE_INFO DriveInfo)
288 /*
289  * FUNCTION: Start a sense status command
290  * ARGUMENTS:
291  *     DriveInfo: Drive to inquire about
292  * RETURNS:
293  *     STATUS_SUCCESS if the command is successfully queued to the controller
294  *     STATUS_UNSUCCESSFUL if not
295  * NOTES:
296  *     - Generates an interrupt
297  *     - hard-wired to head 0
298  */
299 {
300     UCHAR Buffer[2];
301     int i;
302 
303     PAGED_CODE();
304 
305     TRACE_(FLOPPY, "HwSenseDriveStatus called\n");
306 
307     Buffer[0] = COMMAND_SENSE_DRIVE_STATUS;
308     Buffer[1] = DriveInfo->UnitNumber; /* hard-wired to head 0 for now */
309 
310     for(i = 0; i < 2; i++)
311         if(Send_Byte(DriveInfo->ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
312         {
313             WARN_(FLOPPY, "HwSenseDriveStatus: failed to write FIFO\n");
314             return STATUS_UNSUCCESSFUL;
315         }
316 
317     return STATUS_SUCCESS;
318 }
319 
320 
321 NTSTATUS NTAPI
HwReadWriteData(PCONTROLLER_INFO ControllerInfo,BOOLEAN Read,UCHAR Unit,UCHAR Cylinder,UCHAR Head,UCHAR Sector,UCHAR BytesPerSector,UCHAR EndOfTrack,UCHAR Gap3Length,UCHAR DataLength)322 HwReadWriteData(PCONTROLLER_INFO ControllerInfo,
323                 BOOLEAN Read,
324                 UCHAR Unit,
325                 UCHAR Cylinder,
326                 UCHAR Head,
327                 UCHAR Sector,
328                 UCHAR BytesPerSector,
329                 UCHAR EndOfTrack,
330                 UCHAR Gap3Length,
331                 UCHAR DataLength)
332 /*
333  * FUNCTION: Read or write data to the drive
334  * ARGUMENTS:
335  *     ControllerInfo: controller to target the read/write request to
336  *     Read: TRUE if the device should be read; FALSE if written
337  *     Unit: Drive number to target
338  *     Cylinder: cylinder to start the read on
339  *     Head: head to start the read on
340  *     Sector: sector to start the read on (1-based!)
341  *     BytesPerSector: sector size constant (hardware.h)
342  *     EndOfTrack: Marks the last sector number to read/write on the track
343  *     Gap3Length: Gap length for the operation
344  *     DataLength: Bytes to read, *unless* BytesPerSector is specified
345  * RETURNS:
346  *     STATUS_SUCCESS if the operation was successfully queued to the controller
347  *     STATUS_UNSUCCESSFUL otherwise
348  * NOTES:
349  *     - Generates an interrupt
350  */
351 {
352     UCHAR Buffer[9];
353     int i;
354 
355     PAGED_CODE();
356 
357     /* Shouldn't be using DataLength in this driver */
358     ASSERT(DataLength == 0xff);
359 
360     /* Build the command to send */
361     if(Read)
362         Buffer[0] = COMMAND_READ_DATA;
363     else
364         Buffer[0] = COMMAND_WRITE_DATA;
365 
366     Buffer[0] |= READ_DATA_MFM | READ_DATA_MT;
367 
368     Buffer[1] = (Head << COMMAND_HEAD_NUMBER_SHIFT) | Unit;
369     Buffer[2] = Cylinder;
370     Buffer[3] = Head;
371     Buffer[4] = Sector;
372     Buffer[5] = BytesPerSector;
373     Buffer[6] = EndOfTrack;
374     Buffer[7] = Gap3Length;
375     Buffer[8] = DataLength;
376 
377     /* Send the command */
378     for(i = 0; i < 9; i++)
379     {
380         INFO_(FLOPPY, "HwReadWriteData: Sending a command byte to the FIFO: 0x%x\n", Buffer[i]);
381 
382         if(Send_Byte(ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
383         {
384             WARN_(FLOPPY, "HwReadWriteData: Unable to write to the FIFO\n");
385             return STATUS_UNSUCCESSFUL;
386         }
387     }
388 
389     return STATUS_SUCCESS;
390 }
391 
392 
393 NTSTATUS NTAPI
HwRecalibrateResult(PCONTROLLER_INFO ControllerInfo)394 HwRecalibrateResult(PCONTROLLER_INFO ControllerInfo)
395 /*
396  * FUNCTION: Get the result of a recalibrate command
397  * ARGUMENTS:
398  *     ControllerInfo: controller to query
399  * RETURNS:
400  *     STATUS_SUCCESS if the recalibratewas a success
401  *     STATUS_UNSUCCESSFUL otherwise
402  * NOTES:
403  *     - This function tests the error conditions itself, and boils the
404  *       whole thing down to a single SUCCESS or FAILURE result
405  *     - Called post-interrupt; does not interrupt
406  * TODO
407  *     - perhaps handle more status
408  */
409 {
410     UCHAR Buffer[2];
411     int i;
412 
413     PAGED_CODE();
414 
415     if(Send_Byte(ControllerInfo, COMMAND_SENSE_INTERRUPT_STATUS) != STATUS_SUCCESS)
416     {
417         WARN_(FLOPPY, "HwRecalibrateResult: Unable to write the controller\n");
418         return STATUS_UNSUCCESSFUL;
419     }
420 
421     for(i = 0; i < 2; i++)
422         if(Get_Byte(ControllerInfo, &Buffer[i]) != STATUS_SUCCESS)
423         {
424             WARN_(FLOPPY, "HwRecalibrateResult: unable to read FIFO\n");
425             return STATUS_UNSUCCESSFUL;
426         }
427 
428     /* Validate  that it did what we told it to */
429     INFO_(FLOPPY, "HwRecalibrateResult results: ST0: 0x%x PCN: 0x%x\n", Buffer[0], Buffer[1]);
430 
431     /*
432      * Buffer[0] = ST0
433      * Buffer[1] = PCN
434      */
435 
436     /* Is the PCN 0? */
437     if(Buffer[1] != 0)
438     {
439         WARN_(FLOPPY, "HwRecalibrateResult: PCN not 0\n");
440         return STATUS_UNSUCCESSFUL;
441     }
442 
443     /* test seek complete */
444     if((Buffer[0] & SR0_SEEK_COMPLETE) != SR0_SEEK_COMPLETE)
445     {
446         WARN_(FLOPPY, "HwRecalibrateResult: Failed to complete the seek\n");
447         return STATUS_UNSUCCESSFUL;
448     }
449 
450     /* Is the equipment check flag set?  Could be no disk in drive... */
451     if((Buffer[0] & SR0_EQUIPMENT_CHECK) == SR0_EQUIPMENT_CHECK)
452     {
453         WARN_(FLOPPY, "HwRecalibrateResult: Seeked to track 0 successfully, but EC is set; returning failure\n");
454         return STATUS_UNSUCCESSFUL;
455     }
456 
457     return STATUS_SUCCESS;
458 }
459 
460 
461 NTSTATUS NTAPI
HwReadWriteResult(PCONTROLLER_INFO ControllerInfo)462 HwReadWriteResult(PCONTROLLER_INFO ControllerInfo)
463 /*
464  * FUNCTION: Get the result of a read or write from the controller
465  * ARGUMENTS:
466  *     ControllerInfo: controller to query
467  * RETURNS:
468  *     STATUS_SUCCESS if the read/write was a success
469  *     STATUS_UNSUCCESSFUL otherwise
470  * NOTES:
471  *     - This function tests the error conditions itself, and boils the
472  *       whole thing down to a single SUCCESS or FAILURE result
473  *     - Called post-interrupt; does not interrupt
474  * TODO:
475  *     - perhaps handle more status
476  */
477 {
478     UCHAR Buffer[7];
479     int i;
480 
481     PAGED_CODE();
482 
483     for(i = 0; i < 7; i++)
484         if(Get_Byte(ControllerInfo, &Buffer[i]) != STATUS_SUCCESS)
485         {
486             WARN_(FLOPPY, "HwReadWriteResult: unable to read fifo\n");
487             return STATUS_UNSUCCESSFUL;
488         }
489 
490     /* Validate  that it did what we told it to */
491     INFO_(FLOPPY, "HwReadWriteResult results: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", Buffer[0], Buffer[1], Buffer[2], Buffer[3],
492           Buffer[4], Buffer[5], Buffer[6]);
493 
494     /* Last command successful? */
495     if((Buffer[0] & SR0_LAST_COMMAND_STATUS) != SR0_LCS_SUCCESS)
496         return STATUS_UNSUCCESSFUL;
497 
498     return STATUS_SUCCESS;
499 }
500 
501 
502 NTSTATUS NTAPI
HwRecalibrate(PDRIVE_INFO DriveInfo)503 HwRecalibrate(PDRIVE_INFO DriveInfo)
504 /*
505  * FUNCTION: Start a recalibration of a drive
506  * ARGUMENTS:
507  *     DriveInfo: Drive to recalibrate
508  * RETURNS:
509  *     STATUS_SUCCESS if the command was successfully queued to the controller
510  *     STATUS_UNSUCCESSFUL otherwise
511  * NOTES:
512  *     - Generates an interrupt
513  */
514 {
515     PCONTROLLER_INFO ControllerInfo = DriveInfo->ControllerInfo;
516     UCHAR Unit = DriveInfo->UnitNumber;
517     UCHAR Buffer[2];
518     int i;
519 
520     TRACE_(FLOPPY, "HwRecalibrate called\n");
521 
522     PAGED_CODE();
523 
524     Buffer[0] = COMMAND_RECALIBRATE;
525     Buffer[1] = Unit;
526 
527     for(i = 0; i < 2; i++)
528         if(Send_Byte(ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
529         {
530             WARN_(FLOPPY, "HwRecalibrate: unable to write FIFO\n");
531             return STATUS_UNSUCCESSFUL;
532         }
533 
534     return STATUS_SUCCESS;
535 }
536 
537 
538 NTSTATUS NTAPI
HwSenseInterruptStatus(PCONTROLLER_INFO ControllerInfo)539 HwSenseInterruptStatus(PCONTROLLER_INFO ControllerInfo)
540 /*
541  * FUNCTION: Send a sense interrupt status command to a controller
542  * ARGUMENTS:
543  *     ControllerInfo: controller to queue the command to
544  * RETURNS:
545  *     STATUS_SUCCESS if the command is queued successfully
546  *     STATUS_UNSUCCESSFUL if not
547  */
548 {
549     UCHAR Buffer[2];
550     int i;
551 
552     PAGED_CODE();
553 
554     if(Send_Byte(ControllerInfo, COMMAND_SENSE_INTERRUPT_STATUS) != STATUS_SUCCESS)
555     {
556         WARN_(FLOPPY, "HwSenseInterruptStatus: failed to write controller\n");
557         return STATUS_UNSUCCESSFUL;
558     }
559 
560     for(i = 0; i  < 2; i++)
561     {
562         if(Get_Byte(ControllerInfo, &Buffer[i]) != STATUS_SUCCESS)
563         {
564             WARN_(FLOPPY, "HwSenseInterruptStatus: failed to read controller\n");
565             return STATUS_UNSUCCESSFUL;
566         }
567     }
568 
569     INFO_(FLOPPY, "HwSenseInterruptStatus returned 0x%x 0x%x\n", Buffer[0], Buffer[1]);
570 
571     return STATUS_SUCCESS;
572 }
573 
574 
575 NTSTATUS NTAPI
HwReadId(PDRIVE_INFO DriveInfo,UCHAR Head)576 HwReadId(PDRIVE_INFO DriveInfo, UCHAR Head)
577 /*
578  * FUNCTION: Issue a read id command to the drive
579  * ARGUMENTS:
580  *     DriveInfo: Drive to read id from
581  *     Head: Head to read the ID from
582  * RETURNS:
583  *     STATUS_SUCCESS if the command is queued
584  *     STATUS_UNSUCCESSFUL otherwise
585  * NOTES:
586  *     - Generates an interrupt
587  */
588 {
589     UCHAR Buffer[2];
590     int i;
591 
592     TRACE_(FLOPPY, "HwReadId called\n");
593 
594     PAGED_CODE();
595 
596     Buffer[0] = COMMAND_READ_ID | READ_ID_MFM;
597     Buffer[1] = (Head << COMMAND_HEAD_NUMBER_SHIFT) | DriveInfo->UnitNumber;
598 
599     for(i = 0; i < 2; i++)
600         if(Send_Byte(DriveInfo->ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
601         {
602             WARN_(FLOPPY, "HwReadId: unable to send bytes to fifo\n");
603             return STATUS_UNSUCCESSFUL;
604         }
605 
606     return STATUS_SUCCESS;
607 }
608 
609 
610 NTSTATUS NTAPI
HwFormatTrack(PCONTROLLER_INFO ControllerInfo,UCHAR Unit,UCHAR Head,UCHAR BytesPerSector,UCHAR SectorsPerTrack,UCHAR Gap3Length,UCHAR FillerPattern)611 HwFormatTrack(PCONTROLLER_INFO ControllerInfo,
612               UCHAR Unit,
613               UCHAR Head,
614               UCHAR BytesPerSector,
615               UCHAR SectorsPerTrack,
616               UCHAR Gap3Length,
617               UCHAR FillerPattern)
618 /*
619  * FUNCTION: Format a track
620  * ARGUMENTS:
621  *     ControllerInfo: controller to target with the request
622  *     Unit: drive to format on
623  *     Head: head to format on
624  *     BytesPerSector: constant from hardware.h to select density
625  *     SectorsPerTrack: sectors per track
626  *     Gap3Length: gap length to use during format
627  *     FillerPattern: pattern to write into the data portion of sectors
628  * RETURNS:
629  *     STATUS_SUCCESS if the command is successfully queued
630  *     STATUS_UNSUCCESSFUL otherwise
631  */
632 {
633     UCHAR Buffer[6];
634     int i;
635 
636     TRACE_(FLOPPY, "HwFormatTrack called\n");
637 
638     PAGED_CODE();
639 
640     Buffer[0] = COMMAND_FORMAT_TRACK;
641     Buffer[1] = (Head << COMMAND_HEAD_NUMBER_SHIFT) | Unit;
642     Buffer[2] = BytesPerSector;
643     Buffer[3] = SectorsPerTrack;
644     Buffer[4] = Gap3Length;
645     Buffer[5] = FillerPattern;
646 
647     for(i = 0; i < 6; i++)
648         if(Send_Byte(ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
649         {
650             WARN_(FLOPPY, "HwFormatTrack: unable to send bytes to floppy\n");
651             return STATUS_UNSUCCESSFUL;
652         }
653 
654     return STATUS_SUCCESS;
655 }
656 
657 
658 NTSTATUS NTAPI
HwSeek(PDRIVE_INFO DriveInfo,UCHAR Cylinder)659 HwSeek(PDRIVE_INFO DriveInfo, UCHAR Cylinder)
660 /*
661  * FUNCTION: Seek the heads to a particular cylinder
662  * ARGUMENTS:
663  *     DriveInfo: Drive to seek
664  *     Cylinder: cylinder to move to
665  * RETURNS:
666  *     STATUS_SUCCESS if the command is successfully sent
667  *     STATUS_UNSUCCESSFUL otherwise
668  * NOTES:
669  *     - Generates an interrupt
670  */
671 {
672     LARGE_INTEGER Delay;
673     UCHAR Buffer[3];
674     int i;
675 
676     TRACE_(FLOPPY, "HwSeek called for cyl 0x%x\n", Cylinder);
677 
678     PAGED_CODE();
679 
680     Buffer[0] = COMMAND_SEEK;
681     Buffer[1] = DriveInfo->UnitNumber;
682     Buffer[2] = Cylinder;
683 
684     for(i = 0; i < 3; i++)
685         if(Send_Byte(DriveInfo->ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
686         {
687             WARN_(FLOPPY, "HwSeek: failed to write fifo\n");
688             return STATUS_UNSUCCESSFUL;
689         }
690 
691     /* Wait for the head to settle */
692     Delay.QuadPart = 10 * 1000;
693     Delay.QuadPart *= -1;
694     Delay.QuadPart *= DriveInfo->FloppyDeviceData.HeadSettleTime;
695 
696     KeDelayExecutionThread(KernelMode, FALSE, &Delay);
697 
698     return STATUS_SUCCESS;
699 }
700 
701 
702 NTSTATUS NTAPI
HwConfigure(PCONTROLLER_INFO ControllerInfo,BOOLEAN EIS,BOOLEAN EFIFO,BOOLEAN POLL,UCHAR FIFOTHR,UCHAR PRETRK)703 HwConfigure(PCONTROLLER_INFO ControllerInfo,
704             BOOLEAN EIS,
705             BOOLEAN EFIFO,
706             BOOLEAN POLL,
707             UCHAR FIFOTHR,
708             UCHAR PRETRK)
709 /*
710  * FUNCTION: Sends configuration to the drive
711  * ARGUMENTS:
712  *     ControllerInfo: controller to target with the request
713  *     EIS: Enable implied seek
714  *     EFIFO: Enable advanced fifo
715  *     POLL: Enable polling
716  *     FIFOTHR: fifo threshold
717  *     PRETRK: precomp (see intel datasheet)
718  * RETURNS:
719  *     STATUS_SUCCESS if the command is successfully sent
720  *     STATUS_UNSUCCESSFUL otherwise
721  * NOTES:
722  *     - No interrupt
723  */
724 {
725     UCHAR Buffer[4];
726     int i;
727 
728     TRACE_(FLOPPY, "HwConfigure called\n");
729 
730     PAGED_CODE();
731 
732     Buffer[0] = COMMAND_CONFIGURE;
733     Buffer[1] = 0;
734     Buffer[2] = (EIS * CONFIGURE_EIS) + (EFIFO * CONFIGURE_EFIFO) + (POLL * CONFIGURE_POLL) + (FIFOTHR);
735     Buffer[3] = PRETRK;
736 
737     for(i = 0; i < 4; i++)
738         if(Send_Byte(ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
739         {
740             WARN_(FLOPPY, "HwConfigure: failed to write the fifo\n");
741             return STATUS_UNSUCCESSFUL;
742         }
743 
744     return STATUS_SUCCESS;
745 }
746 
747 
748 NTSTATUS NTAPI
HwGetVersion(PCONTROLLER_INFO ControllerInfo)749 HwGetVersion(PCONTROLLER_INFO ControllerInfo)
750 /*
751  * FUNCTION: Gets the version of the controller
752  * ARGUMENTS:
753  *     ControllerInfo: controller to target with the request
754  *     ConfigValue: Configuration value to send to the drive (see header)
755  * RETURNS:
756  *     Version number returned by the command, or
757  *     0 on failure
758  * NOTE:
759  *     - This command doesn't interrupt, so we go right to reading after
760  *       we issue the command
761  */
762 {
763     UCHAR Buffer;
764 
765     PAGED_CODE();
766 
767     if(Send_Byte(ControllerInfo, COMMAND_VERSION) != STATUS_SUCCESS)
768     {
769         WARN_(FLOPPY, "HwGetVersion: unable to write fifo\n");
770         return STATUS_UNSUCCESSFUL;
771     }
772 
773     if(Get_Byte(ControllerInfo, &Buffer) != STATUS_SUCCESS)
774     {
775         WARN_(FLOPPY, "HwGetVersion: unable to write fifo\n");
776         return STATUS_UNSUCCESSFUL;
777     }
778 
779     INFO_(FLOPPY, "HwGetVersion returning version 0x%x\n", Buffer);
780 
781     return Buffer;
782 }
783 
784 NTSTATUS NTAPI
HwDiskChanged(PDRIVE_INFO DriveInfo,PBOOLEAN DiskChanged)785 HwDiskChanged(PDRIVE_INFO DriveInfo, PBOOLEAN DiskChanged)
786 /*
787  * FUNCTION: Detect whether the hardware has sensed a disk change
788  * ARGUMENTS:
789  *     DriveInfo: pointer to the drive that we are to check
790  *     DiskChanged: boolean that is set with whether or not the controller thinks there has been a disk change
791  * RETURNS:
792  *     STATUS_SUCCESS if the drive is successfully queried
793  * NOTES:
794  *     - Does not interrupt.
795  *     - Guessing a bit at the Model30 stuff
796  */
797 {
798     UCHAR Buffer;
799     PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO) DriveInfo->ControllerInfo;
800 
801     Buffer = READ_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_INPUT_REGISTER);
802 
803     TRACE_(FLOPPY, "HwDiskChanged: read 0x%x from DIR\n", Buffer);
804 
805     if(ControllerInfo->Model30)
806     {
807         if(!(Buffer & DIR_DISKETTE_CHANGE))
808         {
809             INFO_(FLOPPY, "HdDiskChanged - Model30 - returning TRUE\n");
810             *DiskChanged = TRUE;
811         }
812         else
813         {
814             INFO_(FLOPPY, "HdDiskChanged - Model30 - returning FALSE\n");
815             *DiskChanged = FALSE;
816         }
817     }
818     else
819     {
820         if(Buffer & DIR_DISKETTE_CHANGE)
821         {
822             INFO_(FLOPPY, "HdDiskChanged - PS2 - returning TRUE\n");
823             *DiskChanged = TRUE;
824         }
825         else
826         {
827             INFO_(FLOPPY, "HdDiskChanged - PS2 - returning FALSE\n");
828             *DiskChanged = FALSE;
829         }
830     }
831 
832     return STATUS_SUCCESS;
833 }
834 
835 NTSTATUS NTAPI
HwSenseDriveStatusResult(PCONTROLLER_INFO ControllerInfo,PUCHAR Status)836 HwSenseDriveStatusResult(PCONTROLLER_INFO ControllerInfo, PUCHAR Status)
837 /*
838  * FUNCTION: Get the result of a sense drive status command
839  * ARGUMENTS:
840  *     ControllerInfo: controller to query
841  *     Status: Status from the drive sense command
842  * RETURNS:
843  *     STATUS_SUCCESS if we can successfully read the status
844  *     STATUS_UNSUCCESSFUL otherwise
845  * NOTES:
846  *     - Called post-interrupt; does not interrupt
847  */
848 {
849     PAGED_CODE();
850 
851     if(Get_Byte(ControllerInfo, Status) != STATUS_SUCCESS)
852     {
853         WARN_(FLOPPY, "HwSenseDriveStatus: unable to read fifo\n");
854         return STATUS_UNSUCCESSFUL;
855     }
856 
857     TRACE_(FLOPPY, "HwSenseDriveStatusResult: ST3: 0x%x\n", *Status);
858 
859     return STATUS_SUCCESS;
860 }
861 
862 
863 NTSTATUS NTAPI
HwReadIdResult(PCONTROLLER_INFO ControllerInfo,PUCHAR CurCylinder,PUCHAR CurHead)864 HwReadIdResult(PCONTROLLER_INFO ControllerInfo,
865                PUCHAR CurCylinder,
866                PUCHAR CurHead)
867 /*
868  * FUNCTION: Get the result of a read id command
869  * ARGUMENTS:
870  *     ControllerInfo: controller to query
871  *     CurCylinder: Returns the cylinder that we're at
872  *     CurHead: Returns the head that we're at
873  * RETURNS:
874  *     STATUS_SUCCESS if the read id was a success
875  *     STATUS_UNSUCCESSFUL otherwise
876  * NOTES:
877  *     - This function tests the error conditions itself, and boils the
878  *       whole thing down to a single SUCCESS or FAILURE result
879  *     - Called post-interrupt; does not interrupt
880  * TODO
881  *     - perhaps handle more status
882  */
883 {
884     UCHAR Buffer[7] = {0,0,0,0,0,0,0};
885     int i;
886 
887     PAGED_CODE();
888 
889     for(i = 0; i < 7; i++)
890         if(Get_Byte(ControllerInfo, &Buffer[i]) != STATUS_SUCCESS)
891         {
892             WARN_(FLOPPY, "ReadIdResult(): can't read from the controller\n");
893             return STATUS_UNSUCCESSFUL;
894         }
895 
896     /* Validate  that it did what we told it to */
897     INFO_(FLOPPY, "ReadId results: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", Buffer[0], Buffer[1], Buffer[2], Buffer[3],
898           Buffer[4], Buffer[5], Buffer[6]);
899 
900     /* Last command successful? */
901     if((Buffer[0] & SR0_LAST_COMMAND_STATUS) != SR0_LCS_SUCCESS)
902     {
903         WARN_(FLOPPY, "ReadId didn't return last command success\n");
904         return STATUS_UNSUCCESSFUL;
905     }
906 
907     /* ID mark found? */
908     if(Buffer[1] & SR1_CANNOT_FIND_ID_ADDRESS)
909     {
910         WARN_(FLOPPY, "ReadId didn't find an address mark\n");
911         return STATUS_UNSUCCESSFUL;
912     }
913 
914     if(CurCylinder)
915         *CurCylinder = Buffer[3];
916 
917     if(CurHead)
918         *CurHead = Buffer[4];
919 
920     return STATUS_SUCCESS;
921 }
922 
923 
924 NTSTATUS NTAPI
HwSpecify(PCONTROLLER_INFO ControllerInfo,UCHAR HeadLoadTime,UCHAR HeadUnloadTime,UCHAR StepRateTime,BOOLEAN NonDma)925 HwSpecify(PCONTROLLER_INFO ControllerInfo,
926           UCHAR HeadLoadTime,
927           UCHAR HeadUnloadTime,
928           UCHAR StepRateTime,
929           BOOLEAN NonDma)
930 /*
931  * FUNCTION: Set up timing and DMA mode for the controller
932  * ARGUMENTS:
933  *     ControllerInfo: Controller to set up
934  *     HeadLoadTime: Head load time (see data sheet for details)
935  *     HeadUnloadTime: Head unload time
936  *     StepRateTime: Step rate time
937  *     NonDma: TRUE to disable DMA mode
938  * RETURNS:
939  *     STATUS_SUCCESS if the controller is successfully programmed
940  *     STATUS_UNSUCCESSFUL if not
941  * NOTES:
942  *     - Does not interrupt
943  *
944  * TODO: Figure out timings
945  */
946 {
947     UCHAR Buffer[3];
948     int i;
949 
950     Buffer[0] = COMMAND_SPECIFY;
951     /*
952     Buffer[1] = (StepRateTime << 4) + HeadUnloadTime;
953     Buffer[2] = (HeadLoadTime << 1) + (NonDma ? 1 : 0);
954     */
955     Buffer[1] = 0xdf;
956     Buffer[2] = 0x2;
957 
958     //INFO_(FLOPPY, "HwSpecify: sending 0x%x 0x%x 0x%x to FIFO\n", Buffer[0], Buffer[1], Buffer[2]);
959     WARN_(FLOPPY, "HWSPECIFY: FIXME - sending 0x3 0xd1 0x2 to FIFO\n");
960 
961     for(i = 0; i < 3; i++)
962         if(Send_Byte(ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
963         {
964             WARN_(FLOPPY, "HwSpecify: unable to write to controller\n");
965             return STATUS_UNSUCCESSFUL;
966         }
967 
968     return STATUS_SUCCESS;
969 }
970 
971 
972 NTSTATUS NTAPI
HwReset(PCONTROLLER_INFO ControllerInfo)973 HwReset(PCONTROLLER_INFO ControllerInfo)
974 /*
975  * FUNCTION: Reset the controller
976  * ARGUMENTS:
977  *     ControllerInfo: controller to reset
978  * RETURNS:
979  *     STATUS_SUCCESS in all cases
980  * NOTES:
981  *     - Generates an interrupt that must be serviced four times (one per drive)
982  */
983 {
984     TRACE_(FLOPPY, "HwReset called\n");
985 
986     /* Write the reset bit in the DRSR */
987     WRITE_PORT_UCHAR(ControllerInfo->BaseAddress + DATA_RATE_SELECT_REGISTER, DRSR_SW_RESET);
988 
989     /* Check for the reset bit in the DOR and set it if necessary (see Intel doc) */
990     if(!(READ_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_OUTPUT_REGISTER) & DOR_RESET))
991     {
992         HwDumpRegisters(ControllerInfo);
993         INFO_(FLOPPY, "HwReset: Setting Enable bit\n");
994         WRITE_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_OUTPUT_REGISTER, DOR_DMA_IO_INTERFACE_ENABLE|DOR_RESET);
995         HwDumpRegisters(ControllerInfo);
996 
997         if(!(READ_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_OUTPUT_REGISTER) & DOR_RESET))
998         {
999             WARN_(FLOPPY, "HwReset: failed to set the DOR enable bit!\n");
1000             HwDumpRegisters(ControllerInfo);
1001             return STATUS_UNSUCCESSFUL;
1002         }
1003     }
1004 
1005     return STATUS_SUCCESS;
1006 }
1007 
1008 
1009 NTSTATUS NTAPI
HwPowerOff(PCONTROLLER_INFO ControllerInfo)1010 HwPowerOff(PCONTROLLER_INFO ControllerInfo)
1011 /*
1012  * FUNCTION: Power down a controller
1013  * ARGUMENTS:
1014  *     ControllerInfo: Controller to power down
1015  * RETURNS:
1016  *     STATUS_SUCCESS
1017  * NOTES:
1018  *     - Wake up with a hardware reset
1019  */
1020 {
1021     TRACE_(FLOPPY, "HwPowerOff called on controller 0x%p\n", ControllerInfo);
1022 
1023     WRITE_PORT_UCHAR(ControllerInfo->BaseAddress + DATA_RATE_SELECT_REGISTER, DRSR_POWER_DOWN);
1024 
1025     return STATUS_SUCCESS;
1026 }
1027 
1028 VOID NTAPI
HwDumpRegisters(PCONTROLLER_INFO ControllerInfo)1029 HwDumpRegisters(PCONTROLLER_INFO ControllerInfo)
1030 /*
1031  * FUNCTION: Dump all readable registers from the floppy controller
1032  * ARGUMENTS:
1033  *     ControllerInfo: Controller to dump registers from
1034  */
1035 {
1036     UNREFERENCED_PARAMETER(ControllerInfo);
1037 
1038     INFO_(FLOPPY, "STATUS:\n");
1039     INFO_(FLOPPY, "STATUS_REGISTER_A = 0x%x\n", READ_PORT_UCHAR(ControllerInfo->BaseAddress + STATUS_REGISTER_A));
1040     INFO_(FLOPPY, "STATUS_REGISTER_B = 0x%x\n", READ_PORT_UCHAR(ControllerInfo->BaseAddress + STATUS_REGISTER_B));
1041     INFO_(FLOPPY, "DIGITAL_OUTPUT_REGISTER = 0x%x\n", READ_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_OUTPUT_REGISTER));
1042     INFO_(FLOPPY, "MAIN_STATUS_REGISTER =0x%x\n", READ_PORT_UCHAR(ControllerInfo->BaseAddress + MAIN_STATUS_REGISTER));
1043     INFO_(FLOPPY, "DIGITAL_INPUT_REGISTER = 0x%x\n", READ_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_INPUT_REGISTER));
1044 }
1045