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