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