[STORAGE] Storage driver cleanup part 4
[reactos.git] / drivers / storage / floppy / 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 */
49
50 #include "precomp.h"
51
52 #include <debug.h>
53
54 /*
55 * Hardware Support Routines
56 */
57
58 static BOOLEAN NTAPI
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }