2 Internal floppy disk controller programming functions for the floppy driver.
4 Copyright (c) 2006 - 2009, Intel Corporation.<BR>
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #include "IsaFloppy.h"
18 Detect whether a floppy drive is present or not.
20 @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV
22 @retval EFI_SUCCESS The floppy disk drive is present
23 @retval EFI_NOT_FOUND The floppy disk drive is not present
27 IN FDC_BLK_IO_DEV
*FdcDev
32 FdcDev
->BlkIo
.Media
= &FdcDev
->BlkMedia
;
34 Status
= FddIdentify (FdcDev
);
35 if (EFI_ERROR (Status
)) {
39 FdcDev
->BlkIo
.Reset
= FdcReset
;
40 FdcDev
->BlkIo
.FlushBlocks
= FddFlushBlocks
;
41 FdcDev
->BlkIo
.ReadBlocks
= FddReadBlocks
;
42 FdcDev
->BlkIo
.WriteBlocks
= FddWriteBlocks
;
43 FdcDev
->BlkMedia
.LogicalPartition
= FALSE
;
44 FdcDev
->BlkMedia
.WriteCaching
= FALSE
;
50 Do recalibrate and check if the drive is present or not
51 and set the media parameters if the driver is present.
53 @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV
55 @retval EFI_SUCCESS The floppy disk drive is present
56 @retval EFI_DEVICE_ERROR The floppy disk drive is not present
60 IN FDC_BLK_IO_DEV
*FdcDev
66 // Set Floppy Disk Controller's motor on
68 Status
= MotorOn (FdcDev
);
69 if (EFI_ERROR (Status
)) {
70 return EFI_DEVICE_ERROR
;
73 Status
= Recalibrate (FdcDev
);
75 if (EFI_ERROR (Status
)) {
77 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
78 return EFI_DEVICE_ERROR
;
81 // Set Media Parameter
83 FdcDev
->BlkIo
.Media
->RemovableMedia
= TRUE
;
84 FdcDev
->BlkIo
.Media
->MediaPresent
= TRUE
;
85 FdcDev
->BlkIo
.Media
->MediaId
= 0;
90 Status
= DisketChanged (FdcDev
);
92 if (Status
== EFI_NO_MEDIA
) {
93 FdcDev
->BlkIo
.Media
->MediaPresent
= FALSE
;
94 } else if ((Status
!= EFI_MEDIA_CHANGED
) &&
95 (Status
!= EFI_SUCCESS
)) {
101 // Check Disk Write Protected
103 Status
= SenseDrvStatus (FdcDev
, 0);
105 if (Status
== EFI_WRITE_PROTECTED
) {
106 FdcDev
->BlkIo
.Media
->ReadOnly
= TRUE
;
107 } else if (Status
== EFI_SUCCESS
) {
108 FdcDev
->BlkIo
.Media
->ReadOnly
= FALSE
;
110 return EFI_DEVICE_ERROR
;
116 // Set Media Default Type
118 FdcDev
->BlkIo
.Media
->BlockSize
= DISK_1440K_BYTEPERSECTOR
;
119 FdcDev
->BlkIo
.Media
->LastBlock
= DISK_1440K_EOT
* 2 * (DISK_1440K_MAXTRACKNUM
+ 1) - 1;
125 Reset the Floppy Logic Drive.
127 @param FdcDev FDC_BLK_IO_DEV * : A pointer to the FDC_BLK_IO_DEV
129 @retval EFI_SUCCESS: The Floppy Logic Drive is reset
130 @retval EFI_DEVICE_ERROR: The Floppy Logic Drive is not functioning correctly and
136 IN FDC_BLK_IO_DEV
*FdcDev
140 UINT8 StatusRegister0
;
141 UINT8 PresentCylinderNumber
;
145 // Report reset progress code
147 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
149 EFI_PERIPHERAL_REMOVABLE_MEDIA
| EFI_P_PC_RESET
,
154 // Reset specified Floppy Logic Drive according to FdcDev -> Disk
155 // Set Digital Output Register(DOR) to do reset work
156 // bit0 & bit1 of DOR : Drive Select
158 // bit3 : DMA and Int bit
159 // Reset : a "0" written to bit2 resets the FDC, this reset will remain
161 // a "1" is written to this bit.
163 // use bit0 & bit1 to select the logic drive
167 Data
= (UINT8
) (Data
| (SELECT_DRV
& FdcDev
->Disk
));
168 FdcWritePort (FdcDev
, FDC_REGISTER_DOR
, Data
);
171 // wait some time,at least 120us
173 MicroSecondDelay (500);
178 // write "1" to bit3 : enable DMA
181 FdcWritePort (FdcDev
, FDC_REGISTER_DOR
, Data
);
186 MicroSecondDelay (2000);
189 // wait specified floppy logic drive is not busy
191 if (EFI_ERROR (FddWaitForBSYClear (FdcDev
, 1))) {
192 return EFI_DEVICE_ERROR
;
195 // Set the Transfer Data Rate
197 FdcWritePort (FdcDev
, FDC_REGISTER_CCR
, 0x0);
202 MicroSecondDelay (100);
205 // Issue Sense interrupt command for each drive (total 4 drives)
207 for (Index
= 0; Index
< 4; Index
++) {
208 if (EFI_ERROR (SenseIntStatus (FdcDev
, &StatusRegister0
, &PresentCylinderNumber
))) {
209 return EFI_DEVICE_ERROR
;
213 // issue Specify command
215 if (EFI_ERROR (Specify (FdcDev
))) {
216 return EFI_DEVICE_ERROR
;
223 Turn the floppy disk drive's motor on.
224 The drive's motor must be on before any command can be executed.
226 @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV
228 @retval EFI_SUCCESS The drive's motor was turned on successfully
229 @retval EFI_DEVICE_ERROR The drive is busy, so can not turn motor on
233 IN FDC_BLK_IO_DEV
*FdcDev
240 // Control of the floppy drive motors is a big pain. If motor is off, you have
241 // to turn it on first. But you can not leave the motor on all the time, since
242 // that would wear out the disk. On the other hand, if you turn the motor off
243 // after each operation, the system performance will be awful. The compromise
244 // used in this driver is to leave the motor on for 2 seconds after
245 // each operation. If a new operation is started in that interval(2s),
246 // the motor need not be turned on again. If no new operation is started,
247 // a timer goes off and the motor is turned off
252 Status
= gBS
->SetTimer (FdcDev
->Event
, TimerCancel
, 0);
253 ASSERT_EFI_ERROR (Status
);
256 // Get the motor status
258 DorData
= FdcReadPort (FdcDev
, FDC_REGISTER_DOR
);
260 if (((FdcDev
->Disk
== FdcDisk0
) && ((DorData
& 0x10) == 0x10)) ||
261 ((FdcDev
->Disk
== FdcDisk1
) && ((DorData
& 0x21) == 0x21))
266 // The drive's motor is off, so need turn it on
267 // first look at command and drive are busy or not
269 if (EFI_ERROR (FddWaitForBSYClear (FdcDev
, 1))) {
270 return EFI_DEVICE_ERROR
;
273 // for drive A: 1CH, drive B: 2DH
276 DorData
= (UINT8
) (DorData
| (SELECT_DRV
& FdcDev
->Disk
));
277 if (FdcDev
->Disk
== FdcDisk0
) {
281 DorData
|= DRVA_MOTOR_ON
;
286 DorData
|= DRVB_MOTOR_ON
;
289 FdcWritePort (FdcDev
, FDC_REGISTER_DOR
, DorData
);
294 MicroSecondDelay (4000);
300 Set a Timer and when Timer goes off, turn the motor off.
302 @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV
304 @retval EFI_SUCCESS Set the Timer successfully
305 @retval EFI_INVALID_PARAMETER Fail to Set the timer
309 IN FDC_BLK_IO_DEV
*FdcDev
313 // Set the timer : 2s
315 return gBS
->SetTimer (FdcDev
->Event
, TimerRelative
, 20000000);
319 Detect whether the disk in the drive is changed or not.
321 @param[in] FdcDev A pointer to FDC_BLK_IO_DEV
323 @retval EFI_SUCCESS No disk media change
324 @retval EFI_DEVICE_ERROR Fail to do the recalibrate or seek operation
325 @retval EFI_NO_MEDIA No disk in the drive
326 @retval EFI_MEDIA_CHANGED There is a new disk in the drive
330 IN FDC_BLK_IO_DEV
*FdcDev
339 Data
= FdcReadPort (FdcDev
, FDC_REGISTER_DIR
);
344 MicroSecondDelay (50);
346 if ((Data
& DIR_DCL
) == 0x80) {
348 // disk change line is active
350 if (FdcDev
->PresentCylinderNumber
!= 0) {
351 Status
= Recalibrate (FdcDev
);
353 Status
= Seek (FdcDev
, 0x30);
356 if (EFI_ERROR (Status
)) {
357 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
358 return EFI_DEVICE_ERROR
;
360 // Fail to do the seek or recalibrate operation
364 Data
= FdcReadPort (FdcDev
, FDC_REGISTER_DIR
);
369 MicroSecondDelay (50);
371 if ((Data
& DIR_DCL
) == 0x80) {
375 return EFI_MEDIA_CHANGED
;
382 Do the Specify command, this command sets DMA operation
383 and the initial values for each of the three internal
384 times: HUT, SRT and HLT.
386 @param[in] FdcDev Pointer to instance of FDC_BLK_IO_DEV
388 @retval EFI_SUCCESS Execute the Specify command successfully
389 @retval EFI_DEVICE_ERROR Fail to execute the command
393 IN FDC_BLK_IO_DEV
*FdcDev
396 FDD_SPECIFY_CMD Command
;
398 UINT8
*CommandPointer
;
400 ZeroMem (&Command
, sizeof (FDD_SPECIFY_CMD
));
401 Command
.CommandCode
= SPECIFY_CMD
;
405 Command
.SrtHut
= 0xdf;
411 Command
.HltNd
= 0x02;
413 CommandPointer
= (UINT8
*) (&Command
);
414 for (Index
= 0; Index
< sizeof (FDD_SPECIFY_CMD
); Index
++) {
415 if (EFI_ERROR (DataOutByte (FdcDev
, CommandPointer
++))) {
416 return EFI_DEVICE_ERROR
;
424 Set the head of floppy drive to track 0.
426 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
427 @retval EFI_SUCCESS: Execute the Recalibrate operation successfully
428 @retval EFI_DEVICE_ERROR: Fail to execute the Recalibrate operation
433 IN FDC_BLK_IO_DEV
*FdcDev
436 FDD_COMMAND_PACKET2 Command
;
438 UINT8 StatusRegister0
;
439 UINT8 PresentCylinderNumber
;
440 UINT8
*CommandPointer
;
446 ZeroMem (&Command
, sizeof (FDD_COMMAND_PACKET2
));
447 Command
.CommandCode
= RECALIBRATE_CMD
;
451 if (FdcDev
->Disk
== FdcDisk0
) {
452 Command
.DiskHeadSel
= 0;
457 Command
.DiskHeadSel
= 1;
463 CommandPointer
= (UINT8
*) (&Command
);
464 for (Index
= 0; Index
< sizeof (FDD_COMMAND_PACKET2
); Index
++) {
465 if (EFI_ERROR (DataOutByte (FdcDev
, CommandPointer
++))) {
466 return EFI_DEVICE_ERROR
;
472 MicroSecondDelay (250000);
474 // need modify according to 1.44M or 2.88M
476 if (EFI_ERROR (SenseIntStatus (FdcDev
, &StatusRegister0
, &PresentCylinderNumber
))) {
477 return EFI_DEVICE_ERROR
;
480 if ((StatusRegister0
& 0xf0) == 0x20 && PresentCylinderNumber
== 0) {
481 FdcDev
->PresentCylinderNumber
= 0;
482 FdcDev
->ControllerState
->NeedRecalibrate
= FALSE
;
487 return EFI_DEVICE_ERROR
;
498 Set the head of floppy drive to the new cylinder.
500 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
501 @param Lba EFI_LBA : The logic block address want to seek
503 @retval EFI_SUCCESS: Execute the Seek operation successfully
504 @retval EFI_DEVICE_ERROR: Fail to execute the Seek operation
509 IN FDC_BLK_IO_DEV
*FdcDev
,
513 FDD_SEEK_CMD Command
;
517 UINT8 StatusRegister0
;
518 UINT8
*CommandPointer
;
519 UINT8 PresentCylinderNumber
;
523 if (FdcDev
->ControllerState
->NeedRecalibrate
) {
524 if (EFI_ERROR (Recalibrate (FdcDev
))) {
525 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
526 return EFI_DEVICE_ERROR
;
530 EndOfTrack
= DISK_1440K_EOT
;
532 // Calculate cylinder based on Lba and EOT
534 Cylinder
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
/ 2);
537 // if the destination cylinder is the present cylinder, unnecessary to do the
540 if (FdcDev
->PresentCylinderNumber
== Cylinder
) {
544 // Calculate the head : 0 or 1
546 Head
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
% 2);
548 ZeroMem (&Command
, sizeof (FDD_SEEK_CMD
));
549 Command
.CommandCode
= SEEK_CMD
;
550 if (FdcDev
->Disk
== FdcDisk0
) {
551 Command
.DiskHeadSel
= 0;
556 Command
.DiskHeadSel
= 1;
562 Command
.DiskHeadSel
= (UINT8
) (Command
.DiskHeadSel
| (Head
<< 2));
563 Command
.NewCylinder
= Cylinder
;
565 CommandPointer
= (UINT8
*) (&Command
);
566 for (Index
= 0; Index
< sizeof (FDD_SEEK_CMD
); Index
++) {
567 if (EFI_ERROR (DataOutByte (FdcDev
, CommandPointer
++))) {
568 return EFI_DEVICE_ERROR
;
574 MicroSecondDelay (100);
577 // Calculate waiting time
579 if (FdcDev
->PresentCylinderNumber
> Cylinder
) {
580 DelayTime
= (UINT8
) (FdcDev
->PresentCylinderNumber
- Cylinder
);
582 DelayTime
= (UINT8
) (Cylinder
- FdcDev
->PresentCylinderNumber
);
585 MicroSecondDelay ((DelayTime
+ 1) * 4000);
587 if (EFI_ERROR (SenseIntStatus (FdcDev
, &StatusRegister0
, &PresentCylinderNumber
))) {
588 return EFI_DEVICE_ERROR
;
591 if ((StatusRegister0
& 0xf0) == 0x20) {
592 FdcDev
->PresentCylinderNumber
= Command
.NewCylinder
;
595 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
596 return EFI_DEVICE_ERROR
;
601 Do the Sense Interrupt Status command, this command
602 resets the interrupt signal.
604 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
605 @param StatusRegister0 UINT8 *: Be used to save Status Register 0 read from FDC
606 @param PresentCylinderNumber UINT8 *: Be used to save present cylinder number
609 @retval EFI_SUCCESS: Execute the Sense Interrupt Status command successfully
610 @retval EFI_DEVICE_ERROR: Fail to execute the command
615 IN FDC_BLK_IO_DEV
*FdcDev
,
616 IN OUT UINT8
*StatusRegister0
,
617 IN OUT UINT8
*PresentCylinderNumber
622 Command
= SENSE_INT_STATUS_CMD
;
623 if (EFI_ERROR (DataOutByte (FdcDev
, &Command
))) {
624 return EFI_DEVICE_ERROR
;
627 if (EFI_ERROR (DataInByte (FdcDev
, StatusRegister0
))) {
628 return EFI_DEVICE_ERROR
;
631 if (EFI_ERROR (DataInByte (FdcDev
, PresentCylinderNumber
))) {
632 return EFI_DEVICE_ERROR
;
639 Do the Sense Drive Status command.
641 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
642 @param Lba EFI_LBA : Logic block address
644 @retval EFI_SUCCESS: Execute the Sense Drive Status command successfully
645 @retval EFI_DEVICE_ERROR: Fail to execute the command
646 @retval EFI_WRITE_PROTECTED:The disk is write protected
651 IN FDC_BLK_IO_DEV
*FdcDev
,
655 FDD_COMMAND_PACKET2 Command
;
659 UINT8 StatusRegister3
;
660 UINT8
*CommandPointer
;
663 // Sense Drive Status command obtains drive status information,
664 // it has not execution phase and goes directly to the result phase from the
665 // command phase, Status Register 3 contains the drive status information
667 ZeroMem (&Command
, sizeof (FDD_COMMAND_PACKET2
));
668 Command
.CommandCode
= SENSE_DRV_STATUS_CMD
;
670 if (FdcDev
->Disk
== FdcDisk0
) {
671 Command
.DiskHeadSel
= 0;
673 Command
.DiskHeadSel
= 1;
676 EndOfTrack
= DISK_1440K_EOT
;
677 Head
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
% 2);
678 Command
.DiskHeadSel
= (UINT8
) (Command
.DiskHeadSel
| (Head
<< 2));
680 CommandPointer
= (UINT8
*) (&Command
);
681 for (Index
= 0; Index
< sizeof (FDD_COMMAND_PACKET2
); Index
++) {
682 if (EFI_ERROR (DataOutByte (FdcDev
, CommandPointer
++))) {
683 return EFI_DEVICE_ERROR
;
687 if (EFI_ERROR (DataInByte (FdcDev
, &StatusRegister3
))) {
688 return EFI_DEVICE_ERROR
;
693 MicroSecondDelay (50);
696 // Check Status Register 3 to get drive status information
698 return CheckStatus3 (StatusRegister3
);
702 Update the disk media properties and if necessary reinstall Block I/O interface.
704 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
706 @retval EFI_SUCCESS: Do the operation successfully
707 @retval EFI_DEVICE_ERROR: Fail to the operation
712 IN FDC_BLK_IO_DEV
*FdcDev
717 BOOLEAN ReadOnlyLastTime
;
718 BOOLEAN MediaPresentLastTime
;
721 ReadOnlyLastTime
= FdcDev
->BlkIo
.Media
->ReadOnly
;
722 MediaPresentLastTime
= FdcDev
->BlkIo
.Media
->MediaPresent
;
727 Status
= DisketChanged (FdcDev
);
729 if (Status
== EFI_MEDIA_CHANGED
) {
730 FdcDev
->BlkIo
.Media
->MediaId
++;
731 FdcDev
->BlkIo
.Media
->MediaPresent
= TRUE
;
733 } else if (Status
== EFI_NO_MEDIA
) {
734 FdcDev
->BlkIo
.Media
->MediaPresent
= FALSE
;
735 } else if (Status
!= EFI_SUCCESS
) {
743 if (FdcDev
->BlkIo
.Media
->MediaPresent
) {
745 // Check disk write protected
747 Status
= SenseDrvStatus (FdcDev
, 0);
748 if (Status
== EFI_WRITE_PROTECTED
) {
749 FdcDev
->BlkIo
.Media
->ReadOnly
= TRUE
;
751 FdcDev
->BlkIo
.Media
->ReadOnly
= FALSE
;
755 if (FdcDev
->BlkIo
.Media
->MediaPresent
&& (ReadOnlyLastTime
!= FdcDev
->BlkIo
.Media
->ReadOnly
)) {
759 if (MediaPresentLastTime
!= FdcDev
->BlkIo
.Media
->MediaPresent
) {
764 Status
= gBS
->ReinstallProtocolInterface (
766 &gEfiBlockIoProtocolGuid
,
771 if (EFI_ERROR (Status
)) {
780 Set the data rate and so on.
782 @param FdcDev A pointer to FDC_BLK_IO_DEV
784 @retval EFI_SUCCESS success to set the data rate
788 IN FDC_BLK_IO_DEV
*FdcDev
794 // Set data rate 500kbs
796 FdcWritePort (FdcDev
, FDC_REGISTER_CCR
, 0x0);
801 MicroSecondDelay (50);
803 Status
= Specify (FdcDev
);
805 if (EFI_ERROR (Status
)) {
806 return EFI_DEVICE_ERROR
;
813 Read or Write a number of blocks in the same cylinder.
815 @param FdcDev A pointer to FDC_BLK_IO_DEV
816 @param HostAddress device address
817 @param Lba The starting logic block address to read from on the device
818 @param NumberOfBlocks The number of block wanted to be read or write
819 @param Read Operation type: read or write
821 @retval EFI_SUCCESS Success operate
825 ReadWriteDataSector (
826 IN FDC_BLK_IO_DEV
*FdcDev
,
827 IN VOID
*HostAddress
,
829 IN UINTN NumberOfBlocks
,
834 FDD_COMMAND_PACKET1 Command
;
835 FDD_RESULT_PACKET Result
;
838 UINT8
*CommandPointer
;
840 EFI_PHYSICAL_ADDRESS DeviceAddress
;
841 EFI_ISA_IO_PROTOCOL
*IsaIo
;
844 EFI_ISA_IO_PROTOCOL_OPERATION Operation
;
847 EFI_ISA_ACPI_RESOURCE
*ResourceItem
;
850 Status
= Seek (FdcDev
, Lba
);
851 if (EFI_ERROR (Status
)) {
852 return EFI_DEVICE_ERROR
;
857 IsaIo
= FdcDev
->IsaIo
;
858 NumberofBytes
= NumberOfBlocks
* 512;
860 Operation
= EfiIsaIoOperationSlaveWrite
;
862 Operation
= EfiIsaIoOperationSlaveRead
;
865 ResourceItem
= IsaIo
->ResourceList
->ResourceItem
;
867 while (ResourceItem
[Index
].Type
!= EfiIsaAcpiResourceEndOfList
) {
868 if (ResourceItem
[Index
].Type
== EfiIsaAcpiResourceDma
) {
875 if (ResourceItem
[Index
].Type
== EfiIsaAcpiResourceEndOfList
) {
876 return EFI_DEVICE_ERROR
;
879 Channel
= (UINT8
) IsaIo
->ResourceList
->ResourceItem
[Index
].StartRange
;
880 Attribute
= IsaIo
->ResourceList
->ResourceItem
[Index
].Attribute
;
882 Status1
= IsaIo
->Map (
892 if (EFI_ERROR (Status1
)) {
897 // Allocate Read or Write command packet
899 ZeroMem (&Command
, sizeof (FDD_COMMAND_PACKET1
));
901 Command
.CommandCode
= READ_DATA_CMD
| CMD_MT
| CMD_MFM
| CMD_SK
;
903 Command
.CommandCode
= WRITE_DATA_CMD
| CMD_MT
| CMD_MFM
;
906 FillPara (FdcDev
, Lba
, &Command
);
909 // Write command bytes to FDC
911 CommandPointer
= (UINT8
*) (&Command
);
912 for (Index
= 0; Index
< sizeof (FDD_COMMAND_PACKET1
); Index
++) {
913 if (EFI_ERROR (DataOutByte (FdcDev
, CommandPointer
++))) {
914 return EFI_DEVICE_ERROR
;
918 // wait for some time
920 Times
= (STALL_1_SECOND
/ 50) + 1;
922 if ((FdcReadPort (FdcDev
, FDC_REGISTER_MSR
) & 0xc0) == 0xc0) {
926 MicroSecondDelay (50);
928 } while (Times
>= 0);
934 // Read result bytes from FDC
936 CommandPointer
= (UINT8
*) (&Result
);
937 for (Index
= 0; Index
< sizeof (FDD_RESULT_PACKET
); Index
++) {
938 if (EFI_ERROR (DataInByte (FdcDev
, CommandPointer
++))) {
939 return EFI_DEVICE_ERROR
;
943 // Flush before Unmap
946 Status1
= IsaIo
->Flush (IsaIo
);
947 if (EFI_ERROR (Status1
)) {
954 Status1
= IsaIo
->Unmap (IsaIo
, Mapping
);
955 if (EFI_ERROR (Status1
)) {
959 return CheckResult (&Result
, FdcDev
);
963 Fill in FDD command's parameter.
965 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
966 @param Lba The starting logic block address to read from on the device
967 @param Command FDD command
972 IN FDC_BLK_IO_DEV
*FdcDev
,
974 IN FDD_COMMAND_PACKET1
*Command
980 // Get EndOfTrack from the Para table
982 EndOfTrack
= DISK_1440K_EOT
;
985 // Fill the command parameter
987 if (FdcDev
->Disk
== FdcDisk0
) {
988 Command
->DiskHeadSel
= 0;
990 Command
->DiskHeadSel
= 1;
993 Command
->Cylinder
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
/ 2);
994 Command
->Head
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
% 2);
995 Command
->Sector
= (UINT8
) ((UINT8
) ((UINTN
) Lba
% EndOfTrack
) + 1);
996 Command
->DiskHeadSel
= (UINT8
) (Command
->DiskHeadSel
| (Command
->Head
<< 2));
997 Command
->Number
= DISK_1440K_NUMBER
;
998 Command
->EndOfTrack
= DISK_1440K_EOT
;
999 Command
->GapLength
= DISK_1440K_GPL
;
1000 Command
->DataLength
= DISK_1440K_DTL
;
1004 Read result byte from Data Register of FDC.
1006 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
1007 @param Pointer Buffer to store the byte read from FDC
1009 @retval EFI_SUCCESS Read result byte from FDC successfully
1010 @retval EFI_DEVICE_ERROR The FDC is not ready to be read
1015 IN FDC_BLK_IO_DEV
*FdcDev
,
1022 // wait for 1ms and detect the FDC is ready to be read
1024 if (EFI_ERROR (FddDRQReady (FdcDev
, DATA_IN
, 1))) {
1025 return EFI_DEVICE_ERROR
;
1031 Data
= FdcReadPort (FdcDev
, FDC_REGISTER_DTR
);
1036 MicroSecondDelay (50);
1043 Write command byte to Data Register of FDC.
1045 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
1046 @param Pointer Be used to save command byte written to FDC
1048 @retval EFI_SUCCESS: Write command byte to FDC successfully
1049 @retval EFI_DEVICE_ERROR: The FDC is not ready to be written
1054 IN FDC_BLK_IO_DEV
*FdcDev
,
1061 // wait for 1ms and detect the FDC is ready to be written
1063 if (EFI_ERROR (FddDRQReady (FdcDev
, DATA_OUT
, 1))) {
1067 return EFI_DEVICE_ERROR
;
1072 FdcWritePort (FdcDev
, FDC_REGISTER_DTR
, Data
);
1077 MicroSecondDelay (50);
1083 Detect the specified floppy logic drive is busy or not within a period of time.
1085 @param FdcDev Indicate it is drive A or drive B
1086 @param TimeoutInSeconds the time period for waiting
1088 @retval EFI_SUCCESS: The drive and command are not busy
1089 @retval EFI_TIMEOUT: The drive or command is still busy after a period time that
1090 set by TimeoutInSeconds
1094 FddWaitForBSYClear (
1095 IN FDC_BLK_IO_DEV
*FdcDev
,
1096 IN UINTN TimeoutInSeconds
1100 UINT8 StatusRegister
;
1104 // How to determine drive and command are busy or not: by the bits of
1105 // Main Status Register
1106 // bit0: Drive 0 busy (drive A)
1107 // bit1: Drive 1 busy (drive B)
1108 // bit4: Command busy
1111 // set mask: for drive A set bit0 & bit4; for drive B set bit1 & bit4
1113 Mask
= (UINT8
) ((FdcDev
->Disk
== FdcDisk0
? MSR_DAB
: MSR_DBB
) | MSR_CB
);
1115 Delay
= ((TimeoutInSeconds
* STALL_1_MSECOND
) / 50) + 1;
1117 StatusRegister
= FdcReadPort (FdcDev
, FDC_REGISTER_MSR
);
1118 if ((StatusRegister
& Mask
) == 0x00) {
1125 MicroSecondDelay (50);
1127 } while (Delay
>= 0);
1138 Routine Description: Determine whether FDC is ready to write or read.
1140 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
1141 @param Dio BOOLEAN: Indicate the FDC is waiting to write or read
1142 @param TimeoutInSeconds UINTN: The time period for waiting
1144 @retval EFI_SUCCESS: FDC is ready to write or read
1145 @retval EFI_NOT_READY: FDC is not ready within the specified time period
1150 IN FDC_BLK_IO_DEV
*FdcDev
,
1152 IN UINTN TimeoutInSeconds
1156 UINT8 StatusRegister
;
1160 // Before writing to FDC or reading from FDC, the Host must examine
1161 // the bit7(RQM) and bit6(DIO) of the Main Status Register.
1163 // command bytes can not be written to Data Register
1164 // unless RQM is 1 and DIO is 0
1165 // result bytes can not be read from Data Register
1166 // unless RQM is 1 and DIO is 1
1168 DataInOut
= (UINT8
) (Dio
<< 6);
1170 // in order to compare bit6
1172 Delay
= ((TimeoutInSeconds
* STALL_1_MSECOND
) / 50) + 1;
1174 StatusRegister
= FdcReadPort (FdcDev
, FDC_REGISTER_MSR
);
1175 if ((StatusRegister
& MSR_RQM
) == MSR_RQM
&& (StatusRegister
& MSR_DIO
) == DataInOut
) {
1182 MicroSecondDelay (50);
1187 } while (Delay
>= 0);
1190 return EFI_NOT_READY
;
1192 // FDC is not ready within the specified time period
1200 Set FDC control structure's attribute according to result.
1202 @param Result Point to result structure
1203 @param FdcDev FDC control structure
1205 @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value
1206 @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value
1207 @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value
1208 @retval EFI_SUCCESS - GC_TODO: Add description for return value
1213 IN FDD_RESULT_PACKET
*Result
,
1214 IN OUT FDC_BLK_IO_DEV
*FdcDev
1218 // Check Status Register0
1220 if ((Result
->Status0
& STS0_IC
) != IC_NT
) {
1221 if ((Result
->Status0
& STS0_SE
) == 0x20) {
1225 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
1228 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
1229 return EFI_DEVICE_ERROR
;
1232 // Check Status Register1
1234 if ((Result
->Status1
& (STS1_EN
| STS1_DE
| STS1_OR
| STS1_ND
| STS1_NW
| STS1_MA
)) != 0) {
1235 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
1236 return EFI_DEVICE_ERROR
;
1239 // Check Status Register2
1241 if ((Result
->Status2
& (STS2_CM
| STS2_DD
| STS2_WC
| STS2_BC
| STS2_MD
)) != 0) {
1242 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
1243 return EFI_DEVICE_ERROR
;
1250 Check the drive status information.
1252 @param StatusRegister3 the value of Status Register 3
1254 @retval EFI_SUCCESS The disk is not write protected
1255 @retval EFI_WRITE_PROTECTED: The disk is write protected
1260 IN UINT8 StatusRegister3
1263 if ((StatusRegister3
& STS3_WP
) != 0) {
1264 return EFI_WRITE_PROTECTED
;
1271 Calculate the number of block in the same cylinder according to LBA.
1273 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
1274 @param LBA EFI_LBA: The starting logic block address
1275 @param NumberOfBlocks UINTN: The number of blocks
1277 @return The number of blocks in the same cylinder which the starting
1278 logic block address is LBA
1282 GetTransferBlockCount (
1283 IN FDC_BLK_IO_DEV
*FdcDev
,
1285 IN UINTN NumberOfBlocks
1290 UINT8 SectorsInTrack
;
1293 // Calculate the number of block in the same cylinder
1295 EndOfTrack
= DISK_1440K_EOT
;
1296 Head
= (UINT8
) ((UINTN
) LBA
/ EndOfTrack
% 2);
1298 SectorsInTrack
= (UINT8
) (EndOfTrack
* (2 - Head
) - (UINT8
) ((UINTN
) LBA
% EndOfTrack
));
1299 if (SectorsInTrack
< NumberOfBlocks
) {
1300 return SectorsInTrack
;
1302 return NumberOfBlocks
;
1307 When the Timer(2s) off, turn the drive's motor off.
1309 @param Event EFI_EVENT: Event(the timer) whose notification function is being
1311 @param Context VOID *: Pointer to the notification function's context
1321 FDC_BLK_IO_DEV
*FdcDev
;
1324 FdcDev
= (FDC_BLK_IO_DEV
*) Context
;
1327 // Get the motor status
1329 Data
= FdcReadPort (FdcDev
, FDC_REGISTER_DOR
);
1331 if (((FdcDev
->Disk
== FdcDisk0
) && ((Data
& 0x10) != 0x10)) ||
1332 ((FdcDev
->Disk
== FdcDisk1
) && ((Data
& 0x21) != 0x21))
1337 // the motor is on, so need motor off
1340 Data
= (UINT8
) (Data
| (SELECT_DRV
& FdcDev
->Disk
));
1341 FdcWritePort (FdcDev
, FDC_REGISTER_DOR
, Data
);
1342 MicroSecondDelay (500);
1346 Read an I/O port of FDC.
1348 @param[in] FdcDev A pointer to FDC_BLK_IO_DEV.
1349 @param[in] Offset The address offset of the I/O port.
1351 @retval 8-bit data read from the I/O port.
1355 IN FDC_BLK_IO_DEV
*FdcDev
,
1362 Status
= FdcDev
->IsaIo
->Io
.Read (
1365 FdcDev
->BaseAddress
+ Offset
,
1369 ASSERT_EFI_ERROR (Status
);
1375 Write an I/O port of FDC.
1377 @param[in] FdcDev A pointer to FDC_BLK_IO_DEV
1378 @param[in] Offset The address offset of the I/O port
1379 @param[in] Data 8-bit Value written to the I/O port
1383 IN FDC_BLK_IO_DEV
*FdcDev
,
1390 Status
= FdcDev
->IsaIo
->Io
.Write (
1393 FdcDev
->BaseAddress
+ Offset
,
1397 ASSERT_EFI_ERROR (Status
);