]> git.proxmox.com Git - mirror_edk2.git/commitdiff
MdeModulePkg: Add IdeBusPei driver
authorjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>
Mon, 27 Jun 2011 23:31:21 +0000 (23:31 +0000)
committerjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>
Mon, 27 Jun 2011 23:31:21 +0000 (23:31 +0000)
Signed-off-by: jljusten
Reviewed-by: mdkinney
Reviewed-by: geekboy15a
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11902 6f19259b-4bc3-4df7-8a09-765794883524

MdeModulePkg/Bus/Pci/IdeBusPei/AtapiPeim.c [new file with mode: 0644]
MdeModulePkg/Bus/Pci/IdeBusPei/AtapiPeim.h [new file with mode: 0644]
MdeModulePkg/Bus/Pci/IdeBusPei/IdeBusPei.inf [new file with mode: 0644]
MdeModulePkg/Include/Ppi/AtaController.h [new file with mode: 0644]
MdeModulePkg/MdeModulePkg.dec
MdeModulePkg/MdeModulePkg.dsc

diff --git a/MdeModulePkg/Bus/Pci/IdeBusPei/AtapiPeim.c b/MdeModulePkg/Bus/Pci/IdeBusPei/AtapiPeim.c
new file mode 100644 (file)
index 0000000..cc9b06a
--- /dev/null
@@ -0,0 +1,2275 @@
+/** @file\r
+PEIM to produce gEfiPeiVirtualBlockIoPpiGuid PPI for ATA controllers in the platform.\r
+This PPI canl be consumed by PEIM which produce gEfiPeiDeviceRecoveryModulePpiGuid\r
+for Atapi CD ROM device.\r
+\r
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions\r
+of the BSD License which accompanies this distribution.  The\r
+full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "AtapiPeim.h"\r
+\r
+/**\r
+  Initializes the Atapi Block Io PPI.  \r
+  \r
+  @param[in]  FileHandle           Handle of the file being invoked.\r
+  @param[in]  PeiServices          Describes the list of possible PEI Services.\r
+\r
+  @retval     EFI_SUCCESS          Operation performed successfully.\r
+  @retval     EFI_OUT_OF_RESOURCES Not enough memory to allocate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiPeimEntry (\r
+  IN EFI_PEI_FILE_HANDLE        FileHandle,\r
+  IN CONST EFI_PEI_SERVICES     **PeiServices\r
+  )\r
+{\r
+  PEI_ATA_CONTROLLER_PPI  *AtaControllerPpi;\r
+  EFI_STATUS              Status;\r
+  ATAPI_BLK_IO_DEV        *AtapiBlkIoDev;\r
+\r
+  Status = PeiServicesRegisterForShadow (FileHandle);\r
+  if (!EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = PeiServicesLocatePpi (\r
+              &gPeiAtaControllerPpiGuid,\r
+              0,\r
+              NULL,\r
+              (VOID **) &AtaControllerPpi\r
+              );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  AtapiBlkIoDev = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (*AtapiBlkIoDev)));\r
+  if (AtapiBlkIoDev == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  AtapiBlkIoDev->Signature        = ATAPI_BLK_IO_DEV_SIGNATURE;\r
+  AtapiBlkIoDev->AtaControllerPpi = AtaControllerPpi;\r
+\r
+  //\r
+  // atapi device enumeration and build private data\r
+  //\r
+  AtapiEnumerateDevices (AtapiBlkIoDev);\r
+\r
+  AtapiBlkIoDev->AtapiBlkIo.GetNumberOfBlockDevices = AtapiGetNumberOfBlockDevices;\r
+  AtapiBlkIoDev->AtapiBlkIo.GetBlockDeviceMediaInfo = AtapiGetBlockDeviceMediaInfo;\r
+  AtapiBlkIoDev->AtapiBlkIo.ReadBlocks              = AtapiReadBlocks;\r
+\r
+  AtapiBlkIoDev->PpiDescriptor.Flags                = (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST);\r
+  AtapiBlkIoDev->PpiDescriptor.Guid                 = &gEfiPeiVirtualBlockIoPpiGuid;\r
+  AtapiBlkIoDev->PpiDescriptor.Ppi                  = &AtapiBlkIoDev->AtapiBlkIo;\r
+\r
+  DEBUG ((EFI_D_INFO, "Atatpi Device Count is %d\n", AtapiBlkIoDev->DeviceCount));\r
+  if (AtapiBlkIoDev->DeviceCount != 0) {\r
+    Status = PeiServicesInstallPpi (&AtapiBlkIoDev->PpiDescriptor);\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Gets the count of block I/O devices that one specific block driver detects.\r
+\r
+  This function is used for getting the count of block I/O devices that one \r
+  specific block driver detects.  To the PEI ATAPI driver, it returns the number\r
+  of all the detected ATAPI devices it detects during the enumeration process. \r
+  To the PEI legacy floppy driver, it returns the number of all the legacy \r
+  devices it finds during its enumeration process. If no device is detected, \r
+  then the function will return zero.  \r
+  \r
+  @param[in]  PeiServices          General-purpose services that are available \r
+                                   to every PEIM.\r
+  @param[in]  This                 Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI \r
+                                   instance.\r
+  @param[out] NumberBlockDevices   The number of block I/O devices discovered.\r
+\r
+  @retval     EFI_SUCCESS          Operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiGetNumberOfBlockDevices (\r
+  IN   EFI_PEI_SERVICES                  **PeiServices,\r
+  IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI   *This,\r
+  OUT  UINTN                             *NumberBlockDevices\r
+  )\r
+{\r
+  ATAPI_BLK_IO_DEV  *AtapiBlkIoDev;\r
+\r
+  AtapiBlkIoDev = NULL;\r
+\r
+  AtapiBlkIoDev       = PEI_RECOVERY_ATAPI_FROM_BLKIO_THIS (This);\r
+\r
+  *NumberBlockDevices = AtapiBlkIoDev->DeviceCount;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Gets a block device's media information.\r
+\r
+  This function will provide the caller with the specified block device's media \r
+  information. If the media changes, calling this function will update the media \r
+  information accordingly.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to every\r
+                            PEIM\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants \r
+                            to talk. Because the driver that implements Block I/O \r
+                            PPIs will manage multiple block devices, the PPIs that \r
+                            want to talk to a single device must specify the \r
+                            device index that was assigned during the enumeration\r
+                            process. This index is a number from one to \r
+                            NumberBlockDevices.\r
+  @param[out] MediaInfo     The media information of the specified block media.  \r
+                            The caller is responsible for the ownership of this \r
+                            data structure.\r
+  \r
+  @retval EFI_SUCCESS           Media information about the specified block device \r
+                                was obtained successfully.\r
+  @retval EFI_DEVICE_ERROR      Cannot get the media information due to a hardware \r
+                                error.\r
+  @retval Others                Other failure occurs.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiGetBlockDeviceMediaInfo (\r
+  IN   EFI_PEI_SERVICES                     **PeiServices,\r
+  IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI        *This,\r
+  IN   UINTN                                DeviceIndex,\r
+  OUT  EFI_PEI_BLOCK_IO_MEDIA               *MediaInfo\r
+  )\r
+{\r
+  UINTN             DeviceCount;\r
+  ATAPI_BLK_IO_DEV  *AtapiBlkIoDev;\r
+  EFI_STATUS        Status;\r
+  UINTN             Index;\r
+\r
+  AtapiBlkIoDev = NULL;\r
+\r
+  if (This == NULL || MediaInfo == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  AtapiBlkIoDev = PEI_RECOVERY_ATAPI_FROM_BLKIO_THIS (This);\r
+\r
+  DeviceCount   = AtapiBlkIoDev->DeviceCount;\r
+\r
+  //\r
+  // DeviceIndex is a value from 1 to NumberBlockDevices.\r
+  //\r
+  if ((DeviceIndex < 1) || (DeviceIndex > DeviceCount) || (DeviceIndex > MAX_IDE_DEVICES)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Index = DeviceIndex - 1;\r
+\r
+  //\r
+  // probe media and retrieve latest media information\r
+  //\r
+  DEBUG ((EFI_D_INFO, "Atatpi GetInfo DevicePosition is %d\n", AtapiBlkIoDev->DeviceInfo[Index].DevicePosition));  \r
+  DEBUG ((EFI_D_INFO, "Atatpi GetInfo DeviceType is   %d\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.DeviceType));\r
+  DEBUG ((EFI_D_INFO, "Atatpi GetInfo MediaPresent is %d\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.MediaPresent));\r
+  DEBUG ((EFI_D_INFO, "Atatpi GetInfo BlockSize is  0x%x\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.BlockSize));\r
+  DEBUG ((EFI_D_INFO, "Atatpi GetInfo LastBlock is  0x%x\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.LastBlock));\r
+\r
+  Status = DetectMedia (\r
+             AtapiBlkIoDev,\r
+             AtapiBlkIoDev->DeviceInfo[Index].DevicePosition,\r
+             &AtapiBlkIoDev->DeviceInfo[Index].MediaInfo\r
+             );\r
+  if (Status != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  DEBUG ((EFI_D_INFO, "Atatpi GetInfo DevicePosition is %d\n", AtapiBlkIoDev->DeviceInfo[Index].DevicePosition));\r
+  DEBUG ((EFI_D_INFO, "Atatpi GetInfo DeviceType is   %d\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.DeviceType));\r
+  DEBUG ((EFI_D_INFO, "Atatpi GetInfo MediaPresent is %d\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.MediaPresent));\r
+  DEBUG ((EFI_D_INFO, "Atatpi GetInfo BlockSize is  0x%x\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.BlockSize));\r
+  DEBUG ((EFI_D_INFO, "Atatpi GetInfo LastBlock is  0x%x\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.LastBlock));\r
+  \r
+  //\r
+  // Get media info from AtapiBlkIoDev\r
+  //\r
+  CopyMem (MediaInfo, &AtapiBlkIoDev->DeviceInfo[Index].MediaInfo, sizeof(EFI_PEI_BLOCK_IO_MEDIA));\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Reads the requested number of blocks from the specified block device.\r
+\r
+  The function reads the requested number of blocks from the device. All the \r
+  blocks are read, or an error is returned. If there is no media in the device,\r
+  the function returns EFI_NO_MEDIA.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to \r
+                            every PEIM.\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants \r
+                            to talk. Because the driver that implements Block I/O \r
+                            PPIs will manage multiple block devices, the PPIs that \r
+                            want to talk to a single device must specify the device \r
+                            index that was assigned during the enumeration process. \r
+                            This index is a number from one to NumberBlockDevices.\r
+  @param[in]  StartLBA      The starting logical block address (LBA) to read from\r
+                            on the device\r
+  @param[in]  BufferSize    The size of the Buffer in bytes. This number must be\r
+                            a multiple of the intrinsic block size of the device.\r
+  @param[out] Buffer        A pointer to the destination buffer for the data.\r
+                            The caller is responsible for the ownership of the \r
+                            buffer.\r
+                         \r
+  @retval EFI_SUCCESS             The data was read correctly from the device.\r
+  @retval EFI_DEVICE_ERROR        The device reported an error while attempting \r
+                                  to perform the read operation.\r
+  @retval EFI_INVALID_PARAMETER   The read request contains LBAs that are not \r
+                                  valid, or the buffer is not properly aligned.\r
+  @retval EFI_NO_MEDIA            There is no media in the device.\r
+  @retval EFI_BAD_BUFFER_SIZE     The BufferSize parameter is not a multiple of\r
+                                  the intrinsic block size of the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiReadBlocks (\r
+  IN   EFI_PEI_SERVICES                  **PeiServices,\r
+  IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI     *This,\r
+  IN   UINTN                             DeviceIndex,\r
+  IN   EFI_PEI_LBA                       StartLBA,\r
+  IN   UINTN                             BufferSize,\r
+  OUT  VOID                              *Buffer\r
+  )\r
+{\r
+\r
+  EFI_PEI_BLOCK_IO_MEDIA  MediaInfo;\r
+  EFI_STATUS          Status;\r
+  UINTN               NumberOfBlocks;\r
+  UINTN               BlockSize;\r
+  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev;\r
+\r
+  AtapiBlkIoDev = NULL;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  AtapiBlkIoDev = PEI_RECOVERY_ATAPI_FROM_BLKIO_THIS (This);\r
+\r
+  if (Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (BufferSize == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Status = AtapiGetBlockDeviceMediaInfo (\r
+            PeiServices,\r
+            This,\r
+            DeviceIndex,\r
+            &MediaInfo\r
+            );\r
+  if (Status != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (!MediaInfo.MediaPresent) {\r
+    return EFI_NO_MEDIA;\r
+  }\r
+\r
+  BlockSize = MediaInfo.BlockSize;\r
+\r
+  if (BufferSize % BlockSize != 0) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  NumberOfBlocks = BufferSize / BlockSize;\r
+\r
+  if ((StartLBA + NumberOfBlocks - 1) > AtapiBlkIoDev->DeviceInfo[DeviceIndex - 1].MediaInfo.LastBlock) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = ReadSectors (\r
+            AtapiBlkIoDev,\r
+            AtapiBlkIoDev->DeviceInfo[DeviceIndex - 1].DevicePosition,\r
+            Buffer,\r
+            StartLBA,\r
+            NumberOfBlocks,\r
+            BlockSize\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Enumerate Atapi devices.\r
+\r
+  This function is used to enumerate Atatpi device in Ide channel.\r
+\r
+  @param[in]  AtapiBlkIoDev  A pointer to atapi block IO device\r
+\r
+**/\r
+VOID\r
+AtapiEnumerateDevices (\r
+  IN  ATAPI_BLK_IO_DEV  *AtapiBlkIoDev\r
+  )\r
+{\r
+  UINT8               Index1;\r
+  UINT8               Index2;\r
+  UINTN               DevicePosition;\r
+  EFI_PEI_BLOCK_IO_MEDIA  MediaInfo;\r
+  EFI_STATUS          Status;\r
+  UINTN               DeviceCount;\r
+  UINT16              CommandBlockBaseAddr;\r
+  UINT16              ControlBlockBaseAddr;\r
+  UINT32              IdeEnabledNumber;\r
+  IDE_REGS_BASE_ADDR  IdeRegsBaseAddr[MAX_IDE_CHANNELS];\r
+\r
+  DeviceCount = 0;\r
+  DevicePosition = 0;\r
+\r
+  //\r
+  // Scan IDE bus for ATAPI devices\r
+  //\r
+\r
+  //\r
+  // Enable Sata and IDE controller.\r
+  //\r
+  AtapiBlkIoDev->AtaControllerPpi->EnableAtaChannel (\r
+                                  (EFI_PEI_SERVICES **) GetPeiServicesTablePointer(),\r
+                                  AtapiBlkIoDev->AtaControllerPpi,\r
+                                  PEI_ICH_IDE_PRIMARY | PEI_ICH_IDE_SECONDARY\r
+                                  );\r
+\r
+  //\r
+  // Allow SATA Devices to spin-up. This is needed if \r
+  // SEC and PEI phase is too short, for example Release Build.\r
+  //\r
+  DEBUG ((EFI_D_INFO, "Delay for %d seconds for SATA devices to spin-up\n", PcdGet16 (PcdSataSpinUpDelayInSecForRecoveryPath)));\r
+  MicroSecondDelay (PcdGet16 (PcdSataSpinUpDelayInSecForRecoveryPath) * 1000 * 1000); //\r
+\r
+  //\r
+  // Get four channels (primary or secondary Pata, Sata Channel) Command and Control Regs Base address.\r
+  //\r
+  IdeEnabledNumber = AtapiBlkIoDev->AtaControllerPpi->GetIdeRegsBaseAddr (\r
+                                                      (EFI_PEI_SERVICES **) GetPeiServicesTablePointer(),\r
+                                                      AtapiBlkIoDev->AtaControllerPpi,\r
+                                                      IdeRegsBaseAddr\r
+                                                      );\r
+\r
+  //\r
+  // Using Command and Control Regs Base Address to fill other registers.\r
+  //\r
+  for (Index1 = 0; Index1 < IdeEnabledNumber; Index1 ++) { \r
+    CommandBlockBaseAddr               = IdeRegsBaseAddr[Index1].CommandBlockBaseAddr;\r
+    AtapiBlkIoDev->IdeIoPortReg[Index1].Data         = CommandBlockBaseAddr;\r
+    AtapiBlkIoDev->IdeIoPortReg[Index1].Reg1.Feature = (UINT16) (CommandBlockBaseAddr + 0x1);\r
+    AtapiBlkIoDev->IdeIoPortReg[Index1].SectorCount  = (UINT16) (CommandBlockBaseAddr + 0x2);\r
+    AtapiBlkIoDev->IdeIoPortReg[Index1].SectorNumber = (UINT16) (CommandBlockBaseAddr + 0x3);\r
+    AtapiBlkIoDev->IdeIoPortReg[Index1].CylinderLsb  = (UINT16) (CommandBlockBaseAddr + 0x4);\r
+    AtapiBlkIoDev->IdeIoPortReg[Index1].CylinderMsb  = (UINT16) (CommandBlockBaseAddr + 0x5);\r
+    AtapiBlkIoDev->IdeIoPortReg[Index1].Head         = (UINT16) (CommandBlockBaseAddr + 0x6);\r
+    AtapiBlkIoDev->IdeIoPortReg[Index1].Reg.Command  = (UINT16) (CommandBlockBaseAddr + 0x7);\r
+\r
+    ControlBlockBaseAddr                = IdeRegsBaseAddr[Index1].ControlBlockBaseAddr;\r
+    AtapiBlkIoDev->IdeIoPortReg[Index1].Alt.DeviceControl = ControlBlockBaseAddr;\r
+    AtapiBlkIoDev->IdeIoPortReg[Index1].DriveAddress      = (UINT16) (ControlBlockBaseAddr + 0x1);\r
+    \r
+    //\r
+    // Scan IDE bus for ATAPI devices IDE or Sata device\r
+    //\r
+    for (Index2 = IdeMaster; Index2 < IdeMaxDevice; Index2++) {\r
+      //\r
+      // Pata & Sata, Primary & Secondary channel, Master & Slave device\r
+      //\r
+      DevicePosition = (UINTN) (Index1 * 2 + Index2);\r
+\r
+      if (DiscoverAtapiDevice (AtapiBlkIoDev, DevicePosition, &MediaInfo)) {\r
+        //\r
+        // ATAPI Device at DevicePosition is found.\r
+        //\r
+        AtapiBlkIoDev->DeviceInfo[DeviceCount].DevicePosition = DevicePosition;\r
+        //\r
+        // Retrieve Media Info\r
+        //\r
+        Status  = DetectMedia (AtapiBlkIoDev, DevicePosition, &MediaInfo);\r
+        CopyMem (&(AtapiBlkIoDev->DeviceInfo[DeviceCount].MediaInfo), &MediaInfo, sizeof (MediaInfo));\r
+       \r
+        DEBUG ((EFI_D_INFO, "Atatpi Device Position is %d\n", DevicePosition));\r
+        DEBUG ((EFI_D_INFO, "Atatpi DeviceType is   %d\n", MediaInfo.DeviceType));\r
+        DEBUG ((EFI_D_INFO, "Atatpi MediaPresent is %d\n", MediaInfo.MediaPresent));\r
+        DEBUG ((EFI_D_INFO, "Atatpi BlockSize is  0x%x\n", MediaInfo.BlockSize));\r
+\r
+        if (EFI_ERROR (Status)) {\r
+          AtapiBlkIoDev->DeviceInfo[DeviceCount].MediaInfo.MediaPresent = FALSE;\r
+          AtapiBlkIoDev->DeviceInfo[DeviceCount].MediaInfo.LastBlock    = 0;\r
+        }\r
+        DeviceCount += 1;\r
+      }\r
+    }\r
+  }\r
+\r
+  AtapiBlkIoDev->DeviceCount = DeviceCount;\r
+}\r
+\r
+/**\r
+  Detect Atapi devices.\r
+  \r
+  @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition  An integer to signify device position.\r
+  @param[out] MediaInfo       The media information of the specified block media.\r
+\r
+  @retval TRUE                Atapi device exists in specified position.\r
+  @retval FALSE               Atapi device does not exist in specified position.\r
+\r
+**/\r
+BOOLEAN\r
+DiscoverAtapiDevice (\r
+  IN  ATAPI_BLK_IO_DEV              *AtapiBlkIoDev,\r
+  IN  UINTN                         DevicePosition,\r
+  OUT EFI_PEI_BLOCK_IO_MEDIA        *MediaInfo\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  if (!DetectIDEController (AtapiBlkIoDev, DevicePosition)) {\r
+    return FALSE;\r
+  }\r
+  //\r
+  // test if it is an ATAPI device (only supported device)\r
+  //\r
+  if (ATAPIIdentify (AtapiBlkIoDev, DevicePosition) == EFI_SUCCESS) {\r
+\r
+    Status = Inquiry (AtapiBlkIoDev, DevicePosition, MediaInfo);\r
+    if (!EFI_ERROR (Status)) {\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Check power mode of Atapi devices.\r
+  \r
+  @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition  An integer to signify device position.\r
+  @param[in]  AtaCommand      The Ata Command passed in.\r
+\r
+  @retval EFI_SUCCESS         The Atapi device support power mode.\r
+  @retval EFI_NOT_FOUND       The Atapi device not found.\r
+  @retval EFI_TIMEOUT         Atapi command transaction is time out.\r
+  @retval EFI_ABORTED         Atapi command abort.\r
+\r
+**/\r
+EFI_STATUS\r
+CheckPowerMode (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  UINTN               DevicePosition,\r
+  IN  UINT8               AtaCommand\r
+  )\r
+{\r
+  UINT8       Channel;\r
+  UINT8       Device;\r
+  UINT16      StatusRegister;\r
+  UINT16      HeadRegister;\r
+  UINT16      CommandRegister;\r
+  UINT16      ErrorRegister;\r
+  UINT16      SectorCountRegister;\r
+  EFI_STATUS  Status;\r
+  UINT8       StatusValue;\r
+  UINT8       ErrorValue;\r
+  UINT8       SectorCountValue;\r
+\r
+  Channel             = (UINT8) (DevicePosition / 2);\r
+  Device              = (UINT8) (DevicePosition % 2);\r
+\r
+  ASSERT (Channel < MAX_IDE_CHANNELS);\r
+\r
+  StatusRegister      = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Status;\r
+  HeadRegister        = AtapiBlkIoDev->IdeIoPortReg[Channel].Head;\r
+  CommandRegister     = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Command;\r
+  ErrorRegister       = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg1.Error;\r
+  SectorCountRegister = AtapiBlkIoDev->IdeIoPortReg[Channel].SectorCount;\r
+\r
+  //\r
+  // select device\r
+  //\r
+  IoWrite8 (HeadRegister, (UINT8) ((Device << 4) | 0xe0));\r
+\r
+  //\r
+  // refresh the SectorCount register\r
+  //\r
+  SectorCountValue = 0x55;\r
+  IoWrite8 (SectorCountRegister, SectorCountValue);\r
+\r
+  //\r
+  // select device\r
+  //\r
+  IoWrite8 (HeadRegister, (UINT8) ((Device << 4) | 0xe0));\r
+\r
+  Status = DRDYReady (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 100);\r
+\r
+  //\r
+  // select device\r
+  //\r
+  IoWrite8 (HeadRegister, (UINT8) ((Device << 4) | 0xe0));\r
+  //\r
+  // send 'check power' commandd via Command Register\r
+  //\r
+  IoWrite8 (CommandRegister, AtaCommand);\r
+\r
+  Status = WaitForBSYClear (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 3000);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  StatusValue = IoRead8 (StatusRegister);\r
+\r
+  //\r
+  // command returned status is DRDY, indicating device supports the command,\r
+  // so device is present.\r
+  //\r
+  if ((StatusValue & ATA_STSREG_DRDY) == ATA_STSREG_DRDY) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  SectorCountValue = IoRead8 (SectorCountRegister);\r
+\r
+  //\r
+  // command returned status is ERR & ABRT_ERR, indicating device does not support\r
+  // the command, so device is present.\r
+  //\r
+  if ((StatusValue & ATA_STSREG_ERR) == ATA_STSREG_ERR) {\r
+    ErrorValue = IoRead8 (ErrorRegister);\r
+    if ((ErrorValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {\r
+      return EFI_ABORTED;\r
+    } else {\r
+      //\r
+      // According to spec, no other error code is valid\r
+      //\r
+      return EFI_NOT_FOUND;\r
+    }\r
+  }\r
+\r
+  if ((SectorCountValue == 0x00) || (SectorCountValue == 0x80) || (SectorCountValue == 0xff)) {\r
+    //\r
+    // Write SectorCount 0x55 but return valid state value. Maybe no device\r
+    // exists or some slow kind of ATAPI device exists.\r
+    //\r
+    IoWrite8 (HeadRegister, (UINT8) ((Device << 4) | 0xe0));\r
+\r
+    //\r
+    // write 0x55 and 0xaa to SectorCounter register,\r
+    // if the data could be written into the register,\r
+    // indicating the device is present, otherwise the device is not present.\r
+    //\r
+    SectorCountValue = 0x55;\r
+    IoWrite8 (SectorCountRegister, SectorCountValue);\r
+    MicroSecondDelay (10000);\r
+\r
+    SectorCountValue = IoRead8 (SectorCountRegister);\r
+    if (SectorCountValue != 0x55) {\r
+      return EFI_NOT_FOUND;\r
+    }\r
+    //\r
+    // Send a "ATAPI TEST UNIT READY" command ... slow but accurate\r
+    //\r
+    Status = TestUnitReady (AtapiBlkIoDev, DevicePosition);\r
+    return Status;\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+  Detect if an IDE controller exists in specified position.\r
+  \r
+  @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition  An integer to signify device position.\r
+\r
+  @retval TRUE         The Atapi device exists.\r
+  @retval FALSE        The Atapi device does not present.\r
+\r
+**/\r
+BOOLEAN\r
+DetectIDEController (\r
+  IN  ATAPI_BLK_IO_DEV   *AtapiBlkIoDev,\r
+  IN  UINTN              DevicePosition\r
+  )\r
+{\r
+  UINT8       Channel;\r
+  EFI_STATUS  Status;\r
+  UINT8       AtaCommand;\r
+\r
+  Channel           = (UINT8) (DevicePosition / 2);\r
+\r
+  ASSERT (Channel < MAX_IDE_CHANNELS);\r
+  //\r
+  //  Wait 31 seconds for BSY clear\r
+  //\r
+  Status = WaitForBSYClear (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 31000);\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+  //\r
+  // Send 'check power' command for IDE device\r
+  //\r
+  AtaCommand  = 0xE5;\r
+  Status      = CheckPowerMode (AtapiBlkIoDev, DevicePosition, AtaCommand);\r
+  if ((Status == EFI_ABORTED) || (Status == EFI_SUCCESS)) {\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Wait specified time interval to poll for BSY bit clear in the Status Register.\r
+  \r
+  @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.\r
+  @param[in]  IdeIoRegisters         A pointer to IDE IO registers.\r
+  @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.\r
+\r
+  @retval EFI_SUCCESS        BSY bit is cleared in the specified time interval.\r
+  @retval EFI_TIMEOUT        BSY bit is not cleared in the specified time interval.\r
+\r
+**/\r
+EFI_STATUS\r
+WaitForBSYClear (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  IDE_BASE_REGISTERS  *IdeIoRegisters,\r
+  IN  UINTN               TimeoutInMilliSeconds\r
+  )\r
+{\r
+  UINTN   Delay;\r
+  UINT16  StatusRegister;\r
+  UINT8   StatusValue;\r
+\r
+  StatusValue     = 0;\r
+\r
+  StatusRegister  = IdeIoRegisters->Reg.Status;\r
+\r
+  Delay           = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;\r
+  do {\r
+    StatusValue = IoRead8 (StatusRegister);\r
+    if ((StatusValue & ATA_STSREG_BSY) == 0x00) {\r
+      break;\r
+    }\r
+    MicroSecondDelay (250);\r
+\r
+    Delay--;\r
+\r
+  } while (Delay != 0);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Wait specified time interval to poll for DRDY bit set in the Status register.\r
+  \r
+  @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.\r
+  @param[in]  IdeIoRegisters         A pointer to IDE IO registers.\r
+  @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.\r
+\r
+  @retval EFI_SUCCESS        DRDY bit is set in the specified time interval.\r
+  @retval EFI_TIMEOUT        DRDY bit is not set in the specified time interval.\r
+\r
+**/\r
+EFI_STATUS\r
+DRDYReady (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  IDE_BASE_REGISTERS  *IdeIoRegisters,\r
+  IN  UINTN               TimeoutInMilliSeconds\r
+  )\r
+{\r
+  UINTN   Delay;\r
+  UINT16  StatusRegister;\r
+  UINT8   StatusValue;\r
+  UINT8   ErrValue;\r
+\r
+  StatusValue     = 0;\r
+\r
+  StatusRegister  = IdeIoRegisters->Reg.Status;\r
+\r
+  Delay           = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;\r
+  do {\r
+    StatusValue = IoRead8 (StatusRegister);\r
+    //\r
+    //  BSY == 0 , DRDY == 1\r
+    //\r
+    if ((StatusValue & (ATA_STSREG_DRDY | ATA_STSREG_BSY)) == ATA_STSREG_DRDY) {\r
+      break;\r
+    }\r
+\r
+  if ((StatusValue & (ATA_STSREG_ERR | ATA_STSREG_BSY)) == ATA_STSREG_ERR) {\r
+    ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);\r
+    if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {\r
+    return EFI_ABORTED;\r
+    }\r
+  }\r
+  \r
+    MicroSecondDelay (250);\r
+\r
+    Delay--;\r
+\r
+  } while (Delay != 0);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Wait specified time interval to poll for DRQ bit clear in the Status Register.\r
+  \r
+  @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.\r
+  @param[in]  IdeIoRegisters         A pointer to IDE IO registers.\r
+  @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.\r
+\r
+  @retval EFI_SUCCESS        DRQ bit is cleared in the specified time interval.\r
+  @retval EFI_TIMEOUT        DRQ bit is not cleared in the specified time interval.\r
+\r
+**/\r
+EFI_STATUS\r
+DRQClear (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  IDE_BASE_REGISTERS  *IdeIoRegisters,\r
+  IN  UINTN               TimeoutInMilliSeconds\r
+  )\r
+{\r
+  UINTN   Delay;\r
+  UINT16  StatusRegister;\r
+  UINT8   StatusValue;\r
+  UINT8   ErrValue;\r
+\r
+  StatusValue     = 0;\r
+\r
+  StatusRegister  = IdeIoRegisters->Reg.Status;\r
+\r
+  Delay           = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;\r
+  do {\r
+\r
+    StatusValue = IoRead8 (StatusRegister);\r
+\r
+    //\r
+    // wait for BSY == 0 and DRQ == 0\r
+    //\r
+    if ((StatusValue & (ATA_STSREG_DRQ | ATA_STSREG_BSY)) == 0) {\r
+      break;\r
+    }\r
+\r
+  if ((StatusValue & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {\r
+    ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);\r
+    if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {\r
+    return EFI_ABORTED;\r
+    }\r
+  }\r
+  \r
+    MicroSecondDelay (250);\r
+\r
+    Delay--;\r
+  } while (Delay != 0);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Wait specified time interval to poll for DRQ bit clear in the Alternate Status Register.\r
+  \r
+  @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.\r
+  @param[in]  IdeIoRegisters         A pointer to IDE IO registers.\r
+  @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.\r
+\r
+  @retval EFI_SUCCESS        DRQ bit is cleared in the specified time interval.\r
+  @retval EFI_TIMEOUT        DRQ bit is not cleared in the specified time interval.\r
+\r
+**/\r
+EFI_STATUS\r
+DRQClear2 (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  IDE_BASE_REGISTERS  *IdeIoRegisters,\r
+  IN  UINTN               TimeoutInMilliSeconds\r
+  )\r
+{\r
+  UINTN   Delay;\r
+  UINT16  AltStatusRegister;\r
+  UINT8   AltStatusValue;\r
+  UINT8   ErrValue;\r
+\r
+  AltStatusValue    = 0;\r
+\r
+  AltStatusRegister = IdeIoRegisters->Alt.AltStatus;\r
+\r
+  Delay             = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;\r
+  do {\r
+\r
+    AltStatusValue = IoRead8 (AltStatusRegister);\r
+\r
+    //\r
+    // wait for BSY == 0 and DRQ == 0\r
+    //\r
+    if ((AltStatusValue & (ATA_STSREG_DRQ | ATA_STSREG_BSY)) == 0) {\r
+      break;\r
+    }\r
+\r
+  if ((AltStatusValue & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {\r
+    ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);\r
+    if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {\r
+    return EFI_ABORTED;\r
+    }\r
+  }\r
+  \r
+    MicroSecondDelay (250);\r
+\r
+    Delay--;\r
+  } while (Delay != 0);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Wait specified time interval to poll for DRQ bit set in the Status Register.\r
+\r
+  @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.\r
+  @param[in]  IdeIoRegisters         A pointer to IDE IO registers.\r
+  @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.\r
+\r
+  @retval EFI_SUCCESS        DRQ bit is set in the specified time interval.\r
+  @retval EFI_TIMEOUT        DRQ bit is not set in the specified time interval.\r
+  @retval EFI_ABORTED        Operation Aborted.\r
+\r
+**/\r
+EFI_STATUS\r
+DRQReady (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  IDE_BASE_REGISTERS  *IdeIoRegisters,\r
+  IN  UINTN               TimeoutInMilliSeconds\r
+  )\r
+{\r
+  UINTN   Delay;\r
+  UINT16  StatusRegister;\r
+  UINT8   StatusValue;\r
+  UINT8   ErrValue;\r
+\r
+  StatusValue     = 0;\r
+  ErrValue        = 0;\r
+\r
+  StatusRegister  = IdeIoRegisters->Reg.Status;\r
+\r
+  Delay           = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;\r
+  do {\r
+    //\r
+    //  read Status Register will clear interrupt\r
+    //\r
+    StatusValue = IoRead8 (StatusRegister);\r
+\r
+    //\r
+    //  BSY==0,DRQ==1\r
+    //\r
+    if ((StatusValue & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) {\r
+      break;\r
+    }\r
+\r
+    if ((StatusValue & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {\r
+\r
+      ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);\r
+      if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {\r
+        return EFI_ABORTED;\r
+      }\r
+    }\r
+    MicroSecondDelay (250);\r
+\r
+    Delay--;\r
+  } while (Delay != 0);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Wait specified time interval to poll for DRQ bit set in the Alternate Status Register.\r
+\r
+  @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.\r
+  @param[in]  IdeIoRegisters         A pointer to IDE IO registers.\r
+  @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.\r
+\r
+  @retval EFI_SUCCESS        DRQ bit is set in the specified time interval.\r
+  @retval EFI_TIMEOUT        DRQ bit is not set in the specified time interval.\r
+  @retval EFI_ABORTED        Operation Aborted.\r
+\r
+**/\r
+EFI_STATUS\r
+DRQReady2 (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  IDE_BASE_REGISTERS  *IdeIoRegisters,\r
+  IN  UINTN               TimeoutInMilliSeconds\r
+  )\r
+{\r
+  UINTN   Delay;\r
+  UINT16  AltStatusRegister;\r
+  UINT8   AltStatusValue;\r
+  UINT8   ErrValue;\r
+\r
+  AltStatusValue    = 0;\r
+\r
+  AltStatusRegister = IdeIoRegisters->Alt.AltStatus;\r
+\r
+  Delay             = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;\r
+  do {\r
+\r
+    AltStatusValue = IoRead8 (AltStatusRegister);\r
+\r
+    //\r
+    //  BSY==0,DRQ==1\r
+    //\r
+    if ((AltStatusValue & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) {\r
+      break;\r
+    }\r
+\r
+    if ((AltStatusValue & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {\r
+\r
+      ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);\r
+      if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {\r
+        return EFI_ABORTED;\r
+      }\r
+    }\r
+    MicroSecondDelay (250);\r
+\r
+    Delay--;\r
+  } while (Delay != 0);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Check if there is an error in Status Register.\r
+\r
+  @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.\r
+  @param[in]  StatusReg         The address to IDE IO registers.\r
+\r
+  @retval EFI_SUCCESS        Operation success.\r
+  @retval EFI_DEVICE_ERROR   Device error.\r
+\r
+**/\r
+EFI_STATUS\r
+CheckErrorStatus (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  UINT16              StatusReg\r
+  )\r
+{\r
+  UINT8 StatusValue;\r
+\r
+  StatusValue = IoRead8 (StatusReg);\r
+\r
+  if ((StatusValue & (ATA_STSREG_ERR | ATA_STSREG_DWF | ATA_STSREG_CORR)) == 0) {\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  return EFI_DEVICE_ERROR;\r
+\r
+}\r
+\r
+/**\r
+  Idendify Atapi devices.\r
+\r
+  @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition    An integer to signify device position.\r
+\r
+  @retval EFI_SUCCESS        Identify successfully.\r
+  @retval EFI_DEVICE_ERROR   Device cannot be identified successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+ATAPIIdentify (\r
+  IN  ATAPI_BLK_IO_DEV        *AtapiBlkIoDev,\r
+  IN  UINTN                   DevicePosition\r
+  )\r
+{\r
+  ATAPI_IDENTIFY_DATA  AtapiIdentifyData;\r
+  UINT8       Channel;\r
+  UINT8       Device;\r
+  UINT16      StatusReg;\r
+  UINT16      HeadReg;\r
+  UINT16      CommandReg;\r
+  UINT16      DataReg;\r
+  UINT16      SectorCountReg;\r
+  UINT16      SectorNumberReg;\r
+  UINT16      CylinderLsbReg;\r
+  UINT16      CylinderMsbReg;\r
+\r
+  UINT32      WordCount;\r
+  UINT32      Increment;\r
+  UINT32      Index;\r
+  UINT32      ByteCount;\r
+  UINT16      *Buffer16;\r
+\r
+  EFI_STATUS  Status;\r
+\r
+  ByteCount       = sizeof (AtapiIdentifyData);\r
+  Buffer16        = (UINT16 *) &AtapiIdentifyData;\r
+\r
+  Channel         = (UINT8) (DevicePosition / 2);\r
+  Device          = (UINT8) (DevicePosition % 2);\r
+\r
+  ASSERT (Channel < MAX_IDE_CHANNELS);\r
+\r
+  StatusReg       = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Status;\r
+  HeadReg         = AtapiBlkIoDev->IdeIoPortReg[Channel].Head;\r
+  CommandReg      = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Command;\r
+  DataReg         = AtapiBlkIoDev->IdeIoPortReg[Channel].Data;\r
+  SectorCountReg  = AtapiBlkIoDev->IdeIoPortReg[Channel].SectorCount;\r
+  SectorNumberReg = AtapiBlkIoDev->IdeIoPortReg[Channel].SectorNumber;\r
+  CylinderLsbReg  = AtapiBlkIoDev->IdeIoPortReg[Channel].CylinderLsb;\r
+  CylinderMsbReg  = AtapiBlkIoDev->IdeIoPortReg[Channel].CylinderMsb;\r
+\r
+  //\r
+  // Send ATAPI Identify Command to get IDENTIFY data.\r
+  //\r
+  if (WaitForBSYClear (\r
+        AtapiBlkIoDev,\r
+        &(AtapiBlkIoDev->IdeIoPortReg[Channel]),\r
+        ATATIMEOUT\r
+        ) != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // select device via Head/Device register.\r
+  // Before write Head/Device register, BSY and DRQ must be 0.\r
+  //\r
+  if (DRQClear2 (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), ATATIMEOUT) != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  //  e0:1110,0000-- bit7 and bit5 are reserved bits.\r
+  //           bit6 set means LBA mode\r
+  //\r
+  IoWrite8 (HeadReg, (UINT8) ((Device << 4) | 0xe0));\r
+\r
+  //\r
+  // set all the command parameters\r
+  // Before write to all the following registers, BSY and DRQ must be 0.\r
+  //\r
+  if (DRQClear2 (\r
+        AtapiBlkIoDev,\r
+        &(AtapiBlkIoDev->IdeIoPortReg[Channel]),\r
+        ATATIMEOUT\r
+        ) != EFI_SUCCESS) {\r
+\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  IoWrite8 (SectorCountReg, 0);\r
+  IoWrite8 (SectorNumberReg, 0);\r
+  IoWrite8 (CylinderLsbReg, 0);\r
+  IoWrite8 (CylinderMsbReg, 0);\r
+\r
+  //\r
+  // send command via Command Register\r
+  //\r
+  IoWrite8 (CommandReg, ATA_CMD_IDENTIFY_DEVICE);\r
+\r
+  //\r
+  // According to PIO data in protocol, host can perform a series of reads to the\r
+  // data register after each time device set DRQ ready;\r
+  // The data size of "a series of read" is command specific.\r
+  // For most ATA command, data size received from device will not exceed 1 sector,\r
+  // hense the data size for "a series of read" can be the whole data size of one command request.\r
+  // For ATA command such as Read Sector command, whole data size of one ATA command request is often larger\r
+  // than 1 sector, according to the Read Sector command, the data size of "a series of read" is exactly\r
+  // 1 sector.\r
+  // Here for simplification reason, we specify the data size for "a series of read" to\r
+  // 1 sector (256 words) if whole data size of one ATA commmand request is larger than 256 words.\r
+  //\r
+  Increment = 256;\r
+  //\r
+  // 256 words\r
+  //\r
+  WordCount = 0;\r
+  //\r
+  // WordCount is used to record bytes of currently transfered data\r
+  //\r
+  while (WordCount < ByteCount / 2) {\r
+    //\r
+    // Poll DRQ bit set, data transfer can be performed only when DRQ is ready.\r
+    //\r
+    Status = DRQReady2 (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), ATATIMEOUT);\r
+    if (Status != EFI_SUCCESS) {\r
+      return Status;\r
+    }\r
+\r
+    if (CheckErrorStatus (AtapiBlkIoDev, StatusReg) != EFI_SUCCESS) {\r
+\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+    //\r
+    // Get the byte count for one series of read\r
+    //\r
+    if ((WordCount + Increment) > ByteCount / 2) {\r
+      Increment = ByteCount / 2 - WordCount;\r
+    }\r
+    //\r
+    // perform a series of read without check DRQ ready\r
+    //\r
+    for (Index = 0; Index < Increment; Index++) {\r
+      *Buffer16++ = IoRead16 (DataReg);\r
+    }\r
+\r
+    WordCount += Increment;\r
+\r
+  }\r
+  //\r
+  // while\r
+  //\r
+  if (DRQClear (\r
+        AtapiBlkIoDev,\r
+        &(AtapiBlkIoDev->IdeIoPortReg[Channel]),\r
+        ATATIMEOUT\r
+        ) != EFI_SUCCESS) {\r
+    return CheckErrorStatus (AtapiBlkIoDev, StatusReg);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+}\r
+\r
+/**\r
+  Sends out ATAPI Test Unit Ready Packet Command to the specified device\r
+  to find out whether device is accessible.\r
+\r
+  @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition    An integer to signify device position.\r
+\r
+  @retval EFI_SUCCESS        TestUnit command executed successfully.\r
+  @retval EFI_DEVICE_ERROR   Device cannot be executed TestUnit command successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+TestUnitReady (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  UINTN               DevicePosition\r
+  )\r
+{\r
+  ATAPI_PACKET_COMMAND  Packet;\r
+  EFI_STATUS            Status;\r
+\r
+  //\r
+  // fill command packet\r
+  //\r
+  ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));\r
+  Packet.TestUnitReady.opcode = ATA_CMD_TEST_UNIT_READY;\r
+\r
+  //\r
+  // send command packet\r
+  //\r
+  Status = AtapiPacketCommandIn (AtapiBlkIoDev, DevicePosition, &Packet, NULL, 0, ATAPITIMEOUT);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Send out ATAPI commands conforms to the Packet Command with PIO Data In Protocol.\r
+  \r
+  @param[in]  AtapiBlkIoDev         A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition        An integer to signify device position.\r
+  @param[in]  Packet                A pointer to ATAPI command packet.\r
+  @param[in]  Buffer                Buffer to contain requested transfer data from device.\r
+  @param[in]  ByteCount             Requested transfer data length.\r
+  @param[in]  TimeoutInMilliSeconds Time out value, in unit of milliseconds.\r
+\r
+  @retval EFI_SUCCESS        Command executed successfully.\r
+  @retval EFI_DEVICE_ERROR   Device cannot be executed command successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+AtapiPacketCommandIn (\r
+  IN  ATAPI_BLK_IO_DEV      *AtapiBlkIoDev,\r
+  IN  UINTN                 DevicePosition,\r
+  IN  ATAPI_PACKET_COMMAND  *Packet,\r
+  IN  UINT16                *Buffer,\r
+  IN  UINT32                ByteCount,\r
+  IN  UINTN                 TimeoutInMilliSeconds\r
+  )\r
+{\r
+  UINT8       Channel;\r
+  UINT8       Device;\r
+  UINT16      StatusReg;\r
+  UINT16      HeadReg;\r
+  UINT16      CommandReg;\r
+  UINT16      FeatureReg;\r
+  UINT16      CylinderLsbReg;\r
+  UINT16      CylinderMsbReg;\r
+  UINT16      DeviceControlReg;\r
+  UINT16      DataReg;\r
+  EFI_STATUS  Status;\r
+  UINT32      Count;\r
+  UINT16      *CommandIndex;\r
+  UINT16      *PtrBuffer;\r
+  UINT32      Index;\r
+  UINT8       StatusValue;\r
+  UINT32      WordCount;\r
+\r
+  //\r
+  // required transfer data in word unit.\r
+  //\r
+  UINT32      RequiredWordCount;\r
+\r
+  //\r
+  // actual transfer data in word unit.\r
+  //\r
+  UINT32      ActualWordCount;\r
+\r
+  Channel           = (UINT8) (DevicePosition / 2);\r
+  Device            = (UINT8) (DevicePosition % 2);\r
+\r
+  ASSERT (Channel < MAX_IDE_CHANNELS);\r
+\r
+  StatusReg         = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Status;\r
+  HeadReg           = AtapiBlkIoDev->IdeIoPortReg[Channel].Head;\r
+  CommandReg        = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Command;\r
+  FeatureReg        = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg1.Feature;\r
+  CylinderLsbReg    = AtapiBlkIoDev->IdeIoPortReg[Channel].CylinderLsb;\r
+  CylinderMsbReg    = AtapiBlkIoDev->IdeIoPortReg[Channel].CylinderMsb;\r
+  DeviceControlReg  = AtapiBlkIoDev->IdeIoPortReg[Channel].Alt.DeviceControl;\r
+  DataReg           = AtapiBlkIoDev->IdeIoPortReg[Channel].Data;\r
+\r
+  //\r
+  // Set all the command parameters by fill related registers.\r
+  // Before write to all the following registers, BSY and DRQ must be 0.\r
+  //\r
+  if (DRQClear2 (\r
+        AtapiBlkIoDev,\r
+        &(AtapiBlkIoDev->IdeIoPortReg[Channel]),\r
+        ATATIMEOUT\r
+        ) != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Select device via Device/Head Register.\r
+  // DEFAULT_CMD: 0xa0 (1010,0000)\r
+  //\r
+  IoWrite8 (HeadReg, (UINT8) ((Device << 4) | ATA_DEFAULT_CMD));\r
+\r
+  //\r
+  // No OVL; No DMA\r
+  //\r
+  IoWrite8 (FeatureReg, 0x00);\r
+\r
+  //\r
+  // set the transfersize to MAX_ATAPI_BYTE_COUNT to let the device\r
+  // determine how many data should be transfered.\r
+  //\r
+  IoWrite8 (CylinderLsbReg, (UINT8) (ATAPI_MAX_BYTE_COUNT & 0x00ff));\r
+  IoWrite8 (CylinderMsbReg, (UINT8) (ATAPI_MAX_BYTE_COUNT >> 8));\r
+\r
+  //\r
+  //  DEFAULT_CTL:0x0a (0000,1010)\r
+  //  Disable interrupt\r
+  //\r
+  IoWrite8 (DeviceControlReg, ATA_DEFAULT_CTL);\r
+\r
+  //\r
+  // Send Packet command to inform device\r
+  // that the following data bytes are command packet.\r
+  //\r
+  IoWrite8 (CommandReg, ATA_CMD_PACKET);\r
+\r
+  Status = DRQReady (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), TimeoutInMilliSeconds);\r
+  if (Status != EFI_SUCCESS) {\r
+    return Status;\r
+  }\r
+  //\r
+  // Send out command packet\r
+  //\r
+  CommandIndex = Packet->Data16;\r
+  for (Count = 0; Count < 6; Count++, CommandIndex++) {\r
+    IoWrite16 (DataReg, *CommandIndex);\r
+    MicroSecondDelay (10);\r
+  }\r
+\r
+  StatusValue = IoRead8 (StatusReg);\r
+  if ((StatusValue & ATA_STSREG_ERR) == ATA_STSREG_ERR) {\r
+    //\r
+    // Trouble! Something's wrong here... Wait some time and return. 3 second is\r
+    // supposed to be long enough for a device reset latency or error recovery\r
+    //\r
+    MicroSecondDelay (3000000);\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (Buffer == NULL || ByteCount == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // call PioReadWriteData() function to get\r
+  // requested transfer data form device.\r
+  //\r
+  PtrBuffer         = Buffer;\r
+  RequiredWordCount = ByteCount / 2;\r
+  //\r
+  // ActuralWordCount means the word count of data really transfered.\r
+  //\r
+  ActualWordCount = 0;\r
+\r
+  Status          = EFI_SUCCESS;\r
+  while ((Status == EFI_SUCCESS) && (ActualWordCount < RequiredWordCount)) {\r
+    //\r
+    // before each data transfer stream, the host should poll DRQ bit ready,\r
+    // which informs device is ready to transfer data.\r
+    //\r
+    if (DRQReady2 (\r
+          AtapiBlkIoDev,\r
+          &(AtapiBlkIoDev->IdeIoPortReg[Channel]),\r
+          TimeoutInMilliSeconds\r
+          ) != EFI_SUCCESS) {\r
+      return CheckErrorStatus (AtapiBlkIoDev, StatusReg);\r
+    }\r
+    //\r
+    // read Status Register will clear interrupt\r
+    //\r
+    StatusValue = IoRead8 (StatusReg);\r
+\r
+    //\r
+    // get current data transfer size from Cylinder Registers.\r
+    //\r
+    WordCount = IoRead8 (CylinderMsbReg) << 8;\r
+    WordCount = WordCount | IoRead8 (CylinderLsbReg);\r
+    WordCount = WordCount & 0xffff;\r
+    WordCount /= 2;\r
+\r
+    //\r
+    // perform a series data In/Out.\r
+    //\r
+    for (Index = 0; (Index < WordCount) && (ActualWordCount < RequiredWordCount); Index++, ActualWordCount++) {\r
+\r
+      *PtrBuffer = IoRead16 (DataReg);\r
+\r
+      PtrBuffer++;\r
+\r
+    }\r
+\r
+    if (((ATAPI_REQUEST_SENSE_CMD *) Packet)->opcode == ATA_CMD_REQUEST_SENSE && ActualWordCount >= 4) {\r
+      RequiredWordCount = MIN (\r
+                            RequiredWordCount,\r
+                            (UINT32) (4 + (((ATAPI_REQUEST_SENSE_DATA *) Buffer)->addnl_sense_length / 2))\r
+                            );\r
+    }\r
+\r
+  }\r
+  //\r
+  // After data transfer is completed, normally, DRQ bit should clear.\r
+  //\r
+  Status = DRQClear2 (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), TimeoutInMilliSeconds);\r
+  if (Status != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // read status register to check whether error happens.\r
+  //\r
+  Status = CheckErrorStatus (AtapiBlkIoDev, StatusReg);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Sends out ATAPI Inquiry Packet Command to the specified device.\r
+  This command will return INQUIRY data of the device.\r
+\r
+  @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition  An integer to signify device position.\r
+  @param[out] MediaInfo       The media information of the specified block media.\r
+\r
+  @retval EFI_SUCCESS        Command executed successfully.\r
+  @retval EFI_DEVICE_ERROR   Device cannot be executed command successfully.\r
+  @retval EFI_UNSUPPORTED    Unsupported device type.\r
+\r
+**/\r
+EFI_STATUS\r
+Inquiry (\r
+  IN  ATAPI_BLK_IO_DEV              *AtapiBlkIoDev,\r
+  IN  UINTN                         DevicePosition,\r
+  OUT EFI_PEI_BLOCK_IO_MEDIA        *MediaInfo\r
+  )\r
+{\r
+  ATAPI_PACKET_COMMAND  Packet;\r
+  EFI_STATUS            Status;\r
+  ATAPI_INQUIRY_DATA          Idata;\r
+\r
+  //\r
+  // prepare command packet for the ATAPI Inquiry Packet Command.\r
+  //\r
+  ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));\r
+\r
+  Packet.Inquiry.opcode             = ATA_CMD_INQUIRY;\r
+  Packet.Inquiry.page_code          = 0;\r
+  Packet.Inquiry.allocation_length  = (UINT8) sizeof (ATAPI_INQUIRY_DATA);\r
+\r
+  //\r
+  // Send command packet and get requested Inquiry data.\r
+  //\r
+  Status = AtapiPacketCommandIn (\r
+            AtapiBlkIoDev,\r
+            DevicePosition,\r
+            &Packet,\r
+            (UINT16 *) (&Idata),\r
+            sizeof (ATAPI_INQUIRY_DATA),\r
+            ATAPITIMEOUT\r
+            //50\r
+            );\r
+\r
+  if (Status != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Identify device type via INQUIRY data.\r
+  //\r
+  switch (Idata.peripheral_type & 0x1f) {\r
+  case 0x00:\r
+    //\r
+    // Magnetic Disk\r
+    //\r
+    MediaInfo->DeviceType   = IdeLS120;\r
+    MediaInfo->MediaPresent = FALSE;\r
+    MediaInfo->LastBlock    = 0;\r
+    MediaInfo->BlockSize    = 0x200;\r
+    break;\r
+\r
+  case 0x05:\r
+    //\r
+    // CD-ROM\r
+    //\r
+    MediaInfo->DeviceType   = IdeCDROM;\r
+    MediaInfo->MediaPresent = FALSE;\r
+    MediaInfo->LastBlock    = 0;\r
+    MediaInfo->BlockSize    = 0x800;\r
+    break;\r
+\r
+  default:\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**  \r
+  Used before read/write blocks from/to ATAPI device media. \r
+  Since ATAPI device media is removable, it is necessary to detect\r
+  whether media is present and get current present media's information.\r
+\r
+  @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition    An integer to signify device position.\r
+  @param[in, out] MediaInfo         The media information of the specified block media.\r
+\r
+  @retval EFI_SUCCESS           Command executed successfully.\r
+  @retval EFI_DEVICE_ERROR      Some device errors happen.\r
+  @retval EFI_OUT_OF_RESOURCES  Can not allocate required resources.\r
+\r
+**/\r
+EFI_STATUS\r
+DetectMedia (\r
+  IN  ATAPI_BLK_IO_DEV              *AtapiBlkIoDev,\r
+  IN  UINTN                         DevicePosition,\r
+  IN OUT EFI_PEI_BLOCK_IO_MEDIA     *MediaInfo\r
+  )\r
+{\r
+\r
+  UINTN                   Index;\r
+  UINTN                   RetryNum;\r
+  UINTN                   MaxRetryNum;\r
+  ATAPI_REQUEST_SENSE_DATA      *SenseBuffers;\r
+  BOOLEAN                 NeedReadCapacity;\r
+  BOOLEAN                 NeedRetry;\r
+  EFI_STATUS              Status;\r
+  UINT8                   SenseCounts;\r
+\r
+  SenseBuffers = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (*SenseBuffers)));\r
+  if (SenseBuffers == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  \r
+  //\r
+  // Test Unit Ready command is used to detect whether device is accessible,\r
+  // the device will produce corresponding Sense data.\r
+  //\r
+  for (Index = 0; Index < 2; Index++) {\r
+\r
+    Status = TestUnitReady (AtapiBlkIoDev, DevicePosition);\r
+    if (Status != EFI_SUCCESS) {\r
+      Status = ResetDevice (AtapiBlkIoDev, DevicePosition, FALSE);\r
+\r
+      if (Status != EFI_SUCCESS) {\r
+        ResetDevice (AtapiBlkIoDev, DevicePosition, TRUE);\r
+      }\r
+\r
+    } else {\r
+      break;\r
+    }\r
+  }\r
+\r
+  SenseCounts       = MAX_SENSE_KEY_COUNT;\r
+  Status            = EFI_SUCCESS;\r
+  NeedReadCapacity  = TRUE;\r
+\r
+  for (Index = 0; Index < 5; Index++) {\r
+    SenseCounts = MAX_SENSE_KEY_COUNT;\r
+    Status = RequestSense (\r
+              AtapiBlkIoDev,\r
+              DevicePosition,\r
+              SenseBuffers,\r
+              &SenseCounts\r
+              );\r
+    DEBUG ((EFI_D_INFO, "Atapi Request Sense Count is %d\n", SenseCounts));\r
+    if (IsDeviceStateUnclear (SenseBuffers, SenseCounts) || IsNoMedia (SenseBuffers, SenseCounts)) {\r
+      //\r
+      // We are not sure whether the media is present or not, try again\r
+      //\r
+      TestUnitReady (AtapiBlkIoDev, DevicePosition);\r
+    } else {\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+\r
+    if (IsNoMedia (SenseBuffers, SenseCounts)) {\r
+\r
+      NeedReadCapacity        = FALSE;\r
+      MediaInfo->MediaPresent = FALSE;\r
+      MediaInfo->LastBlock    = 0;\r
+    }\r
+\r
+    if (IsMediaError (SenseBuffers, SenseCounts)) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  if (NeedReadCapacity) {\r
+    //\r
+    // at most retry 5 times\r
+    //\r
+    MaxRetryNum = 5;\r
+    RetryNum    = 1;\r
+    //\r
+    // initial retry once\r
+    //\r
+    for (Index = 0; (Index < RetryNum) && (Index < MaxRetryNum); Index++) {\r
+\r
+      Status = ReadCapacity (AtapiBlkIoDev, DevicePosition, MediaInfo);\r
+      MicroSecondDelay (200000);\r
+      SenseCounts = MAX_SENSE_KEY_COUNT;\r
+\r
+      if (Status != EFI_SUCCESS) {\r
+\r
+        Status = RequestSense (AtapiBlkIoDev, DevicePosition, SenseBuffers, &SenseCounts);\r
+        //\r
+        // If Request Sense data failed, reset the device and retry.\r
+        //\r
+        if (Status != EFI_SUCCESS) {\r
+\r
+          Status = ResetDevice (AtapiBlkIoDev, DevicePosition, FALSE);\r
+          //\r
+          // if ATAPI soft reset fail,\r
+          // use stronger reset mechanism -- ATA soft reset.\r
+          //\r
+          if (Status != EFI_SUCCESS) {\r
+            ResetDevice (AtapiBlkIoDev, DevicePosition, TRUE);\r
+          }\r
+\r
+          RetryNum++;\r
+          //\r
+          // retry once more\r
+          //\r
+          continue;\r
+        }\r
+        //\r
+        // No Media\r
+        //\r
+        if (IsNoMedia (SenseBuffers, SenseCounts)) {\r
+\r
+          MediaInfo->MediaPresent = FALSE;\r
+          MediaInfo->LastBlock    = 0;\r
+          break;\r
+        }\r
+\r
+        if (IsMediaError (SenseBuffers, SenseCounts)) {\r
+          return EFI_DEVICE_ERROR;\r
+        }\r
+\r
+        if (!IsDriveReady (SenseBuffers, SenseCounts, &NeedRetry)) {\r
+          //\r
+          // Drive not ready: if NeedRetry, then retry once more;\r
+          // else return error\r
+          //\r
+          if (NeedRetry) {\r
+            RetryNum++;\r
+            continue;\r
+          } else {\r
+            return EFI_DEVICE_ERROR;\r
+          }\r
+        }\r
+        //\r
+        // if read capacity fail not for above reasons, retry once more\r
+        //\r
+        RetryNum++;\r
+\r
+      }\r
+\r
+    }\r
+\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**  \r
+  Reset specified Atapi device.\r
+\r
+  @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition    An integer to signify device position.\r
+  @param[in]  Extensive         If TRUE, use ATA soft reset, otherwise use Atapi soft reset.\r
+\r
+  @retval EFI_SUCCESS           Command executed successfully.\r
+  @retval EFI_DEVICE_ERROR      Some device errors happen.\r
+\r
+**/\r
+EFI_STATUS\r
+ResetDevice (\r
+  IN  ATAPI_BLK_IO_DEV  *AtapiBlkIoDev,\r
+  IN  UINTN             DevicePosition,\r
+  IN  BOOLEAN           Extensive\r
+  )\r
+{\r
+  UINT8   DevControl;\r
+  UINT8   Command;\r
+  UINT8   DeviceSelect;\r
+  UINT16  DeviceControlReg;\r
+  UINT16  CommandReg;\r
+  UINT16  HeadReg;\r
+  UINT8   Channel;\r
+  UINT8   Device;\r
+\r
+  Channel           = (UINT8) (DevicePosition / 2);\r
+  Device            = (UINT8) (DevicePosition % 2);\r
+\r
+  ASSERT (Channel < MAX_IDE_CHANNELS);\r
+\r
+  DeviceControlReg  = AtapiBlkIoDev->IdeIoPortReg[Channel].Alt.DeviceControl;\r
+  CommandReg        = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Command;\r
+  HeadReg           = AtapiBlkIoDev->IdeIoPortReg[Channel].Head;\r
+\r
+  if (Extensive) {\r
+\r
+    DevControl = 0;\r
+    DevControl |= ATA_CTLREG_SRST;\r
+    //\r
+    // set SRST bit to initiate soft reset\r
+    //\r
+    DevControl |= BIT1;\r
+    //\r
+    // disable Interrupt\r
+    //\r
+    IoWrite8 (DeviceControlReg, DevControl);\r
+\r
+    //\r
+    // Wait 10us\r
+    //\r
+    MicroSecondDelay (10);\r
+\r
+    //\r
+    // Clear SRST bit\r
+    //\r
+    DevControl &= 0xfb;\r
+    //\r
+    // 0xfb:1111,1011\r
+    //\r
+    IoWrite8 (DeviceControlReg, DevControl);\r
+\r
+    //\r
+    // slave device needs at most 31s to clear BSY\r
+    //\r
+    if (WaitForBSYClear (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 31000) == EFI_TIMEOUT) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+  } else {\r
+    //\r
+    // for ATAPI device, no need to wait DRDY ready after device selecting.\r
+    // bit7 and bit5 are both set to 1 for backward compatibility\r
+    //\r
+    DeviceSelect = (UINT8) (((BIT7 | BIT5) | (Device << 4)));\r
+    IoWrite8 (HeadReg, DeviceSelect);\r
+\r
+    Command = ATA_CMD_SOFT_RESET;\r
+    IoWrite8 (CommandReg, Command);\r
+\r
+    //\r
+    // BSY cleared is the only status return to the host by the device when reset is completed\r
+    // slave device needs at most 31s to clear BSY\r
+    //\r
+    if (WaitForBSYClear (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 31000) != EFI_SUCCESS) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+    //\r
+    // stall 5 seconds to make the device status stable\r
+    //\r
+    MicroSecondDelay (STALL_1_SECONDS * 5);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+}\r
+\r
+/**  \r
+  Sends out ATAPI Request Sense Packet Command to the specified device.\r
+\r
+  @param[in]      AtapiBlkIoDev   A pointer to atapi block IO device.\r
+  @param[in]      DevicePosition  An integer to signify device position.\r
+  @param[in]      SenseBuffers    Pointer to sense buffer.\r
+  @param[in, out] SenseCounts     Length of sense buffer.\r
+\r
+  @retval EFI_SUCCESS           Command executed successfully.\r
+  @retval EFI_DEVICE_ERROR      Some device errors happen.\r
+\r
+**/\r
+EFI_STATUS\r
+RequestSense (\r
+  IN  ATAPI_BLK_IO_DEV           *AtapiBlkIoDev,\r
+  IN  UINTN                      DevicePosition,\r
+  IN  ATAPI_REQUEST_SENSE_DATA   *SenseBuffers,\r
+  IN  OUT  UINT8                 *SenseCounts\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  ATAPI_REQUEST_SENSE_DATA    *Sense;\r
+  UINT16                *Ptr;\r
+  BOOLEAN               SenseReq;\r
+  ATAPI_PACKET_COMMAND  Packet;\r
+\r
+  ZeroMem (SenseBuffers, sizeof (ATAPI_REQUEST_SENSE_DATA) * (*SenseCounts));\r
+  //\r
+  // fill command packet for Request Sense Packet Command\r
+  //\r
+  ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));\r
+  Packet.RequestSence.opcode            = ATA_CMD_REQUEST_SENSE;\r
+  Packet.RequestSence.allocation_length = (UINT8) sizeof (ATAPI_REQUEST_SENSE_DATA);\r
+\r
+  Ptr = (UINT16 *) SenseBuffers;\r
+  //\r
+  // initialize pointer\r
+  //\r
+  *SenseCounts = 0;\r
+  //\r
+  //  request sense data from device continiously until no sense data exists in the device.\r
+  //\r
+  for (SenseReq = TRUE; SenseReq;) {\r
+\r
+    Sense = (ATAPI_REQUEST_SENSE_DATA *) Ptr;\r
+\r
+    //\r
+    // send out Request Sense Packet Command and get one Sense data form device\r
+    //\r
+    Status = AtapiPacketCommandIn (\r
+              AtapiBlkIoDev,\r
+              DevicePosition,\r
+              &Packet,\r
+              Ptr,\r
+              sizeof (ATAPI_REQUEST_SENSE_DATA),\r
+              ATAPITIMEOUT\r
+              );\r
+    //\r
+    // failed to get Sense data\r
+    //\r
+    if (Status != EFI_SUCCESS) {\r
+      if (*SenseCounts == 0) {\r
+        return EFI_DEVICE_ERROR;\r
+      } else {\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+\r
+    (*SenseCounts)++;\r
+\r
+    if (*SenseCounts > MAX_SENSE_KEY_COUNT) {\r
+      return EFI_SUCCESS;\r
+    }\r
+    //\r
+    // We limit MAX sense data count to 20 in order to avoid dead loop. Some\r
+    // incompatible ATAPI devices don't retrive NO_SENSE when there is no media.\r
+    // In this case, dead loop occurs if we don't have a gatekeeper. 20 is\r
+    // supposed to be large enough for any ATAPI device.\r
+    //\r
+    if ((Sense->sense_key != ATA_SK_NO_SENSE) && ((*SenseCounts) < 20)) {\r
+\r
+      Ptr += sizeof (ATAPI_REQUEST_SENSE_DATA) / 2;\r
+      //\r
+      // Ptr is word based pointer\r
+      //\r
+    } else {\r
+      //\r
+      // when no sense key, skip out the loop\r
+      //\r
+      SenseReq = FALSE;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**  \r
+  Sends out ATAPI Read Capacity Packet Command to the specified device.\r
+  This command will return the information regarding the capacity of the\r
+  media in the device.\r
+\r
+  @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition  An integer to signify device position.\r
+  @param[in, out] MediaInfo       The media information of the specified block media.\r
+\r
+  @retval EFI_SUCCESS           Command executed successfully.\r
+  @retval EFI_DEVICE_ERROR      Some device errors happen.\r
+\r
+**/\r
+EFI_STATUS\r
+ReadCapacity (\r
+  IN  ATAPI_BLK_IO_DEV              *AtapiBlkIoDev,\r
+  IN  UINTN                         DevicePosition,\r
+  IN OUT EFI_PEI_BLOCK_IO_MEDIA     *MediaInfo\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  ATAPI_PACKET_COMMAND      Packet;\r
+\r
+  //\r
+  // used for capacity data returned from ATAPI device\r
+  //\r
+  ATAPI_READ_CAPACITY_DATA        Data;\r
+  ATAPI_READ_FORMAT_CAPACITY_DATA FormatData;\r
+\r
+  ZeroMem (&Data, sizeof (Data));\r
+  ZeroMem (&FormatData, sizeof (FormatData));\r
+\r
+  if (MediaInfo->DeviceType == IdeCDROM) {\r
+\r
+    ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));\r
+    Packet.Inquiry.opcode = ATA_CMD_READ_CAPACITY;\r
+    Status = AtapiPacketCommandIn (\r
+              AtapiBlkIoDev,\r
+              DevicePosition,\r
+              &Packet,\r
+              (UINT16 *) (&Data),\r
+              sizeof (ATAPI_READ_CAPACITY_DATA),\r
+              ATAPITIMEOUT\r
+              );\r
+\r
+  } else {\r
+    //\r
+    // DeviceType == IdeLS120\r
+    //\r
+    ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));\r
+    Packet.ReadFormatCapacity.opcode                = ATA_CMD_READ_FORMAT_CAPACITY;\r
+    Packet.ReadFormatCapacity.allocation_length_lo  = 12;\r
+    Status = AtapiPacketCommandIn (\r
+              AtapiBlkIoDev,\r
+              DevicePosition,\r
+              &Packet,\r
+              (UINT16 *) (&FormatData),\r
+              sizeof (ATAPI_READ_FORMAT_CAPACITY_DATA),\r
+              ATAPITIMEOUT*10\r
+              );\r
+  }\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+\r
+    if (MediaInfo->DeviceType == IdeCDROM) {\r
+\r
+      MediaInfo->LastBlock    = (Data.LastLba3 << 24) | (Data.LastLba2 << 16) | (Data.LastLba1 << 8) | Data.LastLba0;\r
+      MediaInfo->MediaPresent = TRUE;\r
+      //\r
+      // Because the user data portion in the sector of the Data CD supported\r
+      // is always 800h\r
+      //\r
+      MediaInfo->BlockSize = 0x800;\r
+    }\r
+\r
+    if (MediaInfo->DeviceType == IdeLS120) {\r
+\r
+      if (FormatData.DesCode == 3) {\r
+        MediaInfo->MediaPresent = FALSE;\r
+        MediaInfo->LastBlock    = 0;\r
+      } else {\r
+        MediaInfo->LastBlock = (FormatData.LastLba3 << 24) |\r
+          (FormatData.LastLba2 << 16) |\r
+          (FormatData.LastLba1 << 8) |\r
+          FormatData.LastLba0;\r
+        MediaInfo->LastBlock--;\r
+\r
+        MediaInfo->MediaPresent = TRUE;\r
+\r
+        MediaInfo->BlockSize    = 0x200;\r
+\r
+      }\r
+    }\r
+\r
+    return EFI_SUCCESS;\r
+\r
+  } else {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+}\r
+\r
+/**  \r
+  Perform read from disk in block unit.\r
+\r
+  @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition  An integer to signify device position.\r
+  @param[in]  Buffer          Buffer to contain read data.\r
+  @param[in]  StartLba        Starting LBA address.\r
+  @param[in]  NumberOfBlocks  Number of blocks to read.\r
+  @param[in]  BlockSize       Size of each block.\r
+\r
+  @retval EFI_SUCCESS           Command executed successfully.\r
+  @retval EFI_DEVICE_ERROR      Some device errors happen.\r
+\r
+**/\r
+EFI_STATUS\r
+ReadSectors (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  UINTN               DevicePosition,\r
+  IN  VOID                *Buffer,\r
+  IN  EFI_PEI_LBA         StartLba,\r
+  IN  UINTN               NumberOfBlocks,\r
+  IN  UINTN               BlockSize\r
+  )\r
+{\r
+\r
+  ATAPI_PACKET_COMMAND  Packet;\r
+  ATAPI_READ10_CMD      *Read10Packet;\r
+  EFI_STATUS            Status;\r
+  UINTN                 BlocksRemaining;\r
+  UINT32                Lba32;\r
+  UINT32                ByteCount;\r
+  UINT16                SectorCount;\r
+  VOID                  *PtrBuffer;\r
+  UINT16                MaxBlock;\r
+\r
+  //\r
+  // fill command packet for Read(10) command\r
+  //\r
+  ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));\r
+  Read10Packet  = &Packet.Read10;\r
+  Lba32         = (UINT32) StartLba;\r
+  PtrBuffer     = Buffer;\r
+\r
+  //\r
+  // limit the data bytes that can be transfered by one Read(10) Command\r
+  //\r
+  MaxBlock = (UINT16) (0x10000 / BlockSize);\r
+  //\r
+  // (64k bytes)\r
+  //\r
+  BlocksRemaining = NumberOfBlocks;\r
+\r
+  Status          = EFI_SUCCESS;\r
+  while (BlocksRemaining > 0) {\r
+\r
+    if (BlocksRemaining <= MaxBlock) {\r
+      SectorCount = (UINT16) BlocksRemaining;\r
+    } else {\r
+      SectorCount = MaxBlock;\r
+    }\r
+    //\r
+    // fill the Packet data sturcture\r
+    //\r
+    Read10Packet->opcode = ATA_CMD_READ_10;\r
+\r
+    //\r
+    // Lba0 ~ Lba3 specify the start logical block address of the data transfer.\r
+    // Lba0 is MSB, Lba3 is LSB\r
+    //\r
+    Read10Packet->Lba3  = (UINT8) (Lba32 & 0xff);\r
+    Read10Packet->Lba2  = (UINT8) (Lba32 >> 8);\r
+    Read10Packet->Lba1  = (UINT8) (Lba32 >> 16);\r
+    Read10Packet->Lba0  = (UINT8) (Lba32 >> 24);\r
+\r
+    //\r
+    // TranLen0 ~ TranLen1 specify the transfer length in block unit.\r
+    // TranLen0 is MSB, TranLen is LSB\r
+    //\r
+    Read10Packet->TranLen1  = (UINT8) (SectorCount & 0xff);\r
+    Read10Packet->TranLen0  = (UINT8) (SectorCount >> 8);\r
+\r
+    ByteCount               = (UINT32) (SectorCount * BlockSize);\r
+\r
+    Status = AtapiPacketCommandIn (\r
+              AtapiBlkIoDev,\r
+              DevicePosition,\r
+              &Packet,\r
+              (UINT16 *) PtrBuffer,\r
+              ByteCount,\r
+              ATAPILONGTIMEOUT\r
+              );\r
+    if (Status != EFI_SUCCESS) {\r
+      return Status;\r
+    }\r
+\r
+    Lba32 += SectorCount;\r
+    PtrBuffer = (UINT8 *) PtrBuffer + SectorCount * BlockSize;\r
+    BlocksRemaining -= SectorCount;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**  \r
+  Check if there is media according to sense data.\r
+\r
+  @param[in]  SenseData   Pointer to sense data.\r
+  @param[in]  SenseCounts Count of sense data.\r
+\r
+  @retval TRUE    No media\r
+  @retval FALSE   Media exists\r
+\r
+**/\r
+BOOLEAN\r
+IsNoMedia (\r
+  IN  ATAPI_REQUEST_SENSE_DATA  *SenseData,\r
+  IN  UINTN                     SenseCounts\r
+  )\r
+{\r
+  ATAPI_REQUEST_SENSE_DATA  *SensePtr;\r
+  UINTN               Index;\r
+  BOOLEAN             IsNoMedia;\r
+\r
+  IsNoMedia = FALSE;\r
+\r
+  SensePtr  = SenseData;\r
+\r
+  for (Index = 0; Index < SenseCounts; Index++) {\r
+\r
+    if ((SensePtr->sense_key == ATA_SK_NOT_READY) && (SensePtr->addnl_sense_code == ATA_ASC_NO_MEDIA)) {\r
+      IsNoMedia = TRUE;\r
+    }\r
+\r
+    SensePtr++;\r
+  }\r
+\r
+  return IsNoMedia;\r
+}\r
+\r
+/**  \r
+  Check if device state is unclear according to sense data.\r
+\r
+  @param[in]  SenseData   Pointer to sense data.\r
+  @param[in]  SenseCounts Count of sense data.\r
+\r
+  @retval TRUE    Device state is unclear\r
+  @retval FALSE   Device state is clear  \r
+\r
+**/\r
+BOOLEAN\r
+IsDeviceStateUnclear (\r
+  IN  ATAPI_REQUEST_SENSE_DATA    *SenseData,\r
+  IN  UINTN                       SenseCounts\r
+  )\r
+{\r
+  ATAPI_REQUEST_SENSE_DATA  *SensePtr;\r
+  UINTN               Index;\r
+  BOOLEAN             Unclear;\r
+\r
+  Unclear  = FALSE;\r
+\r
+  SensePtr  = SenseData;\r
+\r
+  for (Index = 0; Index < SenseCounts; Index++) {\r
+\r
+    if (SensePtr->sense_key == 0x06) {\r
+      //\r
+      // Sense key is 0x06 means the device is just be reset or media just\r
+      // changed. The current state of the device is unclear.\r
+      //\r
+      Unclear = TRUE;\r
+      break;\r
+    }\r
+\r
+    SensePtr++;\r
+  }\r
+\r
+  return Unclear;\r
+}\r
+\r
+/**  \r
+  Check if there is media error according to sense data.\r
+\r
+  @param[in]  SenseData   Pointer to sense data.\r
+  @param[in]  SenseCounts Count of sense data.\r
+\r
+  @retval TRUE    Media error\r
+  @retval FALSE   No media error\r
+\r
+**/\r
+BOOLEAN\r
+IsMediaError (\r
+  IN  ATAPI_REQUEST_SENSE_DATA  *SenseData,\r
+  IN  UINTN                     SenseCounts\r
+  )\r
+{\r
+  ATAPI_REQUEST_SENSE_DATA  *SensePtr;\r
+  UINTN               Index;\r
+  BOOLEAN             IsError;\r
+\r
+  IsError   = FALSE;\r
+\r
+  SensePtr  = SenseData;\r
+\r
+  for (Index = 0; Index < SenseCounts; Index++) {\r
+\r
+    switch (SensePtr->sense_key) {\r
+\r
+    case ATA_SK_MEDIUM_ERROR:\r
+      switch (SensePtr->addnl_sense_code) {\r
+      case ATA_ASC_MEDIA_ERR1:\r
+        //\r
+        // fall through\r
+        //\r
+      case ATA_ASC_MEDIA_ERR2:\r
+        //\r
+        // fall through\r
+        //\r
+      case ATA_ASC_MEDIA_ERR3:\r
+        //\r
+        // fall through\r
+        //\r
+      case ATA_ASC_MEDIA_ERR4:\r
+        IsError = TRUE;\r
+        break;\r
+\r
+      default:\r
+        break;\r
+      }\r
+\r
+      break;\r
+\r
+    case ATA_SK_NOT_READY:\r
+      switch (SensePtr->addnl_sense_code) {\r
+      case ATA_ASC_MEDIA_UPSIDE_DOWN:\r
+        IsError = TRUE;\r
+        break;\r
+\r
+      default:\r
+        break;\r
+      }\r
+      break;\r
+\r
+    default:\r
+      break;\r
+    }\r
+\r
+    SensePtr++;\r
+  }\r
+\r
+  return IsError;\r
+}\r
+\r
+/**  \r
+  Check if drive is ready according to sense data.\r
+\r
+  @param[in]  SenseData   Pointer to sense data.\r
+  @param[in]  SenseCounts Count of sense data.\r
+  @param[out] NeedRetry   Indicate if retry is needed.\r
+\r
+  @retval TRUE    Drive ready\r
+  @retval FALSE   Drive not ready\r
+\r
+**/\r
+BOOLEAN\r
+IsDriveReady (\r
+  IN  ATAPI_REQUEST_SENSE_DATA    *SenseData,\r
+  IN  UINTN                       SenseCounts,\r
+  OUT BOOLEAN                     *NeedRetry\r
+  )\r
+{\r
+  ATAPI_REQUEST_SENSE_DATA  *SensePtr;\r
+  UINTN               Index;\r
+  BOOLEAN             IsReady;\r
+\r
+  IsReady     = TRUE;\r
+  *NeedRetry  = FALSE;\r
+\r
+  SensePtr    = SenseData;\r
+\r
+  for (Index = 0; Index < SenseCounts; Index++) {\r
+\r
+    switch (SensePtr->sense_key) {\r
+\r
+    case ATA_SK_NOT_READY:\r
+      switch (SensePtr->addnl_sense_code) {\r
+      case ATA_ASC_NOT_READY:\r
+        switch (SensePtr->addnl_sense_code_qualifier) {\r
+        case ATA_ASCQ_IN_PROGRESS:\r
+          IsReady     = FALSE;\r
+          *NeedRetry  = TRUE;\r
+          break;\r
+\r
+        default:\r
+          IsReady     = FALSE;\r
+          *NeedRetry  = FALSE;\r
+          break;\r
+        }\r
+        break;\r
+\r
+      default:\r
+        break;\r
+      }\r
+      break;\r
+\r
+    default:\r
+      break;\r
+    }\r
+\r
+    SensePtr++;\r
+  }\r
+\r
+  return IsReady;\r
+}\r
diff --git a/MdeModulePkg/Bus/Pci/IdeBusPei/AtapiPeim.h b/MdeModulePkg/Bus/Pci/IdeBusPei/AtapiPeim.h
new file mode 100644 (file)
index 0000000..5cbd6fd
--- /dev/null
@@ -0,0 +1,667 @@
+/** @file\r
+Private Include file for IdeBus PEIM.\r
+\r
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions\r
+of the BSD License which accompanies this distribution.  The\r
+full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _RECOVERY_ATAPI_H_\r
+#define _RECOVERY_ATAPI_H_\r
+\r
+#include <PiPei.h>\r
+\r
+#include <Ppi/BlockIo.h>\r
+#include <Ppi/AtaController.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/TimerLib.h>\r
+#include <Library/PeimEntryPoint.h>\r
+#include <Library/PeiServicesLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/PeiServicesTablePointerLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PcdLib.h>\r
+\r
+\r
+#include <IndustryStandard/Atapi.h>\r
+\r
+#define MAX_SENSE_KEY_COUNT 6\r
+#define MAX_IDE_CHANNELS    4  // Ide and Sata Primary, Secondary Channel.\r
+#define MAX_IDE_DEVICES     8  // Ide, Sata Primary, Secondary and Master, Slave device.\r
+\r
+typedef enum {\r
+  IdePrimary    = 0,\r
+  IdeSecondary  = 1,\r
+  IdeMaxChannel = 2\r
+} EFI_IDE_CHANNEL;\r
+\r
+typedef enum {\r
+  IdeMaster     = 0,\r
+  IdeSlave      = 1,\r
+  IdeMaxDevice  = 2\r
+} EFI_IDE_DEVICE;\r
+\r
+//\r
+// IDE Registers\r
+//\r
+typedef union {\r
+  UINT16  Command;        /* when write */\r
+  UINT16  Status;         /* when read */\r
+} IDE_CMD_OR_STATUS;\r
+\r
+typedef union {\r
+  UINT16  Error;          /* when read */\r
+  UINT16  Feature;        /* when write */\r
+} IDE_ERROR_OR_FEATURE;\r
+\r
+typedef union {\r
+  UINT16  AltStatus;      /* when read */\r
+  UINT16  DeviceControl;  /* when write */\r
+} IDE_ALTSTATUS_OR_DEVICECONTROL;\r
+\r
+//\r
+// IDE registers set\r
+//\r
+typedef struct {\r
+  UINT16                          Data;\r
+  IDE_ERROR_OR_FEATURE            Reg1;\r
+  UINT16                          SectorCount;\r
+  UINT16                          SectorNumber;\r
+  UINT16                          CylinderLsb;\r
+  UINT16                          CylinderMsb;\r
+  UINT16                          Head;\r
+  IDE_CMD_OR_STATUS               Reg;\r
+\r
+  IDE_ALTSTATUS_OR_DEVICECONTROL  Alt;\r
+  UINT16                          DriveAddress;\r
+} IDE_BASE_REGISTERS;\r
+\r
+typedef struct {\r
+\r
+  UINTN                   DevicePosition;\r
+  EFI_PEI_BLOCK_IO_MEDIA  MediaInfo;\r
+\r
+} PEI_ATAPI_DEVICE_INFO;\r
+\r
+#define ATAPI_BLK_IO_DEV_SIGNATURE  SIGNATURE_32 ('a', 'b', 'i', 'o')\r
+typedef struct {\r
+  UINTN                           Signature;\r
+\r
+  EFI_PEI_RECOVERY_BLOCK_IO_PPI   AtapiBlkIo;\r
+  EFI_PEI_PPI_DESCRIPTOR          PpiDescriptor;\r
+  PEI_ATA_CONTROLLER_PPI          *AtaControllerPpi;\r
+\r
+  UINTN                           DeviceCount;\r
+  PEI_ATAPI_DEVICE_INFO           DeviceInfo[MAX_IDE_DEVICES];   //for max 8 device\r
+  IDE_BASE_REGISTERS              IdeIoPortReg[MAX_IDE_CHANNELS]; //for max 4 channel.\r
+} ATAPI_BLK_IO_DEV;\r
+\r
+#define PEI_RECOVERY_ATAPI_FROM_BLKIO_THIS(a) CR (a, ATAPI_BLK_IO_DEV, AtapiBlkIo, ATAPI_BLK_IO_DEV_SIGNATURE)\r
+\r
+\r
+#define STALL_1_MILLI_SECOND  1000  // stall 1 ms\r
+#define STALL_1_SECONDS       1000 * STALL_1_MILLI_SECOND\r
+\r
+//\r
+// Time Out Value For IDE Device Polling\r
+//\r
+// ATATIMEOUT is used for waiting time out for ATA device\r
+//\r
+#define ATATIMEOUT  1000  // 1 second\r
+// ATAPITIMEOUT is used for waiting operation\r
+// except read and write time out for ATAPI device\r
+//\r
+#define ATAPITIMEOUT  1000  // 1 second\r
+// ATAPILONGTIMEOUT is used for waiting read and\r
+// write operation timeout for ATAPI device\r
+//\r
+#define CDROMLONGTIMEOUT  2000  // 2 seconds\r
+#define ATAPILONGTIMEOUT  5000  // 5 seconds\r
+\r
+//\r
+// PEI Recovery Block I/O PPI\r
+//\r
+\r
+/**\r
+  Gets the count of block I/O devices that one specific block driver detects.\r
+\r
+  This function is used for getting the count of block I/O devices that one \r
+  specific block driver detects.  To the PEI ATAPI driver, it returns the number\r
+  of all the detected ATAPI devices it detects during the enumeration process. \r
+  To the PEI legacy floppy driver, it returns the number of all the legacy \r
+  devices it finds during its enumeration process. If no device is detected, \r
+  then the function will return zero.  \r
+  \r
+  @param[in]  PeiServices          General-purpose services that are available \r
+                                   to every PEIM.\r
+  @param[in]  This                 Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI \r
+                                   instance.\r
+  @param[out] NumberBlockDevices   The number of block I/O devices discovered.\r
+\r
+  @retval     EFI_SUCCESS          Operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiGetNumberOfBlockDevices (\r
+  IN   EFI_PEI_SERVICES                  **PeiServices,\r
+  IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI     *This,\r
+  OUT  UINTN                             *NumberBlockDevices\r
+  );\r
+\r
+/**\r
+  Gets a block device's media information.\r
+\r
+  This function will provide the caller with the specified block device's media \r
+  information. If the media changes, calling this function will update the media \r
+  information accordingly.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to every\r
+                            PEIM\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants \r
+                            to talk. Because the driver that implements Block I/O \r
+                            PPIs will manage multiple block devices, the PPIs that \r
+                            want to talk to a single device must specify the \r
+                            device index that was assigned during the enumeration\r
+                            process. This index is a number from one to \r
+                            NumberBlockDevices.\r
+  @param[out] MediaInfo     The media information of the specified block media.  \r
+                            The caller is responsible for the ownership of this \r
+                            data structure.\r
+  \r
+  @retval EFI_SUCCESS           Media information about the specified block device \r
+                                was obtained successfully.\r
+  @retval EFI_DEVICE_ERROR      Cannot get the media information due to a hardware \r
+                                error.\r
+  @retval Others                Other failure occurs.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiGetBlockDeviceMediaInfo (\r
+  IN   EFI_PEI_SERVICES                     **PeiServices,\r
+  IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI        *This,\r
+  IN   UINTN                                DeviceIndex,\r
+  OUT  EFI_PEI_BLOCK_IO_MEDIA               *MediaInfo\r
+  );\r
+\r
+/**\r
+  Reads the requested number of blocks from the specified block device.\r
+\r
+  The function reads the requested number of blocks from the device. All the \r
+  blocks are read, or an error is returned. If there is no media in the device,\r
+  the function returns EFI_NO_MEDIA.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to \r
+                            every PEIM.\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants \r
+                            to talk. Because the driver that implements Block I/O \r
+                            PPIs will manage multiple block devices, the PPIs that \r
+                            want to talk to a single device must specify the device \r
+                            index that was assigned during the enumeration process. \r
+                            This index is a number from one to NumberBlockDevices.\r
+  @param[in]  StartLBA      The starting logical block address (LBA) to read from\r
+                            on the device\r
+  @param[in]  BufferSize    The size of the Buffer in bytes. This number must be\r
+                            a multiple of the intrinsic block size of the device.\r
+  @param[out] Buffer        A pointer to the destination buffer for the data.\r
+                            The caller is responsible for the ownership of the \r
+                            buffer.\r
+                         \r
+  @retval EFI_SUCCESS             The data was read correctly from the device.\r
+  @retval EFI_DEVICE_ERROR        The device reported an error while attempting \r
+                                  to perform the read operation.\r
+  @retval EFI_INVALID_PARAMETER   The read request contains LBAs that are not \r
+                                  valid, or the buffer is not properly aligned.\r
+  @retval EFI_NO_MEDIA            There is no media in the device.\r
+  @retval EFI_BAD_BUFFER_SIZE     The BufferSize parameter is not a multiple of\r
+                                  the intrinsic block size of the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiReadBlocks (\r
+  IN   EFI_PEI_SERVICES                  **PeiServices,\r
+  IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI     *This,\r
+  IN   UINTN                             DeviceIndex,\r
+  IN   EFI_PEI_LBA                       StartLBA,\r
+  IN   UINTN                             BufferSize,\r
+  OUT  VOID                              *Buffer\r
+  );\r
+\r
+//\r
+// Internal functions\r
+//\r
+\r
+/**\r
+  Enumerate Atapi devices.\r
+\r
+  This function is used to enumerate Atatpi device in Ide channel.\r
+\r
+  @param[in]  AtapiBlkIoDev  A pointer to atapi block IO device\r
+\r
+**/\r
+VOID\r
+AtapiEnumerateDevices (\r
+  IN  ATAPI_BLK_IO_DEV  *AtapiBlkIoDev\r
+  );\r
+\r
+/**\r
+  Detect Atapi devices.\r
+  \r
+  @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition  An integer to signify device position.\r
+  @param[out] MediaInfo       The media information of the specified block media.\r
+\r
+  @retval TRUE                Atapi device exists in specified position.\r
+  @retval FALSE               Atapi device does not exist in specified position.\r
+\r
+**/\r
+BOOLEAN\r
+DiscoverAtapiDevice (\r
+  IN  ATAPI_BLK_IO_DEV              *AtapiBlkIoDev,\r
+  IN  UINTN                         DevicePosition,\r
+  OUT EFI_PEI_BLOCK_IO_MEDIA        *MediaInfo\r
+  );\r
+\r
+/**\r
+  Detect if an IDE controller exists in specified position.\r
+  \r
+  @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition  An integer to signify device position.\r
+\r
+  @retval TRUE         The Atapi device exists.\r
+  @retval FALSE        The Atapi device does not present.\r
+\r
+**/\r
+BOOLEAN\r
+DetectIDEController (\r
+  IN  ATAPI_BLK_IO_DEV   *AtapiBlkIoDev,\r
+  IN  UINTN              DevicePosition\r
+  );\r
+\r
+/**\r
+  Wait specified time interval to poll for BSY bit clear in the Status Register.\r
+  \r
+  @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.\r
+  @param[in]  IdeIoRegisters         A pointer to IDE IO registers.\r
+  @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.\r
+\r
+  @retval EFI_SUCCESS        BSY bit is cleared in the specified time interval.\r
+  @retval EFI_TIMEOUT        BSY bit is not cleared in the specified time interval.\r
+\r
+**/\r
+EFI_STATUS\r
+WaitForBSYClear (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  IDE_BASE_REGISTERS  *IdeIoRegisters,\r
+  IN  UINTN               TimeoutInMilliSeconds\r
+  );\r
+\r
+/**\r
+  Wait specified time interval to poll for DRDY bit set in the Status register.\r
+  \r
+  @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.\r
+  @param[in]  IdeIoRegisters         A pointer to IDE IO registers.\r
+  @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.\r
+\r
+  @retval EFI_SUCCESS        DRDY bit is set in the specified time interval.\r
+  @retval EFI_TIMEOUT        DRDY bit is not set in the specified time interval.\r
+\r
+**/\r
+EFI_STATUS\r
+DRDYReady (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  IDE_BASE_REGISTERS  *IdeIoRegisters,\r
+  IN  UINTN               TimeoutInMilliSeconds\r
+  );\r
+\r
+/**\r
+  Wait specified time interval to poll for DRQ bit clear in the Status Register.\r
+  \r
+  @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.\r
+  @param[in]  IdeIoRegisters         A pointer to IDE IO registers.\r
+  @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.\r
+\r
+  @retval EFI_SUCCESS        DRQ bit is cleared in the specified time interval.\r
+  @retval EFI_TIMEOUT        DRQ bit is not cleared in the specified time interval.\r
+\r
+**/\r
+EFI_STATUS\r
+DRQClear (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  IDE_BASE_REGISTERS  *IdeIoRegisters,\r
+  IN  UINTN               TimeoutInMilliSeconds\r
+  );\r
+\r
+/**\r
+  Wait specified time interval to poll for DRQ bit clear in the Alternate Status Register.\r
+  \r
+  @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.\r
+  @param[in]  IdeIoRegisters         A pointer to IDE IO registers.\r
+  @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.\r
+\r
+  @retval EFI_SUCCESS        DRQ bit is cleared in the specified time interval.\r
+  @retval EFI_TIMEOUT        DRQ bit is not cleared in the specified time interval.\r
+\r
+**/\r
+EFI_STATUS\r
+DRQClear2 (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  IDE_BASE_REGISTERS  *IdeIoRegisters,\r
+  IN  UINTN               TimeoutInMilliSeconds\r
+  );\r
+\r
+/**\r
+  Wait specified time interval to poll for DRQ bit set in the Status Register.\r
+\r
+  @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.\r
+  @param[in]  IdeIoRegisters         A pointer to IDE IO registers.\r
+  @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.\r
+\r
+  @retval EFI_SUCCESS        DRQ bit is set in the specified time interval.\r
+  @retval EFI_TIMEOUT        DRQ bit is not set in the specified time interval.\r
+  @retval EFI_ABORTED        Operation Aborted.\r
+\r
+**/\r
+EFI_STATUS\r
+DRQReady (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  IDE_BASE_REGISTERS  *IdeIoRegisters,\r
+  IN  UINTN               TimeoutInMilliSeconds\r
+  );\r
+\r
+/**\r
+  Wait specified time interval to poll for DRQ bit set in the Alternate Status Register.\r
+\r
+  @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.\r
+  @param[in]  IdeIoRegisters         A pointer to IDE IO registers.\r
+  @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.\r
+\r
+  @retval EFI_SUCCESS        DRQ bit is set in the specified time interval.\r
+  @retval EFI_TIMEOUT        DRQ bit is not set in the specified time interval.\r
+  @retval EFI_ABORTED        Operation Aborted.\r
+\r
+**/\r
+EFI_STATUS\r
+DRQReady2 (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  IDE_BASE_REGISTERS  *IdeIoRegisters,\r
+  IN  UINTN               TimeoutInMilliSeconds\r
+  );\r
+\r
+/**\r
+  Check if there is an error in Status Register.\r
+\r
+  @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.\r
+  @param[in]  StatusReg         The address to IDE IO registers.\r
+\r
+  @retval EFI_SUCCESS        Operation success.\r
+  @retval EFI_DEVICE_ERROR   Device error.\r
+\r
+**/\r
+EFI_STATUS\r
+CheckErrorStatus (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  UINT16              StatusReg\r
+  );\r
+\r
+/**\r
+  Idendify Atapi devices.\r
+\r
+  @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition    An integer to signify device position.\r
+\r
+  @retval EFI_SUCCESS        Identify successfully.\r
+  @retval EFI_DEVICE_ERROR   Device cannot be identified successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+ATAPIIdentify (\r
+  IN  ATAPI_BLK_IO_DEV        *AtapiBlkIoDev,\r
+  IN  UINTN                   DevicePosition\r
+  );\r
+\r
+/**\r
+  Sends out ATAPI Test Unit Ready Packet Command to the specified device\r
+  to find out whether device is accessible.\r
+\r
+  @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition    An integer to signify device position.\r
+\r
+  @retval EFI_SUCCESS        TestUnit command executed successfully.\r
+  @retval EFI_DEVICE_ERROR   Device cannot be executed TestUnit command successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+TestUnitReady (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  UINTN               DevicePosition\r
+  ) ;\r
+\r
+/**\r
+  Send out ATAPI commands conforms to the Packet Command with PIO Data In Protocol.\r
+  \r
+  @param[in]  AtapiBlkIoDev         A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition        An integer to signify device position.\r
+  @param[in]  Packet                A pointer to ATAPI command packet.\r
+  @param[in]  Buffer                Buffer to contain requested transfer data from device.\r
+  @param[in]  ByteCount             Requested transfer data length.\r
+  @param[in]  TimeoutInMilliSeconds Time out value, in unit of milliseconds.\r
+\r
+  @retval EFI_SUCCESS        Command executed successfully.\r
+  @retval EFI_DEVICE_ERROR   Device cannot be executed command successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+AtapiPacketCommandIn (\r
+  IN  ATAPI_BLK_IO_DEV      *AtapiBlkIoDev,\r
+  IN  UINTN                 DevicePosition,\r
+  IN  ATAPI_PACKET_COMMAND  *Packet,\r
+  IN  UINT16                *Buffer,\r
+  IN  UINT32                ByteCount,\r
+  IN  UINTN                 TimeoutInMilliSeconds\r
+  );\r
+\r
+/**\r
+  Sends out ATAPI Inquiry Packet Command to the specified device.\r
+  This command will return INQUIRY data of the device.\r
+\r
+  @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition  An integer to signify device position.\r
+  @param[out] MediaInfo       The media information of the specified block media.\r
+\r
+  @retval EFI_SUCCESS        Command executed successfully.\r
+  @retval EFI_DEVICE_ERROR   Device cannot be executed command successfully.\r
+  @retval EFI_UNSUPPORTED    Unsupported device type.\r
+\r
+**/\r
+EFI_STATUS\r
+Inquiry (\r
+  IN  ATAPI_BLK_IO_DEV              *AtapiBlkIoDev,\r
+  IN  UINTN                         DevicePosition,\r
+  OUT EFI_PEI_BLOCK_IO_MEDIA        *MediaInfo\r
+  );\r
+\r
+/**  \r
+  Used before read/write blocks from/to ATAPI device media. \r
+  Since ATAPI device media is removable, it is necessary to detect\r
+  whether media is present and get current present media's information.  \r
+\r
+  @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition    An integer to signify device position.\r
+  @param[in, out] MediaInfo         The media information of the specified block media.\r
+\r
+  @retval EFI_SUCCESS           Command executed successfully.\r
+  @retval EFI_DEVICE_ERROR      Some device errors happen.\r
+  @retval EFI_OUT_OF_RESOURCES  Can not allocate required resources.\r
+\r
+**/\r
+EFI_STATUS\r
+DetectMedia (\r
+  IN  ATAPI_BLK_IO_DEV              *AtapiBlkIoDev,\r
+  IN  UINTN                         DevicePosition,\r
+  IN OUT EFI_PEI_BLOCK_IO_MEDIA     *MediaInfo\r
+  );\r
+\r
+/**  \r
+  Reset specified Atapi device.\r
+\r
+  @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition    An integer to signify device position.\r
+  @param[in]  Extensive         If TRUE, use ATA soft reset, otherwise use Atapi soft reset.\r
+\r
+  @retval EFI_SUCCESS           Command executed successfully.\r
+  @retval EFI_DEVICE_ERROR      Some device errors happen.\r
+\r
+**/\r
+EFI_STATUS\r
+ResetDevice (\r
+  IN  ATAPI_BLK_IO_DEV  *AtapiBlkIoDev,\r
+  IN  UINTN             DevicePosition,\r
+  IN  BOOLEAN           Extensive\r
+  );\r
+\r
+/**  \r
+  Sends out ATAPI Request Sense Packet Command to the specified device.\r
+\r
+  @param[in]      AtapiBlkIoDev   A pointer to atapi block IO device.\r
+  @param[in]      DevicePosition  An integer to signify device position.\r
+  @param[in]      SenseBuffers    Pointer to sense buffer.\r
+  @param[in, out] SenseCounts     Length of sense buffer.\r
+\r
+  @retval EFI_SUCCESS           Command executed successfully.\r
+  @retval EFI_DEVICE_ERROR      Some device errors happen.\r
+\r
+**/\r
+EFI_STATUS\r
+RequestSense (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  UINTN               DevicePosition,\r
+  IN  ATAPI_REQUEST_SENSE_DATA  *SenseBuffers,\r
+  IN OUT  UINT8           *SenseCounts\r
+  );\r
+\r
+/**  \r
+  Sends out ATAPI Read Capacity Packet Command to the specified device.\r
+  This command will return the information regarding the capacity of the\r
+  media in the device.\r
+\r
+  @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition  An integer to signify device position.\r
+  @param[in, out] MediaInfo       The media information of the specified block media.\r
+\r
+  @retval EFI_SUCCESS           Command executed successfully.\r
+  @retval EFI_DEVICE_ERROR      Some device errors happen.\r
+\r
+**/\r
+EFI_STATUS\r
+ReadCapacity (\r
+  IN  ATAPI_BLK_IO_DEV              *AtapiBlkIoDev,\r
+  IN  UINTN                         DevicePosition,\r
+  IN OUT EFI_PEI_BLOCK_IO_MEDIA     *MediaInfo\r
+  );\r
+\r
+/**  \r
+  Perform read from disk in block unit.\r
+\r
+  @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.\r
+  @param[in]  DevicePosition  An integer to signify device position.\r
+  @param[in]  Buffer          Buffer to contain read data.\r
+  @param[in]  StartLba        Starting LBA address.\r
+  @param[in]  NumberOfBlocks  Number of blocks to read.\r
+  @param[in]  BlockSize       Size of each block.\r
+\r
+  @retval EFI_SUCCESS           Command executed successfully.\r
+  @retval EFI_DEVICE_ERROR      Some device errors happen.\r
+\r
+**/\r
+EFI_STATUS\r
+ReadSectors (\r
+  IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,\r
+  IN  UINTN               DevicePosition,\r
+  IN  VOID                *Buffer,\r
+  IN  EFI_PEI_LBA         StartLba,\r
+  IN  UINTN               NumberOfBlocks,\r
+  IN  UINTN               BlockSize\r
+  );\r
+\r
+/**  \r
+  Check if there is media according to sense data.\r
+\r
+  @param[in]  SenseData   Pointer to sense data.\r
+  @param[in]  SenseCounts Count of sense data.\r
+\r
+  @retval TRUE    No media\r
+  @retval FALSE   Media exists\r
+\r
+**/\r
+BOOLEAN\r
+IsNoMedia (\r
+  IN  ATAPI_REQUEST_SENSE_DATA    *SenseData,\r
+  IN  UINTN                 SenseCounts\r
+  );\r
+\r
+/**  \r
+  Check if device state is unclear according to sense data.\r
+\r
+  @param[in]  SenseData   Pointer to sense data.\r
+  @param[in]  SenseCounts Count of sense data.\r
+\r
+  @retval TRUE    Device state is unclear\r
+  @retval FALSE   Device state is clear  \r
+\r
+**/\r
+BOOLEAN\r
+IsDeviceStateUnclear (\r
+  IN  ATAPI_REQUEST_SENSE_DATA    *SenseData,\r
+  IN  UINTN                 SenseCounts\r
+  );\r
+\r
+/**  \r
+  Check if there is media error according to sense data.\r
+\r
+  @param[in]  SenseData   Pointer to sense data.\r
+  @param[in]  SenseCounts Count of sense data.\r
+\r
+  @retval TRUE    Media error\r
+  @retval FALSE   No media error\r
+\r
+**/\r
+BOOLEAN\r
+IsMediaError (\r
+  IN  ATAPI_REQUEST_SENSE_DATA    *SenseData,\r
+  IN  UINTN                 SenseCounts\r
+  );\r
+\r
+/**  \r
+  Check if drive is ready according to sense data.\r
+\r
+  @param[in]  SenseData   Pointer to sense data.\r
+  @param[in]  SenseCounts Count of sense data.\r
+  @param[out] NeedRetry   Indicate if retry is needed.\r
+\r
+  @retval TRUE    Drive ready\r
+  @retval FALSE   Drive not ready\r
+\r
+**/\r
+BOOLEAN\r
+IsDriveReady (\r
+  IN  ATAPI_REQUEST_SENSE_DATA    *SenseData,\r
+  IN  UINTN                 SenseCounts,\r
+  OUT BOOLEAN               *NeedRetry\r
+  );\r
+\r
+#endif\r
diff --git a/MdeModulePkg/Bus/Pci/IdeBusPei/IdeBusPei.inf b/MdeModulePkg/Bus/Pci/IdeBusPei/IdeBusPei.inf
new file mode 100644 (file)
index 0000000..f28124c
--- /dev/null
@@ -0,0 +1,66 @@
+## @file\r
+# PEIM to produce gEfiPeiVirtualBlockIoPpiGuid PPI for ATA controllers in the platform.\r
+# This PPI canl be consumed by PEIM which produce gEfiPeiDeviceRecoveryModulePpiGuid\r
+# for Atapi CD ROM device.\r
+#\r
+# This module discovers CDROM devices in Legacy and native mode and installs block IO ppis for them.\r
+# Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions\r
+# of the BSD License which accompanies this distribution.  The\r
+# full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = IdeBusPei\r
+  FILE_GUID                      = B7A5041A-78BA-49e3-B73B-54C757811FB6\r
+  MODULE_TYPE                    = PEIM\r
+  VERSION_STRING                 = 1.0\r
+\r
+  ENTRY_POINT                    = AtapiPeimEntry\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources]\r
+  AtapiPeim.h\r
+  AtapiPeim.c\r
+\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+  IoLib\r
+  BaseMemoryLib\r
+  PeiServicesLib\r
+  PeimEntryPoint\r
+  DebugLib\r
+  TimerLib\r
+  PeiServicesTablePointerLib\r
+  MemoryAllocationLib\r
+\r
+[Ppis]\r
+  gPeiAtaControllerPpiGuid                      # PPI ALWAYS_CONSUMED\r
+  gEfiPeiVirtualBlockIoPpiGuid                  # PPI ALWAYS_PRODUCED\r
+  \r
+[Pcd]\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSataSpinUpDelayInSecForRecoveryPath\r
+\r
+[Depex]\r
+  gEfiPeiMemoryDiscoveredPpiGuid AND gEfiPeiBootInRecoveryModePpiGuid AND gPeiAtaControllerPpiGuid\r
+\r
+\r
+\r
diff --git a/MdeModulePkg/Include/Ppi/AtaController.h b/MdeModulePkg/Include/Ppi/AtaController.h
new file mode 100644 (file)
index 0000000..aa4ae56
--- /dev/null
@@ -0,0 +1,162 @@
+/** @file\r
+  Define the PPI to abstract the functions that enable IDE and SATA channels, and to retrieve\r
+  the base I/O port address for each of the enabled IDE and SATA channels.\r
+  \r
+Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>\r
+\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions\r
+of the BSD License which accompanies this distribution.  The\r
+full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _PEI_ATA_CONTROLLER_PPI_H_\r
+#define _PEI_ATA_CONTROLLER_PPI_H_\r
+\r
+///\r
+/// Global ID for the PEI_ATA_CONTROLLER_PPI. \r
+///\r
+#define PEI_ATA_CONTROLLER_PPI_GUID \\r
+  { \\r
+    0xa45e60d1, 0xc719, 0x44aa, {0xb0, 0x7a, 0xaa, 0x77, 0x7f, 0x85, 0x90, 0x6d } \\r
+  }\r
+\r
+///\r
+/// Forward declaration for the PEI_ATA_CONTROLLER_PPI.\r
+///\r
+typedef struct _PEI_ATA_CONTROLLER_PPI PEI_ATA_CONTROLLER_PPI;\r
+\r
+///\r
+/// This bit is used in the ChannelMask parameter of EnableAtaChannel() to \r
+/// disable the IDE channels. \r
+/// This is designed for old generation chipset with PATA/SATA controllers. \r
+/// It may be ignored in PPI implementation for new generation chipset without PATA controller. \r
+///\r
+#define PEI_ICH_IDE_NONE        0x00\r
+\r
+///\r
+/// This bit is used in the ChannelMask parameter of EnableAtaChannel() to \r
+/// enable the Primary IDE channel.\r
+/// This is designed for old generation chipset with PATA/SATA controllers. \r
+/// It may be ignored in PPI implementation for new generation chipset without PATA controller. \r
+///\r
+#define PEI_ICH_IDE_PRIMARY     0x01\r
+\r
+///\r
+/// This bit is used in the ChannelMask parameter of EnableAtaChannel() to \r
+/// enable the Secondary IDE channel.\r
+/// This is designed for old generation chipset with PATA/SATA controllers. \r
+/// It may be ignored in PPI implementation for new generation chipset without PATA controller. \r
+///\r
+#define PEI_ICH_IDE_SECONDARY   0x02\r
+\r
+///\r
+/// This bit is used in the ChannelMask parameter of EnableAtaChannel() to \r
+/// disable the SATA channel.\r
+/// This is designed for old generation chipset with PATA/SATA controllers. \r
+/// It may be ignored in PPI implementation for new generation chipset without PATA controller. \r
+///\r
+#define PEI_ICH_SATA_NONE       0x04\r
+\r
+///\r
+/// This bit is used in the ChannelMask parameter of EnableAtaChannel() to \r
+/// enable the Primary SATA channel.\r
+/// This is designed for old generation chipset with PATA/SATA controllers. \r
+/// It may be ignored in PPI implementation for new generation chipset without PATA controller. \r
+///\r
+#define PEI_ICH_SATA_PRIMARY    0x08\r
+\r
+///\r
+/// This bit is used in the ChannelMask parameter of EnableAtaChannel() to \r
+/// enable the Secondary SATA channel.\r
+/// This is designed for old generation chipset with PATA/SATA controllers. \r
+/// It may be ignored in PPI implementation for new generation chipset without PATA controller. \r
+///\r
+#define PEI_ICH_SATA_SECONDARY  0x010\r
+\r
+///\r
+/// Structure that contains the base addresses for the IDE registers\r
+///\r
+typedef struct {\r
+  ///\r
+  /// Base I/O port address of the IDE controller's command block\r
+  ///\r
+  UINT16  CommandBlockBaseAddr;\r
+  ///\r
+  /// Base I/O port address of the IDE controller's control block\r
+  ///\r
+  UINT16  ControlBlockBaseAddr;\r
+} IDE_REGS_BASE_ADDR;\r
+\r
+/**\r
+  Sets IDE and SATA channels to an enabled or disabled state.\r
+\r
+  This service enables or disables the IDE and SATA channels specified by ChannelMask.\r
+  It may ignore ChannelMask setting to enable or disable IDE and SATA channels based on the platform policy. \r
+  The number of the enabled channels will be returned by GET_IDE_REGS_BASE_ADDR() function. \r
+\r
+  If the new state is set, then EFI_SUCCESS is returned.  If the new state can\r
+  not be set, then EFI_DEVICE_ERROR is returned.\r
+\r
+  @param[in] PeiServices   The pointer to the PEI Services Table.\r
+  @param[in] This          The pointer to this instance of the PEI_ATA_CONTROLLER_PPI.\r
+  @param[in] ChannelMask   The bitmask that identifies the IDE and SATA channels to \r
+                           enable or disable. This paramter is optional. \r
+\r
+  @retval EFI_SUCCESS        The IDE or SATA channels were enabled or disabled successfully.\r
+  @retval EFI_DEVICE_ERROR   The IDE or SATA channels could not be enabled or disabled.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *PEI_ENABLE_ATA)(\r
+  IN EFI_PEI_SERVICES        **PeiServices,\r
+  IN PEI_ATA_CONTROLLER_PPI  *This,\r
+  IN UINT8                   ChannelMask\r
+  );\r
+\r
+/**\r
+  Retrieves the I/O port base addresses for command and control registers of the \r
+  enabled IDE/SATA channels.\r
+\r
+  This service fills in the structure poionted to by IdeRegsBaseAddr with the I/O\r
+  port base addresses for the command and control registers of the IDE and SATA\r
+  channels that were previously enabled in EnableAtaChannel().  The number of \r
+  enabled IDE and SATA channels is returned.\r
+\r
+  @param[in]  PeiServices       The pointer to the PEI Services Table.\r
+  @param[in]  This              The pointer to this instance of the PEI_ATA_CONTROLLER_PPI.\r
+  @param[out] IdeRegsBaseAddr   The pointer to caller allocated space to return the \r
+                                I/O port base addresses of the IDE and SATA channels \r
+                                that were previosuly enabled with EnableAtaChannel().\r
+\r
+  @return   The number of enabled IDE and SATA channels in the platform.\r
+\r
+**/\r
+typedef\r
+UINT32\r
+(EFIAPI *GET_IDE_REGS_BASE_ADDR)(\r
+  IN  EFI_PEI_SERVICES        **PeiServices,\r
+  IN  PEI_ATA_CONTROLLER_PPI  *This,\r
+  OUT IDE_REGS_BASE_ADDR      *IdeRegsBaseAddr \r
+  );\r
+\r
+///\r
+/// This PPI contains services to enable and disable IDE and SATA channels and\r
+/// retrieves the base I/O port addresses to the enabled IDE and SATA channels.\r
+///\r
+struct _PEI_ATA_CONTROLLER_PPI {\r
+  PEI_ENABLE_ATA          EnableAtaChannel;\r
+  GET_IDE_REGS_BASE_ADDR  GetIdeRegsBaseAddr;\r
+};\r
+\r
+extern EFI_GUID gPeiAtaControllerPpiGuid;\r
+\r
+#endif\r
+\r
+\r
index 0987b7955d2db997dee646d80b735d129f44d043..086688ca59adb69dcfda99c9595a056184dbfc09 100644 (file)
   gIdleLoopEventGuid  = { 0x3c8d294c, 0x5fc3, 0x4451, { 0xbb, 0x31, 0xc4, 0xc0, 0x32, 0x29, 0x5e, 0x6c } }\r
 \r
 [Ppis]\r
+  ## Include/Ppi/AtaController.h\r
+  gPeiAtaControllerPpiGuid       = { 0xa45e60d1, 0xc719, 0x44aa, { 0xb0, 0x7a, 0xaa, 0x77, 0x7f, 0x85, 0x90, 0x6d }}\r
+\r
   ## Include/Ppi/UsbHostController.h\r
   gPeiUsbHostControllerPpiGuid   = { 0x652B38A9, 0x77F4, 0x453F, { 0x89, 0xD5, 0xE7, 0xBD, 0xC3, 0x52, 0xFC, 0x53 }}\r
 \r
   #  0x70 (EFI_BACKGROUND_LIGHTGRAY)\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdBrowserFieldBackgroundHighlightColor|0x00|UINT8|0x0001005A\r
 \r
+  ## Time in second to delay for SATA devices to spin-up for recovery.\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSataSpinUpDelayInSecForRecoveryPath|15|UINT16|0x0001005B\r
+\r
 [PcdsPatchableInModule]\r
   ## Specify  memory size with page number for PEI code when \r
   #  the feature of Loading Module at Fixed Address is enabled\r
index a81e4d8f7aa1a5855b5ca257fba77ed96f3b3574..a273c2726349dbff7d71022788c6d33e959e1e6d 100644 (file)
   MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf\r
   MdeModulePkg/Bus/Pci/UhciPei/UhciPei.inf\r
   MdeModulePkg/Bus/Pci/EhciPei/EhciPei.inf\r
+  MdeModulePkg/Bus/Pci/IdeBusPei/IdeBusPei.inf\r
   MdeModulePkg/Bus/Usb/UsbBusPei/UsbBusPei.inf\r
   MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPei.inf\r
   MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf\r