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