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