2
Internal floppy disk controller programming functions for the floppy driver.
4
Copyright (c) 2006 - 2009, Intel Corporation. All rights reserved.<BR>
5
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);
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);