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