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
10 The internal function for the floppy driver
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
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.
23 #include "IsaFloppy.h"
27 Detect the floppy drive is presented or not
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
36 IN FDC_BLK_IO_DEV
*FdcDev
41 FdcDev
->BlkIo
.Media
= &FdcDev
->BlkMedia
;
44 // Call FddIndentify subroutine
46 Status
= FddIdentify (FdcDev
);
47 if (EFI_ERROR (Status
)) {
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
;
63 Do recalibrate and see the drive is presented or not
64 Set the media parameters
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
72 IN FDC_BLK_IO_DEV
*FdcDev
78 // Set Floppy Disk Controller's motor on
80 Status
= MotorOn (FdcDev
);
81 if (EFI_ERROR (Status
)) {
82 return EFI_DEVICE_ERROR
;
85 Status
= Recalibrate (FdcDev
);
87 if (EFI_ERROR (Status
)) {
89 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
90 return EFI_DEVICE_ERROR
;
93 // Set Media Parameter
95 FdcDev
->BlkIo
.Media
->RemovableMedia
= TRUE
;
96 FdcDev
->BlkIo
.Media
->MediaPresent
= TRUE
;
100 FdcDev
->BlkIo
.Media
->MediaId
= 0;
105 Status
= DisketChanged (FdcDev
);
107 if (Status
== EFI_NO_MEDIA
) {
108 FdcDev
->BlkIo
.Media
->MediaPresent
= FALSE
;
109 } else if ((Status
!= EFI_MEDIA_CHANGED
) &&
110 (Status
!= EFI_SUCCESS
)) {
116 // Check Disk Write Protected
118 Status
= SenseDrvStatus (FdcDev
, 0);
120 if (Status
== EFI_WRITE_PROTECTED
) {
121 FdcDev
->BlkIo
.Media
->ReadOnly
= TRUE
;
122 } else if (Status
== EFI_SUCCESS
) {
123 FdcDev
->BlkIo
.Media
->ReadOnly
= FALSE
;
125 return EFI_DEVICE_ERROR
;
131 // Set Media Default Type
133 FdcDev
->BlkIo
.Media
->BlockSize
= DISK_1440K_BYTEPERSECTOR
;
134 FdcDev
->BlkIo
.Media
->LastBlock
= DISK_1440K_EOT
* 2 * (DISK_1440K_MAXTRACKNUM
+ 1) - 1;
141 Reset the Floppy Logic Drive
143 @param FdcDev FDC_BLK_IO_DEV * : A pointer to the Data Structure FDC_BLK_IO_DEV
145 @retval EFI_SUCCESS: The Floppy Logic Drive is reset
146 @retval EFI_DEVICE_ERROR: The Floppy Logic Drive is not functioning correctly and
152 IN FDC_BLK_IO_DEV
*FdcDev
156 UINT8 StatusRegister0
;
157 UINT8 PresentCylinderNumber
;
161 // Report reset progress code
163 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
165 EFI_PERIPHERAL_REMOVABLE_MEDIA
| EFI_P_PC_RESET
,
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
174 // bit3 : DMA and Int bit
175 // Reset : a "0" written to bit2 resets the FDC, this reset will remain
177 // a "1" is written to this bit.
179 // use bit0 & bit1 to select the logic drive
183 data
= (UINT8
) (data
| (SELECT_DRV
& FdcDev
->Disk
));
184 FdcWritePort (FdcDev
, FDC_REGISTER_DOR
, data
);
187 // wait some time,at least 120us
189 MicroSecondDelay (500);
194 // write "1" to bit3 : enable DMA
197 FdcWritePort (FdcDev
, FDC_REGISTER_DOR
, data
);
202 MicroSecondDelay (2000);
205 // wait specified floppy logic drive is not busy
207 if (EFI_ERROR (FddWaitForBSYClear (FdcDev
, 1))) {
208 return EFI_DEVICE_ERROR
;
211 // Set the Transfer Data Rate
213 FdcWritePort (FdcDev
, FDC_REGISTER_CCR
, 0x0);
218 MicroSecondDelay (100);
221 // Issue Sense interrupt command for each drive (total 4 drives)
223 for (Index
= 0; Index
< 4; Index
++) {
224 if (EFI_ERROR (SenseIntStatus (FdcDev
, &StatusRegister0
, &PresentCylinderNumber
))) {
225 return EFI_DEVICE_ERROR
;
229 // issue Specify command
231 if (EFI_ERROR (Specify (FdcDev
))) {
232 return EFI_DEVICE_ERROR
;
240 Turn the drive's motor on
241 The drive's motor must be on before any command can be executed
243 @param FdcDev FDC_BLK_IO_DEV * : A pointer to the Data Structure FDC_BLK_IO_DEV
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)
252 IN FDC_BLK_IO_DEV
*FdcDev
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
271 Status
= gBS
->SetTimer (FdcDev
->Event
, TimerCancel
, 0);
273 if (EFI_ERROR (Status
)) {
274 return EFI_INVALID_PARAMETER
;
277 // Get the motor status
279 data
= FdcReadPort (FdcDev
, FDC_REGISTER_DOR
);
281 if (((FdcDev
->Disk
== FDC_DISK0
) && ((data
& 0x10) == 0x10)) ||
282 ((FdcDev
->Disk
== FDC_DISK1
) && ((data
& 0x21) == 0x21))
287 // The drive's motor is off, so need turn it on
288 // first look at command and drive are busy or not
290 if (EFI_ERROR (FddWaitForBSYClear (FdcDev
, 1))) {
291 return EFI_DEVICE_ERROR
;
294 // for drive A: 1CH, drive B: 2DH
297 data
= (UINT8
) (data
| (SELECT_DRV
& FdcDev
->Disk
));
298 if (FdcDev
->Disk
== FDC_DISK0
) {
302 data
|= DRVA_MOTOR_ON
;
307 data
|= DRVB_MOTOR_ON
;
310 FdcWritePort (FdcDev
, FDC_REGISTER_DOR
, data
);
315 MicroSecondDelay (4000);
322 Set a Timer and when Timer goes off, turn the motor off
325 @param FdcDev FDC_BLK_IO_DEV * : A pointer to the Data Structure FDC_BLK_IO_DEV
327 @retval EFI_SUCCESS: Set the Timer successfully
328 @retval EFI_INVALID_PARAMETER: Fail to Set the timer
333 IN FDC_BLK_IO_DEV
*FdcDev
337 // Set the timer : 2s
339 return gBS
->SetTimer (FdcDev
->Event
, TimerRelative
, 20000000);
343 Detect the disk in the drive is changed or not
346 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
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
355 IN FDC_BLK_IO_DEV
*FdcDev
364 data
= FdcReadPort (FdcDev
, FDC_REGISTER_DIR
);
369 MicroSecondDelay (50);
371 if ((data
& DIR_DCL
) == 0x80) {
373 // disk change line is active
375 if (FdcDev
->PresentCylinderNumber
!= 0) {
376 Status
= Recalibrate (FdcDev
);
378 Status
= Seek (FdcDev
, 0x30);
381 if (EFI_ERROR (Status
)) {
382 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
383 return EFI_DEVICE_ERROR
;
385 // Fail to do the seek or recalibrate operation
389 data
= FdcReadPort (FdcDev
, FDC_REGISTER_DIR
);
394 MicroSecondDelay (50);
396 if ((data
& DIR_DCL
) == 0x80) {
400 return EFI_MEDIA_CHANGED
;
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
411 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
413 @retval EFI_SUCCESS: Execute the Specify command successfully
414 @retval EFI_DEVICE_ERROR: Fail to execute the command
419 IN FDC_BLK_IO_DEV
*FdcDev
422 FDD_SPECIFY_CMD Command
;
424 UINT8
*CommandPointer
;
426 ZeroMem (&Command
, sizeof (FDD_SPECIFY_CMD
));
427 Command
.CommandCode
= SPECIFY_CMD
;
431 Command
.SrtHut
= 0xdf;
437 Command
.HltNd
= 0x02;
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
;
450 Set the head of floppy drive to track 0
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
459 IN FDC_BLK_IO_DEV
*FdcDev
462 FDD_COMMAND_PACKET2 Command
;
464 UINT8 StatusRegister0
;
465 UINT8 PresentCylinderNumber
;
466 UINT8
*CommandPointer
;
472 ZeroMem (&Command
, sizeof (FDD_COMMAND_PACKET2
));
473 Command
.CommandCode
= RECALIBRATE_CMD
;
477 if (FdcDev
->Disk
== FDC_DISK0
) {
478 Command
.DiskHeadSel
= 0;
483 Command
.DiskHeadSel
= 1;
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
;
498 MicroSecondDelay (250000);
500 // need modify according to 1.44M or 2.88M
502 if (EFI_ERROR (SenseIntStatus (FdcDev
, &StatusRegister0
, &PresentCylinderNumber
))) {
503 return EFI_DEVICE_ERROR
;
506 if ((StatusRegister0
& 0xf0) == 0x20 && PresentCylinderNumber
== 0) {
507 FdcDev
->PresentCylinderNumber
= 0;
508 FdcDev
->ControllerState
->NeedRecalibrate
= FALSE
;
513 return EFI_DEVICE_ERROR
;
524 Set the head of floppy drive to the new cylinder
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
529 @retval EFI_SUCCESS: Execute the Seek operation successfully
530 @retval EFI_DEVICE_ERROR: Fail to execute the Seek operation
535 IN FDC_BLK_IO_DEV
*FdcDev
,
539 FDD_SEEK_CMD Command
;
543 UINT8 StatusRegister0
;
544 UINT8
*CommandPointer
;
545 UINT8 PresentCylinderNumber
;
549 if (FdcDev
->ControllerState
->NeedRecalibrate
) {
550 if (EFI_ERROR (Recalibrate (FdcDev
))) {
551 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
552 return EFI_DEVICE_ERROR
;
556 EndOfTrack
= DISK_1440K_EOT
;
558 // Calculate cylinder based on Lba and EOT
560 Cylinder
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
/ 2);
563 // if the destination cylinder is the present cylinder, unnecessary to do the
566 if (FdcDev
->PresentCylinderNumber
== Cylinder
) {
570 // Calculate the head : 0 or 1
572 Head
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
% 2);
574 ZeroMem (&Command
, sizeof (FDD_SEEK_CMD
));
575 Command
.CommandCode
= SEEK_CMD
;
576 if (FdcDev
->Disk
== FDC_DISK0
) {
577 Command
.DiskHeadSel
= 0;
582 Command
.DiskHeadSel
= 1;
588 Command
.DiskHeadSel
= (UINT8
) (Command
.DiskHeadSel
| (Head
<< 2));
589 Command
.NewCylinder
= Cylinder
;
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
;
600 MicroSecondDelay (100);
603 // Calculate waiting time
605 if (FdcDev
->PresentCylinderNumber
> Cylinder
) {
606 DelayTime
= (UINT8
) (FdcDev
->PresentCylinderNumber
- Cylinder
);
608 DelayTime
= (UINT8
) (Cylinder
- FdcDev
->PresentCylinderNumber
);
611 MicroSecondDelay ((DelayTime
+ 1) * 4000);
613 if (EFI_ERROR (SenseIntStatus (FdcDev
, &StatusRegister0
, &PresentCylinderNumber
))) {
614 return EFI_DEVICE_ERROR
;
617 if ((StatusRegister0
& 0xf0) == 0x20) {
618 FdcDev
->PresentCylinderNumber
= Command
.NewCylinder
;
621 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
622 return EFI_DEVICE_ERROR
;
627 Do the Sense Interrupt Status command, this command
628 resets the interrupt signal
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
635 @retval EFI_SUCCESS: Execute the Sense Interrupt Status command successfully
636 @retval EFI_DEVICE_ERROR: Fail to execute the command
641 IN FDC_BLK_IO_DEV
*FdcDev
,
642 IN OUT UINT8
*StatusRegister0
,
643 IN OUT UINT8
*PresentCylinderNumber
648 command
= SENSE_INT_STATUS_CMD
;
649 if (EFI_ERROR (DataOutByte (FdcDev
, &command
))) {
650 return EFI_DEVICE_ERROR
;
653 if (EFI_ERROR (DataInByte (FdcDev
, StatusRegister0
))) {
654 return EFI_DEVICE_ERROR
;
657 if (EFI_ERROR (DataInByte (FdcDev
, PresentCylinderNumber
))) {
658 return EFI_DEVICE_ERROR
;
665 Do the Sense Drive Status command
667 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
668 @param Lba EFI_LBA : Logic block address
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
677 IN FDC_BLK_IO_DEV
*FdcDev
,
681 FDD_COMMAND_PACKET2 Command
;
685 UINT8 StatusRegister3
;
686 UINT8
*CommandPointer
;
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
693 ZeroMem (&Command
, sizeof (FDD_COMMAND_PACKET2
));
694 Command
.CommandCode
= SENSE_DRV_STATUS_CMD
;
696 if (FdcDev
->Disk
== FDC_DISK0
) {
697 Command
.DiskHeadSel
= 0;
699 Command
.DiskHeadSel
= 1;
702 EndOfTrack
= DISK_1440K_EOT
;
703 Head
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
% 2);
704 Command
.DiskHeadSel
= (UINT8
) (Command
.DiskHeadSel
| (Head
<< 2));
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
;
713 if (EFI_ERROR (DataInByte (FdcDev
, &StatusRegister3
))) {
714 return EFI_DEVICE_ERROR
;
719 MicroSecondDelay (50);
722 // Check Status Register 3 to get drive status information
724 return CheckStatus3 (StatusRegister3
);
728 Update the disk media properties and if necessary
729 reinstall Block I/O interface
731 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
733 @retval EFI_SUCCESS: Do the operation successfully
734 @retval EFI_DEVICE_ERROR: Fail to the operation
739 IN FDC_BLK_IO_DEV
*FdcDev
744 BOOLEAN bReadOnlyLastTime
;
745 BOOLEAN bMediaPresentLastTime
;
748 bReadOnlyLastTime
= FdcDev
->BlkIo
.Media
->ReadOnly
;
749 bMediaPresentLastTime
= FdcDev
->BlkIo
.Media
->MediaPresent
;
754 Status
= DisketChanged (FdcDev
);
756 if (Status
== EFI_MEDIA_CHANGED
) {
757 FdcDev
->BlkIo
.Media
->MediaId
++;
758 FdcDev
->BlkIo
.Media
->MediaPresent
= TRUE
;
760 } else if (Status
== EFI_NO_MEDIA
) {
761 FdcDev
->BlkIo
.Media
->MediaPresent
= FALSE
;
762 } else if (Status
!= EFI_SUCCESS
) {
770 if (FdcDev
->BlkIo
.Media
->MediaPresent
) {
772 // Check disk write protected
774 Status
= SenseDrvStatus (FdcDev
, 0);
775 if (Status
== EFI_WRITE_PROTECTED
) {
776 FdcDev
->BlkIo
.Media
->ReadOnly
= TRUE
;
778 FdcDev
->BlkIo
.Media
->ReadOnly
= FALSE
;
782 if (FdcDev
->BlkIo
.Media
->MediaPresent
&& (bReadOnlyLastTime
!= FdcDev
->BlkIo
.Media
->ReadOnly
)) {
786 if (bMediaPresentLastTime
!= FdcDev
->BlkIo
.Media
->MediaPresent
) {
791 Status
= gBS
->ReinstallProtocolInterface (
793 &gEfiBlockIoProtocolGuid
,
798 if (EFI_ERROR (Status
)) {
807 Set the data rate and so on
809 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
811 @retval EFI_SUCCESS success to set the data rate
815 IN FDC_BLK_IO_DEV
*FdcDev
821 // Set data rate 500kbs
823 FdcWritePort (FdcDev
, FDC_REGISTER_CCR
, 0x0);
828 MicroSecondDelay (50);
830 Status
= Specify (FdcDev
);
832 if (EFI_ERROR (Status
)) {
833 return EFI_DEVICE_ERROR
;
840 Read or Write a number of blocks in the same cylinder
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
848 @retval EFI_SUCCESS Success operate
852 ReadWriteDataSector (
853 IN FDC_BLK_IO_DEV
*FdcDev
,
854 IN VOID
*HostAddress
,
856 IN UINTN NumberOfBlocks
,
861 FDD_COMMAND_PACKET1 Command
;
862 FDD_RESULT_PACKET Result
;
865 UINT8
*CommandPointer
;
867 EFI_PHYSICAL_ADDRESS DeviceAddress
;
868 EFI_ISA_IO_PROTOCOL
*IsaIo
;
871 EFI_ISA_IO_PROTOCOL_OPERATION Operation
;
874 EFI_ISA_ACPI_RESOURCE
*ResourceItem
;
877 Status
= Seek (FdcDev
, Lba
);
878 if (EFI_ERROR (Status
)) {
879 return EFI_DEVICE_ERROR
;
884 IsaIo
= FdcDev
->IsaIo
;
885 NumberofBytes
= NumberOfBlocks
* 512;
887 Operation
= EfiIsaIoOperationSlaveWrite
;
889 Operation
= EfiIsaIoOperationSlaveRead
;
892 ResourceItem
= IsaIo
->ResourceList
->ResourceItem
;
894 while (ResourceItem
[Index
].Type
!= EfiIsaAcpiResourceEndOfList
) {
895 if (ResourceItem
[Index
].Type
== EfiIsaAcpiResourceDma
) {
902 if (ResourceItem
[Index
].Type
== EfiIsaAcpiResourceEndOfList
) {
903 return EFI_DEVICE_ERROR
;
906 Channel
= (UINT8
) IsaIo
->ResourceList
->ResourceItem
[Index
].StartRange
;
907 Attribute
= IsaIo
->ResourceList
->ResourceItem
[Index
].Attribute
;
909 Status1
= IsaIo
->Map (
919 if (EFI_ERROR (Status1
)) {
924 // Allocate Read or Write command packet
926 ZeroMem (&Command
, sizeof (FDD_COMMAND_PACKET1
));
928 Command
.CommandCode
= READ_DATA_CMD
| CMD_MT
| CMD_MFM
| CMD_SK
;
930 Command
.CommandCode
= WRITE_DATA_CMD
| CMD_MT
| CMD_MFM
;
933 FillPara (FdcDev
, Lba
, &Command
);
936 // Write command bytes to FDC
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
;
945 // wait for some time
947 Times
= (STALL_1_SECOND
/ 50) + 1;
949 if ((FdcReadPort (FdcDev
, FDC_REGISTER_MSR
) & 0xc0) == 0xc0) {
953 MicroSecondDelay (50);
961 // Read result bytes from FDC
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
;
970 // Flush before Unmap
973 Status1
= IsaIo
->Flush (IsaIo
);
974 if (EFI_ERROR (Status1
)) {
981 Status1
= IsaIo
->Unmap (IsaIo
, Mapping
);
982 if (EFI_ERROR (Status1
)) {
986 return CheckResult (&Result
, FdcDev
);
990 Fill in FDD command's parameter
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
999 IN FDC_BLK_IO_DEV
*FdcDev
,
1001 IN FDD_COMMAND_PACKET1
*Command
1007 // Get EndOfTrack from the Para table
1009 EndOfTrack
= DISK_1440K_EOT
;
1012 // Fill the command parameter
1014 if (FdcDev
->Disk
== FDC_DISK0
) {
1015 Command
->DiskHeadSel
= 0;
1017 Command
->DiskHeadSel
= 1;
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
;
1031 Read result byte from Data Register of FDC
1033 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
1034 @param Pointer UINT8 *: Be used to save result byte read from FDC
1037 @retval EFI_SUCCESS: Read result byte from FDC successfully
1038 @retval EFI_DEVICE_ERROR: The FDC is not ready to be read
1043 IN FDC_BLK_IO_DEV
*FdcDev
,
1044 IN OUT UINT8
*Pointer
1050 // wait for 1ms and detect the FDC is ready to be read
1052 if (EFI_ERROR (FddDRQReady (FdcDev
, DATA_IN
, 1))) {
1053 return EFI_DEVICE_ERROR
;
1059 data
= FdcReadPort (FdcDev
, FDC_REGISTER_DTR
);
1064 MicroSecondDelay (50);
1071 Write command byte to Data Register of FDC
1073 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
1074 @param Pointer Be used to save command byte written to FDC
1076 @retval EFI_SUCCESS: Write command byte to FDC successfully
1077 @retval EFI_DEVICE_ERROR: The FDC is not ready to be written
1082 IN FDC_BLK_IO_DEV
*FdcDev
,
1089 // wait for 1ms and detect the FDC is ready to be written
1091 if (EFI_ERROR (FddDRQReady (FdcDev
, DATA_OUT
, 1))) {
1092 return EFI_DEVICE_ERROR
;
1100 FdcWritePort (FdcDev
, FDC_REGISTER_DTR
, data
);
1105 MicroSecondDelay (50);
1111 Detect the specified floppy logic drive is busy or
1112 not within a period of time
1114 @param FdcDev Indicate it is drive A or drive B
1115 @param TimeoutInSeconds the time period for waiting
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
1123 FddWaitForBSYClear (
1124 IN FDC_BLK_IO_DEV
*FdcDev
,
1125 IN UINTN TimeoutInSeconds
1129 UINT8 StatusRegister
;
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
1140 // set mask: for drive A set bit0 & bit4; for drive B set bit1 & bit4
1142 Mask
= (UINT8
) ((FdcDev
->Disk
== FDC_DISK0
? MSR_DAB
: MSR_DBB
) | MSR_CB
);
1144 Delay
= ((TimeoutInSeconds
* STALL_1_MSECOND
) / 50) + 1;
1146 StatusRegister
= FdcReadPort (FdcDev
, FDC_REGISTER_MSR
);
1147 if ((StatusRegister
& Mask
) == 0x00) {
1154 MicroSecondDelay (50);
1167 Routine Description: Determine whether FDC is ready to write or read
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
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
1179 IN FDC_BLK_IO_DEV
*FdcDev
,
1181 IN UINTN TimeoutInSeconds
1185 UINT8 StatusRegister
;
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.
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
1197 DataInOut
= (UINT8
) (Dio
<< 6);
1199 // in order to compare bit6
1201 Delay
= ((TimeoutInSeconds
* STALL_1_MSECOND
) / 50) + 1;
1203 StatusRegister
= FdcReadPort (FdcDev
, FDC_REGISTER_MSR
);
1204 if ((StatusRegister
& MSR_RQM
) == MSR_RQM
&& (StatusRegister
& MSR_DIO
) == DataInOut
) {
1211 MicroSecondDelay (50);
1219 return EFI_NOT_READY
;
1221 // FDC is not ready within the specified time period
1229 Set FDC control structure's attribute according to
1232 @param Result Point to result structure
1233 @param FdcDev FDC control structure
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
1243 IN FDD_RESULT_PACKET
*Result
,
1244 IN OUT FDC_BLK_IO_DEV
*FdcDev
1248 // Check Status Register0
1250 if ((Result
->Status0
& STS0_IC
) != IC_NT
) {
1251 if ((Result
->Status0
& STS0_SE
) == 0x20) {
1255 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
1258 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
1259 return EFI_DEVICE_ERROR
;
1262 // Check Status Register1
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
;
1269 // Check Status Register2
1271 if (Result
->Status2
& (STS2_CM
| STS2_DD
| STS2_WC
| STS2_BC
| STS2_MD
)) {
1272 FdcDev
->ControllerState
->NeedRecalibrate
= TRUE
;
1273 return EFI_DEVICE_ERROR
;
1280 Check the drive status information
1282 @param StatusRegister3 the value of Status Register 3
1284 @retval EFI_SUCCESS The disk is not write protected
1285 @retval EFI_WRITE_PROTECTED: The disk is write protected
1290 IN UINT8 StatusRegister3
1293 if (StatusRegister3
& STS3_WP
) {
1294 return EFI_WRITE_PROTECTED
;
1301 Calculate the number of block in the same cylinder
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
1308 @return The number of blocks in the same cylinder which the starting
1309 logic block address is LBA
1313 GetTransferBlockCount (
1314 IN FDC_BLK_IO_DEV
*FdcDev
,
1316 IN UINTN NumberOfBlocks
1321 UINT8 SectorsInTrack
;
1324 // Calculate the number of block in the same cylinder
1326 EndOfTrack
= DISK_1440K_EOT
;
1327 Head
= (UINT8
) ((UINTN
) LBA
/ EndOfTrack
% 2);
1329 SectorsInTrack
= (UINT8
) (EndOfTrack
* (2 - Head
) - (UINT8
) ((UINTN
) LBA
% EndOfTrack
));
1330 if (SectorsInTrack
< NumberOfBlocks
) {
1331 return SectorsInTrack
;
1333 return NumberOfBlocks
;
1338 When the Timer(2s) off, turn the drive's motor off
1340 @param Event EFI_EVENT: Event(the timer) whose notification function is being
1342 @param Context VOID *: Pointer to the notification function's context
1352 FDC_BLK_IO_DEV
*FdcDev
;
1355 FdcDev
= (FDC_BLK_IO_DEV
*) Context
;
1358 // Get the motor status
1360 data
= FdcReadPort (FdcDev
, FDC_REGISTER_DOR
);
1362 if (((FdcDev
->Disk
== FDC_DISK0
) && ((data
& 0x10) != 0x10)) ||
1363 ((FdcDev
->Disk
== FDC_DISK1
) && ((data
& 0x21) != 0x21))
1368 // the motor is on, so need motor off
1371 data
= (UINT8
) (data
| (SELECT_DRV
& FdcDev
->Disk
));
1372 FdcWritePort (FdcDev
, FDC_REGISTER_DOR
, data
);
1373 MicroSecondDelay (500);
1377 Read I/O port for FDC
1379 @param FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
1380 @param Offset The offset address of port
1385 IN FDC_BLK_IO_DEV
*FdcDev
,
1394 FdcDev
->IsaIo
->Io
.Read (
1397 FdcDev
->BaseAddress
+ Offset
,
1406 Write I/O port for FDC
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
1415 IN FDC_BLK_IO_DEV
*FdcDev
,
1424 FdcDev
->IsaIo
->Io
.Write (
1427 FdcDev
->BaseAddress
+ Offset
,