2 Internal floppy disk controller programming functions for the floppy driver.
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
12 Detect whether a floppy drive is present or not.
14 @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV
16 @retval EFI_SUCCESS The floppy disk drive is present
17 @retval EFI_NOT_FOUND The floppy disk drive is not present
21 IN FDC_BLK_IO_DEV
*FdcDev
26 FdcDev
->BlkIo
.Media
= &FdcDev
->BlkMedia
;
28 Status
= FddIdentify (FdcDev
);
29 if (EFI_ERROR (Status
)) {
33 FdcDev
->BlkIo
.Reset
= FdcReset
;
34 FdcDev
->BlkIo
.FlushBlocks
= FddFlushBlocks
;
35 FdcDev
->BlkIo
.ReadBlocks
= FddReadBlocks
;
36 FdcDev
->BlkIo
.WriteBlocks
= FddWriteBlocks
;
37 FdcDev
->BlkMedia
.LogicalPartition
= FALSE
;
38 FdcDev
->BlkMedia
.WriteCaching
= FALSE
;
44 Do recalibrate and check if the drive is present or not
45 and set the media parameters if the driver is present.
47 @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV
49 @retval EFI_SUCCESS The floppy disk drive is present
50 @retval EFI_DEVICE_ERROR The floppy disk drive is not present
54 IN FDC_BLK_IO_DEV
*FdcDev
60 // Set Floppy Disk Controller's motor on
62 Status
= MotorOn (FdcDev
);
63 if (EFI_ERROR (Status
)) {
64 return EFI_DEVICE_ERROR
;
67 Status
= Recalibrate (FdcDev
);
69 if (EFI_ERROR (Status
)) {
71 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
72 return EFI_DEVICE_ERROR
;
75 // Set Media Parameter
77 FdcDev
->BlkIo
.Media
->RemovableMedia
= TRUE
;
78 FdcDev
->BlkIo
.Media
->MediaPresent
= TRUE
;
79 FdcDev
->BlkIo
.Media
->MediaId
= 0;
84 Status
= DisketChanged (FdcDev
);
86 if (Status
== EFI_NO_MEDIA
) {
87 FdcDev
->BlkIo
.Media
->MediaPresent
= FALSE
;
88 } else if ((Status
!= EFI_MEDIA_CHANGED
) &&
89 (Status
!= EFI_SUCCESS
)) {
95 // Check Disk Write Protected
97 Status
= SenseDrvStatus (FdcDev
, 0);
99 if (Status
== EFI_WRITE_PROTECTED
) {
100 FdcDev
->BlkIo
.Media
->ReadOnly
= TRUE
;
101 } else if (Status
== EFI_SUCCESS
) {
102 FdcDev
->BlkIo
.Media
->ReadOnly
= FALSE
;
104 return EFI_DEVICE_ERROR
;
110 // Set Media Default Type
112 FdcDev
->BlkIo
.Media
->BlockSize
= DISK_1440K_BYTEPERSECTOR
;
113 FdcDev
->BlkIo
.Media
->LastBlock
= DISK_1440K_EOT
* 2 * (DISK_1440K_MAXTRACKNUM
+ 1) - 1;
119 Reset the Floppy Logic Drive.
121 @param FdcDev FDC_BLK_IO_DEV * : A pointer to the FDC_BLK_IO_DEV
123 @retval EFI_SUCCESS: The Floppy Logic Drive is reset
124 @retval EFI_DEVICE_ERROR: The Floppy Logic Drive is not functioning correctly and
130 IN FDC_BLK_IO_DEV
*FdcDev
134 UINT8 StatusRegister0
;
135 UINT8 PresentCylinderNumber
;
139 // Report reset progress code
141 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
143 EFI_PERIPHERAL_REMOVABLE_MEDIA
| EFI_P_PC_RESET
,
148 // Reset specified Floppy Logic Drive according to FdcDev -> Disk
149 // Set Digital Output Register(DOR) to do reset work
150 // bit0 & bit1 of DOR : Drive Select
152 // bit3 : DMA and Int bit
153 // Reset : a "0" written to bit2 resets the FDC, this reset will remain
155 // a "1" is written to this bit.
157 // use bit0 & bit1 to select the logic drive
161 Data
= (UINT8
) (Data
| (SELECT_DRV
& FdcDev
->Disk
));
162 FdcWritePort (FdcDev
, FDC_REGISTER_DOR
, Data
);
165 // wait some time,at least 120us
167 MicroSecondDelay (500);
172 // write "1" to bit3 : enable DMA
175 FdcWritePort (FdcDev
, FDC_REGISTER_DOR
, Data
);
180 MicroSecondDelay (2000);
183 // wait specified floppy logic drive is not busy
185 if (EFI_ERROR (FddWaitForBSYClear (FdcDev
, 1))) {
186 return EFI_DEVICE_ERROR
;
189 // Set the Transfer Data Rate
191 FdcWritePort (FdcDev
, FDC_REGISTER_CCR
, 0x0);
196 MicroSecondDelay (100);
199 // Issue Sense interrupt command for each drive (total 4 drives)
201 for (Index
= 0; Index
< 4; Index
++) {
202 if (EFI_ERROR (SenseIntStatus (FdcDev
, &StatusRegister0
, &PresentCylinderNumber
))) {
203 return EFI_DEVICE_ERROR
;
207 // issue Specify command
209 if (EFI_ERROR (Specify (FdcDev
))) {
210 return EFI_DEVICE_ERROR
;
217 Turn the floppy disk drive's motor on.
218 The drive's motor must be on before any command can be executed.
220 @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV
222 @retval EFI_SUCCESS The drive's motor was turned on successfully
223 @retval EFI_DEVICE_ERROR The drive is busy, so can not turn motor on
227 IN FDC_BLK_IO_DEV
*FdcDev
234 // Control of the floppy drive motors is a big pain. If motor is off, you have
235 // to turn it on first. But you can not leave the motor on all the time, since
236 // that would wear out the disk. On the other hand, if you turn the motor off
237 // after each operation, the system performance will be awful. The compromise
238 // used in this driver is to leave the motor on for 2 seconds after
239 // each operation. If a new operation is started in that interval(2s),
240 // the motor need not be turned on again. If no new operation is started,
241 // a timer goes off and the motor is turned off
246 Status
= gBS
->SetTimer (FdcDev
->Event
, TimerCancel
, 0);
247 ASSERT_EFI_ERROR (Status
);
250 // Get the motor status
252 DorData
= FdcReadPort (FdcDev
, FDC_REGISTER_DOR
);
254 if (((FdcDev
->Disk
== FdcDisk0
) && ((DorData
& 0x10) == 0x10)) ||
255 ((FdcDev
->Disk
== FdcDisk1
) && ((DorData
& 0x21) == 0x21))
260 // The drive's motor is off, so need turn it on
261 // first look at command and drive are busy or not
263 if (EFI_ERROR (FddWaitForBSYClear (FdcDev
, 1))) {
264 return EFI_DEVICE_ERROR
;
267 // for drive A: 1CH, drive B: 2DH
270 DorData
= (UINT8
) (DorData
| (SELECT_DRV
& FdcDev
->Disk
));
271 if (FdcDev
->Disk
== FdcDisk0
) {
275 DorData
|= DRVA_MOTOR_ON
;
280 DorData
|= DRVB_MOTOR_ON
;
283 FdcWritePort (FdcDev
, FDC_REGISTER_DOR
, DorData
);
288 MicroSecondDelay (4000);
294 Set a Timer and when Timer goes off, turn the motor off.
296 @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV
298 @retval EFI_SUCCESS Set the Timer successfully
299 @retval EFI_INVALID_PARAMETER Fail to Set the timer
303 IN FDC_BLK_IO_DEV
*FdcDev
307 // Set the timer : 2s
309 return gBS
->SetTimer (FdcDev
->Event
, TimerRelative
, 20000000);
313 Detect whether the disk in the drive is changed or not.
315 @param[in] FdcDev A pointer to FDC_BLK_IO_DEV
317 @retval EFI_SUCCESS No disk media change
318 @retval EFI_DEVICE_ERROR Fail to do the recalibrate or seek operation
319 @retval EFI_NO_MEDIA No disk in the drive
320 @retval EFI_MEDIA_CHANGED There is a new disk in the drive
324 IN FDC_BLK_IO_DEV
*FdcDev
333 Data
= FdcReadPort (FdcDev
, FDC_REGISTER_DIR
);
338 MicroSecondDelay (50);
340 if ((Data
& DIR_DCL
) == 0x80) {
342 // disk change line is active
344 if (FdcDev
->PresentCylinderNumber
!= 0) {
345 Status
= Recalibrate (FdcDev
);
347 Status
= Seek (FdcDev
, 0x30);
350 if (EFI_ERROR (Status
)) {
351 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
352 return EFI_DEVICE_ERROR
;
354 // Fail to do the seek or recalibrate operation
358 Data
= FdcReadPort (FdcDev
, FDC_REGISTER_DIR
);
363 MicroSecondDelay (50);
365 if ((Data
& DIR_DCL
) == 0x80) {
369 return EFI_MEDIA_CHANGED
;
376 Do the Specify command, this command sets DMA operation
377 and the initial values for each of the three internal
378 times: HUT, SRT and HLT.
380 @param[in] FdcDev Pointer to instance of FDC_BLK_IO_DEV
382 @retval EFI_SUCCESS Execute the Specify command successfully
383 @retval EFI_DEVICE_ERROR Fail to execute the command
387 IN FDC_BLK_IO_DEV
*FdcDev
390 FDD_SPECIFY_CMD Command
;
392 UINT8
*CommandPointer
;
394 ZeroMem (&Command
, sizeof (FDD_SPECIFY_CMD
));
395 Command
.CommandCode
= SPECIFY_CMD
;
399 Command
.SrtHut
= 0xdf;
405 Command
.HltNd
= 0x02;
407 CommandPointer
= (UINT8
*) (&Command
);
408 for (Index
= 0; Index
< sizeof (FDD_SPECIFY_CMD
); Index
++) {
409 if (EFI_ERROR (DataOutByte (FdcDev
, CommandPointer
++))) {
410 return EFI_DEVICE_ERROR
;
418 Set the head of floppy drive to track 0.
420 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
421 @retval EFI_SUCCESS: Execute the Recalibrate operation successfully
422 @retval EFI_DEVICE_ERROR: Fail to execute the Recalibrate operation
427 IN FDC_BLK_IO_DEV
*FdcDev
430 FDD_COMMAND_PACKET2 Command
;
432 UINT8 StatusRegister0
;
433 UINT8 PresentCylinderNumber
;
434 UINT8
*CommandPointer
;
440 ZeroMem (&Command
, sizeof (FDD_COMMAND_PACKET2
));
441 Command
.CommandCode
= RECALIBRATE_CMD
;
445 if (FdcDev
->Disk
== FdcDisk0
) {
446 Command
.DiskHeadSel
= 0;
451 Command
.DiskHeadSel
= 1;
457 CommandPointer
= (UINT8
*) (&Command
);
458 for (Index
= 0; Index
< sizeof (FDD_COMMAND_PACKET2
); Index
++) {
459 if (EFI_ERROR (DataOutByte (FdcDev
, CommandPointer
++))) {
460 return EFI_DEVICE_ERROR
;
466 MicroSecondDelay (250000);
468 // need modify according to 1.44M or 2.88M
470 if (EFI_ERROR (SenseIntStatus (FdcDev
, &StatusRegister0
, &PresentCylinderNumber
))) {
471 return EFI_DEVICE_ERROR
;
474 if ((StatusRegister0
& 0xf0) == 0x20 && PresentCylinderNumber
== 0) {
475 FdcDev
->PresentCylinderNumber
= 0;
476 FdcDev
->ControllerState
->NeedRecalibrate
= FALSE
;
481 return EFI_DEVICE_ERROR
;
492 Set the head of floppy drive to the new cylinder.
494 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
495 @param Lba EFI_LBA : The logic block address want to seek
497 @retval EFI_SUCCESS: Execute the Seek operation successfully
498 @retval EFI_DEVICE_ERROR: Fail to execute the Seek operation
503 IN FDC_BLK_IO_DEV
*FdcDev
,
507 FDD_SEEK_CMD Command
;
511 UINT8 StatusRegister0
;
512 UINT8
*CommandPointer
;
513 UINT8 PresentCylinderNumber
;
517 if (FdcDev
->ControllerState
->NeedRecalibrate
) {
518 if (EFI_ERROR (Recalibrate (FdcDev
))) {
519 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
520 return EFI_DEVICE_ERROR
;
524 EndOfTrack
= DISK_1440K_EOT
;
526 // Calculate cylinder based on Lba and EOT
528 Cylinder
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
/ 2);
531 // if the destination cylinder is the present cylinder, unnecessary to do the
534 if (FdcDev
->PresentCylinderNumber
== Cylinder
) {
538 // Calculate the head : 0 or 1
540 Head
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
% 2);
542 ZeroMem (&Command
, sizeof (FDD_SEEK_CMD
));
543 Command
.CommandCode
= SEEK_CMD
;
544 if (FdcDev
->Disk
== FdcDisk0
) {
545 Command
.DiskHeadSel
= 0;
550 Command
.DiskHeadSel
= 1;
556 Command
.DiskHeadSel
= (UINT8
) (Command
.DiskHeadSel
| (Head
<< 2));
557 Command
.NewCylinder
= Cylinder
;
559 CommandPointer
= (UINT8
*) (&Command
);
560 for (Index
= 0; Index
< sizeof (FDD_SEEK_CMD
); Index
++) {
561 if (EFI_ERROR (DataOutByte (FdcDev
, CommandPointer
++))) {
562 return EFI_DEVICE_ERROR
;
568 MicroSecondDelay (100);
571 // Calculate waiting time
573 if (FdcDev
->PresentCylinderNumber
> Cylinder
) {
574 DelayTime
= (UINT8
) (FdcDev
->PresentCylinderNumber
- Cylinder
);
576 DelayTime
= (UINT8
) (Cylinder
- FdcDev
->PresentCylinderNumber
);
579 MicroSecondDelay ((DelayTime
+ 1) * 4000);
581 if (EFI_ERROR (SenseIntStatus (FdcDev
, &StatusRegister0
, &PresentCylinderNumber
))) {
582 return EFI_DEVICE_ERROR
;
585 if ((StatusRegister0
& 0xf0) == 0x20) {
586 FdcDev
->PresentCylinderNumber
= Command
.NewCylinder
;
589 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
590 return EFI_DEVICE_ERROR
;
595 Do the Sense Interrupt Status command, this command
596 resets the interrupt signal.
598 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
599 @param StatusRegister0 UINT8 *: Be used to save Status Register 0 read from FDC
600 @param PresentCylinderNumber UINT8 *: Be used to save present cylinder number
603 @retval EFI_SUCCESS: Execute the Sense Interrupt Status command successfully
604 @retval EFI_DEVICE_ERROR: Fail to execute the command
609 IN FDC_BLK_IO_DEV
*FdcDev
,
610 IN OUT UINT8
*StatusRegister0
,
611 IN OUT UINT8
*PresentCylinderNumber
616 Command
= SENSE_INT_STATUS_CMD
;
617 if (EFI_ERROR (DataOutByte (FdcDev
, &Command
))) {
618 return EFI_DEVICE_ERROR
;
621 if (EFI_ERROR (DataInByte (FdcDev
, StatusRegister0
))) {
622 return EFI_DEVICE_ERROR
;
625 if (EFI_ERROR (DataInByte (FdcDev
, PresentCylinderNumber
))) {
626 return EFI_DEVICE_ERROR
;
633 Do the Sense Drive Status command.
635 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
636 @param Lba EFI_LBA : Logic block address
638 @retval EFI_SUCCESS: Execute the Sense Drive Status command successfully
639 @retval EFI_DEVICE_ERROR: Fail to execute the command
640 @retval EFI_WRITE_PROTECTED:The disk is write protected
645 IN FDC_BLK_IO_DEV
*FdcDev
,
649 FDD_COMMAND_PACKET2 Command
;
653 UINT8 StatusRegister3
;
654 UINT8
*CommandPointer
;
657 // Sense Drive Status command obtains drive status information,
658 // it has not execution phase and goes directly to the result phase from the
659 // command phase, Status Register 3 contains the drive status information
661 ZeroMem (&Command
, sizeof (FDD_COMMAND_PACKET2
));
662 Command
.CommandCode
= SENSE_DRV_STATUS_CMD
;
664 if (FdcDev
->Disk
== FdcDisk0
) {
665 Command
.DiskHeadSel
= 0;
667 Command
.DiskHeadSel
= 1;
670 EndOfTrack
= DISK_1440K_EOT
;
671 Head
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
% 2);
672 Command
.DiskHeadSel
= (UINT8
) (Command
.DiskHeadSel
| (Head
<< 2));
674 CommandPointer
= (UINT8
*) (&Command
);
675 for (Index
= 0; Index
< sizeof (FDD_COMMAND_PACKET2
); Index
++) {
676 if (EFI_ERROR (DataOutByte (FdcDev
, CommandPointer
++))) {
677 return EFI_DEVICE_ERROR
;
681 if (EFI_ERROR (DataInByte (FdcDev
, &StatusRegister3
))) {
682 return EFI_DEVICE_ERROR
;
687 MicroSecondDelay (50);
690 // Check Status Register 3 to get drive status information
692 return CheckStatus3 (StatusRegister3
);
696 Update the disk media properties and if necessary reinstall Block I/O interface.
698 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
700 @retval EFI_SUCCESS: Do the operation successfully
701 @retval EFI_DEVICE_ERROR: Fail to the operation
706 IN FDC_BLK_IO_DEV
*FdcDev
711 BOOLEAN ReadOnlyLastTime
;
712 BOOLEAN MediaPresentLastTime
;
715 ReadOnlyLastTime
= FdcDev
->BlkIo
.Media
->ReadOnly
;
716 MediaPresentLastTime
= FdcDev
->BlkIo
.Media
->MediaPresent
;
721 Status
= DisketChanged (FdcDev
);
723 if (Status
== EFI_MEDIA_CHANGED
) {
724 FdcDev
->BlkIo
.Media
->MediaId
++;
725 FdcDev
->BlkIo
.Media
->MediaPresent
= TRUE
;
727 } else if (Status
== EFI_NO_MEDIA
) {
728 FdcDev
->BlkIo
.Media
->MediaPresent
= FALSE
;
729 } else if (Status
!= EFI_SUCCESS
) {
737 if (FdcDev
->BlkIo
.Media
->MediaPresent
) {
739 // Check disk write protected
741 Status
= SenseDrvStatus (FdcDev
, 0);
742 if (Status
== EFI_WRITE_PROTECTED
) {
743 FdcDev
->BlkIo
.Media
->ReadOnly
= TRUE
;
745 FdcDev
->BlkIo
.Media
->ReadOnly
= FALSE
;
749 if (FdcDev
->BlkIo
.Media
->MediaPresent
&& (ReadOnlyLastTime
!= FdcDev
->BlkIo
.Media
->ReadOnly
)) {
753 if (MediaPresentLastTime
!= FdcDev
->BlkIo
.Media
->MediaPresent
) {
758 Status
= gBS
->ReinstallProtocolInterface (
760 &gEfiBlockIoProtocolGuid
,
765 if (EFI_ERROR (Status
)) {
774 Set the data rate and so on.
776 @param FdcDev A pointer to FDC_BLK_IO_DEV
778 @retval EFI_SUCCESS success to set the data rate
782 IN FDC_BLK_IO_DEV
*FdcDev
788 // Set data rate 500kbs
790 FdcWritePort (FdcDev
, FDC_REGISTER_CCR
, 0x0);
795 MicroSecondDelay (50);
797 Status
= Specify (FdcDev
);
799 if (EFI_ERROR (Status
)) {
800 return EFI_DEVICE_ERROR
;
807 Read or Write a number of blocks in the same cylinder.
809 @param FdcDev A pointer to FDC_BLK_IO_DEV
810 @param HostAddress device address
811 @param Lba The starting logic block address to read from on the device
812 @param NumberOfBlocks The number of block wanted to be read or write
813 @param Read Operation type: read or write
815 @retval EFI_SUCCESS Success operate
819 ReadWriteDataSector (
820 IN FDC_BLK_IO_DEV
*FdcDev
,
821 IN VOID
*HostAddress
,
823 IN UINTN NumberOfBlocks
,
828 FDD_COMMAND_PACKET1 Command
;
829 FDD_RESULT_PACKET Result
;
832 UINT8
*CommandPointer
;
834 EFI_PHYSICAL_ADDRESS DeviceAddress
;
835 EFI_ISA_IO_PROTOCOL
*IsaIo
;
838 EFI_ISA_IO_PROTOCOL_OPERATION Operation
;
841 EFI_ISA_ACPI_RESOURCE
*ResourceItem
;
844 Status
= Seek (FdcDev
, Lba
);
845 if (EFI_ERROR (Status
)) {
846 return EFI_DEVICE_ERROR
;
851 IsaIo
= FdcDev
->IsaIo
;
852 NumberofBytes
= NumberOfBlocks
* 512;
854 Operation
= EfiIsaIoOperationSlaveWrite
;
856 Operation
= EfiIsaIoOperationSlaveRead
;
859 ResourceItem
= IsaIo
->ResourceList
->ResourceItem
;
861 while (ResourceItem
[Index
].Type
!= EfiIsaAcpiResourceEndOfList
) {
862 if (ResourceItem
[Index
].Type
== EfiIsaAcpiResourceDma
) {
869 if (ResourceItem
[Index
].Type
== EfiIsaAcpiResourceEndOfList
) {
870 return EFI_DEVICE_ERROR
;
873 Channel
= (UINT8
) IsaIo
->ResourceList
->ResourceItem
[Index
].StartRange
;
874 Attribute
= IsaIo
->ResourceList
->ResourceItem
[Index
].Attribute
;
876 Status1
= IsaIo
->Map (
886 if (EFI_ERROR (Status1
)) {
891 // Allocate Read or Write command packet
893 ZeroMem (&Command
, sizeof (FDD_COMMAND_PACKET1
));
895 Command
.CommandCode
= READ_DATA_CMD
| CMD_MT
| CMD_MFM
| CMD_SK
;
897 Command
.CommandCode
= WRITE_DATA_CMD
| CMD_MT
| CMD_MFM
;
900 FillPara (FdcDev
, Lba
, &Command
);
903 // Write command bytes to FDC
905 CommandPointer
= (UINT8
*) (&Command
);
906 for (Index
= 0; Index
< sizeof (FDD_COMMAND_PACKET1
); Index
++) {
907 if (EFI_ERROR (DataOutByte (FdcDev
, CommandPointer
++))) {
908 return EFI_DEVICE_ERROR
;
912 // wait for some time
914 Times
= (STALL_1_SECOND
/ 50) + 1;
916 if ((FdcReadPort (FdcDev
, FDC_REGISTER_MSR
) & 0xc0) == 0xc0) {
920 MicroSecondDelay (50);
928 // Read result bytes from FDC
930 CommandPointer
= (UINT8
*) (&Result
);
931 for (Index
= 0; Index
< sizeof (FDD_RESULT_PACKET
); Index
++) {
932 if (EFI_ERROR (DataInByte (FdcDev
, CommandPointer
++))) {
933 return EFI_DEVICE_ERROR
;
937 // Flush before Unmap
940 Status1
= IsaIo
->Flush (IsaIo
);
941 if (EFI_ERROR (Status1
)) {
948 Status1
= IsaIo
->Unmap (IsaIo
, Mapping
);
949 if (EFI_ERROR (Status1
)) {
953 return CheckResult (&Result
, FdcDev
);
957 Fill in FDD command's parameter.
959 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
960 @param Lba The starting logic block address to read from on the device
961 @param Command FDD command
966 IN FDC_BLK_IO_DEV
*FdcDev
,
968 IN FDD_COMMAND_PACKET1
*Command
974 // Get EndOfTrack from the Para table
976 EndOfTrack
= DISK_1440K_EOT
;
979 // Fill the command parameter
981 if (FdcDev
->Disk
== FdcDisk0
) {
982 Command
->DiskHeadSel
= 0;
984 Command
->DiskHeadSel
= 1;
987 Command
->Cylinder
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
/ 2);
988 Command
->Head
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
% 2);
989 Command
->Sector
= (UINT8
) ((UINT8
) ((UINTN
) Lba
% EndOfTrack
) + 1);
990 Command
->DiskHeadSel
= (UINT8
) (Command
->DiskHeadSel
| (Command
->Head
<< 2));
991 Command
->Number
= DISK_1440K_NUMBER
;
992 Command
->EndOfTrack
= DISK_1440K_EOT
;
993 Command
->GapLength
= DISK_1440K_GPL
;
994 Command
->DataLength
= DISK_1440K_DTL
;
998 Read result byte from Data Register of FDC.
1000 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
1001 @param Pointer Buffer to store the byte read from FDC
1003 @retval EFI_SUCCESS Read result byte from FDC successfully
1004 @retval EFI_DEVICE_ERROR The FDC is not ready to be read
1009 IN FDC_BLK_IO_DEV
*FdcDev
,
1016 // wait for 1ms and detect the FDC is ready to be read
1018 if (EFI_ERROR (FddDRQReady (FdcDev
, DATA_IN
, 1))) {
1019 return EFI_DEVICE_ERROR
;
1025 Data
= FdcReadPort (FdcDev
, FDC_REGISTER_DTR
);
1030 MicroSecondDelay (50);
1037 Write command byte to Data Register of FDC.
1039 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
1040 @param Pointer Be used to save command byte written to FDC
1042 @retval EFI_SUCCESS: Write command byte to FDC successfully
1043 @retval EFI_DEVICE_ERROR: The FDC is not ready to be written
1048 IN FDC_BLK_IO_DEV
*FdcDev
,
1055 // wait for 1ms and detect the FDC is ready to be written
1057 if (EFI_ERROR (FddDRQReady (FdcDev
, DATA_OUT
, 1))) {
1061 return EFI_DEVICE_ERROR
;
1066 FdcWritePort (FdcDev
, FDC_REGISTER_DTR
, Data
);
1071 MicroSecondDelay (50);
1077 Detect the specified floppy logic drive is busy or not within a period of time.
1079 @param FdcDev Indicate it is drive A or drive B
1080 @param Timeout The time period for waiting
1082 @retval EFI_SUCCESS: The drive and command are not busy
1083 @retval EFI_TIMEOUT: The drive or command is still busy after a period time that
1088 FddWaitForBSYClear (
1089 IN FDC_BLK_IO_DEV
*FdcDev
,
1094 UINT8 StatusRegister
;
1098 // How to determine drive and command are busy or not: by the bits of
1099 // Main Status Register
1100 // bit0: Drive 0 busy (drive A)
1101 // bit1: Drive 1 busy (drive B)
1102 // bit4: Command busy
1105 // set mask: for drive A set bit0 & bit4; for drive B set bit1 & bit4
1107 Mask
= (UINT8
) ((FdcDev
->Disk
== FdcDisk0
? MSR_DAB
: MSR_DBB
) | MSR_CB
);
1109 Delay
= ((Timeout
* STALL_1_MSECOND
) / 50) + 1;
1111 StatusRegister
= FdcReadPort (FdcDev
, FDC_REGISTER_MSR
);
1112 if ((StatusRegister
& Mask
) == 0x00) {
1119 MicroSecondDelay (50);
1121 } while (Delay
> 0);
1131 Determine whether FDC is ready to write or read.
1133 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
1134 @param Dio BOOLEAN: Indicate the FDC is waiting to write or read
1135 @param Timeout The time period for waiting
1137 @retval EFI_SUCCESS: FDC is ready to write or read
1138 @retval EFI_NOT_READY: FDC is not ready within the specified time period
1143 IN FDC_BLK_IO_DEV
*FdcDev
,
1149 UINT8 StatusRegister
;
1153 // Before writing to FDC or reading from FDC, the Host must examine
1154 // the bit7(RQM) and bit6(DIO) of the Main Status Register.
1156 // command bytes can not be written to Data Register
1157 // unless RQM is 1 and DIO is 0
1158 // result bytes can not be read from Data Register
1159 // unless RQM is 1 and DIO is 1
1161 DataInOut
= (UINT8
) (Dio
<< 6);
1163 // in order to compare bit6
1165 Delay
= ((Timeout
* STALL_1_MSECOND
) / 50) + 1;
1167 StatusRegister
= FdcReadPort (FdcDev
, FDC_REGISTER_MSR
);
1168 if ((StatusRegister
& MSR_RQM
) == MSR_RQM
&& (StatusRegister
& MSR_DIO
) == DataInOut
) {
1175 MicroSecondDelay (50);
1180 } while (Delay
> 0);
1183 return EFI_NOT_READY
;
1185 // FDC is not ready within the specified time period
1193 Set FDC control structure's attribute according to result.
1195 @param Result Point to result structure
1196 @param FdcDev FDC control structure
1198 @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value
1199 @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value
1200 @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value
1201 @retval EFI_SUCCESS - GC_TODO: Add description for return value
1206 IN FDD_RESULT_PACKET
*Result
,
1207 IN OUT FDC_BLK_IO_DEV
*FdcDev
1211 // Check Status Register0
1213 if ((Result
->Status0
& STS0_IC
) != IC_NT
) {
1214 if ((Result
->Status0
& STS0_SE
) == 0x20) {
1218 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
1221 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
1222 return EFI_DEVICE_ERROR
;
1225 // Check Status Register1
1227 if ((Result
->Status1
& (STS1_EN
| STS1_DE
| STS1_OR
| STS1_ND
| STS1_NW
| STS1_MA
)) != 0) {
1228 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
1229 return EFI_DEVICE_ERROR
;
1232 // Check Status Register2
1234 if ((Result
->Status2
& (STS2_CM
| STS2_DD
| STS2_WC
| STS2_BC
| STS2_MD
)) != 0) {
1235 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
1236 return EFI_DEVICE_ERROR
;
1243 Check the drive status information.
1245 @param StatusRegister3 the value of Status Register 3
1247 @retval EFI_SUCCESS The disk is not write protected
1248 @retval EFI_WRITE_PROTECTED: The disk is write protected
1253 IN UINT8 StatusRegister3
1256 if ((StatusRegister3
& STS3_WP
) != 0) {
1257 return EFI_WRITE_PROTECTED
;
1264 Calculate the number of block in the same cylinder according to LBA.
1266 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
1267 @param LBA EFI_LBA: The starting logic block address
1268 @param NumberOfBlocks UINTN: The number of blocks
1270 @return The number of blocks in the same cylinder which the starting
1271 logic block address is LBA
1275 GetTransferBlockCount (
1276 IN FDC_BLK_IO_DEV
*FdcDev
,
1278 IN UINTN NumberOfBlocks
1283 UINT8 SectorsInTrack
;
1286 // Calculate the number of block in the same cylinder
1288 EndOfTrack
= DISK_1440K_EOT
;
1289 Head
= (UINT8
) ((UINTN
) LBA
/ EndOfTrack
% 2);
1291 SectorsInTrack
= (UINT8
) (EndOfTrack
* (2 - Head
) - (UINT8
) ((UINTN
) LBA
% EndOfTrack
));
1292 if (SectorsInTrack
< NumberOfBlocks
) {
1293 return SectorsInTrack
;
1295 return NumberOfBlocks
;
1300 When the Timer(2s) off, turn the drive's motor off.
1302 @param Event EFI_EVENT: Event(the timer) whose notification function is being
1304 @param Context VOID *: Pointer to the notification function's context
1314 FDC_BLK_IO_DEV
*FdcDev
;
1317 FdcDev
= (FDC_BLK_IO_DEV
*) Context
;
1320 // Get the motor status
1322 Data
= FdcReadPort (FdcDev
, FDC_REGISTER_DOR
);
1324 if (((FdcDev
->Disk
== FdcDisk0
) && ((Data
& 0x10) != 0x10)) ||
1325 ((FdcDev
->Disk
== FdcDisk1
) && ((Data
& 0x21) != 0x21))
1330 // the motor is on, so need motor off
1333 Data
= (UINT8
) (Data
| (SELECT_DRV
& FdcDev
->Disk
));
1334 FdcWritePort (FdcDev
, FDC_REGISTER_DOR
, Data
);
1335 MicroSecondDelay (500);
1339 Read an I/O port of FDC.
1341 @param[in] FdcDev A pointer to FDC_BLK_IO_DEV.
1342 @param[in] Offset The address offset of the I/O port.
1344 @retval 8-bit data read from the I/O port.
1348 IN FDC_BLK_IO_DEV
*FdcDev
,
1355 Status
= FdcDev
->IsaIo
->Io
.Read (
1358 FdcDev
->BaseAddress
+ Offset
,
1362 ASSERT_EFI_ERROR (Status
);
1368 Write an I/O port of FDC.
1370 @param[in] FdcDev A pointer to FDC_BLK_IO_DEV
1371 @param[in] Offset The address offset of the I/O port
1372 @param[in] Data 8-bit Value written to the I/O port
1376 IN FDC_BLK_IO_DEV
*FdcDev
,
1383 Status
= FdcDev
->IsaIo
->Io
.Write (
1386 FdcDev
->BaseAddress
+ Offset
,
1390 ASSERT_EFI_ERROR (Status
);