+++ /dev/null
-/** @file\r
- Internal floppy disk controller programming functions for the floppy driver.\r
-\r
-Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
-SPDX-License-Identifier: BSD-2-Clause-Patent\r
-\r
-**/\r
-\r
-#include "IsaFloppy.h"\r
-\r
-/**\r
- Detect whether a floppy drive is present or not.\r
-\r
- @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV\r
-\r
- @retval EFI_SUCCESS The floppy disk drive is present\r
- @retval EFI_NOT_FOUND The floppy disk drive is not present\r
-**/\r
-EFI_STATUS\r
-DiscoverFddDevice (\r
- IN FDC_BLK_IO_DEV *FdcDev\r
- )\r
-{\r
- EFI_STATUS Status;\r
-\r
- FdcDev->BlkIo.Media = &FdcDev->BlkMedia;\r
-\r
- Status = FddIdentify (FdcDev);\r
- if (EFI_ERROR (Status)) {\r
- return EFI_NOT_FOUND;\r
- }\r
-\r
- FdcDev->BlkIo.Reset = FdcReset;\r
- FdcDev->BlkIo.FlushBlocks = FddFlushBlocks;\r
- FdcDev->BlkIo.ReadBlocks = FddReadBlocks;\r
- FdcDev->BlkIo.WriteBlocks = FddWriteBlocks;\r
- FdcDev->BlkMedia.LogicalPartition = FALSE;\r
- FdcDev->BlkMedia.WriteCaching = FALSE;\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Do recalibrate and check if the drive is present or not\r
- and set the media parameters if the driver is present.\r
-\r
- @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV\r
-\r
- @retval EFI_SUCCESS The floppy disk drive is present\r
- @retval EFI_DEVICE_ERROR The floppy disk drive is not present\r
-**/\r
-EFI_STATUS\r
-FddIdentify (\r
- IN FDC_BLK_IO_DEV *FdcDev\r
- )\r
-{\r
- EFI_STATUS Status;\r
-\r
- //\r
- // Set Floppy Disk Controller's motor on\r
- //\r
- Status = MotorOn (FdcDev);\r
- if (EFI_ERROR (Status)) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
-\r
- Status = Recalibrate (FdcDev);\r
-\r
- if (EFI_ERROR (Status)) {\r
- MotorOff (FdcDev);\r
- FdcDev->ControllerState->NeedRecalibrate = TRUE;\r
- return EFI_DEVICE_ERROR;\r
- }\r
- //\r
- // Set Media Parameter\r
- //\r
- FdcDev->BlkIo.Media->RemovableMedia = TRUE;\r
- FdcDev->BlkIo.Media->MediaPresent = TRUE;\r
- FdcDev->BlkIo.Media->MediaId = 0;\r
-\r
- //\r
- // Check Media\r
- //\r
- Status = DisketChanged (FdcDev);\r
-\r
- if (Status == EFI_NO_MEDIA) {\r
- FdcDev->BlkIo.Media->MediaPresent = FALSE;\r
- } else if ((Status != EFI_MEDIA_CHANGED) &&\r
- (Status != EFI_SUCCESS)) {\r
- MotorOff (FdcDev);\r
- return Status;\r
- }\r
-\r
- //\r
- // Check Disk Write Protected\r
- //\r
- Status = SenseDrvStatus (FdcDev, 0);\r
-\r
- if (Status == EFI_WRITE_PROTECTED) {\r
- FdcDev->BlkIo.Media->ReadOnly = TRUE;\r
- } else if (Status == EFI_SUCCESS) {\r
- FdcDev->BlkIo.Media->ReadOnly = FALSE;\r
- } else {\r
- return EFI_DEVICE_ERROR;\r
- }\r
-\r
- MotorOff (FdcDev);\r
-\r
- //\r
- // Set Media Default Type\r
- //\r
- FdcDev->BlkIo.Media->BlockSize = DISK_1440K_BYTEPERSECTOR;\r
- FdcDev->BlkIo.Media->LastBlock = DISK_1440K_EOT * 2 * (DISK_1440K_MAXTRACKNUM + 1) - 1;\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Reset the Floppy Logic Drive.\r
-\r
- @param FdcDev FDC_BLK_IO_DEV * : A pointer to the FDC_BLK_IO_DEV\r
-\r
- @retval EFI_SUCCESS: The Floppy Logic Drive is reset\r
- @retval EFI_DEVICE_ERROR: The Floppy Logic Drive is not functioning correctly and\r
- can not be reset\r
-\r
-**/\r
-EFI_STATUS\r
-FddReset (\r
- IN FDC_BLK_IO_DEV *FdcDev\r
- )\r
-{\r
- UINT8 Data;\r
- UINT8 StatusRegister0;\r
- UINT8 PresentCylinderNumber;\r
- UINTN Index;\r
-\r
- //\r
- // Report reset progress code\r
- //\r
- REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
- EFI_PROGRESS_CODE,\r
- EFI_PERIPHERAL_REMOVABLE_MEDIA | EFI_P_PC_RESET,\r
- FdcDev->DevicePath\r
- );\r
-\r
- //\r
- // Reset specified Floppy Logic Drive according to FdcDev -> Disk\r
- // Set Digital Output Register(DOR) to do reset work\r
- // bit0 & bit1 of DOR : Drive Select\r
- // bit2 : Reset bit\r
- // bit3 : DMA and Int bit\r
- // Reset : a "0" written to bit2 resets the FDC, this reset will remain\r
- // active until\r
- // a "1" is written to this bit.\r
- // Reset step 1:\r
- // use bit0 & bit1 to select the logic drive\r
- // write "0" to bit2\r
- //\r
- Data = 0x0;\r
- Data = (UINT8) (Data | (SELECT_DRV & FdcDev->Disk));\r
- FdcWritePort (FdcDev, FDC_REGISTER_DOR, Data);\r
-\r
- //\r
- // wait some time,at least 120us\r
- //\r
- MicroSecondDelay (500);\r
-\r
- //\r
- // Reset step 2:\r
- // write "1" to bit2\r
- // write "1" to bit3 : enable DMA\r
- //\r
- Data |= 0x0C;\r
- FdcWritePort (FdcDev, FDC_REGISTER_DOR, Data);\r
-\r
- //\r
- // Experience value\r
- //\r
- MicroSecondDelay (2000);\r
-\r
- //\r
- // wait specified floppy logic drive is not busy\r
- //\r
- if (EFI_ERROR (FddWaitForBSYClear (FdcDev, 1))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- //\r
- // Set the Transfer Data Rate\r
- //\r
- FdcWritePort (FdcDev, FDC_REGISTER_CCR, 0x0);\r
-\r
- //\r
- // Experience value\r
- //\r
- MicroSecondDelay (100);\r
-\r
- //\r
- // Issue Sense interrupt command for each drive (total 4 drives)\r
- //\r
- for (Index = 0; Index < 4; Index++) {\r
- if (EFI_ERROR (SenseIntStatus (FdcDev, &StatusRegister0, &PresentCylinderNumber))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- }\r
- //\r
- // issue Specify command\r
- //\r
- if (EFI_ERROR (Specify (FdcDev))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Turn the floppy disk drive's motor on.\r
- The drive's motor must be on before any command can be executed.\r
-\r
- @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV\r
-\r
- @retval EFI_SUCCESS The drive's motor was turned on successfully\r
- @retval EFI_DEVICE_ERROR The drive is busy, so can not turn motor on\r
-**/\r
-EFI_STATUS\r
-MotorOn (\r
- IN FDC_BLK_IO_DEV *FdcDev\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINT8 DorData;\r
-\r
- //\r
- // Control of the floppy drive motors is a big pain. If motor is off, you have\r
- // to turn it on first. But you can not leave the motor on all the time, since\r
- // that would wear out the disk. On the other hand, if you turn the motor off\r
- // after each operation, the system performance will be awful. The compromise\r
- // used in this driver is to leave the motor on for 2 seconds after\r
- // each operation. If a new operation is started in that interval(2s),\r
- // the motor need not be turned on again. If no new operation is started,\r
- // a timer goes off and the motor is turned off\r
- //\r
- //\r
- // Cancel the timer\r
- //\r
- Status = gBS->SetTimer (FdcDev->Event, TimerCancel, 0);\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- //\r
- // Get the motor status\r
- //\r
- DorData = FdcReadPort (FdcDev, FDC_REGISTER_DOR);\r
-\r
- if (((FdcDev->Disk == FdcDisk0) && ((DorData & 0x10) == 0x10)) ||\r
- ((FdcDev->Disk == FdcDisk1) && ((DorData & 0x21) == 0x21))\r
- ) {\r
- return EFI_SUCCESS;\r
- }\r
- //\r
- // The drive's motor is off, so need turn it on\r
- // first look at command and drive are busy or not\r
- //\r
- if (EFI_ERROR (FddWaitForBSYClear (FdcDev, 1))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- //\r
- // for drive A: 1CH, drive B: 2DH\r
- //\r
- DorData = 0x0C;\r
- DorData = (UINT8) (DorData | (SELECT_DRV & FdcDev->Disk));\r
- if (FdcDev->Disk == FdcDisk0) {\r
- //\r
- // drive A\r
- //\r
- DorData |= DRVA_MOTOR_ON;\r
- } else {\r
- //\r
- // drive B\r
- //\r
- DorData |= DRVB_MOTOR_ON;\r
- }\r
-\r
- FdcWritePort (FdcDev, FDC_REGISTER_DOR, DorData);\r
-\r
- //\r
- // Experience value\r
- //\r
- MicroSecondDelay (4000);\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Set a Timer and when Timer goes off, turn the motor off.\r
-\r
- @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV\r
-\r
- @retval EFI_SUCCESS Set the Timer successfully\r
- @retval EFI_INVALID_PARAMETER Fail to Set the timer\r
-**/\r
-EFI_STATUS\r
-MotorOff (\r
- IN FDC_BLK_IO_DEV *FdcDev\r
- )\r
-{\r
- //\r
- // Set the timer : 2s\r
- //\r
- return gBS->SetTimer (FdcDev->Event, TimerRelative, 20000000);\r
-}\r
-\r
-/**\r
- Detect whether the disk in the drive is changed or not.\r
-\r
- @param[in] FdcDev A pointer to FDC_BLK_IO_DEV\r
-\r
- @retval EFI_SUCCESS No disk media change\r
- @retval EFI_DEVICE_ERROR Fail to do the recalibrate or seek operation\r
- @retval EFI_NO_MEDIA No disk in the drive\r
- @retval EFI_MEDIA_CHANGED There is a new disk in the drive\r
-**/\r
-EFI_STATUS\r
-DisketChanged (\r
- IN FDC_BLK_IO_DEV *FdcDev\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINT8 Data;\r
-\r
- //\r
- // Check change line\r
- //\r
- Data = FdcReadPort (FdcDev, FDC_REGISTER_DIR);\r
-\r
- //\r
- // Io delay\r
- //\r
- MicroSecondDelay (50);\r
-\r
- if ((Data & DIR_DCL) == 0x80) {\r
- //\r
- // disk change line is active\r
- //\r
- if (FdcDev->PresentCylinderNumber != 0) {\r
- Status = Recalibrate (FdcDev);\r
- } else {\r
- Status = Seek (FdcDev, 0x30);\r
- }\r
-\r
- if (EFI_ERROR (Status)) {\r
- FdcDev->ControllerState->NeedRecalibrate = TRUE;\r
- return EFI_DEVICE_ERROR;\r
- //\r
- // Fail to do the seek or recalibrate operation\r
- //\r
- }\r
-\r
- Data = FdcReadPort (FdcDev, FDC_REGISTER_DIR);\r
-\r
- //\r
- // Io delay\r
- //\r
- MicroSecondDelay (50);\r
-\r
- if ((Data & DIR_DCL) == 0x80) {\r
- return EFI_NO_MEDIA;\r
- }\r
-\r
- return EFI_MEDIA_CHANGED;\r
- }\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Do the Specify command, this command sets DMA operation\r
- and the initial values for each of the three internal\r
- times: HUT, SRT and HLT.\r
-\r
- @param[in] FdcDev Pointer to instance of FDC_BLK_IO_DEV\r
-\r
- @retval EFI_SUCCESS Execute the Specify command successfully\r
- @retval EFI_DEVICE_ERROR Fail to execute the command\r
-**/\r
-EFI_STATUS\r
-Specify (\r
- IN FDC_BLK_IO_DEV *FdcDev\r
- )\r
-{\r
- FDD_SPECIFY_CMD Command;\r
- UINTN Index;\r
- UINT8 *CommandPointer;\r
-\r
- ZeroMem (&Command, sizeof (FDD_SPECIFY_CMD));\r
- Command.CommandCode = SPECIFY_CMD;\r
- //\r
- // set SRT, HUT\r
- //\r
- Command.SrtHut = 0xdf;\r
- //\r
- // 0xdf;\r
- //\r
- // set HLT and DMA\r
- //\r
- Command.HltNd = 0x02;\r
-\r
- CommandPointer = (UINT8 *) (&Command);\r
- for (Index = 0; Index < sizeof (FDD_SPECIFY_CMD); Index++) {\r
- if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- }\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Set the head of floppy drive to track 0.\r
-\r
- @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV\r
- @retval EFI_SUCCESS: Execute the Recalibrate operation successfully\r
- @retval EFI_DEVICE_ERROR: Fail to execute the Recalibrate operation\r
-\r
-**/\r
-EFI_STATUS\r
-Recalibrate (\r
- IN FDC_BLK_IO_DEV *FdcDev\r
- )\r
-{\r
- FDD_COMMAND_PACKET2 Command;\r
- UINTN Index;\r
- UINT8 StatusRegister0;\r
- UINT8 PresentCylinderNumber;\r
- UINT8 *CommandPointer;\r
- UINT8 Count;\r
-\r
- Count = 2;\r
-\r
- while (Count > 0) {\r
- ZeroMem (&Command, sizeof (FDD_COMMAND_PACKET2));\r
- Command.CommandCode = RECALIBRATE_CMD;\r
- //\r
- // drive select\r
- //\r
- if (FdcDev->Disk == FdcDisk0) {\r
- Command.DiskHeadSel = 0;\r
- //\r
- // 0\r
- //\r
- } else {\r
- Command.DiskHeadSel = 1;\r
- //\r
- // 1\r
- //\r
- }\r
-\r
- CommandPointer = (UINT8 *) (&Command);\r
- for (Index = 0; Index < sizeof (FDD_COMMAND_PACKET2); Index++) {\r
- if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- }\r
- //\r
- // Experience value\r
- //\r
- MicroSecondDelay (250000);\r
- //\r
- // need modify according to 1.44M or 2.88M\r
- //\r
- if (EFI_ERROR (SenseIntStatus (FdcDev, &StatusRegister0, &PresentCylinderNumber))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
-\r
- if ((StatusRegister0 & 0xf0) == 0x20 && PresentCylinderNumber == 0) {\r
- FdcDev->PresentCylinderNumber = 0;\r
- FdcDev->ControllerState->NeedRecalibrate = FALSE;\r
- return EFI_SUCCESS;\r
- } else {\r
- Count--;\r
- if (Count == 0) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- }\r
- }\r
- //\r
- // end while\r
- //\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Set the head of floppy drive to the new cylinder.\r
-\r
- @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV\r
- @param Lba EFI_LBA : The logic block address want to seek\r
-\r
- @retval EFI_SUCCESS: Execute the Seek operation successfully\r
- @retval EFI_DEVICE_ERROR: Fail to execute the Seek operation\r
-\r
-**/\r
-EFI_STATUS\r
-Seek (\r
- IN FDC_BLK_IO_DEV *FdcDev,\r
- IN EFI_LBA Lba\r
- )\r
-{\r
- FDD_SEEK_CMD Command;\r
- UINT8 EndOfTrack;\r
- UINT8 Head;\r
- UINT8 Cylinder;\r
- UINT8 StatusRegister0;\r
- UINT8 *CommandPointer;\r
- UINT8 PresentCylinderNumber;\r
- UINTN Index;\r
- UINT8 DelayTime;\r
-\r
- if (FdcDev->ControllerState->NeedRecalibrate) {\r
- if (EFI_ERROR (Recalibrate (FdcDev))) {\r
- FdcDev->ControllerState->NeedRecalibrate = TRUE;\r
- return EFI_DEVICE_ERROR;\r
- }\r
- }\r
-\r
- EndOfTrack = DISK_1440K_EOT;\r
- //\r
- // Calculate cylinder based on Lba and EOT\r
- //\r
- Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2);\r
-\r
- //\r
- // if the destination cylinder is the present cylinder, unnecessary to do the\r
- // seek operation\r
- //\r
- if (FdcDev->PresentCylinderNumber == Cylinder) {\r
- return EFI_SUCCESS;\r
- }\r
- //\r
- // Calculate the head : 0 or 1\r
- //\r
- Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);\r
-\r
- ZeroMem (&Command, sizeof (FDD_SEEK_CMD));\r
- Command.CommandCode = SEEK_CMD;\r
- if (FdcDev->Disk == FdcDisk0) {\r
- Command.DiskHeadSel = 0;\r
- //\r
- // 0\r
- //\r
- } else {\r
- Command.DiskHeadSel = 1;\r
- //\r
- // 1\r
- //\r
- }\r
-\r
- Command.DiskHeadSel = (UINT8) (Command.DiskHeadSel | (Head << 2));\r
- Command.NewCylinder = Cylinder;\r
-\r
- CommandPointer = (UINT8 *) (&Command);\r
- for (Index = 0; Index < sizeof (FDD_SEEK_CMD); Index++) {\r
- if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- }\r
- //\r
- // Io delay\r
- //\r
- MicroSecondDelay (100);\r
-\r
- //\r
- // Calculate waiting time\r
- //\r
- if (FdcDev->PresentCylinderNumber > Cylinder) {\r
- DelayTime = (UINT8) (FdcDev->PresentCylinderNumber - Cylinder);\r
- } else {\r
- DelayTime = (UINT8) (Cylinder - FdcDev->PresentCylinderNumber);\r
- }\r
-\r
- MicroSecondDelay ((DelayTime + 1) * 4000);\r
-\r
- if (EFI_ERROR (SenseIntStatus (FdcDev, &StatusRegister0, &PresentCylinderNumber))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
-\r
- if ((StatusRegister0 & 0xf0) == 0x20) {\r
- FdcDev->PresentCylinderNumber = Command.NewCylinder;\r
- return EFI_SUCCESS;\r
- } else {\r
- FdcDev->ControllerState->NeedRecalibrate = TRUE;\r
- return EFI_DEVICE_ERROR;\r
- }\r
-}\r
-\r
-/**\r
- Do the Sense Interrupt Status command, this command\r
- resets the interrupt signal.\r
-\r
- @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV\r
- @param StatusRegister0 UINT8 *: Be used to save Status Register 0 read from FDC\r
- @param PresentCylinderNumber UINT8 *: Be used to save present cylinder number\r
- read from FDC\r
-\r
- @retval EFI_SUCCESS: Execute the Sense Interrupt Status command successfully\r
- @retval EFI_DEVICE_ERROR: Fail to execute the command\r
-\r
-**/\r
-EFI_STATUS\r
-SenseIntStatus (\r
- IN FDC_BLK_IO_DEV *FdcDev,\r
- IN OUT UINT8 *StatusRegister0,\r
- IN OUT UINT8 *PresentCylinderNumber\r
- )\r
-{\r
- UINT8 Command;\r
-\r
- Command = SENSE_INT_STATUS_CMD;\r
- if (EFI_ERROR (DataOutByte (FdcDev, &Command))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
-\r
- if (EFI_ERROR (DataInByte (FdcDev, StatusRegister0))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
-\r
- if (EFI_ERROR (DataInByte (FdcDev, PresentCylinderNumber))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Do the Sense Drive Status command.\r
-\r
- @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV\r
- @param Lba EFI_LBA : Logic block address\r
-\r
- @retval EFI_SUCCESS: Execute the Sense Drive Status command successfully\r
- @retval EFI_DEVICE_ERROR: Fail to execute the command\r
- @retval EFI_WRITE_PROTECTED:The disk is write protected\r
-\r
-**/\r
-EFI_STATUS\r
-SenseDrvStatus (\r
- IN FDC_BLK_IO_DEV *FdcDev,\r
- IN EFI_LBA Lba\r
- )\r
-{\r
- FDD_COMMAND_PACKET2 Command;\r
- UINT8 Head;\r
- UINT8 EndOfTrack;\r
- UINTN Index;\r
- UINT8 StatusRegister3;\r
- UINT8 *CommandPointer;\r
-\r
- //\r
- // Sense Drive Status command obtains drive status information,\r
- // it has not execution phase and goes directly to the result phase from the\r
- // command phase, Status Register 3 contains the drive status information\r
- //\r
- ZeroMem (&Command, sizeof (FDD_COMMAND_PACKET2));\r
- Command.CommandCode = SENSE_DRV_STATUS_CMD;\r
-\r
- if (FdcDev->Disk == FdcDisk0) {\r
- Command.DiskHeadSel = 0;\r
- } else {\r
- Command.DiskHeadSel = 1;\r
- }\r
-\r
- EndOfTrack = DISK_1440K_EOT;\r
- Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);\r
- Command.DiskHeadSel = (UINT8) (Command.DiskHeadSel | (Head << 2));\r
-\r
- CommandPointer = (UINT8 *) (&Command);\r
- for (Index = 0; Index < sizeof (FDD_COMMAND_PACKET2); Index++) {\r
- if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- }\r
-\r
- if (EFI_ERROR (DataInByte (FdcDev, &StatusRegister3))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- //\r
- // Io delay\r
- //\r
- MicroSecondDelay (50);\r
-\r
- //\r
- // Check Status Register 3 to get drive status information\r
- //\r
- return CheckStatus3 (StatusRegister3);\r
-}\r
-\r
-/**\r
- Update the disk media properties and if necessary reinstall Block I/O interface.\r
-\r
- @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV\r
-\r
- @retval EFI_SUCCESS: Do the operation successfully\r
- @retval EFI_DEVICE_ERROR: Fail to the operation\r
-\r
-**/\r
-EFI_STATUS\r
-DetectMedia (\r
- IN FDC_BLK_IO_DEV *FdcDev\r
- )\r
-{\r
- EFI_STATUS Status;\r
- BOOLEAN Reset;\r
- BOOLEAN ReadOnlyLastTime;\r
- BOOLEAN MediaPresentLastTime;\r
-\r
- Reset = FALSE;\r
- ReadOnlyLastTime = FdcDev->BlkIo.Media->ReadOnly;\r
- MediaPresentLastTime = FdcDev->BlkIo.Media->MediaPresent;\r
-\r
- //\r
- // Check disk change\r
- //\r
- Status = DisketChanged (FdcDev);\r
-\r
- if (Status == EFI_MEDIA_CHANGED) {\r
- FdcDev->BlkIo.Media->MediaId++;\r
- FdcDev->BlkIo.Media->MediaPresent = TRUE;\r
- Reset = TRUE;\r
- } else if (Status == EFI_NO_MEDIA) {\r
- FdcDev->BlkIo.Media->MediaPresent = FALSE;\r
- } else if (Status != EFI_SUCCESS) {\r
- MotorOff (FdcDev);\r
- return Status;\r
- //\r
- // EFI_DEVICE_ERROR\r
- //\r
- }\r
-\r
- if (FdcDev->BlkIo.Media->MediaPresent) {\r
- //\r
- // Check disk write protected\r
- //\r
- Status = SenseDrvStatus (FdcDev, 0);\r
- if (Status == EFI_WRITE_PROTECTED) {\r
- FdcDev->BlkIo.Media->ReadOnly = TRUE;\r
- } else {\r
- FdcDev->BlkIo.Media->ReadOnly = FALSE;\r
- }\r
- }\r
-\r
- if (FdcDev->BlkIo.Media->MediaPresent && (ReadOnlyLastTime != FdcDev->BlkIo.Media->ReadOnly)) {\r
- Reset = TRUE;\r
- }\r
-\r
- if (MediaPresentLastTime != FdcDev->BlkIo.Media->MediaPresent) {\r
- Reset = TRUE;\r
- }\r
-\r
- if (Reset) {\r
- Status = gBS->ReinstallProtocolInterface (\r
- FdcDev->Handle,\r
- &gEfiBlockIoProtocolGuid,\r
- &FdcDev->BlkIo,\r
- &FdcDev->BlkIo\r
- );\r
-\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
- }\r
- }\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Set the data rate and so on.\r
-\r
- @param FdcDev A pointer to FDC_BLK_IO_DEV\r
-\r
- @retval EFI_SUCCESS success to set the data rate\r
-**/\r
-EFI_STATUS\r
-Setup (\r
- IN FDC_BLK_IO_DEV *FdcDev\r
- )\r
-{\r
- EFI_STATUS Status;\r
-\r
- //\r
- // Set data rate 500kbs\r
- //\r
- FdcWritePort (FdcDev, FDC_REGISTER_CCR, 0x0);\r
-\r
- //\r
- // Io delay\r
- //\r
- MicroSecondDelay (50);\r
-\r
- Status = Specify (FdcDev);\r
-\r
- if (EFI_ERROR (Status)) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Read or Write a number of blocks in the same cylinder.\r
-\r
- @param FdcDev A pointer to FDC_BLK_IO_DEV\r
- @param HostAddress device address\r
- @param Lba The starting logic block address to read from on the device\r
- @param NumberOfBlocks The number of block wanted to be read or write\r
- @param Read Operation type: read or write\r
-\r
- @retval EFI_SUCCESS Success operate\r
-\r
-**/\r
-EFI_STATUS\r
-ReadWriteDataSector (\r
- IN FDC_BLK_IO_DEV *FdcDev,\r
- IN VOID *HostAddress,\r
- IN EFI_LBA Lba,\r
- IN UINTN NumberOfBlocks,\r
- IN BOOLEAN Read\r
- )\r
-{\r
- EFI_STATUS Status;\r
- FDD_COMMAND_PACKET1 Command;\r
- FDD_RESULT_PACKET Result;\r
- UINTN Index;\r
- UINTN Times;\r
- UINT8 *CommandPointer;\r
-\r
- EFI_PHYSICAL_ADDRESS DeviceAddress;\r
- EFI_ISA_IO_PROTOCOL *IsaIo;\r
- UINTN NumberofBytes;\r
- VOID *Mapping;\r
- EFI_ISA_IO_PROTOCOL_OPERATION Operation;\r
- EFI_STATUS Status1;\r
- UINT8 Channel;\r
- EFI_ISA_ACPI_RESOURCE *ResourceItem;\r
- UINT32 Attribute;\r
-\r
- Status = Seek (FdcDev, Lba);\r
- if (EFI_ERROR (Status)) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- //\r
- // Map Dma\r
- //\r
- IsaIo = FdcDev->IsaIo;\r
- NumberofBytes = NumberOfBlocks * 512;\r
- if (Read == READ) {\r
- Operation = EfiIsaIoOperationSlaveWrite;\r
- } else {\r
- Operation = EfiIsaIoOperationSlaveRead;\r
- }\r
-\r
- ResourceItem = IsaIo->ResourceList->ResourceItem;\r
- Index = 0;\r
- while (ResourceItem[Index].Type != EfiIsaAcpiResourceEndOfList) {\r
- if (ResourceItem[Index].Type == EfiIsaAcpiResourceDma) {\r
- break;\r
- }\r
-\r
- Index++;\r
- }\r
-\r
- if (ResourceItem[Index].Type == EfiIsaAcpiResourceEndOfList) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
-\r
- Channel = (UINT8) IsaIo->ResourceList->ResourceItem[Index].StartRange;\r
- Attribute = IsaIo->ResourceList->ResourceItem[Index].Attribute;\r
-\r
- Status1 = IsaIo->Map (\r
- IsaIo,\r
- Operation,\r
- Channel,\r
- Attribute,\r
- HostAddress,\r
- &NumberofBytes,\r
- &DeviceAddress,\r
- &Mapping\r
- );\r
- if (EFI_ERROR (Status1)) {\r
- return Status1;\r
- }\r
-\r
- //\r
- // Allocate Read or Write command packet\r
- //\r
- ZeroMem (&Command, sizeof (FDD_COMMAND_PACKET1));\r
- if (Read == READ) {\r
- Command.CommandCode = READ_DATA_CMD | CMD_MT | CMD_MFM | CMD_SK;\r
- } else {\r
- Command.CommandCode = WRITE_DATA_CMD | CMD_MT | CMD_MFM;\r
- }\r
-\r
- FillPara (FdcDev, Lba, &Command);\r
-\r
- //\r
- // Write command bytes to FDC\r
- //\r
- CommandPointer = (UINT8 *) (&Command);\r
- for (Index = 0; Index < sizeof (FDD_COMMAND_PACKET1); Index++) {\r
- if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- }\r
- //\r
- // wait for some time\r
- //\r
- Times = (STALL_1_SECOND / 50) + 1;\r
- do {\r
- if ((FdcReadPort (FdcDev, FDC_REGISTER_MSR) & 0xc0) == 0xc0) {\r
- break;\r
- }\r
-\r
- MicroSecondDelay (50);\r
- Times = Times - 1;\r
- } while (Times > 0);\r
-\r
- if (Times == 0) {\r
- return EFI_TIMEOUT;\r
- }\r
- //\r
- // Read result bytes from FDC\r
- //\r
- CommandPointer = (UINT8 *) (&Result);\r
- for (Index = 0; Index < sizeof (FDD_RESULT_PACKET); Index++) {\r
- if (EFI_ERROR (DataInByte (FdcDev, CommandPointer++))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- }\r
- //\r
- // Flush before Unmap\r
- //\r
- if (Read == READ) {\r
- Status1 = IsaIo->Flush (IsaIo);\r
- if (EFI_ERROR (Status1)) {\r
- return Status1;\r
- }\r
- }\r
- //\r
- // Unmap Dma\r
- //\r
- Status1 = IsaIo->Unmap (IsaIo, Mapping);\r
- if (EFI_ERROR (Status1)) {\r
- return Status1;\r
- }\r
-\r
- return CheckResult (&Result, FdcDev);\r
-}\r
-\r
-/**\r
- Fill in FDD command's parameter.\r
-\r
- @param FdcDev Pointer to instance of FDC_BLK_IO_DEV\r
- @param Lba The starting logic block address to read from on the device\r
- @param Command FDD command\r
-\r
-**/\r
-VOID\r
-FillPara (\r
- IN FDC_BLK_IO_DEV *FdcDev,\r
- IN EFI_LBA Lba,\r
- IN FDD_COMMAND_PACKET1 *Command\r
- )\r
-{\r
- UINT8 EndOfTrack;\r
-\r
- //\r
- // Get EndOfTrack from the Para table\r
- //\r
- EndOfTrack = DISK_1440K_EOT;\r
-\r
- //\r
- // Fill the command parameter\r
- //\r
- if (FdcDev->Disk == FdcDisk0) {\r
- Command->DiskHeadSel = 0;\r
- } else {\r
- Command->DiskHeadSel = 1;\r
- }\r
-\r
- Command->Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2);\r
- Command->Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);\r
- Command->Sector = (UINT8) ((UINT8) ((UINTN) Lba % EndOfTrack) + 1);\r
- Command->DiskHeadSel = (UINT8) (Command->DiskHeadSel | (Command->Head << 2));\r
- Command->Number = DISK_1440K_NUMBER;\r
- Command->EndOfTrack = DISK_1440K_EOT;\r
- Command->GapLength = DISK_1440K_GPL;\r
- Command->DataLength = DISK_1440K_DTL;\r
-}\r
-\r
-/**\r
- Read result byte from Data Register of FDC.\r
-\r
- @param FdcDev Pointer to instance of FDC_BLK_IO_DEV\r
- @param Pointer Buffer to store the byte read from FDC\r
-\r
- @retval EFI_SUCCESS Read result byte from FDC successfully\r
- @retval EFI_DEVICE_ERROR The FDC is not ready to be read\r
-\r
-**/\r
-EFI_STATUS\r
-DataInByte (\r
- IN FDC_BLK_IO_DEV *FdcDev,\r
- OUT UINT8 *Pointer\r
- )\r
-{\r
- UINT8 Data;\r
-\r
- //\r
- // wait for 1ms and detect the FDC is ready to be read\r
- //\r
- if (EFI_ERROR (FddDRQReady (FdcDev, DATA_IN, 1))) {\r
- return EFI_DEVICE_ERROR;\r
- //\r
- // is not ready\r
- //\r
- }\r
-\r
- Data = FdcReadPort (FdcDev, FDC_REGISTER_DTR);\r
-\r
- //\r
- // Io delay\r
- //\r
- MicroSecondDelay (50);\r
-\r
- *Pointer = Data;\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Write command byte to Data Register of FDC.\r
-\r
- @param FdcDev Pointer to instance of FDC_BLK_IO_DEV\r
- @param Pointer Be used to save command byte written to FDC\r
-\r
- @retval EFI_SUCCESS: Write command byte to FDC successfully\r
- @retval EFI_DEVICE_ERROR: The FDC is not ready to be written\r
-\r
-**/\r
-EFI_STATUS\r
-DataOutByte (\r
- IN FDC_BLK_IO_DEV *FdcDev,\r
- IN UINT8 *Pointer\r
- )\r
-{\r
- UINT8 Data;\r
-\r
- //\r
- // wait for 1ms and detect the FDC is ready to be written\r
- //\r
- if (EFI_ERROR (FddDRQReady (FdcDev, DATA_OUT, 1))) {\r
- //\r
- // Not ready\r
- //\r
- return EFI_DEVICE_ERROR;\r
- }\r
-\r
- Data = *Pointer;\r
-\r
- FdcWritePort (FdcDev, FDC_REGISTER_DTR, Data);\r
-\r
- //\r
- // Io delay\r
- //\r
- MicroSecondDelay (50);\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Detect the specified floppy logic drive is busy or not within a period of time.\r
-\r
- @param FdcDev Indicate it is drive A or drive B\r
- @param Timeout The time period for waiting\r
-\r
- @retval EFI_SUCCESS: The drive and command are not busy\r
- @retval EFI_TIMEOUT: The drive or command is still busy after a period time that\r
- set by Timeout\r
-\r
-**/\r
-EFI_STATUS\r
-FddWaitForBSYClear (\r
- IN FDC_BLK_IO_DEV *FdcDev,\r
- IN UINTN Timeout\r
- )\r
-{\r
- UINTN Delay;\r
- UINT8 StatusRegister;\r
- UINT8 Mask;\r
-\r
- //\r
- // How to determine drive and command are busy or not: by the bits of\r
- // Main Status Register\r
- // bit0: Drive 0 busy (drive A)\r
- // bit1: Drive 1 busy (drive B)\r
- // bit4: Command busy\r
- //\r
- //\r
- // set mask: for drive A set bit0 & bit4; for drive B set bit1 & bit4\r
- //\r
- Mask = (UINT8) ((FdcDev->Disk == FdcDisk0 ? MSR_DAB : MSR_DBB) | MSR_CB);\r
-\r
- Delay = ((Timeout * STALL_1_MSECOND) / 50) + 1;\r
- do {\r
- StatusRegister = FdcReadPort (FdcDev, FDC_REGISTER_MSR);\r
- if ((StatusRegister & Mask) == 0x00) {\r
- break;\r
- //\r
- // not busy\r
- //\r
- }\r
-\r
- MicroSecondDelay (50);\r
- Delay = Delay - 1;\r
- } while (Delay > 0);\r
-\r
- if (Delay == 0) {\r
- return EFI_TIMEOUT;\r
- }\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Determine whether FDC is ready to write or read.\r
-\r
- @param FdcDev Pointer to instance of FDC_BLK_IO_DEV\r
- @param Dio BOOLEAN: Indicate the FDC is waiting to write or read\r
- @param Timeout The time period for waiting\r
-\r
- @retval EFI_SUCCESS: FDC is ready to write or read\r
- @retval EFI_NOT_READY: FDC is not ready within the specified time period\r
-\r
-**/\r
-EFI_STATUS\r
-FddDRQReady (\r
- IN FDC_BLK_IO_DEV *FdcDev,\r
- IN BOOLEAN Dio,\r
- IN UINTN Timeout\r
- )\r
-{\r
- UINTN Delay;\r
- UINT8 StatusRegister;\r
- UINT8 DataInOut;\r
-\r
- //\r
- // Before writing to FDC or reading from FDC, the Host must examine\r
- // the bit7(RQM) and bit6(DIO) of the Main Status Register.\r
- // That is to say:\r
- // command bytes can not be written to Data Register\r
- // unless RQM is 1 and DIO is 0\r
- // result bytes can not be read from Data Register\r
- // unless RQM is 1 and DIO is 1\r
- //\r
- DataInOut = (UINT8) (Dio << 6);\r
- //\r
- // in order to compare bit6\r
- //\r
- Delay = ((Timeout * STALL_1_MSECOND) / 50) + 1;\r
- do {\r
- StatusRegister = FdcReadPort (FdcDev, FDC_REGISTER_MSR);\r
- if ((StatusRegister & MSR_RQM) == MSR_RQM && (StatusRegister & MSR_DIO) == DataInOut) {\r
- break;\r
- //\r
- // FDC is ready\r
- //\r
- }\r
-\r
- MicroSecondDelay (50);\r
- //\r
- // Stall for 50 us\r
- //\r
- Delay = Delay - 1;\r
- } while (Delay > 0);\r
-\r
- if (Delay == 0) {\r
- return EFI_NOT_READY;\r
- //\r
- // FDC is not ready within the specified time period\r
- //\r
- }\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Set FDC control structure's attribute according to result.\r
-\r
- @param Result Point to result structure\r
- @param FdcDev FDC control structure\r
-\r
- @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value\r
- @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value\r
- @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value\r
- @retval EFI_SUCCESS - GC_TODO: Add description for return value\r
-\r
-**/\r
-EFI_STATUS\r
-CheckResult (\r
- IN FDD_RESULT_PACKET *Result,\r
- IN OUT FDC_BLK_IO_DEV *FdcDev\r
- )\r
-{\r
- //\r
- // Check Status Register0\r
- //\r
- if ((Result->Status0 & STS0_IC) != IC_NT) {\r
- if ((Result->Status0 & STS0_SE) == 0x20) {\r
- //\r
- // seek error\r
- //\r
- FdcDev->ControllerState->NeedRecalibrate = TRUE;\r
- }\r
-\r
- FdcDev->ControllerState->NeedRecalibrate = TRUE;\r
- return EFI_DEVICE_ERROR;\r
- }\r
- //\r
- // Check Status Register1\r
- //\r
- if ((Result->Status1 & (STS1_EN | STS1_DE | STS1_OR | STS1_ND | STS1_NW | STS1_MA)) != 0) {\r
- FdcDev->ControllerState->NeedRecalibrate = TRUE;\r
- return EFI_DEVICE_ERROR;\r
- }\r
- //\r
- // Check Status Register2\r
- //\r
- if ((Result->Status2 & (STS2_CM | STS2_DD | STS2_WC | STS2_BC | STS2_MD)) != 0) {\r
- FdcDev->ControllerState->NeedRecalibrate = TRUE;\r
- return EFI_DEVICE_ERROR;\r
- }\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Check the drive status information.\r
-\r
- @param StatusRegister3 the value of Status Register 3\r
-\r
- @retval EFI_SUCCESS The disk is not write protected\r
- @retval EFI_WRITE_PROTECTED: The disk is write protected\r
-\r
-**/\r
-EFI_STATUS\r
-CheckStatus3 (\r
- IN UINT8 StatusRegister3\r
- )\r
-{\r
- if ((StatusRegister3 & STS3_WP) != 0) {\r
- return EFI_WRITE_PROTECTED;\r
- }\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Calculate the number of block in the same cylinder according to LBA.\r
-\r
- @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV\r
- @param LBA EFI_LBA: The starting logic block address\r
- @param NumberOfBlocks UINTN: The number of blocks\r
-\r
- @return The number of blocks in the same cylinder which the starting\r
- logic block address is LBA\r
-\r
-**/\r
-UINTN\r
-GetTransferBlockCount (\r
- IN FDC_BLK_IO_DEV *FdcDev,\r
- IN EFI_LBA LBA,\r
- IN UINTN NumberOfBlocks\r
- )\r
-{\r
- UINT8 EndOfTrack;\r
- UINT8 Head;\r
- UINT8 SectorsInTrack;\r
-\r
- //\r
- // Calculate the number of block in the same cylinder\r
- //\r
- EndOfTrack = DISK_1440K_EOT;\r
- Head = (UINT8) ((UINTN) LBA / EndOfTrack % 2);\r
-\r
- SectorsInTrack = (UINT8) (EndOfTrack * (2 - Head) - (UINT8) ((UINTN) LBA % EndOfTrack));\r
- if (SectorsInTrack < NumberOfBlocks) {\r
- return SectorsInTrack;\r
- } else {\r
- return NumberOfBlocks;\r
- }\r
-}\r
-\r
-/**\r
- When the Timer(2s) off, turn the drive's motor off.\r
-\r
- @param Event EFI_EVENT: Event(the timer) whose notification function is being\r
- invoked\r
- @param Context VOID *: Pointer to the notification function's context\r
-\r
-**/\r
-VOID\r
-EFIAPI\r
-FddTimerProc (\r
- IN EFI_EVENT Event,\r
- IN VOID *Context\r
- )\r
-{\r
- FDC_BLK_IO_DEV *FdcDev;\r
- UINT8 Data;\r
-\r
- FdcDev = (FDC_BLK_IO_DEV *) Context;\r
-\r
- //\r
- // Get the motor status\r
- //\r
- Data = FdcReadPort (FdcDev, FDC_REGISTER_DOR);\r
-\r
- if (((FdcDev->Disk == FdcDisk0) && ((Data & 0x10) != 0x10)) ||\r
- ((FdcDev->Disk == FdcDisk1) && ((Data & 0x21) != 0x21))\r
- ) {\r
- return ;\r
- }\r
- //\r
- // the motor is on, so need motor off\r
- //\r
- Data = 0x0C;\r
- Data = (UINT8) (Data | (SELECT_DRV & FdcDev->Disk));\r
- FdcWritePort (FdcDev, FDC_REGISTER_DOR, Data);\r
- MicroSecondDelay (500);\r
-}\r
-\r
-/**\r
- Read an I/O port of FDC.\r
-\r
- @param[in] FdcDev A pointer to FDC_BLK_IO_DEV.\r
- @param[in] Offset The address offset of the I/O port.\r
-\r
- @retval 8-bit data read from the I/O port.\r
-**/\r
-UINT8\r
-FdcReadPort (\r
- IN FDC_BLK_IO_DEV *FdcDev,\r
- IN UINT32 Offset\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINT8 Data;\r
-\r
- Status = FdcDev->IsaIo->Io.Read (\r
- FdcDev->IsaIo,\r
- EfiIsaIoWidthUint8,\r
- FdcDev->BaseAddress + Offset,\r
- 1,\r
- &Data\r
- );\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- return Data;\r
-}\r
-\r
-/**\r
- Write an I/O port of FDC.\r
-\r
- @param[in] FdcDev A pointer to FDC_BLK_IO_DEV\r
- @param[in] Offset The address offset of the I/O port\r
- @param[in] Data 8-bit Value written to the I/O port\r
-**/\r
-VOID\r
-FdcWritePort (\r
- IN FDC_BLK_IO_DEV *FdcDev,\r
- IN UINT32 Offset,\r
- IN UINT8 Data\r
- )\r
-{\r
- EFI_STATUS Status;\r
-\r
- Status = FdcDev->IsaIo->Io.Write (\r
- FdcDev->IsaIo,\r
- EfiIsaIoWidthUint8,\r
- FdcDev->BaseAddress + Offset,\r
- 1,\r
- &Data\r
- );\r
- ASSERT_EFI_ERROR (Status);\r
-}\r
-\r