]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Pci/AtapiPassThruDxe/AtapiPassThru.c
Move Pci/AtapiPassThru/Dxe to Pci/AtapiPassThruDxe.
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / AtapiPassThruDxe / AtapiPassThru.c
diff --git a/MdeModulePkg/Bus/Pci/AtapiPassThruDxe/AtapiPassThru.c b/MdeModulePkg/Bus/Pci/AtapiPassThruDxe/AtapiPassThru.c
new file mode 100644 (file)
index 0000000..f14d9e3
--- /dev/null
@@ -0,0 +1,2177 @@
+/** @file\r
+  Copyright (c) 2006, Intel Corporation                                                         \r
+  All rights reserved. This program and the accompanying materials                          \r
+  are licensed and made available under the terms and conditions of the BSD License         \r
+  which accompanies this distribution.  The 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 "AtapiPassThru.h"\r
+\r
+///\r
+/// IDE registers' fixed address\r
+///\r
+static IDE_BASE_REGISTERS   gAtapiIoPortRegisters[2] = {\r
+  { 0x1f0, { 0x1f1 }, 0x1f2, 0x1f3, 0x1f4, 0x1f5, 0x1f6, { 0x1f7 }, { 0x3f6 }, 0x3f7, 0 },\r
+  { 0x170, { 0x171 }, 0x172, 0x173, 0x174, 0x175, 0x176, { 0x177 }, { 0x376 }, 0x377, 0 } \r
+};\r
+\r
+static SCSI_COMMAND_SET     gEndTable = { 0xff, (DATA_DIRECTION) 0xff };\r
+\r
+///\r
+/// This table contains all the supported ATAPI commands.\r
+///\r
+static SCSI_COMMAND_SET     gSupportedATAPICommands[] = {\r
+  { OP_INQUIRY,                     DataIn  },\r
+  { OP_LOAD_UNLOAD_CD,              NoData  },\r
+  { OP_MECHANISM_STATUS,            DataIn  },\r
+  { OP_MODE_SELECT_10,              DataOut },\r
+  { OP_MODE_SENSE_10,               DataIn  },\r
+  { OP_PAUSE_RESUME,                NoData  },\r
+  { OP_PLAY_AUDIO_10,               DataIn  },\r
+  { OP_PLAY_AUDIO_MSF,              DataIn  },\r
+  { OP_PLAY_CD,                     DataIn  },\r
+  { OP_PLAY_CD_MSF,                 DataIn  },\r
+  { OP_PREVENT_ALLOW_MEDIUM_REMOVAL,NoData  },\r
+  { OP_READ_10,                     DataIn  },\r
+  { OP_READ_12,                     DataIn  },\r
+  { OP_READ_CAPACITY,               DataIn  },\r
+  { OP_READ_CD,                     DataIn  },\r
+  { OP_READ_CD_MSF,                 DataIn  },\r
+  { OP_READ_HEADER,                 DataIn  },\r
+  { OP_READ_SUB_CHANNEL,            DataIn  },\r
+  { OP_READ_TOC,                    DataIn  },\r
+  { OP_REQUEST_SENSE,               DataIn  },\r
+  { OP_SCAN,                        NoData  },\r
+  { OP_SEEK_10,                     NoData  },\r
+  { OP_SET_CD_SPEED,                DataOut },\r
+  { OP_STOPPLAY_SCAN,               NoData  },\r
+  { OP_START_STOP_UNIT,             NoData  },\r
+  { OP_TEST_UNIT_READY,             NoData  },\r
+  { OP_FORMAT_UNIT,                 DataOut },\r
+  { OP_READ_FORMAT_CAPACITIES,      DataIn  },\r
+  { OP_VERIFY,                      DataOut },\r
+  { OP_WRITE_10,                    DataOut },\r
+  { OP_WRITE_12,                    DataOut },\r
+  { OP_WRITE_AND_VERIFY,            DataOut },\r
+  { 0xff,                           (DATA_DIRECTION) 0xff    } \r
+};\r
+\r
+static CHAR16               *gControllerNameString  = (CHAR16 *) L"ATAPI Controller";\r
+static CHAR16               *gAtapiChannelString    = (CHAR16 *) L"ATAPI Channel";\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gAtapiScsiPassThruDriverBinding = {\r
+  AtapiScsiPassThruDriverBindingSupported,\r
+  AtapiScsiPassThruDriverBindingStart,\r
+  AtapiScsiPassThruDriverBindingStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+/**\r
+  Supported.\r
+\r
+  (Standard DriverBinding Protocol Supported() function)\r
+\r
+  @return EFI_STATUS\r
+\r
+  @todo    This - add argument and description to function comment\r
+  @todo    Controller - add argument and description to function comment\r
+  @todo    RemainingDevicePath - add argument and description to function comment\r
+  @todo    EFI_UNSUPPORTED - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiScsiPassThruDriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  EFI_PCI_IO_PROTOCOL *PciIo;\r
+  PCI_TYPE00          Pci;\r
+\r
+  //\r
+  // Open the IO Abstraction(s) needed to perform the supported test\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiPciIoProtocolGuid,\r
+                  (VOID **) &PciIo,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  //\r
+  // Use the PCI I/O Protocol to see if Controller is a IDE Controller that\r
+  // can be managed by this driver.  Read the PCI Configuration Header\r
+  // for this device.\r
+  //\r
+  Status = PciIo->Pci.Read (\r
+                        PciIo,\r
+                        EfiPciIoWidthUint32,\r
+                        0,\r
+                        sizeof (Pci) / sizeof (UINT32),\r
+                        &Pci\r
+                        );\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->CloseProtocol (\r
+          Controller,\r
+          &gEfiPciIoProtocolGuid,\r
+          This->DriverBindingHandle,\r
+          Controller\r
+          );\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  if (Pci.Hdr.ClassCode[2] != PCI_CLASS_MASS_STORAGE || Pci.Hdr.ClassCode[1] != PCI_CLASS_IDE) {\r
+\r
+    Status = EFI_UNSUPPORTED;\r
+  }\r
+\r
+  gBS->CloseProtocol (\r
+        Controller,\r
+        &gEfiPciIoProtocolGuid,\r
+        This->DriverBindingHandle,\r
+        Controller\r
+        );\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Create handles for IDE channels specified by RemainingDevicePath.\r
+  Install SCSI Pass Thru Protocol onto each created handle.\r
+\r
+  (Standard DriverBinding Protocol Start() function)\r
+\r
+  @return EFI_STATUS\r
+\r
+  @todo    This - add argument and description to function comment\r
+  @todo    Controller - add argument and description to function comment\r
+  @todo    RemainingDevicePath - add argument and description to function comment\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiScsiPassThruDriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  EFI_PCI_IO_PROTOCOL *PciIo;\r
+\r
+  PciIo = NULL;\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiPciIoProtocolGuid,\r
+                  (VOID **) &PciIo,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = PciIo->Attributes (\r
+                    PciIo,\r
+                    EfiPciIoAttributeOperationEnable,\r
+                    EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO | EFI_PCI_DEVICE_ENABLE,\r
+                    NULL\r
+                    );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  //\r
+  // Create SCSI Pass Thru instance for the IDE channel.\r
+  //\r
+  Status = RegisterAtapiScsiPassThru (This, Controller, PciIo);\r
+\r
+Done:\r
+  if (EFI_ERROR (Status)) {\r
+    if (PciIo) {\r
+      PciIo->Attributes (\r
+              PciIo,\r
+              EfiPciIoAttributeOperationDisable,\r
+              EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO | EFI_PCI_DEVICE_ENABLE,\r
+              NULL\r
+              );\r
+    }\r
+\r
+    gBS->CloseProtocol (\r
+          Controller,\r
+          &gEfiPciIoProtocolGuid,\r
+          This->DriverBindingHandle,\r
+          Controller\r
+          );\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Stop.\r
+\r
+  (Standard DriverBinding Protocol Stop() function)\r
+\r
+  @return EFI_STATUS\r
+\r
+  @todo    This - add argument and description to function comment\r
+  @todo    Controller - add argument and description to function comment\r
+  @todo    NumberOfChildren - add argument and description to function comment\r
+  @todo    ChildHandleBuffer - add argument and description to function comment\r
+  @todo    EFI_SUCCESS - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiScsiPassThruDriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL     *This,\r
+  IN  EFI_HANDLE                      Controller,\r
+  IN  UINTN                           NumberOfChildren,\r
+  IN  EFI_HANDLE                      *ChildHandleBuffer\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  EFI_SCSI_PASS_THRU_PROTOCOL *ScsiPassThru;\r
+  ATAPI_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiScsiPassThruProtocolGuid,\r
+                  (VOID **) &ScsiPassThru,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (ScsiPassThru);\r
+\r
+  Status = gBS->UninstallProtocolInterface (\r
+                  Controller,\r
+                  &gEfiScsiPassThruProtocolGuid,\r
+                  &AtapiScsiPrivate->ScsiPassThru\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  //\r
+  // Release Pci Io protocol on the controller handle.\r
+  //\r
+  AtapiScsiPrivate->PciIo->Attributes (\r
+                            AtapiScsiPrivate->PciIo,\r
+                            EfiPciIoAttributeOperationDisable,\r
+                            EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO | EFI_PCI_DEVICE_ENABLE,\r
+                            NULL\r
+                            );\r
+\r
+  gBS->CloseProtocol (\r
+        Controller,\r
+        &gEfiPciIoProtocolGuid,\r
+        This->DriverBindingHandle,\r
+        Controller\r
+        );\r
+\r
+  gBS->FreePool (AtapiScsiPrivate);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Attaches SCSI Pass Thru Protocol for specified IDE channel.\r
+\r
+  @param Controller:       Parent device handle to the IDE channel.\r
+  @param PciIo:            PCI I/O protocol attached on the "Controller".\r
+\r
+  @return EFI_SUCCESS Always returned unless installing SCSI Pass Thru Protocol failed.\r
+\r
+  @todo    This - add argument and description to function comment\r
+  @todo    Controller - add argument and description to function comment\r
+  @todo    PciIo - add argument and description to function comment\r
+  @todo    EFI_OUT_OF_RESOURCES - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+RegisterAtapiScsiPassThru (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                  Controller,\r
+  IN  EFI_PCI_IO_PROTOCOL         *PciIo\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  ATAPI_SCSI_PASS_THRU_DEV  *AtapiScsiPrivate;\r
+  UINT64                    Attributes;\r
+\r
+  AtapiScsiPrivate = AllocateZeroPool (sizeof (ATAPI_SCSI_PASS_THRU_DEV));\r
+  if (AtapiScsiPrivate == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Attributes = EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO | EFI_PCI_DEVICE_ENABLE;\r
+  CopyMem (AtapiScsiPrivate->ChannelName, gAtapiChannelString, sizeof (gAtapiChannelString));\r
+\r
+  //\r
+  // Enable channel\r
+  //\r
+  PciIo->Attributes (PciIo, EfiPciIoAttributeOperationSet, Attributes, NULL);\r
+\r
+  AtapiScsiPrivate->Signature = ATAPI_SCSI_PASS_THRU_DEV_SIGNATURE;\r
+  AtapiScsiPrivate->Handle    = Controller;\r
+\r
+  //\r
+  // will reset the IoPort inside each API function.\r
+  //\r
+  AtapiScsiPrivate->IoPort  = gAtapiIoPortRegisters;\r
+  AtapiScsiPrivate->PciIo   = PciIo;\r
+\r
+  //\r
+  // initialize SCSI Pass Thru Protocol interface\r
+  //\r
+  AtapiScsiPrivate->ScsiPassThru.Mode             = &AtapiScsiPrivate->ScsiPassThruMode;\r
+  AtapiScsiPrivate->ScsiPassThru.PassThru         = AtapiScsiPassThruFunction;\r
+  AtapiScsiPrivate->ScsiPassThru.GetNextDevice    = AtapiScsiPassThruGetNextDevice;\r
+  AtapiScsiPrivate->ScsiPassThru.BuildDevicePath  = AtapiScsiPassThruBuildDevicePath;\r
+  AtapiScsiPrivate->ScsiPassThru.GetTargetLun     = AtapiScsiPassThruGetTargetLun;\r
+  AtapiScsiPrivate->ScsiPassThru.ResetChannel     = AtapiScsiPassThruResetChannel;\r
+  AtapiScsiPrivate->ScsiPassThru.ResetTarget      = AtapiScsiPassThruResetTarget;\r
+\r
+  //\r
+  // Set Mode\r
+  //\r
+  CopyMem (AtapiScsiPrivate->ControllerName, gControllerNameString, sizeof (gControllerNameString));\r
+\r
+  AtapiScsiPrivate->ScsiPassThruMode.ControllerName = AtapiScsiPrivate->ControllerName;\r
+  AtapiScsiPrivate->ScsiPassThruMode.ChannelName    = AtapiScsiPrivate->ChannelName;\r
+  AtapiScsiPrivate->ScsiPassThruMode.AdapterId      = 4;\r
+  //\r
+  // non-RAID SCSI controllers should set both physical and logical attributes\r
+  //\r
+  AtapiScsiPrivate->ScsiPassThruMode.Attributes = EFI_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | \r
+                                                  EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;\r
+  AtapiScsiPrivate->ScsiPassThruMode.IoAlign = 0;\r
+\r
+  //\r
+  // Initialize the LatestTargetId to 0xFFFFFFFF (for the GetNextDevice() call).\r
+  //\r
+  AtapiScsiPrivate->LatestTargetId  = 0xFFFFFFFF;\r
+  AtapiScsiPrivate->LatestLun       = 0;\r
+\r
+  Status = gBS->InstallProtocolInterface (\r
+                  &Controller,\r
+                  &gEfiScsiPassThruProtocolGuid,\r
+                  EFI_NATIVE_INTERFACE,\r
+                  &AtapiScsiPrivate->ScsiPassThru\r
+                  );\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Implements EFI_SCSI_PASS_THRU_PROTOCOL.PassThru() function.\r
+\r
+  @param This     The EFI_SCSI_PASS_THRU_PROTOCOL instance.\r
+  @param Target   The Target ID of the ATAPI device to send the SCSI\r
+  Request Packet. To ATAPI devices attached on an IDE\r
+  Channel, Target ID 0 indicates Master device;Target\r
+  ID 1 indicates Slave device.\r
+  @param Lun      The LUN of the ATAPI device to send the SCSI Request\r
+  Packet. To the ATAPI device, Lun is always 0.\r
+  @param Packet   The SCSI Request Packet to send to the ATAPI device\r
+  specified by Target and Lun.\r
+  @param Event    If non-blocking I/O is not supported then Event is ignored,\r
+  and blocking I/O is performed.<br>\r
+  If Event is NULL, then blocking I/O is performed.<br>\r
+  If Event is not NULL and non blocking I/O is supported,\r
+  then non-blocking I/O is performed, and Event will be signaled\r
+  when the SCSI Request Packet completes.\r
+\r
+  @todo    This - add argument and description to function comment\r
+  @todo    EFI_INVALID_PARAMETER - add return value to function comment\r
+  @todo    EFI_SUCCESS - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiScsiPassThruFunction (\r
+  IN EFI_SCSI_PASS_THRU_PROTOCOL                        *This,\r
+  IN UINT32                                             Target,\r
+  IN UINT64                                             Lun,\r
+  IN OUT EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET         *Packet,\r
+  IN EFI_EVENT                                          Event OPTIONAL\r
+  )\r
+{\r
+  ATAPI_SCSI_PASS_THRU_DEV  *AtapiScsiPrivate;\r
+  EFI_STATUS                Status;\r
+\r
+  AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This);\r
+\r
+  //\r
+  // Target is not allowed beyond MAX_TARGET_ID\r
+  //\r
+  if (Target > MAX_TARGET_ID) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  \r
+  //\r
+  // check the data fields in Packet parameter.\r
+  //\r
+  Status = CheckSCSIRequestPacket (Packet);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // If Request Packet targets at the IDE channel itself,\r
+  // do nothing.\r
+  //\r
+  if (Target == This->Mode->AdapterId) {\r
+    Packet->TransferLength = 0;\r
+    return EFI_SUCCESS;\r
+  }\r
+  \r
+  //\r
+  // According to Target ID, reset the Atapi I/O Register mapping\r
+  // (Target Id in [0,1] area, using gAtapiIoPortRegisters[0],\r
+  //  Target Id in [2,3] area, using gAtapiIoPortRegisters[1]\r
+  //\r
+  if ((Target / 2) == 0) {\r
+    AtapiScsiPrivate->IoPort = &gAtapiIoPortRegisters[0];\r
+  } else {\r
+    AtapiScsiPrivate->IoPort = &gAtapiIoPortRegisters[1];\r
+  }\r
+  \r
+  //\r
+  // the ATAPI SCSI interface does not support non-blocking I/O\r
+  // ignore the Event parameter\r
+  //\r
+  // Performs blocking I/O.\r
+  //\r
+  Status = SubmitBlockingIoCommand (AtapiScsiPrivate, Target, Packet);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Used to retrieve the list of legal Target IDs for SCSI devices \r
+  on a SCSI channel.\r
+\r
+  @param  This Protocol instance pointer.\r
+  @param  Target On input, a pointer to the Target ID of a SCSI\r
+  device present on the SCSI channel.  On output,\r
+  a pointer to the Target ID of the next SCSI device\r
+  present on a SCSI channel.  An input value of\r
+  0xFFFFFFFF retrieves the Target ID of the first\r
+  SCSI device present on a SCSI channel.\r
+  @param  Lun On input, a pointer to the LUN of a SCSI device\r
+  present on the SCSI channel. On output, a pointer\r
+  to the LUN of the next SCSI device present on\r
+  a SCSI channel.\r
+\r
+  @retval  EFI_SUCCESS The Target ID and Lun of the next SCSI device\r
+  on the SCSI channel was returned in Target and Lun.\r
+  @retval  EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel.\r
+  @retval  EFI_INVALID_PARAMETER Target is not 0xFFFFFFFF,and Target and Lun were not\r
+  returned on a previous call to GetNextDevice().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiScsiPassThruGetNextDevice (\r
+  IN  EFI_SCSI_PASS_THRU_PROTOCOL    *This,\r
+  IN OUT UINT32                      *Target,\r
+  IN OUT UINT64                      *Lun\r
+  )\r
+{\r
+  ATAPI_SCSI_PASS_THRU_DEV  *AtapiScsiPrivate;\r
+\r
+  //\r
+  // Retrieve Device Private Data Structure.\r
+  //\r
+  AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This);\r
+\r
+  //\r
+  // Check whether Target is valid.\r
+  //\r
+  if (Target == NULL || Lun == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((*Target != 0xFFFFFFFF) &&\r
+      ((*Target != AtapiScsiPrivate->LatestTargetId) ||\r
+      (*Lun != AtapiScsiPrivate->LatestLun))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (*Target == MAX_TARGET_ID) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  if (*Target == 0xFFFFFFFF) {\r
+    *Target = 0;\r
+  } else {\r
+    *Target = AtapiScsiPrivate->LatestTargetId + 1;\r
+  }\r
+\r
+  *Lun = 0;\r
+\r
+  //\r
+  // Update the LatestTargetId.\r
+  //\r
+  AtapiScsiPrivate->LatestTargetId  = *Target;\r
+  AtapiScsiPrivate->LatestLun       = *Lun;\r
+\r
+  return EFI_SUCCESS;\r
+\r
+}\r
+\r
+/**\r
+  Used to allocate and build a device path node for a SCSI device \r
+  on a SCSI channel. Would not build device path for a SCSI Host Controller.\r
+\r
+  @param  This Protocol instance pointer.\r
+  @param  Target The Target ID of the SCSI device for which\r
+  a device path node is to be allocated and built.\r
+  @param  Lun The LUN of the SCSI device for which a device\r
+  path node is to be allocated and built.\r
+  @param  DevicePath A pointer to a single device path node that\r
+  describes the SCSI device specified by\r
+  Target and Lun. This function is responsible\r
+  for allocating the buffer DevicePath with the boot\r
+  service AllocatePool().  It is the caller's\r
+  responsibility to free DevicePath when the caller\r
+  is finished with DevicePath.\r
+\r
+  @retval  EFI_SUCCESS The device path node that describes the SCSI device\r
+  specified by Target and Lun was allocated and\r
+  returned in DevicePath.\r
+  @retval  EFI_NOT_FOUND The SCSI devices specified by Target and Lun does\r
+  not exist on the SCSI channel.\r
+  @retval  EFI_INVALID_PARAMETER DevicePath is NULL.\r
+  @retval  EFI_OUT_OF_RESOURCES There are not enough resources to allocate\r
+  DevicePath.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiScsiPassThruBuildDevicePath (\r
+  IN     EFI_SCSI_PASS_THRU_PROTOCOL    *This,\r
+  IN     UINT32                         Target,\r
+  IN     UINT64                         Lun,\r
+  IN OUT EFI_DEVICE_PATH_PROTOCOL       **DevicePath\r
+  )\r
+{\r
+  EFI_DEV_PATH              *Node;\r
+\r
+  //\r
+  // Validate parameters passed in.\r
+  //\r
+  \r
+  if (DevicePath == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  \r
+  //\r
+  // can not build device path for the SCSI Host Controller.\r
+  //\r
+  if ((Target > (MAX_TARGET_ID - 1)) || (Lun != 0)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  Node = AllocateZeroPool (sizeof (EFI_DEV_PATH));\r
+  if (Node == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Node->DevPath.Type    = MESSAGING_DEVICE_PATH;\r
+  Node->DevPath.SubType = MSG_ATAPI_DP;\r
+  SetDevicePathNodeLength (&Node->DevPath, sizeof (ATAPI_DEVICE_PATH));\r
+\r
+  Node->Atapi.PrimarySecondary  = (UINT8) (Target / 2);\r
+  Node->Atapi.SlaveMaster       = (UINT8) (Target % 2);\r
+  Node->Atapi.Lun               = (UINT16) Lun;\r
+\r
+  *DevicePath                   = (EFI_DEVICE_PATH_PROTOCOL *) Node;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Used to translate a device path node to a Target ID and LUN.\r
+\r
+  @param  This Protocol instance pointer.\r
+  @param  DevicePath A pointer to the device path node that\r
+  describes a SCSI device on the SCSI channel.\r
+  @param  Target A pointer to the Target ID of a SCSI device\r
+  on the SCSI channel.\r
+  @param  Lun A pointer to the LUN of a SCSI device on\r
+  the SCSI channel.\r
+\r
+  @retval  EFI_SUCCESS DevicePath was successfully translated to a\r
+  Target ID and LUN, and they were returned\r
+  in Target and Lun.\r
+  @retval  EFI_INVALID_PARAMETER DevicePath is NULL.\r
+  @retval  EFI_INVALID_PARAMETER Target is NULL.\r
+  @retval  EFI_INVALID_PARAMETER Lun is NULL.\r
+  @retval  EFI_UNSUPPORTED This driver does not support the device path\r
+  node type in DevicePath.\r
+  @retval  EFI_NOT_FOUND A valid translation from DevicePath to a\r
+  Target ID and LUN does not exist.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiScsiPassThruGetTargetLun (\r
+  IN  EFI_SCSI_PASS_THRU_PROTOCOL    *This,\r
+  IN  EFI_DEVICE_PATH_PROTOCOL       *DevicePath,\r
+  OUT UINT32                         *Target,\r
+  OUT UINT64                         *Lun\r
+  )\r
+{\r
+  EFI_DEV_PATH  *Node;\r
+\r
+  //\r
+  // Validate parameters passed in.\r
+  //\r
+  if (DevicePath == NULL || Target == NULL || Lun == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  \r
+  //\r
+  // Check whether the DevicePath belongs to SCSI_DEVICE_PATH\r
+  //\r
+  if ((DevicePath->Type != MESSAGING_DEVICE_PATH) ||\r
+      (DevicePath->SubType != MSG_ATAPI_DP) ||\r
+      (DevicePathNodeLength(DevicePath) != sizeof(ATAPI_DEVICE_PATH))) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Node    = (EFI_DEV_PATH *) DevicePath;\r
+\r
+  *Target = Node->Atapi.PrimarySecondary * 2 + Node->Atapi.SlaveMaster;\r
+  *Lun    = Node->Atapi.Lun;\r
+\r
+  if (*Target > (MAX_TARGET_ID - 1) || *Lun != 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Resets a SCSI channel.This operation resets all the \r
+  SCSI devices connected to the SCSI channel.\r
+\r
+  @param  This Protocol instance pointer.\r
+\r
+  @retval  EFI_SUCCESS The SCSI channel was reset.\r
+  @retval  EFI_UNSUPPORTED The SCSI channel does not support\r
+  a channel reset operation.\r
+  @retval  EFI_DEVICE_ERROR A device error occurred while\r
+  attempting to reset the SCSI channel.\r
+  @retval  EFI_TIMEOUT A timeout occurred while attempting\r
+  to reset the SCSI channel.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiScsiPassThruResetChannel (\r
+  IN  EFI_SCSI_PASS_THRU_PROTOCOL   *This\r
+  )\r
+{\r
+  UINT8                     DeviceControlValue;\r
+  ATAPI_SCSI_PASS_THRU_DEV  *AtapiScsiPrivate;\r
+  UINT8                     Index;\r
+\r
+  AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This);\r
+\r
+  //\r
+  // Reset both Primary channel and Secondary channel.\r
+  // so, the IoPort pointer must point to the right I/O Register group\r
+  //\r
+  for (Index = 0; Index < 2; Index++) {\r
+    //\r
+    // Reset\r
+    //\r
+    AtapiScsiPrivate->IoPort  = &gAtapiIoPortRegisters[Index];\r
+\r
+    DeviceControlValue        = 0;\r
+    //\r
+    // set SRST bit to initiate soft reset\r
+    //\r
+    DeviceControlValue |= SRST;\r
+    //\r
+    // disable Interrupt\r
+    //\r
+    DeviceControlValue |= bit (1);\r
+    WritePortB (\r
+      AtapiScsiPrivate->PciIo,\r
+      AtapiScsiPrivate->IoPort->Alt.DeviceControl,\r
+      DeviceControlValue\r
+      );\r
+\r
+    //\r
+    // Wait 10us\r
+    //\r
+    gBS->Stall (10);\r
+\r
+    //\r
+    // Clear SRST bit\r
+    // 0xfb:1111,1011\r
+    //\r
+    DeviceControlValue &= 0xfb;\r
+    \r
+    WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Alt.DeviceControl, DeviceControlValue);\r
+\r
+    //\r
+    // slave device needs at most 31s to clear BSY\r
+    //\r
+    if (StatusWaitForBSYClear (AtapiScsiPrivate, 31000) == EFI_TIMEOUT) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Resets a SCSI device that is connected to a SCSI channel.\r
+\r
+  @param  This Protocol instance pointer.\r
+  @param  Target The Target ID of the SCSI device to reset.\r
+  @param  Lun The LUN of the SCSI device to reset.\r
+\r
+  @retval  EFI_SUCCESS The SCSI device specified by Target and\r
+  Lun was reset.\r
+  @retval  EFI_UNSUPPORTED The SCSI channel does not support a target\r
+  reset operation.\r
+  @retval  EFI_INVALID_PARAMETER Target or Lun are invalid.\r
+  @retval  EFI_DEVICE_ERROR A device error occurred while attempting\r
+  to reset the SCSI device specified by Target\r
+  and Lun.\r
+  @retval  EFI_TIMEOUT A timeout occurred while attempting to reset\r
+  the SCSI device specified by Target and Lun.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtapiScsiPassThruResetTarget (\r
+  IN EFI_SCSI_PASS_THRU_PROTOCOL    *This,\r
+  IN UINT32                         Target,\r
+  IN UINT64                         Lun\r
+  )\r
+{\r
+  ATAPI_SCSI_PASS_THRU_DEV  *AtapiScsiPrivate;\r
+  UINT8                     Command;\r
+  UINT8                     DeviceSelect;\r
+\r
+  AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This);\r
+\r
+  if (Target > MAX_TARGET_ID) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Directly return EFI_SUCCESS if want to reset the host controller\r
+  //\r
+  if (Target == This->Mode->AdapterId) {\r
+    return EFI_SUCCESS;\r
+  }\r
+  \r
+  //\r
+  // According to Target ID, reset the Atapi I/O Register mapping\r
+  // (Target Id in [0,1] area, using gAtapiIoPortRegisters[0],\r
+  //  Target Id in [2,3] area, using gAtapiIoPortRegisters[1]\r
+  //\r
+  if ((Target / 2) == 0) {\r
+    AtapiScsiPrivate->IoPort = &gAtapiIoPortRegisters[0];\r
+  } else {\r
+    AtapiScsiPrivate->IoPort = &gAtapiIoPortRegisters[1];\r
+  }\r
+  \r
+  //\r
+  // for ATAPI device, no need to wait DRDY ready after device selecting.\r
+  //\r
+  // bit7 and bit5 are both set to 1 for backward compatibility\r
+  //\r
+  DeviceSelect = (UINT8) (((bit (7) | bit (5)) | (Target << 4)));\r
+  WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Head, DeviceSelect);\r
+\r
+  Command = ATAPI_SOFT_RESET_CMD;\r
+  WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg.Command, Command);\r
+\r
+  //\r
+  // BSY clear is the only status return to the host by the device\r
+  // when reset is complete.\r
+  // slave device needs at most 31s to clear BSY\r
+  //\r
+  if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, 31000))) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  \r
+  //\r
+  // stall 5 seconds to make the device status stable\r
+  //\r
+  gBS->Stall (5000000);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+    \r
+/**\r
+  Checks the parameters in the SCSI Request Packet to make sure\r
+  they are valid for a SCSI Pass Thru request.\r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    Packet - add argument and description to function comment\r
+  @todo    EFI_INVALID_PARAMETER - add return value to function comment\r
+  @todo    EFI_INVALID_PARAMETER - add return value to function comment\r
+  @todo    EFI_INVALID_PARAMETER - add return value to function comment\r
+  @todo    EFI_UNSUPPORTED - add return value to function comment\r
+  @todo    EFI_SUCCESS - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+CheckSCSIRequestPacket (\r
+  EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET      *Packet\r
+  )\r
+{\r
+  if (Packet == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (!ValidCdbLength (Packet->CdbLength)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Packet->Cdb == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  \r
+  //\r
+  // Checks whether the request command is supported.\r
+  //\r
+  if (!IsCommandValid (Packet)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Checks the requested SCSI command: \r
+  Is it supported by this driver?\r
+  Is the Data transfer direction reasonable?\r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    Packet - add argument and description to function comment\r
+**/\r
+BOOLEAN\r
+IsCommandValid (\r
+  EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET   *Packet\r
+  )\r
+{\r
+  UINT8 Index;\r
+  UINT8 *OpCode;\r
+\r
+  OpCode = (UINT8 *) (Packet->Cdb);\r
+\r
+  for (Index = 0; CompareMem (&gSupportedATAPICommands[Index], &gEndTable, sizeof (SCSI_COMMAND_SET)); Index++) {\r
+\r
+    if (*OpCode == gSupportedATAPICommands[Index].OpCode) {\r
+      //\r
+      // Check whether the requested Command is supported by this driver\r
+      //\r
+      if (Packet->DataDirection == DataIn) {\r
+        //\r
+        // Check whether the requested data direction conforms to\r
+        // what it should be.\r
+        //\r
+        if (gSupportedATAPICommands[Index].Direction == DataOut) {\r
+          return FALSE;\r
+        }\r
+      }\r
+\r
+      if (Packet->DataDirection == DataOut) {\r
+        //\r
+        // Check whether the requested data direction conforms to\r
+        // what it should be.\r
+        //\r
+        if (gSupportedATAPICommands[Index].Direction == DataIn) {\r
+          return FALSE;\r
+        }\r
+      }\r
+\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Performs blocking I/O request.\r
+\r
+  @param AtapiScsiPrivate   Private data structure for the specified channel.\r
+  @param Target             The Target ID of the ATAPI device to send the SCSI\r
+  Request Packet. To ATAPI devices attached on an IDE\r
+  Channel, Target ID 0 indicates Master device;Target\r
+  ID 1 indicates Slave device.\r
+  @param Packet             The SCSI Request Packet to send to the ATAPI device\r
+  specified by Target.\r
+\r
+  @todo    AtapiScsiPrivate - add argument and description to function comment\r
+**/\r
+EFI_STATUS\r
+SubmitBlockingIoCommand (\r
+  ATAPI_SCSI_PASS_THRU_DEV                  *AtapiScsiPrivate,\r
+  UINT32                                    Target,\r
+  EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET    *Packet\r
+  )\r
+{\r
+  UINT8       PacketCommand[12];\r
+  UINT64      TimeoutInMicroSeconds;\r
+  EFI_STATUS  PacketCommandStatus;\r
+\r
+  //\r
+  // Fill ATAPI Command Packet according to CDB\r
+  //\r
+  ZeroMem (&PacketCommand, 12);\r
+  CopyMem (&PacketCommand, Packet->Cdb, Packet->CdbLength);\r
+\r
+  //\r
+  // Timeout is 100ns unit, convert it to 1000ns (1us) unit.\r
+  //\r
+  TimeoutInMicroSeconds = DivU64x32 (Packet->Timeout, (UINT32) 10);\r
+\r
+  //\r
+  // Submit ATAPI Command Packet\r
+  //\r
+  PacketCommandStatus = AtapiPacketCommand (\r
+                          AtapiScsiPrivate,\r
+                          Target,\r
+                          PacketCommand,\r
+                          Packet->DataBuffer,\r
+                          &(Packet->TransferLength),\r
+                          (DATA_DIRECTION) Packet->DataDirection,\r
+                          TimeoutInMicroSeconds\r
+                          );\r
+  if (!EFI_ERROR (PacketCommandStatus) || (Packet->SenseData == NULL)) {\r
+    Packet->SenseDataLength = 0;\r
+    return PacketCommandStatus;\r
+  }\r
+  //\r
+  // Return SenseData if PacketCommandStatus matches\r
+  // the following return codes.\r
+  //\r
+  if ((PacketCommandStatus == EFI_WARN_BUFFER_TOO_SMALL) ||\r
+      (PacketCommandStatus == EFI_DEVICE_ERROR) ||\r
+      (PacketCommandStatus == EFI_TIMEOUT)) {\r
+    \r
+    //\r
+    // avoid submit request sense command continuously.\r
+    //\r
+    if (PacketCommand[0] == OP_REQUEST_SENSE) {\r
+      Packet->SenseDataLength = 0;\r
+      return PacketCommandStatus;\r
+    }\r
+\r
+    RequestSenseCommand (\r
+      AtapiScsiPrivate,\r
+      Target,\r
+      Packet->Timeout,\r
+      Packet->SenseData,\r
+      &Packet->SenseDataLength\r
+      );\r
+  }\r
+\r
+  return PacketCommandStatus;\r
+}\r
+\r
+/**\r
+  RequestSenseCommand\r
+\r
+  @param  AtapiScsiPrivate\r
+  @param  Target\r
+  @param  Timeout\r
+  @param  SenseData\r
+  @param  SenseDataLength\r
+\r
+  @todo Add function description\r
+  @todo  AtapiScsiPrivate TODO: add argument description\r
+  @todo  Target TODO: add argument description\r
+  @todo  Timeout TODO: add argument description\r
+  @todo  SenseData TODO: add argument description\r
+  @todo  SenseDataLength TODO: add argument description\r
+  @todo add return values\r
+**/\r
+EFI_STATUS\r
+RequestSenseCommand (\r
+  ATAPI_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,\r
+  UINT32                      Target,\r
+  UINT64                      Timeout,\r
+  VOID                        *SenseData,\r
+  UINT8                       *SenseDataLength\r
+  )\r
+{\r
+  EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  Packet;\r
+  UINT8                                   Cdb[12];\r
+  EFI_STATUS                              Status;\r
+\r
+  ZeroMem (&Packet, sizeof (EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET));\r
+  ZeroMem (Cdb, 12);\r
+\r
+  Cdb[0]                = OP_REQUEST_SENSE;\r
+  Cdb[4]                = (UINT8) (*SenseDataLength);\r
+\r
+  Packet.Timeout        = Timeout;\r
+  Packet.DataBuffer     = SenseData;\r
+  Packet.SenseData      = NULL;\r
+  Packet.Cdb            = Cdb;\r
+  Packet.TransferLength = *SenseDataLength;\r
+  Packet.CdbLength      = 12;\r
+  Packet.DataDirection  = DataIn;\r
+\r
+  Status                = SubmitBlockingIoCommand (AtapiScsiPrivate, Target, &Packet);\r
+  *SenseDataLength      = (UINT8) (Packet.TransferLength);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Submits ATAPI command packet to the specified ATAPI device.\r
+\r
+  @param AtapiScsiPrivate:   Private data structure for the specified channel.\r
+  @param Target:             The Target ID of the ATAPI device to send the SCSI\r
+  Request Packet. To ATAPI devices attached on an IDE\r
+  Channel, Target ID 0 indicates Master device;Target\r
+  ID 1 indicates Slave device.\r
+  @param PacketCommand:      Points to the ATAPI command packet.\r
+  @param Buffer:             Points to the transferred data.\r
+  @param ByteCount:          When input,indicates the buffer size; when output,\r
+  indicates the actually transferred data size.\r
+  @param Direction:          Indicates the data transfer direction.\r
+  @param TimeoutInMicroSeconds: The timeout, in micro second units,\r
+  to use for the execution of this ATAPI command.\r
+  A TimeoutInMicroSeconds value of 0 means that\r
+  this function will wait indefinitely for the ATAPI\r
+  command to execute.\r
+  <P>\r
+  If TimeoutInMicroSeconds is greater than zero, then\r
+  this function will return EFI_TIMEOUT if the time\r
+  required to execute the ATAPI command is greater\r
+  than TimeoutInMicroSeconds.\r
+  </P>\r
+\r
+  @todo    AtapiScsiPrivate - add argument and description to function comment\r
+  @todo    PacketCommand - add argument and description to function comment\r
+  @todo    Buffer - add argument and description to function comment\r
+  @todo    ByteCount - add argument and description to function comment\r
+  @todo    Direction - add argument and description to function comment\r
+**/\r
+EFI_STATUS\r
+AtapiPacketCommand (\r
+  ATAPI_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,\r
+  UINT32                      Target,\r
+  UINT8                       *PacketCommand,\r
+  VOID                        *Buffer,\r
+  UINT32                      *ByteCount,\r
+  DATA_DIRECTION              Direction,\r
+  UINT64                      TimeoutInMicroSeconds\r
+  )\r
+{\r
+\r
+  UINT16      *CommandIndex;\r
+  UINT8       Count;\r
+  EFI_STATUS  Status;\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
+  Status = StatusDRQClear (AtapiScsiPrivate, TimeoutInMicroSeconds);\r
+  if (EFI_ERROR (Status)) {\r
+    if (Status == EFI_ABORTED) {\r
+      Status = EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    *ByteCount = 0;\r
+    return Status;\r
+  }\r
+  //\r
+  // Select device via Device/Head Register.\r
+  // "Target = 0" indicates device 0; "Target = 1" indicates device 1\r
+  //\r
+  WritePortB (\r
+    AtapiScsiPrivate->PciIo,\r
+    AtapiScsiPrivate->IoPort->Head,\r
+    (UINT8) ((Target << 4) | DEFAULT_CMD) // DEFAULT_CMD: 0xa0 (1010,0000)\r
+    );\r
+\r
+  //\r
+  // No OVL; No DMA (by setting feature register)\r
+  //\r
+  WritePortB (\r
+    AtapiScsiPrivate->PciIo,\r
+    AtapiScsiPrivate->IoPort->Reg1.Feature,\r
+    0x00\r
+    );\r
+\r
+  //\r
+  // set the transfersize to MAX_ATAPI_BYTE_COUNT to let the device\r
+  // determine how much data should be transfered.\r
+  //\r
+  WritePortB (\r
+    AtapiScsiPrivate->PciIo,\r
+    AtapiScsiPrivate->IoPort->CylinderLsb,\r
+    (UINT8) (MAX_ATAPI_BYTE_COUNT & 0x00ff)\r
+    );\r
+  WritePortB (\r
+    AtapiScsiPrivate->PciIo,\r
+    AtapiScsiPrivate->IoPort->CylinderMsb,\r
+    (UINT8) (MAX_ATAPI_BYTE_COUNT >> 8)\r
+    );\r
+\r
+  //\r
+  //  DEFAULT_CTL:0x0a (0000,1010)\r
+  //  Disable interrupt\r
+  //\r
+  WritePortB (\r
+    AtapiScsiPrivate->PciIo,\r
+    AtapiScsiPrivate->IoPort->Alt.DeviceControl,\r
+    DEFAULT_CTL\r
+    );\r
+\r
+  //\r
+  // Send Packet command to inform device\r
+  // that the following data bytes are command packet.\r
+  //\r
+  WritePortB (\r
+    AtapiScsiPrivate->PciIo,\r
+    AtapiScsiPrivate->IoPort->Reg.Command,\r
+    PACKET_CMD\r
+    );\r
+\r
+  //\r
+  // Before data transfer, BSY should be 0 and DRQ should be 1.\r
+  // if they are not in specified time frame,\r
+  // retrieve Sense Key from Error Register before return.\r
+  //\r
+  Status = StatusDRQReady (AtapiScsiPrivate, TimeoutInMicroSeconds);\r
+  if (EFI_ERROR (Status)) {\r
+    if (Status == EFI_ABORTED) {\r
+      Status = EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    *ByteCount = 0;\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Send out command packet\r
+  //\r
+  CommandIndex = (UINT16 *) PacketCommand;\r
+  for (Count = 0; Count < 6; Count++, CommandIndex++) {\r
+    WritePortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data, *CommandIndex);\r
+  }\r
+\r
+  //\r
+  // call AtapiPassThruPioReadWriteData() function to get\r
+  // requested transfer data form device.\r
+  //\r
+  return AtapiPassThruPioReadWriteData (\r
+          AtapiScsiPrivate,\r
+          Buffer,\r
+          ByteCount,\r
+          Direction,\r
+          TimeoutInMicroSeconds\r
+          );\r
+}\r
+\r
+/**\r
+  Performs data transfer between ATAPI device and host after the\r
+  ATAPI command packet is sent.\r
+\r
+  @param AtapiScsiPrivate:   Private data structure for the specified channel.\r
+  @param Buffer:             Points to the transferred data.\r
+  @param ByteCount:          When input,indicates the buffer size; when output,\r
+  indicates the actually transferred data size.\r
+  @param Direction:          Indicates the data transfer direction.\r
+  @param TimeoutInMicroSeconds: The timeout, in micro second units,\r
+  to use for the execution of this ATAPI command.\r
+  A TimeoutInMicroSeconds value of 0 means that\r
+  this function will wait indefinitely for the ATAPI\r
+  command to execute.\r
+  <P>\r
+  If TimeoutInMicroSeconds is greater than zero, then\r
+  this function will return EFI_TIMEOUT if the time\r
+  required to execute the ATAPI command is greater\r
+  than TimeoutInMicroSeconds.\r
+  </P>\r
+\r
+  @todo    AtapiScsiPrivate - add argument and description to function comment\r
+  @todo    Buffer - add argument and description to function comment\r
+  @todo    ByteCount - add argument and description to function comment\r
+  @todo    Direction - add argument and description to function comment\r
+  @todo    EFI_DEVICE_ERROR - add return value to function comment\r
+  @todo    EFI_DEVICE_ERROR - add return value to function comment\r
+  @todo    EFI_WARN_BUFFER_TOO_SMALL - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+AtapiPassThruPioReadWriteData (\r
+  ATAPI_SCSI_PASS_THRU_DEV  *AtapiScsiPrivate,\r
+  UINT16                    *Buffer,\r
+  UINT32                    *ByteCount,\r
+  DATA_DIRECTION            Direction,\r
+  UINT64                    TimeoutInMicroSeconds\r
+  )\r
+{\r
+  UINT32      Index;\r
+  UINT32      RequiredWordCount;\r
+  UINT32      ActualWordCount;\r
+\r
+  UINT32      WordCount;\r
+  EFI_STATUS  Status;\r
+  UINT16      *ptrBuffer;\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  //\r
+  // Non Data transfer request is also supported.\r
+  //\r
+  if (*ByteCount == 0 || Buffer == NULL) {\r
+    *ByteCount = 0;\r
+    if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, TimeoutInMicroSeconds))) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  ptrBuffer         = Buffer;\r
+  RequiredWordCount = *ByteCount / 2;\r
+\r
+  //\r
+  // ActuralWordCount means the word count of data really transfered.\r
+  //\r
+  ActualWordCount = 0;\r
+\r
+  while (ActualWordCount < RequiredWordCount) {\r
+    //\r
+    // before each data transfer stream, the host should poll DRQ bit ready,\r
+    // which indicates device's ready for data transfer .\r
+    //\r
+    Status = StatusDRQReady (AtapiScsiPrivate, TimeoutInMicroSeconds);\r
+    if (EFI_ERROR (Status)) {\r
+      *ByteCount = ActualWordCount * 2;\r
+\r
+      AtapiPassThruCheckErrorStatus (AtapiScsiPrivate);\r
+\r
+      if (ActualWordCount == 0) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+      //\r
+      // ActualWordCount > 0\r
+      //\r
+      if (ActualWordCount < RequiredWordCount) {\r
+        return EFI_WARN_BUFFER_TOO_SMALL;\r
+      }\r
+    }\r
+    //\r
+    // get current data transfer size from Cylinder Registers.\r
+    //\r
+    WordCount = ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->CylinderMsb) << 8;\r
+    WordCount = WordCount | ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->CylinderLsb);\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
+      if (Direction == DataIn) {\r
+\r
+        *ptrBuffer = ReadPortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data);\r
+      } else {\r
+\r
+        WritePortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data, *ptrBuffer);\r
+      }\r
+\r
+      ptrBuffer++;\r
+\r
+    }\r
+  }\r
+  //\r
+  // After data transfer is completed, normally, DRQ bit should clear.\r
+  //\r
+  StatusDRQClear (AtapiScsiPrivate, TimeoutInMicroSeconds);\r
+\r
+  //\r
+  // read status register to check whether error happens.\r
+  //\r
+  Status      = AtapiPassThruCheckErrorStatus (AtapiScsiPrivate);\r
+\r
+  *ByteCount  = ActualWordCount * 2;\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Read one byte from a specified I/O port.\r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    PciIo - add argument and description to function comment\r
+  @todo    Port - add argument and description to function comment\r
+**/\r
+UINT8\r
+ReadPortB (\r
+  IN  EFI_PCI_IO_PROTOCOL   *PciIo,\r
+  IN  UINT16                Port\r
+  )\r
+{\r
+  UINT8 Data;\r
+\r
+  Data = 0;\r
+  PciIo->Io.Read (\r
+              PciIo,\r
+              EfiPciIoWidthUint8,\r
+              EFI_PCI_IO_PASS_THROUGH_BAR,\r
+              (UINT64) Port,\r
+              1,\r
+              &Data\r
+              );\r
+  return Data;\r
+}\r
+\r
+\r
+/**\r
+  Read one word from a specified I/O port.\r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    PciIo - add argument and description to function comment\r
+  @todo    Port - add argument and description to function comment\r
+**/\r
+UINT16\r
+ReadPortW (\r
+  IN  EFI_PCI_IO_PROTOCOL   *PciIo,\r
+  IN  UINT16                Port\r
+  )\r
+{\r
+  UINT16  Data;\r
+\r
+  Data = 0;\r
+  PciIo->Io.Read (\r
+              PciIo,\r
+              EfiPciIoWidthUint16,\r
+              EFI_PCI_IO_PASS_THROUGH_BAR,\r
+              (UINT64) Port,\r
+              1,\r
+              &Data\r
+              );\r
+  return Data;\r
+}\r
+\r
+\r
+/**\r
+  Write one byte to a specified I/O port.\r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    PciIo - add argument and description to function comment\r
+  @todo    Port - add argument and description to function comment\r
+  @todo    Data - add argument and description to function comment\r
+**/\r
+VOID\r
+WritePortB (\r
+  IN  EFI_PCI_IO_PROTOCOL   *PciIo,\r
+  IN  UINT16                Port,\r
+  IN  UINT8                 Data\r
+  )\r
+{\r
+\r
+  PciIo->Io.Write (\r
+              PciIo,\r
+              EfiPciIoWidthUint8,\r
+              EFI_PCI_IO_PASS_THROUGH_BAR,\r
+              (UINT64) Port,\r
+              1,\r
+              &Data\r
+              );\r
+\r
+}\r
+\r
+\r
+/**\r
+  Write one word to a specified I/O port.\r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    PciIo - add argument and description to function comment\r
+  @todo    Port - add argument and description to function comment\r
+  @todo    Data - add argument and description to function comment\r
+**/\r
+VOID\r
+WritePortW (\r
+  IN  EFI_PCI_IO_PROTOCOL   *PciIo,\r
+  IN  UINT16                Port,\r
+  IN  UINT16                Data\r
+  )\r
+{\r
+\r
+  PciIo->Io.Write (\r
+              PciIo,\r
+              EfiPciIoWidthUint16,\r
+              EFI_PCI_IO_PASS_THROUGH_BAR,\r
+              (UINT64) Port,\r
+              1,\r
+              &Data\r
+              );\r
+}\r
+\r
+/**\r
+  Check whether DRQ is clear in the Status Register. (BSY must also be cleared)\r
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
+  DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is \r
+  elapsed.\r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    AtapiScsiPrivate - add argument and description to function comment\r
+  @todo    TimeoutInMicroSeconds - add argument and description to function comment\r
+  @todo    EFI_ABORTED - add return value to function comment\r
+  @todo    EFI_TIMEOUT - add return value to function comment\r
+  @todo    EFI_SUCCESS - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+StatusDRQClear (\r
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate,\r
+  UINT64                          TimeoutInMicroSeconds\r
+  )\r
+{\r
+  UINT64  Delay;\r
+  UINT8   StatusRegister;\r
+  UINT8   ErrRegister;\r
+\r
+  if (TimeoutInMicroSeconds == 0) {\r
+    Delay = 2;\r
+  } else {\r
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
+  }\r
+\r
+  do {\r
+\r
+    StatusRegister = ReadPortB (\r
+                      AtapiScsiPrivate->PciIo,\r
+                      AtapiScsiPrivate->IoPort->Reg.Status\r
+                      );\r
+\r
+    //\r
+    // wait for BSY == 0 and DRQ == 0\r
+    //\r
+    if ((StatusRegister & (DRQ | BSY)) == 0) {\r
+      break;\r
+    }\r
+    //\r
+    // check whether the command is aborted by the device\r
+    //\r
+    if ((StatusRegister & (BSY | ERR)) == ERR) {\r
+\r
+      ErrRegister = ReadPortB (\r
+                      AtapiScsiPrivate->PciIo,\r
+                      AtapiScsiPrivate->IoPort->Reg1.Error\r
+                      );\r
+      if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {\r
+\r
+        return EFI_ABORTED;\r
+      }\r
+    }\r
+    //\r
+    //  Stall for 30 us\r
+    //\r
+    gBS->Stall (30);\r
+\r
+    //\r
+    // Loop infinitely if not meeting expected condition\r
+    //\r
+    if (TimeoutInMicroSeconds == 0) {\r
+      Delay = 2;\r
+    }\r
+\r
+    Delay--;\r
+  } while (Delay);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Check whether DRQ is clear in the Alternate Status Register. \r
+  (BSY must also be cleared).\r
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
+  DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is \r
+  elapsed.\r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    AtapiScsiPrivate - add argument and description to function comment\r
+  @todo    TimeoutInMicroSeconds - add argument and description to function comment\r
+  @todo    EFI_ABORTED - add return value to function comment\r
+  @todo    EFI_TIMEOUT - add return value to function comment\r
+  @todo    EFI_SUCCESS - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+AltStatusDRQClear (\r
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate,\r
+  UINT64                          TimeoutInMicroSeconds\r
+  )\r
+{\r
+  UINT64  Delay;\r
+  UINT8   AltStatusRegister;\r
+  UINT8   ErrRegister;\r
+\r
+  if (TimeoutInMicroSeconds == 0) {\r
+    Delay = 2;\r
+  } else {\r
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
+  }\r
+\r
+  do {\r
+\r
+    AltStatusRegister = ReadPortB (\r
+                          AtapiScsiPrivate->PciIo,\r
+                          AtapiScsiPrivate->IoPort->Alt.AltStatus\r
+                          );\r
+\r
+    //\r
+    // wait for BSY == 0 and DRQ == 0\r
+    //\r
+    if ((AltStatusRegister & (DRQ | BSY)) == 0) {\r
+      break;\r
+    }\r
+\r
+    if ((AltStatusRegister & (BSY | ERR)) == ERR) {\r
+\r
+      ErrRegister = ReadPortB (\r
+                      AtapiScsiPrivate->PciIo,\r
+                      AtapiScsiPrivate->IoPort->Reg1.Error\r
+                      );\r
+      if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {\r
+\r
+        return EFI_ABORTED;\r
+      }\r
+    }\r
+    //\r
+    //  Stall for 30 us\r
+    //\r
+    gBS->Stall (30);\r
+\r
+    //\r
+    // Loop infinitely if not meeting expected condition\r
+    //\r
+    if (TimeoutInMicroSeconds == 0) {\r
+      Delay = 2;\r
+    }\r
+\r
+    Delay--;\r
+  } while (Delay);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Check whether DRQ is ready in the Status Register. (BSY must also be cleared)\r
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
+  DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is \r
+  elapsed.\r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    AtapiScsiPrivate - add argument and description to function comment\r
+  @todo    TimeoutInMicroSeconds - add argument and description to function comment\r
+  @todo    EFI_ABORTED - add return value to function comment\r
+  @todo    EFI_TIMEOUT - add return value to function comment\r
+  @todo    EFI_SUCCESS - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+StatusDRQReady (\r
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate,\r
+  UINT64                          TimeoutInMicroSeconds\r
+  )\r
+{\r
+  UINT64  Delay;\r
+  UINT8   StatusRegister;\r
+  UINT8   ErrRegister;\r
+\r
+  if (TimeoutInMicroSeconds == 0) {\r
+    Delay = 2;\r
+  } else {\r
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
+  }\r
+\r
+  do {\r
+    //\r
+    //  read Status Register will clear interrupt\r
+    //\r
+    StatusRegister = ReadPortB (\r
+                      AtapiScsiPrivate->PciIo,\r
+                      AtapiScsiPrivate->IoPort->Reg.Status\r
+                      );\r
+\r
+    //\r
+    //  BSY==0,DRQ==1\r
+    //\r
+    if ((StatusRegister & (BSY | DRQ)) == DRQ) {\r
+      break;\r
+    }\r
+\r
+    if ((StatusRegister & (BSY | ERR)) == ERR) {\r
+\r
+      ErrRegister = ReadPortB (\r
+                      AtapiScsiPrivate->PciIo,\r
+                      AtapiScsiPrivate->IoPort->Reg1.Error\r
+                      );\r
+      if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {\r
+        return EFI_ABORTED;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Stall for 30 us\r
+    //\r
+    gBS->Stall (30);\r
+\r
+    //\r
+    // Loop infinitely if not meeting expected condition\r
+    //\r
+    if (TimeoutInMicroSeconds == 0) {\r
+      Delay = 2;\r
+    }\r
+\r
+    Delay--;\r
+  } while (Delay);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Check whether DRQ is ready in the Alternate Status Register. \r
+  (BSY must also be cleared)\r
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
+  DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is \r
+  elapsed.\r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    AtapiScsiPrivate - add argument and description to function comment\r
+  @todo    TimeoutInMicroSeconds - add argument and description to function comment\r
+  @todo    EFI_ABORTED - add return value to function comment\r
+  @todo    EFI_TIMEOUT - add return value to function comment\r
+  @todo    EFI_SUCCESS - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+AltStatusDRQReady (\r
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate,\r
+  UINT64                          TimeoutInMicroSeconds\r
+  )\r
+{\r
+  UINT64  Delay;\r
+  UINT8   AltStatusRegister;\r
+  UINT8   ErrRegister;\r
+\r
+  if (TimeoutInMicroSeconds == 0) {\r
+    Delay = 2;\r
+  } else {\r
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
+  }\r
+\r
+  do {\r
+    //\r
+    //  read Status Register will clear interrupt\r
+    //\r
+    AltStatusRegister = ReadPortB (\r
+                          AtapiScsiPrivate->PciIo,\r
+                          AtapiScsiPrivate->IoPort->Alt.AltStatus\r
+                          );\r
+    //\r
+    //  BSY==0,DRQ==1\r
+    //\r
+    if ((AltStatusRegister & (BSY | DRQ)) == DRQ) {\r
+      break;\r
+    }\r
+\r
+    if ((AltStatusRegister & (BSY | ERR)) == ERR) {\r
+\r
+      ErrRegister = ReadPortB (\r
+                      AtapiScsiPrivate->PciIo,\r
+                      AtapiScsiPrivate->IoPort->Reg1.Error\r
+                      );\r
+      if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {\r
+        return EFI_ABORTED;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Stall for 30 us\r
+    //\r
+    gBS->Stall (30);\r
+\r
+    //\r
+    // Loop infinitely if not meeting expected condition\r
+    //\r
+    if (TimeoutInMicroSeconds == 0) {\r
+      Delay = 2;\r
+    }\r
+\r
+    Delay--;\r
+  } while (Delay);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Check whether BSY is clear in the Status Register.\r
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
+  BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is \r
+  elapsed.\r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    AtapiScsiPrivate - add argument and description to function comment\r
+  @todo    TimeoutInMicroSeconds - add argument and description to function comment\r
+  @todo    EFI_TIMEOUT - add return value to function comment\r
+  @todo    EFI_SUCCESS - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+StatusWaitForBSYClear (\r
+  ATAPI_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,\r
+  UINT64                      TimeoutInMicroSeconds\r
+  )\r
+{\r
+  UINT64  Delay;\r
+  UINT8   StatusRegister;\r
+\r
+  if (TimeoutInMicroSeconds == 0) {\r
+    Delay = 2;\r
+  } else {\r
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
+  }\r
+\r
+  do {\r
+\r
+    StatusRegister = ReadPortB (\r
+                      AtapiScsiPrivate->PciIo,\r
+                      AtapiScsiPrivate->IoPort->Reg.Status\r
+                      );\r
+    if ((StatusRegister & BSY) == 0x00) {\r
+      break;\r
+    }\r
+\r
+    //\r
+    // Stall for 30 us\r
+    //\r
+    gBS->Stall (30);\r
+\r
+    //\r
+    // Loop infinitely if not meeting expected condition\r
+    //\r
+    if (TimeoutInMicroSeconds == 0) {\r
+      Delay = 2;\r
+    }\r
+\r
+    Delay--;\r
+  } while (Delay);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Check whether BSY is clear in the Alternate Status Register.\r
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
+  BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is \r
+  elapsed.\r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    AtapiScsiPrivate - add argument and description to function comment\r
+  @todo    TimeoutInMicroSeconds - add argument and description to function comment\r
+  @todo    EFI_TIMEOUT - add return value to function comment\r
+  @todo    EFI_SUCCESS - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+AltStatusWaitForBSYClear (\r
+  ATAPI_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,\r
+  UINT64                      TimeoutInMicroSeconds\r
+  )\r
+{\r
+  UINT64  Delay;\r
+  UINT8   AltStatusRegister;\r
+\r
+  if (TimeoutInMicroSeconds == 0) {\r
+    Delay = 2;\r
+  } else {\r
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
+  }\r
+\r
+  do {\r
+\r
+    AltStatusRegister = ReadPortB (\r
+                          AtapiScsiPrivate->PciIo,\r
+                          AtapiScsiPrivate->IoPort->Alt.AltStatus\r
+                          );\r
+    if ((AltStatusRegister & BSY) == 0x00) {\r
+      break;\r
+    }\r
+\r
+    //\r
+    // Stall for 30 us\r
+    //\r
+    gBS->Stall (30);\r
+    //\r
+    // Loop infinitely if not meeting expected condition\r
+    //\r
+    if (TimeoutInMicroSeconds == 0) {\r
+      Delay = 2;\r
+    }\r
+\r
+    Delay--;\r
+  } while (Delay);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Check whether DRDY is ready in the Status Register. \r
+  (BSY must also be cleared)\r
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
+  DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is \r
+  elapsed.\r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    AtapiScsiPrivate - add argument and description to function comment\r
+  @todo    TimeoutInMicroSeconds - add argument and description to function comment\r
+  @todo    EFI_ABORTED - add return value to function comment\r
+  @todo    EFI_TIMEOUT - add return value to function comment\r
+  @todo    EFI_SUCCESS - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+StatusDRDYReady (\r
+  ATAPI_SCSI_PASS_THRU_DEV     *AtapiScsiPrivate,\r
+  UINT64                       TimeoutInMicroSeconds\r
+  )\r
+{\r
+  UINT64  Delay;\r
+  UINT8   StatusRegister;\r
+  UINT8   ErrRegister;\r
+\r
+  if (TimeoutInMicroSeconds == 0) {\r
+    Delay = 2;\r
+  } else {\r
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
+  }\r
+\r
+  do {\r
+    StatusRegister = ReadPortB (\r
+                      AtapiScsiPrivate->PciIo,\r
+                      AtapiScsiPrivate->IoPort->Reg.Status\r
+                      );\r
+    //\r
+    //  BSY == 0 , DRDY == 1\r
+    //\r
+    if ((StatusRegister & (DRDY | BSY)) == DRDY) {\r
+      break;\r
+    }\r
+\r
+    if ((StatusRegister & (BSY | ERR)) == ERR) {\r
+\r
+      ErrRegister = ReadPortB (\r
+                      AtapiScsiPrivate->PciIo,\r
+                      AtapiScsiPrivate->IoPort->Reg1.Error\r
+                      );\r
+      if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {\r
+        return EFI_ABORTED;\r
+      }\r
+    }\r
+    \r
+    //\r
+    // Stall for 30 us\r
+    //\r
+    gBS->Stall (30);\r
+    //\r
+    // Loop infinitely if not meeting expected condition\r
+    //\r
+    if (TimeoutInMicroSeconds == 0) {\r
+      Delay = 2;\r
+    }\r
+\r
+    Delay--;\r
+  } while (Delay);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Check whether DRDY is ready in the Alternate Status Register. \r
+  (BSY must also be cleared)\r
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for\r
+  DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is \r
+  elapsed.\r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    AtapiScsiPrivate - add argument and description to function comment\r
+  @todo    TimeoutInMicroSeconds - add argument and description to function comment\r
+  @todo    EFI_ABORTED - add return value to function comment\r
+  @todo    EFI_TIMEOUT - add return value to function comment\r
+  @todo    EFI_SUCCESS - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+AltStatusDRDYReady (\r
+  ATAPI_SCSI_PASS_THRU_DEV     *AtapiScsiPrivate,\r
+  UINT64                       TimeoutInMicroSeconds\r
+  )\r
+{\r
+  UINT64  Delay;\r
+  UINT8   AltStatusRegister;\r
+  UINT8   ErrRegister;\r
+\r
+  if (TimeoutInMicroSeconds == 0) {\r
+    Delay = 2;\r
+  } else {\r
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;\r
+  }\r
+\r
+  do {\r
+    AltStatusRegister = ReadPortB (\r
+                          AtapiScsiPrivate->PciIo,\r
+                          AtapiScsiPrivate->IoPort->Alt.AltStatus\r
+                          );\r
+    //\r
+    //  BSY == 0 , DRDY == 1\r
+    //\r
+    if ((AltStatusRegister & (DRDY | BSY)) == DRDY) {\r
+      break;\r
+    }\r
+\r
+    if ((AltStatusRegister & (BSY | ERR)) == ERR) {\r
+\r
+      ErrRegister = ReadPortB (\r
+                      AtapiScsiPrivate->PciIo,\r
+                      AtapiScsiPrivate->IoPort->Reg1.Error\r
+                      );\r
+      if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {\r
+        return EFI_ABORTED;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Stall for 30 us\r
+    //\r
+    gBS->Stall (30);\r
+    //\r
+    // Loop infinitely if not meeting expected condition\r
+    //\r
+    if (TimeoutInMicroSeconds == 0) {\r
+      Delay = 2;\r
+    }\r
+\r
+    Delay--;\r
+  } while (Delay);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Check Error Register for Error Information. \r
+\r
+  @todo function comment is missing 'Routine Description:'\r
+  @todo function comment is missing 'Arguments:'\r
+  @todo function comment is missing 'Returns:'\r
+  @todo    AtapiScsiPrivate - add argument and description to function comment\r
+  @todo    EFI_SUCCESS - add return value to function comment\r
+  @todo    EFI_DEVICE_ERROR - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+AtapiPassThruCheckErrorStatus (\r
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate\r
+  )\r
+{\r
+  UINT8 StatusRegister;\r
+  UINT8 ErrorRegister;\r
+\r
+  StatusRegister = ReadPortB (\r
+                    AtapiScsiPrivate->PciIo,\r
+                    AtapiScsiPrivate->IoPort->Reg.Status\r
+                    );\r
+  \r
+  DEBUG_CODE_BEGIN ();\r
+\r
+    if (StatusRegister & DWF) {\r
+      DEBUG (\r
+        (EFI_D_BLKIO,\r
+        "AtapiPassThruCheckErrorStatus()-- %02x : Error : Write Fault\n",\r
+        StatusRegister)\r
+        );\r
+    }\r
+\r
+    if (StatusRegister & CORR) {\r
+      DEBUG (\r
+        (EFI_D_BLKIO,\r
+        "AtapiPassThruCheckErrorStatus()-- %02x : Error : Corrected Data\n",\r
+        StatusRegister)\r
+        );\r
+    }\r
+\r
+    if (StatusRegister & ERR) {\r
+      ErrorRegister = ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg1.Error);\r
+      \r
+\r
+      if (ErrorRegister & BBK_ERR) {\r
+        DEBUG (\r
+          (EFI_D_BLKIO,\r
+          "AtapiPassThruCheckErrorStatus()-- %02x : Error : Bad Block Detected\n",\r
+          ErrorRegister)\r
+          );\r
+      }\r
+\r
+      if (ErrorRegister & UNC_ERR) {\r
+        DEBUG (\r
+          (EFI_D_BLKIO,\r
+          "AtapiPassThruCheckErrorStatus()-- %02x : Error : Uncorrectable Data\n",\r
+          ErrorRegister)\r
+          );\r
+      }\r
+\r
+      if (ErrorRegister & MC_ERR) {\r
+        DEBUG (\r
+          (EFI_D_BLKIO,\r
+          "AtapiPassThruCheckErrorStatus()-- %02x : Error : Media Change\n",\r
+          ErrorRegister)\r
+          );\r
+      }\r
+\r
+      if (ErrorRegister & ABRT_ERR) {\r
+        DEBUG (\r
+          (EFI_D_BLKIO,\r
+          "AtapiPassThruCheckErrorStatus()-- %02x : Error : Abort\n",\r
+          ErrorRegister)\r
+          );\r
+      }\r
+\r
+      if (ErrorRegister & TK0NF_ERR) {\r
+        DEBUG (\r
+          (EFI_D_BLKIO,\r
+          "AtapiPassThruCheckErrorStatus()-- %02x : Error : Track 0 Not Found\n",\r
+          ErrorRegister)\r
+          );\r
+      }\r
+\r
+      if (ErrorRegister & AMNF_ERR) {\r
+        DEBUG (\r
+          (EFI_D_BLKIO,\r
+          "AtapiPassThruCheckErrorStatus()-- %02x : Error : Address Mark Not Found\n",\r
+          ErrorRegister)\r
+          );\r
+       }\r
+    }\r
+\r
+  DEBUG_CODE_END ();\r
+\r
+  if ((StatusRegister & (ERR | DWF | CORR)) == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
\r
+  return EFI_DEVICE_ERROR;\r
+}\r
+\r
+/**\r
+  The user Entry Point for module AtapiPassThru. The user code starts with this function.\r
+\r
+  @param[in] ImageHandle    The firmware allocated handle for the EFI image.  \r
+  @param[in] SystemTable    A pointer to the EFI System Table.\r
+  \r
+  @retval EFI_SUCCESS       The entry point is executed successfully.\r
+  @retval other             Some error occurs when executing this entry point.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InitializeAtapiPassThru(\r
+  IN EFI_HANDLE           ImageHandle,\r
+  IN EFI_SYSTEM_TABLE     *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+\r
+  //\r
+  // Install driver model protocol(s).\r
+  //\r
+  Status = EfiLibInstallAllDriverProtocols (\r
+             ImageHandle,\r
+             SystemTable,\r
+             &gAtapiScsiPassThruDriverBinding,\r
+             ImageHandle,\r
+             &gAtapiScsiPassThruComponentName,\r
+             NULL,\r
+             NULL\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  return Status;\r
+}\r