]> git.proxmox.com Git - mirror_edk2.git/blob - IntelFrameworkModulePkg/Bus/Isa/IsaFloppyDxe/IsaFloppyCtrl.c
fab96051fdeb7de1632786e6870bb1572e91e035
[mirror_edk2.git] / IntelFrameworkModulePkg / Bus / Isa / IsaFloppyDxe / IsaFloppyCtrl.c
1 /**@file
2 ISA Floppy Driver
3 1. Support two types diskette drive
4 1.44M drive and 2.88M drive (and now only support 1.44M)
5 2. Support two diskette drives
6 3. Use DMA channel 2 to transfer data
7 4. Do not use interrupt
8 5. Support diskette change line signal and write protect
9
10 The internal function for the floppy driver
11
12 Copyright (c) 2006 - 2007, Intel Corporation.<BR>
13 All rights reserved. This program and the accompanying materials
14 are licensed and made available under the terms and conditions of the BSD License
15 which accompanies this distribution. The full text of the license may be found at
16 http://opensource.org/licenses/bsd-license.php
17
18 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
19 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
20
21 **/
22
23 #include "IsaFloppy.h"
24
25 /**
26
27 Detect the floppy drive is presented or not
28
29 @param FdcDev FDC_BLK_IO_DEV * : A pointer to the Data Structure FDC_BLK_IO_DEV
30 @retval EFI_SUCCESS Drive is presented
31 @retval EFI_NOT_FOUND Drive is not presented
32
33 **/
34 EFI_STATUS
35 DiscoverFddDevice (
36 IN FDC_BLK_IO_DEV *FdcDev
37 )
38 {
39 EFI_STATUS Status;
40
41 FdcDev->BlkIo.Media = &FdcDev->BlkMedia;
42
43 //
44 // Call FddIndentify subroutine
45 //
46 Status = FddIdentify (FdcDev);
47 if (EFI_ERROR (Status)) {
48 return EFI_NOT_FOUND;
49 }
50
51 FdcDev->BlkIo.Reset = FdcReset;
52 FdcDev->BlkIo.FlushBlocks = FddFlushBlocks;
53 FdcDev->BlkIo.ReadBlocks = FddReadBlocks;
54 FdcDev->BlkIo.WriteBlocks = FddWriteBlocks;
55 FdcDev->BlkMedia.LogicalPartition = FALSE;
56 FdcDev->BlkMedia.WriteCaching = FALSE;
57
58 return EFI_SUCCESS;
59 }
60
61 /**
62
63 Do recalibrate and see the drive is presented or not
64 Set the media parameters
65
66 @param FdcDev FDC_BLK_IO_DEV * : A pointer to the Data Structure FDC_BLK_IO_DEV
67 @return the drive is presented or not
68
69 **/
70 EFI_STATUS
71 FddIdentify (
72 IN FDC_BLK_IO_DEV *FdcDev
73 )
74 {
75 EFI_STATUS Status;
76
77 //
78 // Set Floppy Disk Controller's motor on
79 //
80 Status = MotorOn (FdcDev);
81 if (EFI_ERROR (Status)) {
82 return EFI_DEVICE_ERROR;
83 }
84
85 Status = Recalibrate (FdcDev);
86
87 if (EFI_ERROR (Status)) {
88 MotorOff (FdcDev);
89 FdcDev->ControllerState->NeedRecalibrate = TRUE;
90 return EFI_DEVICE_ERROR;
91 }
92 //
93 // Set Media Parameter
94 //
95 FdcDev->BlkIo.Media->RemovableMedia = TRUE;
96 FdcDev->BlkIo.Media->MediaPresent = TRUE;
97 //
98 // investigate
99 //
100 FdcDev->BlkIo.Media->MediaId = 0;
101
102 //
103 // Check Media
104 //
105 Status = DisketChanged (FdcDev);
106
107 if (Status == EFI_NO_MEDIA) {
108 FdcDev->BlkIo.Media->MediaPresent = FALSE;
109 } else if ((Status != EFI_MEDIA_CHANGED) &&
110 (Status != EFI_SUCCESS)) {
111 MotorOff (FdcDev);
112 return Status;
113 }
114
115 //
116 // Check Disk Write Protected
117 //
118 Status = SenseDrvStatus (FdcDev, 0);
119
120 if (Status == EFI_WRITE_PROTECTED) {
121 FdcDev->BlkIo.Media->ReadOnly = TRUE;
122 } else if (Status == EFI_SUCCESS) {
123 FdcDev->BlkIo.Media->ReadOnly = FALSE;
124 } else {
125 return EFI_DEVICE_ERROR;
126 }
127
128 MotorOff (FdcDev);
129
130 //
131 // Set Media Default Type
132 //
133 FdcDev->BlkIo.Media->BlockSize = DISK_1440K_BYTEPERSECTOR;
134 FdcDev->BlkIo.Media->LastBlock = DISK_1440K_EOT * 2 * (DISK_1440K_MAXTRACKNUM + 1) - 1;
135
136 return EFI_SUCCESS;
137 }
138
139 /**
140
141 Reset the Floppy Logic Drive
142
143 @param FdcDev FDC_BLK_IO_DEV * : A pointer to the Data Structure FDC_BLK_IO_DEV
144
145 @retval EFI_SUCCESS: The Floppy Logic Drive is reset
146 @retval EFI_DEVICE_ERROR: The Floppy Logic Drive is not functioning correctly and
147 can not be reset
148
149 **/
150 EFI_STATUS
151 FddReset (
152 IN FDC_BLK_IO_DEV *FdcDev
153 )
154 {
155 UINT8 data;
156 UINT8 StatusRegister0;
157 UINT8 PresentCylinderNumber;
158 UINTN Index;
159
160 //
161 // Report reset progress code
162 //
163 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
164 EFI_PROGRESS_CODE,
165 EFI_PERIPHERAL_REMOVABLE_MEDIA | EFI_P_PC_RESET,
166 FdcDev->DevicePath
167 );
168
169 //
170 // Reset specified Floppy Logic Drive according to FdcDev -> Disk
171 // Set Digital Output Register(DOR) to do reset work
172 // bit0 & bit1 of DOR : Drive Select
173 // bit2 : Reset bit
174 // bit3 : DMA and Int bit
175 // Reset : a "0" written to bit2 resets the FDC, this reset will remain
176 // active until
177 // a "1" is written to this bit.
178 // Reset step 1:
179 // use bit0 & bit1 to select the logic drive
180 // write "0" to bit2
181 //
182 data = 0x0;
183 data = (UINT8) (data | (SELECT_DRV & FdcDev->Disk));
184 FdcWritePort (FdcDev, FDC_REGISTER_DOR, data);
185
186 //
187 // wait some time,at least 120us
188 //
189 MicroSecondDelay (500);
190
191 //
192 // Reset step 2:
193 // write "1" to bit2
194 // write "1" to bit3 : enable DMA
195 //
196 data |= 0x0C;
197 FdcWritePort (FdcDev, FDC_REGISTER_DOR, data);
198
199 //
200 // Experience value
201 //
202 MicroSecondDelay (2000);
203
204 //
205 // wait specified floppy logic drive is not busy
206 //
207 if (EFI_ERROR (FddWaitForBSYClear (FdcDev, 1))) {
208 return EFI_DEVICE_ERROR;
209 }
210 //
211 // Set the Transfer Data Rate
212 //
213 FdcWritePort (FdcDev, FDC_REGISTER_CCR, 0x0);
214
215 //
216 // Experience value
217 //
218 MicroSecondDelay (100);
219
220 //
221 // Issue Sense interrupt command for each drive (total 4 drives)
222 //
223 for (Index = 0; Index < 4; Index++) {
224 if (EFI_ERROR (SenseIntStatus (FdcDev, &StatusRegister0, &PresentCylinderNumber))) {
225 return EFI_DEVICE_ERROR;
226 }
227 }
228 //
229 // issue Specify command
230 //
231 if (EFI_ERROR (Specify (FdcDev))) {
232 return EFI_DEVICE_ERROR;
233 }
234
235 return EFI_SUCCESS;
236 }
237
238 /**
239
240 Turn the drive's motor on
241 The drive's motor must be on before any command can be executed
242
243 @param FdcDev FDC_BLK_IO_DEV * : A pointer to the Data Structure FDC_BLK_IO_DEV
244
245 @retval EFI_SUCCESS: Turn the drive's motor on successfully
246 @retval EFI_DEVICE_ERROR: The drive is busy, so can not turn motor on
247 @retval EFI_INVALID_PARAMETER: Fail to Set timer(Cancel timer)
248
249 **/
250 EFI_STATUS
251 MotorOn (
252 IN FDC_BLK_IO_DEV *FdcDev
253 )
254 {
255 EFI_STATUS Status;
256 UINT8 data;
257
258 //
259 // Control of the floppy drive motors is a big pain. If motor is off, you have
260 // to turn it on first. But you can not leave the motor on all the time, since
261 // that would wear out the disk. On the other hand, if you turn the motor off
262 // after each operation, the system performance will be awful. The compromise
263 // used in this driver is to leave the motor on for 2 seconds after
264 // each operation. If a new operation is started in that interval(2s),
265 // the motor need not be turned on again. If no new operation is started,
266 // a timer goes off and the motor is turned off
267 //
268 //
269 // Cancel the timer
270 //
271 Status = gBS->SetTimer (FdcDev->Event, TimerCancel, 0);
272
273 if (EFI_ERROR (Status)) {
274 return EFI_INVALID_PARAMETER;
275 }
276 //
277 // Get the motor status
278 //
279 data = FdcReadPort (FdcDev, FDC_REGISTER_DOR);
280
281 if (((FdcDev->Disk == FDC_DISK0) && ((data & 0x10) == 0x10)) ||
282 ((FdcDev->Disk == FDC_DISK1) && ((data & 0x21) == 0x21))
283 ) {
284 return EFI_SUCCESS;
285 }
286 //
287 // The drive's motor is off, so need turn it on
288 // first look at command and drive are busy or not
289 //
290 if (EFI_ERROR (FddWaitForBSYClear (FdcDev, 1))) {
291 return EFI_DEVICE_ERROR;
292 }
293 //
294 // for drive A: 1CH, drive B: 2DH
295 //
296 data = 0x0C;
297 data = (UINT8) (data | (SELECT_DRV & FdcDev->Disk));
298 if (FdcDev->Disk == FDC_DISK0) {
299 //
300 // drive A
301 //
302 data |= DRVA_MOTOR_ON;
303 } else {
304 //
305 // drive B
306 //
307 data |= DRVB_MOTOR_ON;
308 }
309
310 FdcWritePort (FdcDev, FDC_REGISTER_DOR, data);
311
312 //
313 // Experience value
314 //
315 MicroSecondDelay (4000);
316
317 return EFI_SUCCESS;
318 }
319
320 /**
321
322 Set a Timer and when Timer goes off, turn the motor off
323
324
325 @param FdcDev FDC_BLK_IO_DEV * : A pointer to the Data Structure FDC_BLK_IO_DEV
326
327 @retval EFI_SUCCESS: Set the Timer successfully
328 @retval EFI_INVALID_PARAMETER: Fail to Set the timer
329
330 **/
331 EFI_STATUS
332 MotorOff (
333 IN FDC_BLK_IO_DEV *FdcDev
334 )
335 {
336 //
337 // Set the timer : 2s
338 //
339 return gBS->SetTimer (FdcDev->Event, TimerRelative, 20000000);
340 }
341
342 /**
343 Detect the disk in the drive is changed or not
344
345
346 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
347
348 @retval EFI_SUCCESS: No disk media change
349 @retval EFI_DEVICE_ERROR: Fail to do the recalibrate or seek operation
350 @retval EFI_NO_MEDIA: No disk in the drive
351 @retval EFI_MEDIA_CHANGED: There is a new disk in the drive
352 **/
353 EFI_STATUS
354 DisketChanged (
355 IN FDC_BLK_IO_DEV *FdcDev
356 )
357 {
358 EFI_STATUS Status;
359 UINT8 data;
360
361 //
362 // Check change line
363 //
364 data = FdcReadPort (FdcDev, FDC_REGISTER_DIR);
365
366 //
367 // Io delay
368 //
369 MicroSecondDelay (50);
370
371 if ((data & DIR_DCL) == 0x80) {
372 //
373 // disk change line is active
374 //
375 if (FdcDev->PresentCylinderNumber != 0) {
376 Status = Recalibrate (FdcDev);
377 } else {
378 Status = Seek (FdcDev, 0x30);
379 }
380
381 if (EFI_ERROR (Status)) {
382 FdcDev->ControllerState->NeedRecalibrate = TRUE;
383 return EFI_DEVICE_ERROR;
384 //
385 // Fail to do the seek or recalibrate operation
386 //
387 }
388
389 data = FdcReadPort (FdcDev, FDC_REGISTER_DIR);
390
391 //
392 // Io delay
393 //
394 MicroSecondDelay (50);
395
396 if ((data & DIR_DCL) == 0x80) {
397 return EFI_NO_MEDIA;
398 }
399
400 return EFI_MEDIA_CHANGED;
401 }
402
403 return EFI_SUCCESS;
404 }
405
406 /**
407 Do the Specify command, this command sets DMA operation
408 and the initial values for each of the three internal
409 times: HUT, SRT and HLT
410
411 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
412
413 @retval EFI_SUCCESS: Execute the Specify command successfully
414 @retval EFI_DEVICE_ERROR: Fail to execute the command
415
416 **/
417 EFI_STATUS
418 Specify (
419 IN FDC_BLK_IO_DEV *FdcDev
420 )
421 {
422 FDD_SPECIFY_CMD Command;
423 UINTN Index;
424 UINT8 *CommandPointer;
425
426 ZeroMem (&Command, sizeof (FDD_SPECIFY_CMD));
427 Command.CommandCode = SPECIFY_CMD;
428 //
429 // set SRT, HUT
430 //
431 Command.SrtHut = 0xdf;
432 //
433 // 0xdf;
434 //
435 // set HLT and DMA
436 //
437 Command.HltNd = 0x02;
438
439 CommandPointer = (UINT8 *) (&Command);
440 for (Index = 0; Index < sizeof (FDD_SPECIFY_CMD); Index++) {
441 if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {
442 return EFI_DEVICE_ERROR;
443 }
444 }
445
446 return EFI_SUCCESS;
447 }
448
449 /**
450 Set the head of floppy drive to track 0
451
452 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
453 @retval EFI_SUCCESS: Execute the Recalibrate operation successfully
454 @retval EFI_DEVICE_ERROR: Fail to execute the Recalibrate operation
455
456 **/
457 EFI_STATUS
458 Recalibrate (
459 IN FDC_BLK_IO_DEV *FdcDev
460 )
461 {
462 FDD_COMMAND_PACKET2 Command;
463 UINTN Index;
464 UINT8 StatusRegister0;
465 UINT8 PresentCylinderNumber;
466 UINT8 *CommandPointer;
467 UINT8 Count;
468
469 Count = 2;
470
471 while (Count > 0) {
472 ZeroMem (&Command, sizeof (FDD_COMMAND_PACKET2));
473 Command.CommandCode = RECALIBRATE_CMD;
474 //
475 // drive select
476 //
477 if (FdcDev->Disk == FDC_DISK0) {
478 Command.DiskHeadSel = 0;
479 //
480 // 0
481 //
482 } else {
483 Command.DiskHeadSel = 1;
484 //
485 // 1
486 //
487 }
488
489 CommandPointer = (UINT8 *) (&Command);
490 for (Index = 0; Index < sizeof (FDD_COMMAND_PACKET2); Index++) {
491 if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {
492 return EFI_DEVICE_ERROR;
493 }
494 }
495 //
496 // Experience value
497 //
498 MicroSecondDelay (250000);
499 //
500 // need modify according to 1.44M or 2.88M
501 //
502 if (EFI_ERROR (SenseIntStatus (FdcDev, &StatusRegister0, &PresentCylinderNumber))) {
503 return EFI_DEVICE_ERROR;
504 }
505
506 if ((StatusRegister0 & 0xf0) == 0x20 && PresentCylinderNumber == 0) {
507 FdcDev->PresentCylinderNumber = 0;
508 FdcDev->ControllerState->NeedRecalibrate = FALSE;
509 return EFI_SUCCESS;
510 } else {
511 Count--;
512 if (Count == 0) {
513 return EFI_DEVICE_ERROR;
514 }
515 }
516 }
517 //
518 // end while
519 //
520 return EFI_SUCCESS;
521 }
522
523 /**
524 Set the head of floppy drive to the new cylinder
525
526 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
527 @param Lba EFI_LBA : The logic block address want to seek
528
529 @retval EFI_SUCCESS: Execute the Seek operation successfully
530 @retval EFI_DEVICE_ERROR: Fail to execute the Seek operation
531
532 **/
533 EFI_STATUS
534 Seek (
535 IN FDC_BLK_IO_DEV *FdcDev,
536 IN EFI_LBA Lba
537 )
538 {
539 FDD_SEEK_CMD Command;
540 UINT8 EndOfTrack;
541 UINT8 Head;
542 UINT8 Cylinder;
543 UINT8 StatusRegister0;
544 UINT8 *CommandPointer;
545 UINT8 PresentCylinderNumber;
546 UINTN Index;
547 UINT8 DelayTime;
548
549 if (FdcDev->ControllerState->NeedRecalibrate) {
550 if (EFI_ERROR (Recalibrate (FdcDev))) {
551 FdcDev->ControllerState->NeedRecalibrate = TRUE;
552 return EFI_DEVICE_ERROR;
553 }
554 }
555
556 EndOfTrack = DISK_1440K_EOT;
557 //
558 // Calculate cylinder based on Lba and EOT
559 //
560 Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2);
561
562 //
563 // if the destination cylinder is the present cylinder, unnecessary to do the
564 // seek operation
565 //
566 if (FdcDev->PresentCylinderNumber == Cylinder) {
567 return EFI_SUCCESS;
568 }
569 //
570 // Calculate the head : 0 or 1
571 //
572 Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
573
574 ZeroMem (&Command, sizeof (FDD_SEEK_CMD));
575 Command.CommandCode = SEEK_CMD;
576 if (FdcDev->Disk == FDC_DISK0) {
577 Command.DiskHeadSel = 0;
578 //
579 // 0
580 //
581 } else {
582 Command.DiskHeadSel = 1;
583 //
584 // 1
585 //
586 }
587
588 Command.DiskHeadSel = (UINT8) (Command.DiskHeadSel | (Head << 2));
589 Command.NewCylinder = Cylinder;
590
591 CommandPointer = (UINT8 *) (&Command);
592 for (Index = 0; Index < sizeof (FDD_SEEK_CMD); Index++) {
593 if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {
594 return EFI_DEVICE_ERROR;
595 }
596 }
597 //
598 // Io delay
599 //
600 MicroSecondDelay (100);
601
602 //
603 // Calculate waiting time
604 //
605 if (FdcDev->PresentCylinderNumber > Cylinder) {
606 DelayTime = (UINT8) (FdcDev->PresentCylinderNumber - Cylinder);
607 } else {
608 DelayTime = (UINT8) (Cylinder - FdcDev->PresentCylinderNumber);
609 }
610
611 MicroSecondDelay ((DelayTime + 1) * 4000);
612
613 if (EFI_ERROR (SenseIntStatus (FdcDev, &StatusRegister0, &PresentCylinderNumber))) {
614 return EFI_DEVICE_ERROR;
615 }
616
617 if ((StatusRegister0 & 0xf0) == 0x20) {
618 FdcDev->PresentCylinderNumber = Command.NewCylinder;
619 return EFI_SUCCESS;
620 } else {
621 FdcDev->ControllerState->NeedRecalibrate = TRUE;
622 return EFI_DEVICE_ERROR;
623 }
624 }
625
626 /**
627 Do the Sense Interrupt Status command, this command
628 resets the interrupt signal
629
630 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
631 @param StatusRegister0 UINT8 *: Be used to save Status Register 0 read from FDC
632 @param PresentCylinderNumber UINT8 *: Be used to save present cylinder number
633 read from FDC
634
635 @retval EFI_SUCCESS: Execute the Sense Interrupt Status command successfully
636 @retval EFI_DEVICE_ERROR: Fail to execute the command
637
638 **/
639 EFI_STATUS
640 SenseIntStatus (
641 IN FDC_BLK_IO_DEV *FdcDev,
642 IN OUT UINT8 *StatusRegister0,
643 IN OUT UINT8 *PresentCylinderNumber
644 )
645 {
646 UINT8 command;
647
648 command = SENSE_INT_STATUS_CMD;
649 if (EFI_ERROR (DataOutByte (FdcDev, &command))) {
650 return EFI_DEVICE_ERROR;
651 }
652
653 if (EFI_ERROR (DataInByte (FdcDev, StatusRegister0))) {
654 return EFI_DEVICE_ERROR;
655 }
656
657 if (EFI_ERROR (DataInByte (FdcDev, PresentCylinderNumber))) {
658 return EFI_DEVICE_ERROR;
659 }
660
661 return EFI_SUCCESS;
662 }
663
664 /**
665 Do the Sense Drive Status command
666
667 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
668 @param Lba EFI_LBA : Logic block address
669
670 @retval EFI_SUCCESS: Execute the Sense Drive Status command successfully
671 @retval EFI_DEVICE_ERROR: Fail to execute the command
672 @retval EFI_WRITE_PROTECTED:The disk is write protected
673
674 **/
675 EFI_STATUS
676 SenseDrvStatus (
677 IN FDC_BLK_IO_DEV *FdcDev,
678 IN EFI_LBA Lba
679 )
680 {
681 FDD_COMMAND_PACKET2 Command;
682 UINT8 Head;
683 UINT8 EndOfTrack;
684 UINTN Index;
685 UINT8 StatusRegister3;
686 UINT8 *CommandPointer;
687
688 //
689 // Sense Drive Status command obtains drive status information,
690 // it has not execution phase and goes directly to the result phase from the
691 // command phase, Status Register 3 contains the drive status information
692 //
693 ZeroMem (&Command, sizeof (FDD_COMMAND_PACKET2));
694 Command.CommandCode = SENSE_DRV_STATUS_CMD;
695
696 if (FdcDev->Disk == FDC_DISK0) {
697 Command.DiskHeadSel = 0;
698 } else {
699 Command.DiskHeadSel = 1;
700 }
701
702 EndOfTrack = DISK_1440K_EOT;
703 Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
704 Command.DiskHeadSel = (UINT8) (Command.DiskHeadSel | (Head << 2));
705
706 CommandPointer = (UINT8 *) (&Command);
707 for (Index = 0; Index < sizeof (FDD_COMMAND_PACKET2); Index++) {
708 if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {
709 return EFI_DEVICE_ERROR;
710 }
711 }
712
713 if (EFI_ERROR (DataInByte (FdcDev, &StatusRegister3))) {
714 return EFI_DEVICE_ERROR;
715 }
716 //
717 // Io delay
718 //
719 MicroSecondDelay (50);
720
721 //
722 // Check Status Register 3 to get drive status information
723 //
724 return CheckStatus3 (StatusRegister3);
725 }
726
727 /**
728 Update the disk media properties and if necessary
729 reinstall Block I/O interface
730
731 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
732
733 @retval EFI_SUCCESS: Do the operation successfully
734 @retval EFI_DEVICE_ERROR: Fail to the operation
735
736 **/
737 EFI_STATUS
738 DetectMedia (
739 IN FDC_BLK_IO_DEV *FdcDev
740 )
741 {
742 EFI_STATUS Status;
743 BOOLEAN bReset;
744 BOOLEAN bReadOnlyLastTime;
745 BOOLEAN bMediaPresentLastTime;
746
747 bReset = FALSE;
748 bReadOnlyLastTime = FdcDev->BlkIo.Media->ReadOnly;
749 bMediaPresentLastTime = FdcDev->BlkIo.Media->MediaPresent;
750
751 //
752 // Check disk change
753 //
754 Status = DisketChanged (FdcDev);
755
756 if (Status == EFI_MEDIA_CHANGED) {
757 FdcDev->BlkIo.Media->MediaId++;
758 FdcDev->BlkIo.Media->MediaPresent = TRUE;
759 bReset = TRUE;
760 } else if (Status == EFI_NO_MEDIA) {
761 FdcDev->BlkIo.Media->MediaPresent = FALSE;
762 } else if (Status != EFI_SUCCESS) {
763 MotorOff (FdcDev);
764 return Status;
765 //
766 // EFI_DEVICE_ERROR
767 //
768 }
769
770 if (FdcDev->BlkIo.Media->MediaPresent) {
771 //
772 // Check disk write protected
773 //
774 Status = SenseDrvStatus (FdcDev, 0);
775 if (Status == EFI_WRITE_PROTECTED) {
776 FdcDev->BlkIo.Media->ReadOnly = TRUE;
777 } else {
778 FdcDev->BlkIo.Media->ReadOnly = FALSE;
779 }
780 }
781
782 if (FdcDev->BlkIo.Media->MediaPresent && (bReadOnlyLastTime != FdcDev->BlkIo.Media->ReadOnly)) {
783 bReset = TRUE;
784 }
785
786 if (bMediaPresentLastTime != FdcDev->BlkIo.Media->MediaPresent) {
787 bReset = TRUE;
788 }
789
790 if (bReset) {
791 Status = gBS->ReinstallProtocolInterface (
792 FdcDev->Handle,
793 &gEfiBlockIoProtocolGuid,
794 &FdcDev->BlkIo,
795 &FdcDev->BlkIo
796 );
797
798 if (EFI_ERROR (Status)) {
799 return Status;
800 }
801 }
802
803 return EFI_SUCCESS;
804 }
805
806 /**
807 Set the data rate and so on
808
809 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
810
811 @retval EFI_SUCCESS success to set the data rate
812 **/
813 EFI_STATUS
814 Setup (
815 IN FDC_BLK_IO_DEV *FdcDev
816 )
817 {
818 EFI_STATUS Status;
819
820 //
821 // Set data rate 500kbs
822 //
823 FdcWritePort (FdcDev, FDC_REGISTER_CCR, 0x0);
824
825 //
826 // Io delay
827 //
828 MicroSecondDelay (50);
829
830 Status = Specify (FdcDev);
831
832 if (EFI_ERROR (Status)) {
833 return EFI_DEVICE_ERROR;
834 }
835
836 return EFI_SUCCESS;
837 }
838
839 /**
840 Read or Write a number of blocks in the same cylinder
841
842 @param FdcDev A pointer to Data Structure FDC_BLK_IO_DEV
843 @param HostAddress device address
844 @param Lba The starting logic block address to read from on the device
845 @param NumberOfBlocks The number of block wanted to be read or write
846 @param Read Operation type: read or write
847
848 @retval EFI_SUCCESS Success operate
849
850 **/
851 EFI_STATUS
852 ReadWriteDataSector (
853 IN FDC_BLK_IO_DEV *FdcDev,
854 IN VOID *HostAddress,
855 IN EFI_LBA Lba,
856 IN UINTN NumberOfBlocks,
857 IN BOOLEAN Read
858 )
859 {
860 EFI_STATUS Status;
861 FDD_COMMAND_PACKET1 Command;
862 FDD_RESULT_PACKET Result;
863 UINTN Index;
864 UINTN Times;
865 UINT8 *CommandPointer;
866
867 EFI_PHYSICAL_ADDRESS DeviceAddress;
868 EFI_ISA_IO_PROTOCOL *IsaIo;
869 UINTN NumberofBytes;
870 VOID *Mapping;
871 EFI_ISA_IO_PROTOCOL_OPERATION Operation;
872 EFI_STATUS Status1;
873 UINT8 Channel;
874 EFI_ISA_ACPI_RESOURCE *ResourceItem;
875 UINT32 Attribute;
876
877 Status = Seek (FdcDev, Lba);
878 if (EFI_ERROR (Status)) {
879 return EFI_DEVICE_ERROR;
880 }
881 //
882 // Map Dma
883 //
884 IsaIo = FdcDev->IsaIo;
885 NumberofBytes = NumberOfBlocks * 512;
886 if (Read == READ) {
887 Operation = EfiIsaIoOperationSlaveWrite;
888 } else {
889 Operation = EfiIsaIoOperationSlaveRead;
890 }
891
892 ResourceItem = IsaIo->ResourceList->ResourceItem;
893 Index = 0;
894 while (ResourceItem[Index].Type != EfiIsaAcpiResourceEndOfList) {
895 if (ResourceItem[Index].Type == EfiIsaAcpiResourceDma) {
896 break;
897 }
898
899 Index++;
900 }
901
902 if (ResourceItem[Index].Type == EfiIsaAcpiResourceEndOfList) {
903 return EFI_DEVICE_ERROR;
904 }
905
906 Channel = (UINT8) IsaIo->ResourceList->ResourceItem[Index].StartRange;
907 Attribute = IsaIo->ResourceList->ResourceItem[Index].Attribute;
908
909 Status1 = IsaIo->Map (
910 IsaIo,
911 Operation,
912 Channel,
913 Attribute,
914 HostAddress,
915 &NumberofBytes,
916 &DeviceAddress,
917 &Mapping
918 );
919 if (EFI_ERROR (Status1)) {
920 return Status1;
921 }
922
923 //
924 // Allocate Read or Write command packet
925 //
926 ZeroMem (&Command, sizeof (FDD_COMMAND_PACKET1));
927 if (Read == READ) {
928 Command.CommandCode = READ_DATA_CMD | CMD_MT | CMD_MFM | CMD_SK;
929 } else {
930 Command.CommandCode = WRITE_DATA_CMD | CMD_MT | CMD_MFM;
931 }
932
933 FillPara (FdcDev, Lba, &Command);
934
935 //
936 // Write command bytes to FDC
937 //
938 CommandPointer = (UINT8 *) (&Command);
939 for (Index = 0; Index < sizeof (FDD_COMMAND_PACKET1); Index++) {
940 if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {
941 return EFI_DEVICE_ERROR;
942 }
943 }
944 //
945 // wait for some time
946 //
947 Times = (STALL_1_SECOND / 50) + 1;
948 do {
949 if ((FdcReadPort (FdcDev, FDC_REGISTER_MSR) & 0xc0) == 0xc0) {
950 break;
951 }
952
953 MicroSecondDelay (50);
954 Times = Times - 1;
955 } while (Times);
956
957 if (Times == 0) {
958 return EFI_TIMEOUT;
959 }
960 //
961 // Read result bytes from FDC
962 //
963 CommandPointer = (UINT8 *) (&Result);
964 for (Index = 0; Index < sizeof (FDD_RESULT_PACKET); Index++) {
965 if (EFI_ERROR (DataInByte (FdcDev, CommandPointer++))) {
966 return EFI_DEVICE_ERROR;
967 }
968 }
969 //
970 // Flush before Unmap
971 //
972 if (Read == READ) {
973 Status1 = IsaIo->Flush (IsaIo);
974 if (EFI_ERROR (Status1)) {
975 return Status1;
976 }
977 }
978 //
979 // Unmap Dma
980 //
981 Status1 = IsaIo->Unmap (IsaIo, Mapping);
982 if (EFI_ERROR (Status1)) {
983 return Status1;
984 }
985
986 return CheckResult (&Result, FdcDev);
987 }
988
989 /**
990 Fill in FDD command's parameter
991
992 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
993 @param Lba The starting logic block address to read from on the device
994 @param Command FDD command
995
996 **/
997 VOID
998 FillPara (
999 IN FDC_BLK_IO_DEV *FdcDev,
1000 IN EFI_LBA Lba,
1001 IN FDD_COMMAND_PACKET1 *Command
1002 )
1003 {
1004 UINT8 EndOfTrack;
1005
1006 //
1007 // Get EndOfTrack from the Para table
1008 //
1009 EndOfTrack = DISK_1440K_EOT;
1010
1011 //
1012 // Fill the command parameter
1013 //
1014 if (FdcDev->Disk == FDC_DISK0) {
1015 Command->DiskHeadSel = 0;
1016 } else {
1017 Command->DiskHeadSel = 1;
1018 }
1019
1020 Command->Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2);
1021 Command->Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
1022 Command->Sector = (UINT8) ((UINT8) ((UINTN) Lba % EndOfTrack) + 1);
1023 Command->DiskHeadSel = (UINT8) (Command->DiskHeadSel | (Command->Head << 2));
1024 Command->Number = DISK_1440K_NUMBER;
1025 Command->EndOfTrack = DISK_1440K_EOT;
1026 Command->GapLength = DISK_1440K_GPL;
1027 Command->DataLength = DISK_1440K_DTL;
1028 }
1029
1030 /**
1031 Read result byte from Data Register of FDC
1032
1033 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
1034 @param Pointer UINT8 *: Be used to save result byte read from FDC
1035
1036
1037 @retval EFI_SUCCESS: Read result byte from FDC successfully
1038 @retval EFI_DEVICE_ERROR: The FDC is not ready to be read
1039
1040 **/
1041 EFI_STATUS
1042 DataInByte (
1043 IN FDC_BLK_IO_DEV *FdcDev,
1044 IN OUT UINT8 *Pointer
1045 )
1046 {
1047 UINT8 data;
1048
1049 //
1050 // wait for 1ms and detect the FDC is ready to be read
1051 //
1052 if (EFI_ERROR (FddDRQReady (FdcDev, DATA_IN, 1))) {
1053 return EFI_DEVICE_ERROR;
1054 //
1055 // is not ready
1056 //
1057 }
1058
1059 data = FdcReadPort (FdcDev, FDC_REGISTER_DTR);
1060
1061 //
1062 // Io delay
1063 //
1064 MicroSecondDelay (50);
1065
1066 *Pointer = data;
1067 return EFI_SUCCESS;
1068 }
1069
1070 /**
1071 Write command byte to Data Register of FDC
1072
1073 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
1074 @param Pointer Be used to save command byte written to FDC
1075
1076 @retval EFI_SUCCESS: Write command byte to FDC successfully
1077 @retval EFI_DEVICE_ERROR: The FDC is not ready to be written
1078
1079 **/
1080 EFI_STATUS
1081 DataOutByte (
1082 IN FDC_BLK_IO_DEV *FdcDev,
1083 IN UINT8 *Pointer
1084 )
1085 {
1086 UINT8 data;
1087
1088 //
1089 // wait for 1ms and detect the FDC is ready to be written
1090 //
1091 if (EFI_ERROR (FddDRQReady (FdcDev, DATA_OUT, 1))) {
1092 return EFI_DEVICE_ERROR;
1093 //
1094 // is not ready
1095 //
1096 }
1097
1098 data = *Pointer;
1099
1100 FdcWritePort (FdcDev, FDC_REGISTER_DTR, data);
1101
1102 //
1103 // Io delay
1104 //
1105 MicroSecondDelay (50);
1106
1107 return EFI_SUCCESS;
1108 }
1109
1110 /**
1111 Detect the specified floppy logic drive is busy or
1112 not within a period of time
1113
1114 @param FdcDev Indicate it is drive A or drive B
1115 @param TimeoutInSeconds the time period for waiting
1116
1117 @retval EFI_SUCCESS: The drive and command are not busy
1118 @retval EFI_TIMEOUT: The drive or command is still busy after a period time that
1119 set by TimeoutInSeconds
1120
1121 **/
1122 EFI_STATUS
1123 FddWaitForBSYClear (
1124 IN FDC_BLK_IO_DEV *FdcDev,
1125 IN UINTN TimeoutInSeconds
1126 )
1127 {
1128 UINTN Delay;
1129 UINT8 StatusRegister;
1130 UINT8 Mask;
1131
1132 //
1133 // How to determine drive and command are busy or not: by the bits of
1134 // Main Status Register
1135 // bit0: Drive 0 busy (drive A)
1136 // bit1: Drive 1 busy (drive B)
1137 // bit4: Command busy
1138 //
1139 //
1140 // set mask: for drive A set bit0 & bit4; for drive B set bit1 & bit4
1141 //
1142 Mask = (UINT8) ((FdcDev->Disk == FDC_DISK0 ? MSR_DAB : MSR_DBB) | MSR_CB);
1143
1144 Delay = ((TimeoutInSeconds * STALL_1_MSECOND) / 50) + 1;
1145 do {
1146 StatusRegister = FdcReadPort (FdcDev, FDC_REGISTER_MSR);
1147 if ((StatusRegister & Mask) == 0x00) {
1148 break;
1149 //
1150 // not busy
1151 //
1152 }
1153
1154 MicroSecondDelay (50);
1155 Delay = Delay - 1;
1156 } while (Delay);
1157
1158 if (Delay == 0) {
1159 return EFI_TIMEOUT;
1160 }
1161
1162 return EFI_SUCCESS;
1163 }
1164
1165 /**
1166
1167 Routine Description: Determine whether FDC is ready to write or read
1168
1169 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
1170 @param Dio BOOLEAN: Indicate the FDC is waiting to write or read
1171 @param TimeoutInSeconds UINTN: The time period for waiting
1172
1173 @retval EFI_SUCCESS: FDC is ready to write or read
1174 @retval EFI_NOT_READY: FDC is not ready within the specified time period
1175
1176 **/
1177 EFI_STATUS
1178 FddDRQReady (
1179 IN FDC_BLK_IO_DEV *FdcDev,
1180 IN BOOLEAN Dio,
1181 IN UINTN TimeoutInSeconds
1182 )
1183 {
1184 UINTN Delay;
1185 UINT8 StatusRegister;
1186 UINT8 DataInOut;
1187
1188 //
1189 // Before writing to FDC or reading from FDC, the Host must examine
1190 // the bit7(RQM) and bit6(DIO) of the Main Status Register.
1191 // That is to say:
1192 // command bytes can not be written to Data Register
1193 // unless RQM is 1 and DIO is 0
1194 // result bytes can not be read from Data Register
1195 // unless RQM is 1 and DIO is 1
1196 //
1197 DataInOut = (UINT8) (Dio << 6);
1198 //
1199 // in order to compare bit6
1200 //
1201 Delay = ((TimeoutInSeconds * STALL_1_MSECOND) / 50) + 1;
1202 do {
1203 StatusRegister = FdcReadPort (FdcDev, FDC_REGISTER_MSR);
1204 if ((StatusRegister & MSR_RQM) == MSR_RQM && (StatusRegister & MSR_DIO) == DataInOut) {
1205 break;
1206 //
1207 // FDC is ready
1208 //
1209 }
1210
1211 MicroSecondDelay (50);
1212 //
1213 // Stall for 50 us
1214 //
1215 Delay = Delay - 1;
1216 } while (Delay);
1217
1218 if (Delay == 0) {
1219 return EFI_NOT_READY;
1220 //
1221 // FDC is not ready within the specified time period
1222 //
1223 }
1224
1225 return EFI_SUCCESS;
1226 }
1227
1228 /**
1229 Set FDC control structure's attribute according to
1230 result
1231
1232 @param Result Point to result structure
1233 @param FdcDev FDC control structure
1234
1235 @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value
1236 @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value
1237 @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value
1238 @retval EFI_SUCCESS - GC_TODO: Add description for return value
1239
1240 **/
1241 EFI_STATUS
1242 CheckResult (
1243 IN FDD_RESULT_PACKET *Result,
1244 IN OUT FDC_BLK_IO_DEV *FdcDev
1245 )
1246 {
1247 //
1248 // Check Status Register0
1249 //
1250 if ((Result->Status0 & STS0_IC) != IC_NT) {
1251 if ((Result->Status0 & STS0_SE) == 0x20) {
1252 //
1253 // seek error
1254 //
1255 FdcDev->ControllerState->NeedRecalibrate = TRUE;
1256 }
1257
1258 FdcDev->ControllerState->NeedRecalibrate = TRUE;
1259 return EFI_DEVICE_ERROR;
1260 }
1261 //
1262 // Check Status Register1
1263 //
1264 if (Result->Status1 & (STS1_EN | STS1_DE | STS1_OR | STS1_ND | STS1_NW | STS1_MA)) {
1265 FdcDev->ControllerState->NeedRecalibrate = TRUE;
1266 return EFI_DEVICE_ERROR;
1267 }
1268 //
1269 // Check Status Register2
1270 //
1271 if (Result->Status2 & (STS2_CM | STS2_DD | STS2_WC | STS2_BC | STS2_MD)) {
1272 FdcDev->ControllerState->NeedRecalibrate = TRUE;
1273 return EFI_DEVICE_ERROR;
1274 }
1275
1276 return EFI_SUCCESS;
1277 }
1278
1279 /**
1280 Check the drive status information
1281
1282 @param StatusRegister3 the value of Status Register 3
1283
1284 @retval EFI_SUCCESS The disk is not write protected
1285 @retval EFI_WRITE_PROTECTED: The disk is write protected
1286
1287 **/
1288 EFI_STATUS
1289 CheckStatus3 (
1290 IN UINT8 StatusRegister3
1291 )
1292 {
1293 if (StatusRegister3 & STS3_WP) {
1294 return EFI_WRITE_PROTECTED;
1295 }
1296
1297 return EFI_SUCCESS;
1298 }
1299
1300 /**
1301 Calculate the number of block in the same cylinder
1302 according to LBA
1303
1304 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
1305 @param LBA EFI_LBA: The starting logic block address
1306 @param NumberOfBlocks UINTN: The number of blocks
1307
1308 @return The number of blocks in the same cylinder which the starting
1309 logic block address is LBA
1310
1311 **/
1312 UINTN
1313 GetTransferBlockCount (
1314 IN FDC_BLK_IO_DEV *FdcDev,
1315 IN EFI_LBA LBA,
1316 IN UINTN NumberOfBlocks
1317 )
1318 {
1319 UINT8 EndOfTrack;
1320 UINT8 Head;
1321 UINT8 SectorsInTrack;
1322
1323 //
1324 // Calculate the number of block in the same cylinder
1325 //
1326 EndOfTrack = DISK_1440K_EOT;
1327 Head = (UINT8) ((UINTN) LBA / EndOfTrack % 2);
1328
1329 SectorsInTrack = (UINT8) (EndOfTrack * (2 - Head) - (UINT8) ((UINTN) LBA % EndOfTrack));
1330 if (SectorsInTrack < NumberOfBlocks) {
1331 return SectorsInTrack;
1332 } else {
1333 return NumberOfBlocks;
1334 }
1335 }
1336
1337 /**
1338 When the Timer(2s) off, turn the drive's motor off
1339
1340 @param Event EFI_EVENT: Event(the timer) whose notification function is being
1341 invoked
1342 @param Context VOID *: Pointer to the notification function's context
1343
1344 **/
1345 VOID
1346 EFIAPI
1347 FddTimerProc (
1348 IN EFI_EVENT Event,
1349 IN VOID *Context
1350 )
1351 {
1352 FDC_BLK_IO_DEV *FdcDev;
1353 UINT8 data;
1354
1355 FdcDev = (FDC_BLK_IO_DEV *) Context;
1356
1357 //
1358 // Get the motor status
1359 //
1360 data = FdcReadPort (FdcDev, FDC_REGISTER_DOR);
1361
1362 if (((FdcDev->Disk == FDC_DISK0) && ((data & 0x10) != 0x10)) ||
1363 ((FdcDev->Disk == FDC_DISK1) && ((data & 0x21) != 0x21))
1364 ) {
1365 return ;
1366 }
1367 //
1368 // the motor is on, so need motor off
1369 //
1370 data = 0x0C;
1371 data = (UINT8) (data | (SELECT_DRV & FdcDev->Disk));
1372 FdcWritePort (FdcDev, FDC_REGISTER_DOR, data);
1373 MicroSecondDelay (500);
1374 }
1375
1376 /**
1377 Read I/O port for FDC
1378
1379 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
1380 @param Offset The offset address of port
1381
1382 **/
1383 UINT8
1384 FdcReadPort (
1385 IN FDC_BLK_IO_DEV *FdcDev,
1386 IN UINT32 Offset
1387 )
1388 {
1389 UINT8 Data;
1390
1391 //
1392 // Call IsaIo
1393 //
1394 FdcDev->IsaIo->Io.Read (
1395 FdcDev->IsaIo,
1396 EfiIsaIoWidthUint8,
1397 FdcDev->BaseAddress + Offset,
1398 1,
1399 &Data
1400 );
1401
1402 return Data;
1403 }
1404
1405 /**
1406 Write I/O port for FDC
1407
1408 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
1409 @param Offset The offset address of port
1410 @param Data Value written to port
1411
1412 **/
1413 VOID
1414 FdcWritePort (
1415 IN FDC_BLK_IO_DEV *FdcDev,
1416 IN UINT32 Offset,
1417 IN UINT8 Data
1418 )
1419 {
1420
1421 //
1422 // Call IsaIo
1423 //
1424 FdcDev->IsaIo->Io.Write (
1425 FdcDev->IsaIo,
1426 EfiIsaIoWidthUint8,
1427 FdcDev->BaseAddress + Offset,
1428 1,
1429 &Data
1430 );
1431 }
1432