]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / MdeModulePkg / Bus / Scsi / ScsiDiskDxe / ScsiDisk.c
index d2a751a8ed5d316b33790d275e5fe1aada36e9db..fbc236cb465e293c4130b56aef94a7b003c33f01 100644 (file)
@@ -1,22 +1,15 @@
 /** @file\r
   SCSI disk driver that layers on every SCSI IO protocol in the system.\r
 \r
-Copyright (c) 2006 - 2008, Intel Corporation. <BR>\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
+Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 1985 - 2022, American Megatrends International LLC.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
-\r
 #include "ScsiDisk.h"\r
 \r
-\r
-EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding = {\r
+EFI_DRIVER_BINDING_PROTOCOL  gScsiDiskDriverBinding = {\r
   ScsiDiskDriverBindingSupported,\r
   ScsiDiskDriverBindingStart,\r
   ScsiDiskDriverBindingStop,\r
@@ -25,26 +18,103 @@ EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding = {
   NULL\r
 };\r
 \r
+EFI_DISK_INFO_PROTOCOL  gScsiDiskInfoProtocolTemplate = {\r
+  EFI_DISK_INFO_SCSI_INTERFACE_GUID,\r
+  ScsiDiskInfoInquiry,\r
+  ScsiDiskInfoIdentify,\r
+  ScsiDiskInfoSenseData,\r
+  ScsiDiskInfoWhichIde\r
+};\r
+\r
+/**\r
+  Allocates an aligned buffer for SCSI disk.\r
+\r
+  This function allocates an aligned buffer for the SCSI disk to perform\r
+  SCSI IO operations. The alignment requirement is from SCSI IO interface.\r
+\r
+  @param  ScsiDiskDevice    The SCSI disk involved for the operation.\r
+  @param  BufferSize        The request buffer size.\r
+\r
+  @return A pointer to the aligned buffer or NULL if the allocation fails.\r
+\r
+**/\r
+VOID *\r
+AllocateAlignedBuffer (\r
+  IN SCSI_DISK_DEV  *ScsiDiskDevice,\r
+  IN UINTN          BufferSize\r
+  )\r
+{\r
+  return AllocateAlignedPages (EFI_SIZE_TO_PAGES (BufferSize), ScsiDiskDevice->ScsiIo->IoAlign);\r
+}\r
+\r
+/**\r
+  Frees an aligned buffer for SCSI disk.\r
+\r
+  This function frees an aligned buffer for the SCSI disk to perform\r
+  SCSI IO operations.\r
+\r
+  @param  Buffer            The aligned buffer to be freed.\r
+  @param  BufferSize        The request buffer size.\r
+\r
+**/\r
+VOID\r
+FreeAlignedBuffer (\r
+  IN VOID   *Buffer,\r
+  IN UINTN  BufferSize\r
+  )\r
+{\r
+  if (Buffer != NULL) {\r
+    FreeAlignedPages (Buffer, EFI_SIZE_TO_PAGES (BufferSize));\r
+  }\r
+}\r
+\r
+/**\r
+  Remove trailing spaces from the string.\r
+\r
+  @param String   The ASCII string to remove the trailing spaces.\r
+\r
+  @retval the new length of the string.\r
+**/\r
+UINTN\r
+RemoveTrailingSpaces (\r
+  IN OUT CHAR8  *String\r
+  )\r
+{\r
+  UINTN  Length;\r
+\r
+  Length = AsciiStrLen (String);\r
+  if (Length == 0) {\r
+    return 0;\r
+  }\r
+\r
+  while ((Length > 0) && (String[Length-1] == ' ')) {\r
+    Length--;\r
+  }\r
+\r
+  String[Length] = '\0';\r
+  return Length;\r
+}\r
+\r
 /**\r
   The user Entry Point for module ScsiDisk.\r
 \r
   The user code starts with this function.\r
 \r
-  @param  ImageHandle    The firmware allocated handle for the EFI image.  \r
+  @param  ImageHandle    The firmware allocated handle for the EFI image.\r
   @param  SystemTable    A pointer to the EFI System Table.\r
-  \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
-InitializeScsiDisk(\r
-  IN EFI_HANDLE           ImageHandle,\r
-  IN EFI_SYSTEM_TABLE     *SystemTable\r
+InitializeScsiDisk (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
   )\r
 {\r
-  EFI_STATUS              Status;\r
+  EFI_STATUS  Status;\r
 \r
   //\r
   // Install driver model protocol(s).\r
@@ -59,7 +129,6 @@ InitializeScsiDisk(
              );\r
   ASSERT_EFI_ERROR (Status);\r
 \r
-\r
   return Status;\r
 }\r
 \r
@@ -97,7 +166,7 @@ ScsiDiskDriverBindingSupported (
   Status = gBS->OpenProtocol (\r
                   Controller,\r
                   &gEfiScsiIoProtocolGuid,\r
-                  (VOID **) &ScsiIo,\r
+                  (VOID **)&ScsiIo,\r
                   This->DriverBindingHandle,\r
                   Controller,\r
                   EFI_OPEN_PROTOCOL_BY_DRIVER\r
@@ -108,7 +177,10 @@ ScsiDiskDriverBindingSupported (
 \r
   Status = ScsiIo->GetDeviceType (ScsiIo, &DeviceType);\r
   if (!EFI_ERROR (Status)) {\r
-    if ((DeviceType == EFI_SCSI_TYPE_DISK) || (DeviceType == EFI_SCSI_TYPE_CDROM)) {\r
+    if ((DeviceType == EFI_SCSI_TYPE_DISK) ||\r
+        (DeviceType == EFI_SCSI_TYPE_CDROM) ||\r
+        (DeviceType == EFI_SCSI_TYPE_WLUN))\r
+    {\r
       Status = EFI_SUCCESS;\r
     } else {\r
       Status = EFI_UNSUPPORTED;\r
@@ -124,7 +196,6 @@ ScsiDiskDriverBindingSupported (
   return Status;\r
 }\r
 \r
-\r
 /**\r
   Start this driver on ControllerHandle.\r
 \r
@@ -159,77 +230,93 @@ ScsiDiskDriverBindingStart (
   UINT8                 Index;\r
   UINT8                 MaxRetry;\r
   BOOLEAN               NeedRetry;\r
+  BOOLEAN               MustReadCapacity;\r
+  CHAR8                 VendorStr[VENDOR_IDENTIFICATION_LENGTH + 1];\r
+  CHAR8                 ProductStr[PRODUCT_IDENTIFICATION_LENGTH + 1];\r
+  CHAR16                DeviceStr[VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2];\r
 \r
-  Status = gBS->AllocatePool (\r
-                  EfiBootServicesData,\r
-                  sizeof (SCSI_DISK_DEV),\r
-                  (VOID **) &ScsiDiskDevice\r
-                  );\r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
-  }\r
+  MustReadCapacity = TRUE;\r
 \r
-  ZeroMem (ScsiDiskDevice, sizeof (SCSI_DISK_DEV));\r
+  ScsiDiskDevice = (SCSI_DISK_DEV *)AllocateZeroPool (sizeof (SCSI_DISK_DEV));\r
+  if (ScsiDiskDevice == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
 \r
   Status = gBS->OpenProtocol (\r
                   Controller,\r
                   &gEfiScsiIoProtocolGuid,\r
-                  (VOID **) &ScsiIo,\r
+                  (VOID **)&ScsiIo,\r
                   This->DriverBindingHandle,\r
                   Controller,\r
                   EFI_OPEN_PROTOCOL_BY_DRIVER\r
                   );\r
   if (EFI_ERROR (Status)) {\r
-    gBS->FreePool (ScsiDiskDevice);\r
+    FreePool (ScsiDiskDevice);\r
     return Status;\r
   }\r
 \r
-  ScsiDiskDevice->Signature         = SCSI_DISK_DEV_SIGNATURE;\r
-  ScsiDiskDevice->ScsiIo            = ScsiIo;\r
-  ScsiDiskDevice->BlkIo.Media       = &ScsiDiskDevice->BlkIoMedia;\r
-  ScsiDiskDevice->BlkIo.Reset       = ScsiDiskReset;\r
-  ScsiDiskDevice->BlkIo.ReadBlocks  = ScsiDiskReadBlocks;\r
-  ScsiDiskDevice->BlkIo.WriteBlocks = ScsiDiskWriteBlocks;\r
-  ScsiDiskDevice->BlkIo.FlushBlocks = ScsiDiskFlushBlocks;\r
-  ScsiDiskDevice->Handle            = Controller;\r
+  ScsiDiskDevice->Signature                         = SCSI_DISK_DEV_SIGNATURE;\r
+  ScsiDiskDevice->ScsiIo                            = ScsiIo;\r
+  ScsiDiskDevice->BlkIo.Revision                    = EFI_BLOCK_IO_PROTOCOL_REVISION3;\r
+  ScsiDiskDevice->BlkIo.Media                       = &ScsiDiskDevice->BlkIoMedia;\r
+  ScsiDiskDevice->BlkIo.Media->IoAlign              = ScsiIo->IoAlign;\r
+  ScsiDiskDevice->BlkIo.Reset                       = ScsiDiskReset;\r
+  ScsiDiskDevice->BlkIo.ReadBlocks                  = ScsiDiskReadBlocks;\r
+  ScsiDiskDevice->BlkIo.WriteBlocks                 = ScsiDiskWriteBlocks;\r
+  ScsiDiskDevice->BlkIo.FlushBlocks                 = ScsiDiskFlushBlocks;\r
+  ScsiDiskDevice->BlkIo2.Media                      = &ScsiDiskDevice->BlkIoMedia;\r
+  ScsiDiskDevice->BlkIo2.Reset                      = ScsiDiskResetEx;\r
+  ScsiDiskDevice->BlkIo2.ReadBlocksEx               = ScsiDiskReadBlocksEx;\r
+  ScsiDiskDevice->BlkIo2.WriteBlocksEx              = ScsiDiskWriteBlocksEx;\r
+  ScsiDiskDevice->BlkIo2.FlushBlocksEx              = ScsiDiskFlushBlocksEx;\r
+  ScsiDiskDevice->StorageSecurity.ReceiveData       = ScsiDiskReceiveData;\r
+  ScsiDiskDevice->StorageSecurity.SendData          = ScsiDiskSendData;\r
+  ScsiDiskDevice->EraseBlock.Revision               = EFI_ERASE_BLOCK_PROTOCOL_REVISION;\r
+  ScsiDiskDevice->EraseBlock.EraseLengthGranularity = 1;\r
+  ScsiDiskDevice->EraseBlock.EraseBlocks            = ScsiDiskEraseBlocks;\r
+  ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt           = 1;\r
+  ScsiDiskDevice->BlockLimitsVpdSupported           = FALSE;\r
+  ScsiDiskDevice->Handle                            = Controller;\r
+  InitializeListHead (&ScsiDiskDevice->AsyncTaskQueue);\r
 \r
   ScsiIo->GetDeviceType (ScsiIo, &(ScsiDiskDevice->DeviceType));\r
   switch (ScsiDiskDevice->DeviceType) {\r
-  case EFI_SCSI_TYPE_DISK:\r
-    ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;\r
-    break;\r
+    case EFI_SCSI_TYPE_DISK:\r
+      ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;\r
+      MustReadCapacity                       = TRUE;\r
+      break;\r
+\r
+    case EFI_SCSI_TYPE_CDROM:\r
+      ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;\r
+      ScsiDiskDevice->BlkIo.Media->ReadOnly  = TRUE;\r
+      MustReadCapacity                       = FALSE;\r
+      break;\r
 \r
-  case EFI_SCSI_TYPE_CDROM:\r
-    ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;\r
-    break;\r
+    case EFI_SCSI_TYPE_WLUN:\r
+      MustReadCapacity = FALSE;\r
+      break;\r
   }\r
+\r
   //\r
   // The Sense Data Array's initial size is 6\r
   //\r
   ScsiDiskDevice->SenseDataNumber = 6;\r
-  Status = gBS->AllocatePool (\r
-                  EfiBootServicesData,\r
-                  sizeof (EFI_SCSI_SENSE_DATA) * ScsiDiskDevice->SenseDataNumber,\r
-                  (VOID **) &(ScsiDiskDevice->SenseData)\r
-                  );\r
-  if (EFI_ERROR (Status)) {\r
+  ScsiDiskDevice->SenseData       = (EFI_SCSI_SENSE_DATA *)AllocateZeroPool (\r
+                                                             sizeof (EFI_SCSI_SENSE_DATA) * ScsiDiskDevice->SenseDataNumber\r
+                                                             );\r
+  if (ScsiDiskDevice->SenseData == NULL) {\r
     gBS->CloseProtocol (\r
-          Controller,\r
-          &gEfiScsiIoProtocolGuid,\r
-          This->DriverBindingHandle,\r
-          Controller\r
-          );\r
-    gBS->FreePool (ScsiDiskDevice);\r
-    return Status;\r
+           Controller,\r
+           &gEfiScsiIoProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           Controller\r
+           );\r
+    FreePool (ScsiDiskDevice);\r
+    return EFI_OUT_OF_RESOURCES;\r
   }\r
 \r
-  ZeroMem (\r
-    ScsiDiskDevice->SenseData,\r
-    sizeof (EFI_SCSI_SENSE_DATA) * ScsiDiskDevice->SenseDataNumber\r
-    );\r
-\r
   //\r
-  // Retrive device information\r
+  // Retrieve device information\r
   //\r
   MaxRetry = 2;\r
   for (Index = 0; Index < MaxRetry; Index++) {\r
@@ -239,65 +326,114 @@ ScsiDiskDriverBindingStart (
     }\r
 \r
     if (!NeedRetry) {\r
-      gBS->FreePool (ScsiDiskDevice->SenseData);\r
+      FreePool (ScsiDiskDevice->SenseData);\r
       gBS->CloseProtocol (\r
              Controller,\r
              &gEfiScsiIoProtocolGuid,\r
              This->DriverBindingHandle,\r
              Controller\r
              );\r
-      gBS->FreePool (ScsiDiskDevice);\r
+      FreePool (ScsiDiskDevice);\r
       return EFI_DEVICE_ERROR;\r
     }\r
   }\r
+\r
   //\r
   // The second parameter "TRUE" means must\r
   // retrieve media capacity\r
   //\r
-  Status = ScsiDiskDetectMedia (ScsiDiskDevice, TRUE, &Temp);\r
+  Status = ScsiDiskDetectMedia (ScsiDiskDevice, MustReadCapacity, &Temp);\r
   if (!EFI_ERROR (Status)) {\r
-    Status = gBS->InstallMultipleProtocolInterfaces (\r
-                    &Controller,\r
-                    &gEfiBlockIoProtocolGuid,\r
-                    &ScsiDiskDevice->BlkIo,\r
-                    NULL\r
-                    );\r
-  }\r
-\r
-  if (EFI_ERROR (Status)) {\r
-    gBS->FreePool (ScsiDiskDevice->SenseData);\r
-    gBS->CloseProtocol (\r
-           Controller,\r
-           &gEfiScsiIoProtocolGuid,\r
-           This->DriverBindingHandle,\r
-           Controller\r
-           );\r
-    gBS->FreePool (ScsiDiskDevice);\r
-    return Status;\r
-  }\r
+    //\r
+    // Determine if Block IO & Block IO2 should be produced on this controller\r
+    // handle\r
+    //\r
+    if (DetermineInstallBlockIo (Controller)) {\r
+      InitializeInstallDiskInfo (ScsiDiskDevice, Controller);\r
+      Status = gBS->InstallMultipleProtocolInterfaces (\r
+                      &Controller,\r
+                      &gEfiBlockIoProtocolGuid,\r
+                      &ScsiDiskDevice->BlkIo,\r
+                      &gEfiBlockIo2ProtocolGuid,\r
+                      &ScsiDiskDevice->BlkIo2,\r
+                      &gEfiDiskInfoProtocolGuid,\r
+                      &ScsiDiskDevice->DiskInfo,\r
+                      NULL\r
+                      );\r
+      if (!EFI_ERROR (Status)) {\r
+        if (DetermineInstallEraseBlock (ScsiDiskDevice, Controller)) {\r
+          Status = gBS->InstallProtocolInterface (\r
+                          &Controller,\r
+                          &gEfiEraseBlockProtocolGuid,\r
+                          EFI_NATIVE_INTERFACE,\r
+                          &ScsiDiskDevice->EraseBlock\r
+                          );\r
+          if (EFI_ERROR (Status)) {\r
+            DEBUG ((DEBUG_ERROR, "ScsiDisk: Failed to install the Erase Block Protocol! Status = %r\n", Status));\r
+          }\r
+        }\r
 \r
-  ScsiDiskDevice->ControllerNameTable = NULL;\r
-  AddUnicodeString2 (\r
-    "eng",\r
-    gScsiDiskComponentName.SupportedLanguages,\r
-    &ScsiDiskDevice->ControllerNameTable,\r
-    L"SCSI Disk Device",\r
-    TRUE\r
-    );\r
-  AddUnicodeString2 (\r
-    "en",\r
-    gScsiDiskComponentName2.SupportedLanguages,\r
-    &ScsiDiskDevice->ControllerNameTable,\r
-    L"SCSI Disk Device",\r
-    FALSE\r
-    );\r
+        if (DetermineInstallStorageSecurity (ScsiDiskDevice, Controller)) {\r
+          Status = gBS->InstallProtocolInterface (\r
+                          &Controller,\r
+                          &gEfiStorageSecurityCommandProtocolGuid,\r
+                          EFI_NATIVE_INTERFACE,\r
+                          &ScsiDiskDevice->StorageSecurity\r
+                          );\r
+          if (EFI_ERROR (Status)) {\r
+            DEBUG ((DEBUG_ERROR, "ScsiDisk: Failed to install the Storage Security Command Protocol! Status = %r\n", Status));\r
+          }\r
+        }\r
 \r
+        CopyMem (\r
+          VendorStr,\r
+          &ScsiDiskDevice->InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET],\r
+          VENDOR_IDENTIFICATION_LENGTH\r
+          );\r
+        VendorStr[VENDOR_IDENTIFICATION_LENGTH] = 0;\r
+        RemoveTrailingSpaces (VendorStr);\r
 \r
-  return EFI_SUCCESS;\r
+        CopyMem (\r
+          ProductStr,\r
+          &ScsiDiskDevice->InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET],\r
+          PRODUCT_IDENTIFICATION_LENGTH\r
+          );\r
+        ProductStr[PRODUCT_IDENTIFICATION_LENGTH] = 0;\r
+        RemoveTrailingSpaces (ProductStr);\r
+\r
+        UnicodeSPrint (DeviceStr, sizeof (DeviceStr), L"%a %a", VendorStr, ProductStr);\r
+\r
+        ScsiDiskDevice->ControllerNameTable = NULL;\r
+        AddUnicodeString2 (\r
+          "eng",\r
+          gScsiDiskComponentName.SupportedLanguages,\r
+          &ScsiDiskDevice->ControllerNameTable,\r
+          DeviceStr,\r
+          TRUE\r
+          );\r
+        AddUnicodeString2 (\r
+          "en",\r
+          gScsiDiskComponentName2.SupportedLanguages,\r
+          &ScsiDiskDevice->ControllerNameTable,\r
+          DeviceStr,\r
+          FALSE\r
+          );\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+  }\r
 \r
+  gBS->FreePool (ScsiDiskDevice->SenseData);\r
+  gBS->FreePool (ScsiDiskDevice);\r
+  gBS->CloseProtocol (\r
+         Controller,\r
+         &gEfiScsiIoProtocolGuid,\r
+         This->DriverBindingHandle,\r
+         Controller\r
+         );\r
+  return Status;\r
 }\r
 \r
-\r
 /**\r
   Stop this driver on ControllerHandle.\r
 \r
@@ -306,7 +442,7 @@ ScsiDiskDriverBindingStart (
   restrictions for this service. DisconnectController() must follow these\r
   calling restrictions. If any other agent wishes to call Stop() it must\r
   also follow these calling restrictions.\r
-  \r
+\r
   @param  This              Protocol instance pointer.\r
   @param  ControllerHandle  Handle of device to stop driver on\r
   @param  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of\r
@@ -320,20 +456,21 @@ ScsiDiskDriverBindingStart (
 EFI_STATUS\r
 EFIAPI\r
 ScsiDiskDriverBindingStop (\r
-  IN  EFI_DRIVER_BINDING_PROTOCOL     *This,\r
-  IN  EFI_HANDLE                      Controller,\r
-  IN  UINTN                           NumberOfChildren,\r
-  IN  EFI_HANDLE                      *ChildHandleBuffer   OPTIONAL\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   Controller,\r
+  IN  UINTN                        NumberOfChildren,\r
+  IN  EFI_HANDLE                   *ChildHandleBuffer   OPTIONAL\r
   )\r
 {\r
-  EFI_BLOCK_IO_PROTOCOL *BlkIo;\r
-  SCSI_DISK_DEV         *ScsiDiskDevice;\r
-  EFI_STATUS            Status;\r
+  EFI_BLOCK_IO_PROTOCOL     *BlkIo;\r
+  EFI_ERASE_BLOCK_PROTOCOL  *EraseBlock;\r
+  SCSI_DISK_DEV             *ScsiDiskDevice;\r
+  EFI_STATUS                Status;\r
 \r
   Status = gBS->OpenProtocol (\r
                   Controller,\r
                   &gEfiBlockIoProtocolGuid,\r
-                  (VOID **) &BlkIo,\r
+                  (VOID **)&BlkIo,\r
                   This->DriverBindingHandle,\r
                   Controller,\r
                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
@@ -342,11 +479,46 @@ ScsiDiskDriverBindingStop (
     return Status;\r
   }\r
 \r
-  ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (BlkIo);\r
-  Status = gBS->UninstallProtocolInterface (\r
+  ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (BlkIo);\r
+\r
+  //\r
+  // Wait for the BlockIo2 requests queue to become empty\r
+  //\r
+  while (!IsListEmpty (&ScsiDiskDevice->AsyncTaskQueue)) {\r
+  }\r
+\r
+  //\r
+  // If Erase Block Protocol is installed, then uninstall this protocol.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiEraseBlockProtocolGuid,\r
+                  (VOID **)&EraseBlock,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = gBS->UninstallProtocolInterface (\r
+                    Controller,\r
+                    &gEfiEraseBlockProtocolGuid,\r
+                    &ScsiDiskDevice->EraseBlock\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  Status = gBS->UninstallMultipleProtocolInterfaces (\r
                   Controller,\r
                   &gEfiBlockIoProtocolGuid,\r
-                  &ScsiDiskDevice->BlkIo\r
+                  &ScsiDiskDevice->BlkIo,\r
+                  &gEfiBlockIo2ProtocolGuid,\r
+                  &ScsiDiskDevice->BlkIo2,\r
+                  &gEfiDiskInfoProtocolGuid,\r
+                  &ScsiDiskDevice->DiskInfo,\r
+                  NULL\r
                   );\r
   if (!EFI_ERROR (Status)) {\r
     gBS->CloseProtocol (\r
@@ -360,6 +532,7 @@ ScsiDiskDriverBindingStop (
 \r
     return EFI_SUCCESS;\r
   }\r
+\r
   //\r
   // errors met\r
   //\r
@@ -376,25 +549,34 @@ ScsiDiskDriverBindingStop (
   @retval EFI_SUCCESS          The device was reset.\r
   @retval EFI_DEVICE_ERROR     The device is not functioning properly and could\r
                                not be reset.\r
-  @return EFI_STATUS is retured from EFI_SCSI_IO_PROTOCOL.ResetDevice().\r
+  @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice().\r
 \r
 **/\r
 EFI_STATUS\r
 EFIAPI\r
 ScsiDiskReset (\r
-  IN  EFI_BLOCK_IO_PROTOCOL   *This,\r
-  IN  BOOLEAN                 ExtendedVerification\r
+  IN  EFI_BLOCK_IO_PROTOCOL  *This,\r
+  IN  BOOLEAN                ExtendedVerification\r
   )\r
 {\r
-  EFI_TPL       OldTpl;\r
-  SCSI_DISK_DEV *ScsiDiskDevice;\r
-  EFI_STATUS    Status;\r
+  EFI_TPL        OldTpl;\r
+  SCSI_DISK_DEV  *ScsiDiskDevice;\r
+  EFI_STATUS     Status;\r
 \r
   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
 \r
-  ScsiDiskDevice  = SCSI_DISK_DEV_FROM_THIS (This);\r
+  ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This);\r
+\r
+  Status = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
 \r
-  Status          = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+  if (EFI_ERROR (Status)) {\r
+    if (Status == EFI_UNSUPPORTED) {\r
+      Status = EFI_SUCCESS;\r
+    } else {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto Done;\r
+    }\r
+  }\r
 \r
   if (!ExtendedVerification) {\r
     goto Done;\r
@@ -402,6 +584,11 @@ ScsiDiskReset (
 \r
   Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
 \r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto Done;\r
+  }\r
+\r
 Done:\r
   gBS->RestoreTPL (OldTpl);\r
   return Status;\r
@@ -427,11 +614,11 @@ Done:
 EFI_STATUS\r
 EFIAPI\r
 ScsiDiskReadBlocks (\r
-  IN  EFI_BLOCK_IO_PROTOCOL   *This,\r
-  IN  UINT32                  MediaId,\r
-  IN  EFI_LBA                 Lba,\r
-  IN  UINTN                   BufferSize,\r
-  OUT VOID                    *Buffer\r
+  IN  EFI_BLOCK_IO_PROTOCOL  *This,\r
+  IN  UINT32                 MediaId,\r
+  IN  EFI_LBA                Lba,\r
+  IN  UINTN                  BufferSize,\r
+  OUT VOID                   *Buffer\r
   )\r
 {\r
   SCSI_DISK_DEV       *ScsiDiskDevice;\r
@@ -442,21 +629,12 @@ ScsiDiskReadBlocks (
   BOOLEAN             MediaChange;\r
   EFI_TPL             OldTpl;\r
 \r
-  MediaChange = FALSE;\r
-  if (Buffer == NULL) {\r
-    return EFI_INVALID_PARAMETER;\r
-  }\r
-\r
-  if (BufferSize == 0) {\r
-    return EFI_SUCCESS;\r
-  }\r
-\r
-  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
-\r
-  ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);\r
-\r
-  if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
+  MediaChange    = FALSE;\r
+  OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);\r
+  ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This);\r
+  Media          = ScsiDiskDevice->BlkIo.Media;\r
 \r
+  if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
     Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
     if (EFI_ERROR (Status)) {\r
       Status = EFI_DEVICE_ERROR;\r
@@ -465,20 +643,56 @@ ScsiDiskReadBlocks (
 \r
     if (MediaChange) {\r
       gBS->ReinstallProtocolInterface (\r
-            ScsiDiskDevice->Handle,\r
-            &gEfiBlockIoProtocolGuid,\r
-            &ScsiDiskDevice->BlkIo,\r
-            &ScsiDiskDevice->BlkIo\r
-            );\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIoProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo,\r
+             &ScsiDiskDevice->BlkIo\r
+             );\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIo2ProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo2,\r
+             &ScsiDiskDevice->BlkIo2\r
+             );\r
+      if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiEraseBlockProtocolGuid,\r
+               &ScsiDiskDevice->EraseBlock,\r
+               &ScsiDiskDevice->EraseBlock\r
+               );\r
+      }\r
+\r
+      if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiStorageSecurityCommandProtocolGuid,\r
+               &ScsiDiskDevice->StorageSecurity,\r
+               &ScsiDiskDevice->StorageSecurity\r
+               );\r
+      }\r
+\r
+      if (Media->MediaPresent) {\r
+        Status = EFI_MEDIA_CHANGED;\r
+      } else {\r
+        Status = EFI_NO_MEDIA;\r
+      }\r
+\r
+      goto Done;\r
     }\r
   }\r
+\r
   //\r
   // Get the intrinsic block size\r
   //\r
-  Media           = ScsiDiskDevice->BlkIo.Media;\r
-  BlockSize       = Media->BlockSize;\r
+  BlockSize = Media->BlockSize;\r
+\r
+  if (BlockSize == 0) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto Done;\r
+  }\r
 \r
-  NumberOfBlocks  = BufferSize / BlockSize;\r
+  NumberOfBlocks = BufferSize / BlockSize;\r
 \r
   if (!(Media->MediaPresent)) {\r
     Status = EFI_NO_MEDIA;\r
@@ -490,6 +704,16 @@ ScsiDiskReadBlocks (
     goto Done;\r
   }\r
 \r
+  if (Buffer == NULL) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto Done;\r
+  }\r
+\r
+  if (BufferSize == 0) {\r
+    Status = EFI_SUCCESS;\r
+    goto Done;\r
+  }\r
+\r
   if (BufferSize % BlockSize != 0) {\r
     Status = EFI_BAD_BUFFER_SIZE;\r
     goto Done;\r
@@ -505,7 +729,7 @@ ScsiDiskReadBlocks (
     goto Done;\r
   }\r
 \r
-  if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {\r
+  if ((Media->IoAlign > 1) && (((UINTN)Buffer & (Media->IoAlign - 1)) != 0)) {\r
     Status = EFI_INVALID_PARAMETER;\r
     goto Done;\r
   }\r
@@ -534,7 +758,7 @@ Done:
   @retval EFI_WRITE_PROTECTED   The device can not be written to.\r
   @retval EFI_DEVICE_ERROR      Fail to detect media.\r
   @retval EFI_NO_MEDIA          Media is not present.\r
-  @retval EFI_MEDIA_CHNAGED     Media has changed.\r
+  @retval EFI_MEDIA_CHANGED     Media has changed.\r
   @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.\r
   @retval EFI_INVALID_PARAMETER Invalid parameter passed in.\r
 \r
@@ -542,11 +766,11 @@ Done:
 EFI_STATUS\r
 EFIAPI\r
 ScsiDiskWriteBlocks (\r
-  IN  EFI_BLOCK_IO_PROTOCOL   *This,\r
-  IN  UINT32                  MediaId,\r
-  IN  EFI_LBA                 Lba,\r
-  IN  UINTN                   BufferSize,\r
-  IN  VOID                    *Buffer\r
+  IN  EFI_BLOCK_IO_PROTOCOL  *This,\r
+  IN  UINT32                 MediaId,\r
+  IN  EFI_LBA                Lba,\r
+  IN  UINTN                  BufferSize,\r
+  IN  VOID                   *Buffer\r
   )\r
 {\r
   SCSI_DISK_DEV       *ScsiDiskDevice;\r
@@ -557,21 +781,12 @@ ScsiDiskWriteBlocks (
   BOOLEAN             MediaChange;\r
   EFI_TPL             OldTpl;\r
 \r
-  MediaChange = FALSE;\r
-  if (Buffer == NULL) {\r
-    return EFI_INVALID_PARAMETER;\r
-  }\r
-\r
-  if (BufferSize == 0) {\r
-    return EFI_SUCCESS;\r
-  }\r
-\r
-  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
-\r
-  ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);\r
-\r
-  if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
+  MediaChange    = FALSE;\r
+  OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);\r
+  ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This);\r
+  Media          = ScsiDiskDevice->BlkIo.Media;\r
 \r
+  if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
     Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
     if (EFI_ERROR (Status)) {\r
       Status = EFI_DEVICE_ERROR;\r
@@ -580,20 +795,56 @@ ScsiDiskWriteBlocks (
 \r
     if (MediaChange) {\r
       gBS->ReinstallProtocolInterface (\r
-            ScsiDiskDevice->Handle,\r
-            &gEfiBlockIoProtocolGuid,\r
-            &ScsiDiskDevice->BlkIo,\r
-            &ScsiDiskDevice->BlkIo\r
-            );\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIoProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo,\r
+             &ScsiDiskDevice->BlkIo\r
+             );\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIo2ProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo2,\r
+             &ScsiDiskDevice->BlkIo2\r
+             );\r
+      if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiEraseBlockProtocolGuid,\r
+               &ScsiDiskDevice->EraseBlock,\r
+               &ScsiDiskDevice->EraseBlock\r
+               );\r
+      }\r
+\r
+      if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiStorageSecurityCommandProtocolGuid,\r
+               &ScsiDiskDevice->StorageSecurity,\r
+               &ScsiDiskDevice->StorageSecurity\r
+               );\r
+      }\r
+\r
+      if (Media->MediaPresent) {\r
+        Status = EFI_MEDIA_CHANGED;\r
+      } else {\r
+        Status = EFI_NO_MEDIA;\r
+      }\r
+\r
+      goto Done;\r
     }\r
   }\r
+\r
   //\r
   // Get the intrinsic block size\r
   //\r
-  Media           = ScsiDiskDevice->BlkIo.Media;\r
-  BlockSize       = Media->BlockSize;\r
+  BlockSize = Media->BlockSize;\r
+\r
+  if (BlockSize == 0) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto Done;\r
+  }\r
 \r
-  NumberOfBlocks  = BufferSize / BlockSize;\r
+  NumberOfBlocks = BufferSize / BlockSize;\r
 \r
   if (!(Media->MediaPresent)) {\r
     Status = EFI_NO_MEDIA;\r
@@ -605,6 +856,21 @@ ScsiDiskWriteBlocks (
     goto Done;\r
   }\r
 \r
+  if (Media->ReadOnly) {\r
+    Status = EFI_WRITE_PROTECTED;\r
+    goto Done;\r
+  }\r
+\r
+  if (BufferSize == 0) {\r
+    Status = EFI_SUCCESS;\r
+    goto Done;\r
+  }\r
+\r
+  if (Buffer == NULL) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto Done;\r
+  }\r
+\r
   if (BufferSize % BlockSize != 0) {\r
     Status = EFI_BAD_BUFFER_SIZE;\r
     goto Done;\r
@@ -620,10 +886,11 @@ ScsiDiskWriteBlocks (
     goto Done;\r
   }\r
 \r
-  if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {\r
+  if ((Media->IoAlign > 1) && (((UINTN)Buffer & (Media->IoAlign - 1)) != 0)) {\r
     Status = EFI_INVALID_PARAMETER;\r
     goto Done;\r
   }\r
+\r
   //\r
   // if all the parameters are valid, then perform read sectors command\r
   // to transfer data from device to host.\r
@@ -648,7 +915,7 @@ Done:
 EFI_STATUS\r
 EFIAPI\r
 ScsiDiskFlushBlocks (\r
-  IN  EFI_BLOCK_IO_PROTOCOL   *This\r
+  IN  EFI_BLOCK_IO_PROTOCOL  *This\r
   )\r
 {\r
   //\r
@@ -657,262 +924,3729 @@ ScsiDiskFlushBlocks (
   return EFI_SUCCESS;\r
 }\r
 \r
-\r
 /**\r
-  Dectect Device and read out capacity ,if error occurs, parse the sense key.\r
+  Reset SCSI Disk.\r
 \r
-  @param  ScsiDiskDevice    The pointer of SCSI_DISK_DEV\r
-  @param  MustReadCapacity  The flag about reading device capacity\r
-  @param  MediaChange       The pointer of flag indicates if media has changed \r
+  @param  This                 The pointer of EFI_BLOCK_IO2_PROTOCOL.\r
+  @param  ExtendedVerification The flag about if extend verificate.\r
 \r
-  @retval EFI_DEVICE_ERROR  Indicates that error occurs\r
-  @retval EFI_SUCCESS       Successfully to detect media\r
+  @retval EFI_SUCCESS          The device was reset.\r
+  @retval EFI_DEVICE_ERROR     The device is not functioning properly and could\r
+                               not be reset.\r
+  @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice().\r
 \r
 **/\r
 EFI_STATUS\r
-ScsiDiskDetectMedia (\r
-  IN   SCSI_DISK_DEV   *ScsiDiskDevice,\r
-  IN   BOOLEAN         MustReadCapacity,\r
-  OUT  BOOLEAN         *MediaChange\r
+EFIAPI\r
+ScsiDiskResetEx (\r
+  IN  EFI_BLOCK_IO2_PROTOCOL  *This,\r
+  IN  BOOLEAN                 ExtendedVerification\r
   )\r
 {\r
-  EFI_STATUS          Status;\r
-  EFI_STATUS          ReadCapacityStatus;\r
-  EFI_SCSI_SENSE_DATA *SenseData;\r
-  UINTN               NumberOfSenseKeys;\r
-  BOOLEAN             NeedRetry;\r
-  BOOLEAN             NeedReadCapacity;\r
-  UINT8               Index;\r
-  UINT8               MaxRetry;\r
-  EFI_BLOCK_IO_MEDIA  OldMedia;\r
-  UINTN               Action;\r
-\r
-  Status              = EFI_SUCCESS;\r
-  ReadCapacityStatus  = EFI_SUCCESS;\r
-  SenseData           = NULL;\r
-  NumberOfSenseKeys   = 0;\r
-  NeedReadCapacity    = FALSE;\r
-  CopyMem (&OldMedia, ScsiDiskDevice->BlkIo.Media, sizeof (OldMedia));\r
-  *MediaChange        = FALSE;\r
-  MaxRetry            = 3;\r
+  EFI_TPL        OldTpl;\r
+  SCSI_DISK_DEV  *ScsiDiskDevice;\r
+  EFI_STATUS     Status;\r
 \r
-  for (Index = 0; Index < MaxRetry; Index++) {\r
-    Status = ScsiDiskTestUnitReady (\r
-              ScsiDiskDevice,\r
-              &NeedRetry,\r
-              &SenseData,\r
-              &NumberOfSenseKeys\r
-              );\r
-    if (!EFI_ERROR (Status)) {\r
-      break;\r
-    }\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
 \r
-    if (!NeedRetry) {\r
-      return Status;\r
+  ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
+\r
+  Status = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    if (Status == EFI_UNSUPPORTED) {\r
+      Status = EFI_SUCCESS;\r
+    } else {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto Done;\r
     }\r
   }\r
 \r
-  if ((Index == MaxRetry) && EFI_ERROR (Status)) {\r
-    return EFI_DEVICE_ERROR;\r
+  if (!ExtendedVerification) {\r
+    goto Done;\r
   }\r
 \r
-  Status = DetectMediaParsingSenseKeys (\r
-            ScsiDiskDevice,\r
-            SenseData,\r
-            NumberOfSenseKeys,\r
-            &Action\r
-            );\r
+  Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
+\r
   if (EFI_ERROR (Status)) {\r
-    return Status;\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto Done;\r
   }\r
-  //\r
-  // ACTION_NO_ACTION: need not read capacity\r
+\r
+Done:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The function is to Read Block from SCSI Disk.\r
+\r
+  @param  This       The pointer of EFI_BLOCK_IO_PROTOCOL.\r
+  @param  MediaId    The Id of Media detected.\r
+  @param  Lba        The logic block address.\r
+  @param  Token      A pointer to the token associated with the transaction.\r
+  @param  BufferSize The size of Buffer.\r
+  @param  Buffer     The buffer to fill the read out data.\r
+\r
+  @retval EFI_SUCCESS           The read request was queued if Token-> Event is\r
+                                not NULL. The data was read correctly from the\r
+                                device if theToken-> Event is NULL.\r
+  @retval EFI_DEVICE_ERROR      The device reported an error while attempting\r
+                                to perform the read operation.\r
+  @retval EFI_NO_MEDIA          There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED     The MediaId is not for the current media.\r
+  @retval EFI_BAD_BUFFER_SIZE   The BufferSize parameter is not a multiple of\r
+                                the intrinsic block size of the device.\r
+  @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not\r
+                                valid, or the buffer is not on proper\r
+                                alignment.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a\r
+                                lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskReadBlocksEx (\r
+  IN     EFI_BLOCK_IO2_PROTOCOL  *This,\r
+  IN     UINT32                  MediaId,\r
+  IN     EFI_LBA                 Lba,\r
+  IN OUT EFI_BLOCK_IO2_TOKEN     *Token,\r
+  IN     UINTN                   BufferSize,\r
+  OUT    VOID                    *Buffer\r
+  )\r
+{\r
+  SCSI_DISK_DEV       *ScsiDiskDevice;\r
+  EFI_BLOCK_IO_MEDIA  *Media;\r
+  EFI_STATUS          Status;\r
+  UINTN               BlockSize;\r
+  UINTN               NumberOfBlocks;\r
+  BOOLEAN             MediaChange;\r
+  EFI_TPL             OldTpl;\r
+\r
+  MediaChange    = FALSE;\r
+  OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);\r
+  ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
+  Media          = ScsiDiskDevice->BlkIo.Media;\r
+\r
+  if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
+    Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+    if (EFI_ERROR (Status)) {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto Done;\r
+    }\r
+\r
+    if (MediaChange) {\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIoProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo,\r
+             &ScsiDiskDevice->BlkIo\r
+             );\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIo2ProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo2,\r
+             &ScsiDiskDevice->BlkIo2\r
+             );\r
+      if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiEraseBlockProtocolGuid,\r
+               &ScsiDiskDevice->EraseBlock,\r
+               &ScsiDiskDevice->EraseBlock\r
+               );\r
+      }\r
+\r
+      if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiStorageSecurityCommandProtocolGuid,\r
+               &ScsiDiskDevice->StorageSecurity,\r
+               &ScsiDiskDevice->StorageSecurity\r
+               );\r
+      }\r
+\r
+      if (Media->MediaPresent) {\r
+        Status = EFI_MEDIA_CHANGED;\r
+      } else {\r
+        Status = EFI_NO_MEDIA;\r
+      }\r
+\r
+      goto Done;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Get the intrinsic block size\r
+  //\r
+  BlockSize = Media->BlockSize;\r
+\r
+  if (BlockSize == 0) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto Done;\r
+  }\r
+\r
+  NumberOfBlocks = BufferSize / BlockSize;\r
+\r
+  if (!(Media->MediaPresent)) {\r
+    Status = EFI_NO_MEDIA;\r
+    goto Done;\r
+  }\r
+\r
+  if (MediaId != Media->MediaId) {\r
+    Status = EFI_MEDIA_CHANGED;\r
+    goto Done;\r
+  }\r
+\r
+  if (Buffer == NULL) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto Done;\r
+  }\r
+\r
+  if (BufferSize == 0) {\r
+    if ((Token != NULL) && (Token->Event != NULL)) {\r
+      Token->TransactionStatus = EFI_SUCCESS;\r
+      gBS->SignalEvent (Token->Event);\r
+    }\r
+\r
+    Status = EFI_SUCCESS;\r
+    goto Done;\r
+  }\r
+\r
+  if (BufferSize % BlockSize != 0) {\r
+    Status = EFI_BAD_BUFFER_SIZE;\r
+    goto Done;\r
+  }\r
+\r
+  if (Lba > Media->LastBlock) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto Done;\r
+  }\r
+\r
+  if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto Done;\r
+  }\r
+\r
+  if ((Media->IoAlign > 1) && (((UINTN)Buffer & (Media->IoAlign - 1)) != 0)) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto Done;\r
+  }\r
+\r
+  //\r
+  // If all the parameters are valid, then perform read sectors command\r
+  // to transfer data from device to host.\r
+  //\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Token->TransactionStatus = EFI_SUCCESS;\r
+    Status                   = ScsiDiskAsyncReadSectors (\r
+                                 ScsiDiskDevice,\r
+                                 Buffer,\r
+                                 Lba,\r
+                                 NumberOfBlocks,\r
+                                 Token\r
+                                 );\r
+  } else {\r
+    Status = ScsiDiskReadSectors (\r
+               ScsiDiskDevice,\r
+               Buffer,\r
+               Lba,\r
+               NumberOfBlocks\r
+               );\r
+  }\r
+\r
+Done:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The function is to Write Block to SCSI Disk.\r
+\r
+  @param  This       The pointer of EFI_BLOCK_IO_PROTOCOL.\r
+  @param  MediaId    The Id of Media detected.\r
+  @param  Lba        The logic block address.\r
+  @param  Token      A pointer to the token associated with the transaction.\r
+  @param  BufferSize The size of Buffer.\r
+  @param  Buffer     The buffer to fill the read out data.\r
+\r
+  @retval EFI_SUCCESS           The data were written correctly to the device.\r
+  @retval EFI_WRITE_PROTECTED   The device cannot be written to.\r
+  @retval EFI_NO_MEDIA          There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED     The MediaId is not for the current media.\r
+  @retval EFI_DEVICE_ERROR      The device reported an error while attempting\r
+                                to perform the write operation.\r
+  @retval EFI_BAD_BUFFER_SIZE   The BufferSize parameter is not a multiple of\r
+                                the intrinsic block size of the device.\r
+  @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not\r
+                                valid, or the buffer is not on proper\r
+                                alignment.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskWriteBlocksEx (\r
+  IN     EFI_BLOCK_IO2_PROTOCOL  *This,\r
+  IN     UINT32                  MediaId,\r
+  IN     EFI_LBA                 Lba,\r
+  IN OUT EFI_BLOCK_IO2_TOKEN     *Token,\r
+  IN     UINTN                   BufferSize,\r
+  IN     VOID                    *Buffer\r
+  )\r
+{\r
+  SCSI_DISK_DEV       *ScsiDiskDevice;\r
+  EFI_BLOCK_IO_MEDIA  *Media;\r
+  EFI_STATUS          Status;\r
+  UINTN               BlockSize;\r
+  UINTN               NumberOfBlocks;\r
+  BOOLEAN             MediaChange;\r
+  EFI_TPL             OldTpl;\r
+\r
+  MediaChange    = FALSE;\r
+  OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);\r
+  ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
+  Media          = ScsiDiskDevice->BlkIo.Media;\r
+\r
+  if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
+    Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+    if (EFI_ERROR (Status)) {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto Done;\r
+    }\r
+\r
+    if (MediaChange) {\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIoProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo,\r
+             &ScsiDiskDevice->BlkIo\r
+             );\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIo2ProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo2,\r
+             &ScsiDiskDevice->BlkIo2\r
+             );\r
+      if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiEraseBlockProtocolGuid,\r
+               &ScsiDiskDevice->EraseBlock,\r
+               &ScsiDiskDevice->EraseBlock\r
+               );\r
+      }\r
+\r
+      if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiStorageSecurityCommandProtocolGuid,\r
+               &ScsiDiskDevice->StorageSecurity,\r
+               &ScsiDiskDevice->StorageSecurity\r
+               );\r
+      }\r
+\r
+      if (Media->MediaPresent) {\r
+        Status = EFI_MEDIA_CHANGED;\r
+      } else {\r
+        Status = EFI_NO_MEDIA;\r
+      }\r
+\r
+      goto Done;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Get the intrinsic block size\r
+  //\r
+  BlockSize = Media->BlockSize;\r
+\r
+  if (BlockSize == 0) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto Done;\r
+  }\r
+\r
+  NumberOfBlocks = BufferSize / BlockSize;\r
+\r
+  if (!(Media->MediaPresent)) {\r
+    Status = EFI_NO_MEDIA;\r
+    goto Done;\r
+  }\r
+\r
+  if (MediaId != Media->MediaId) {\r
+    Status = EFI_MEDIA_CHANGED;\r
+    goto Done;\r
+  }\r
+\r
+  if (Media->ReadOnly) {\r
+    Status = EFI_WRITE_PROTECTED;\r
+    goto Done;\r
+  }\r
+\r
+  if (BufferSize == 0) {\r
+    if ((Token != NULL) && (Token->Event != NULL)) {\r
+      Token->TransactionStatus = EFI_SUCCESS;\r
+      gBS->SignalEvent (Token->Event);\r
+    }\r
+\r
+    Status = EFI_SUCCESS;\r
+    goto Done;\r
+  }\r
+\r
+  if (Buffer == NULL) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto Done;\r
+  }\r
+\r
+  if (BufferSize % BlockSize != 0) {\r
+    Status = EFI_BAD_BUFFER_SIZE;\r
+    goto Done;\r
+  }\r
+\r
+  if (Lba > Media->LastBlock) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto Done;\r
+  }\r
+\r
+  if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto Done;\r
+  }\r
+\r
+  if ((Media->IoAlign > 1) && (((UINTN)Buffer & (Media->IoAlign - 1)) != 0)) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto Done;\r
+  }\r
+\r
+  //\r
+  // if all the parameters are valid, then perform write sectors command\r
+  // to transfer data from device to host.\r
+  //\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Token->TransactionStatus = EFI_SUCCESS;\r
+    Status                   = ScsiDiskAsyncWriteSectors (\r
+                                 ScsiDiskDevice,\r
+                                 Buffer,\r
+                                 Lba,\r
+                                 NumberOfBlocks,\r
+                                 Token\r
+                                 );\r
+  } else {\r
+    Status = ScsiDiskWriteSectors (\r
+               ScsiDiskDevice,\r
+               Buffer,\r
+               Lba,\r
+               NumberOfBlocks\r
+               );\r
+  }\r
+\r
+Done:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Flush the Block Device.\r
+\r
+  @param  This       Indicates a pointer to the calling context.\r
+  @param  Token      A pointer to the token associated with the transaction.\r
+\r
+  @retval EFI_SUCCESS         All outstanding data was written to the device.\r
+  @retval EFI_DEVICE_ERROR    The device reported an error while attempting to\r
+                              write data.\r
+  @retval EFI_WRITE_PROTECTED The device cannot be written to.\r
+  @retval EFI_NO_MEDIA        There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED   The MediaId is not for the current media.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskFlushBlocksEx (\r
+  IN     EFI_BLOCK_IO2_PROTOCOL  *This,\r
+  IN OUT EFI_BLOCK_IO2_TOKEN     *Token\r
+  )\r
+{\r
+  SCSI_DISK_DEV       *ScsiDiskDevice;\r
+  EFI_BLOCK_IO_MEDIA  *Media;\r
+  EFI_STATUS          Status;\r
+  BOOLEAN             MediaChange;\r
+  EFI_TPL             OldTpl;\r
+\r
+  MediaChange    = FALSE;\r
+  OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);\r
+  ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
+  Media          = ScsiDiskDevice->BlkIo.Media;\r
+\r
+  if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
+    Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+    if (EFI_ERROR (Status)) {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto Done;\r
+    }\r
+\r
+    if (MediaChange) {\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIoProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo,\r
+             &ScsiDiskDevice->BlkIo\r
+             );\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIo2ProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo2,\r
+             &ScsiDiskDevice->BlkIo2\r
+             );\r
+      if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiEraseBlockProtocolGuid,\r
+               &ScsiDiskDevice->EraseBlock,\r
+               &ScsiDiskDevice->EraseBlock\r
+               );\r
+      }\r
+\r
+      if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiStorageSecurityCommandProtocolGuid,\r
+               &ScsiDiskDevice->StorageSecurity,\r
+               &ScsiDiskDevice->StorageSecurity\r
+               );\r
+      }\r
+\r
+      if (Media->MediaPresent) {\r
+        Status = EFI_MEDIA_CHANGED;\r
+      } else {\r
+        Status = EFI_NO_MEDIA;\r
+      }\r
+\r
+      goto Done;\r
+    }\r
+  }\r
+\r
+  if (!(Media->MediaPresent)) {\r
+    Status = EFI_NO_MEDIA;\r
+    goto Done;\r
+  }\r
+\r
+  if (Media->ReadOnly) {\r
+    Status = EFI_WRITE_PROTECTED;\r
+    goto Done;\r
+  }\r
+\r
+  //\r
+  // Wait for the BlockIo2 requests queue to become empty\r
+  //\r
+  while (!IsListEmpty (&ScsiDiskDevice->AsyncTaskQueue)) {\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  //\r
+  // Signal caller event\r
+  //\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Token->TransactionStatus = EFI_SUCCESS;\r
+    gBS->SignalEvent (Token->Event);\r
+  }\r
+\r
+Done:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Internal helper notify function which process the result of an asynchronous\r
+  SCSI UNMAP Command and signal the event passed from EraseBlocks.\r
+\r
+  @param  Event    The instance of EFI_EVENT.\r
+  @param  Context  The parameter passed in.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+ScsiDiskAsyncUnmapNotify (\r
+  IN  EFI_EVENT  Event,\r
+  IN  VOID       *Context\r
+  )\r
+{\r
+  SCSI_ERASEBLK_REQUEST            *EraseBlkReq;\r
+  EFI_SCSI_IO_SCSI_REQUEST_PACKET  *CommandPacket;\r
+  EFI_ERASE_BLOCK_TOKEN            *Token;\r
+  EFI_STATUS                       Status;\r
+\r
+  gBS->CloseEvent (Event);\r
+\r
+  EraseBlkReq              = (SCSI_ERASEBLK_REQUEST *)Context;\r
+  CommandPacket            = &EraseBlkReq->CommandPacket;\r
+  Token                    = EraseBlkReq->Token;\r
+  Token->TransactionStatus = EFI_SUCCESS;\r
+\r
+  Status = CheckHostAdapterStatus (CommandPacket->HostAdapterStatus);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "ScsiDiskAsyncUnmapNotify: Host adapter indicating error status 0x%x.\n",\r
+      CommandPacket->HostAdapterStatus\r
+      ));\r
+\r
+    Token->TransactionStatus = Status;\r
+    goto Done;\r
+  }\r
+\r
+  Status = CheckTargetStatus (CommandPacket->TargetStatus);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "ScsiDiskAsyncUnmapNotify: Target indicating error status 0x%x.\n",\r
+      CommandPacket->HostAdapterStatus\r
+      ));\r
+\r
+    Token->TransactionStatus = Status;\r
+    goto Done;\r
+  }\r
+\r
+Done:\r
+  RemoveEntryList (&EraseBlkReq->Link);\r
+  FreePool (CommandPacket->OutDataBuffer);\r
+  FreePool (EraseBlkReq->CommandPacket.Cdb);\r
+  FreePool (EraseBlkReq);\r
+\r
+  gBS->SignalEvent (Token->Event);\r
+}\r
+\r
+/**\r
+  Require the device server to cause one or more LBAs to be unmapped.\r
+\r
+  @param  ScsiDiskDevice         The pointer of ScsiDiskDevice.\r
+  @param  Lba                    The start block number.\r
+  @param  Blocks                 Total block number to be unmapped.\r
+  @param  Token                  The pointer to the token associated with the\r
+                                 non-blocking erase block request.\r
+\r
+  @retval EFI_SUCCESS            Target blocks have been successfully unmapped.\r
+  @retval EFI_DEVICE_ERROR       Fail to unmap the target blocks.\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskUnmap (\r
+  IN SCSI_DISK_DEV          *ScsiDiskDevice,\r
+  IN UINT64                 Lba,\r
+  IN UINTN                  Blocks,\r
+  IN EFI_ERASE_BLOCK_TOKEN  *Token            OPTIONAL\r
+  )\r
+{\r
+  EFI_SCSI_IO_PROTOCOL             *ScsiIo;\r
+  SCSI_ERASEBLK_REQUEST            *EraseBlkReq;\r
+  EFI_SCSI_IO_SCSI_REQUEST_PACKET  *CommandPacket;\r
+  EFI_SCSI_DISK_UNMAP_BLOCK_DESP   *BlkDespPtr;\r
+  EFI_STATUS                       Status;\r
+  EFI_STATUS                       ReturnStatus;\r
+  UINT8                            *Cdb;\r
+  UINT32                           MaxLbaCnt;\r
+  UINT32                           MaxBlkDespCnt;\r
+  UINT32                           BlkDespCnt;\r
+  UINT16                           UnmapParamListLen;\r
+  VOID                             *UnmapParamList;\r
+  EFI_EVENT                        AsyncUnmapEvent;\r
+  EFI_TPL                          OldTpl;\r
+\r
+  ScsiIo          = ScsiDiskDevice->ScsiIo;\r
+  MaxLbaCnt       = ScsiDiskDevice->UnmapInfo.MaxLbaCnt;\r
+  MaxBlkDespCnt   = ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt;\r
+  EraseBlkReq     = NULL;\r
+  UnmapParamList  = NULL;\r
+  AsyncUnmapEvent = NULL;\r
+  ReturnStatus    = EFI_SUCCESS;\r
+\r
+  if (Blocks / (UINTN)MaxLbaCnt > MaxBlkDespCnt) {\r
+    ReturnStatus = EFI_DEVICE_ERROR;\r
+    goto Done;\r
+  }\r
+\r
+  EraseBlkReq = AllocateZeroPool (sizeof (SCSI_ERASEBLK_REQUEST));\r
+  if (EraseBlkReq == NULL) {\r
+    ReturnStatus = EFI_DEVICE_ERROR;\r
+    goto Done;\r
+  }\r
+\r
+  EraseBlkReq->CommandPacket.Cdb = AllocateZeroPool (0xA);\r
+  if (EraseBlkReq->CommandPacket.Cdb == NULL) {\r
+    ReturnStatus = EFI_DEVICE_ERROR;\r
+    goto Done;\r
+  }\r
+\r
+  BlkDespCnt        = (UINT32)((Blocks - 1) / MaxLbaCnt + 1);\r
+  UnmapParamListLen = (UINT16)(sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER)\r
+                               + BlkDespCnt * sizeof (EFI_SCSI_DISK_UNMAP_BLOCK_DESP));\r
+  UnmapParamList = AllocateZeroPool (UnmapParamListLen);\r
+  if (UnmapParamList == NULL) {\r
+    ReturnStatus = EFI_DEVICE_ERROR;\r
+    goto Done;\r
+  }\r
+\r
+  *((UINT16 *)UnmapParamList)     = SwapBytes16 (UnmapParamListLen - 2);\r
+  *((UINT16 *)UnmapParamList + 1) = SwapBytes16 (UnmapParamListLen - sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER));\r
+\r
+  BlkDespPtr = (EFI_SCSI_DISK_UNMAP_BLOCK_DESP *)((UINT8 *)UnmapParamList + sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER));\r
+  while (Blocks > 0) {\r
+    if (Blocks > MaxLbaCnt) {\r
+      *(UINT64 *)(&BlkDespPtr->Lba)      = SwapBytes64 (Lba);\r
+      *(UINT32 *)(&BlkDespPtr->BlockNum) = SwapBytes32 (MaxLbaCnt);\r
+      Blocks                            -= MaxLbaCnt;\r
+      Lba                               += MaxLbaCnt;\r
+    } else {\r
+      *(UINT64 *)(&BlkDespPtr->Lba)      = SwapBytes64 (Lba);\r
+      *(UINT32 *)(&BlkDespPtr->BlockNum) = SwapBytes32 ((UINT32)Blocks);\r
+      Blocks                             = 0;\r
+    }\r
+\r
+    BlkDespPtr++;\r
+  }\r
+\r
+  CommandPacket                    = &EraseBlkReq->CommandPacket;\r
+  CommandPacket->Timeout           = SCSI_DISK_TIMEOUT;\r
+  CommandPacket->OutDataBuffer     = UnmapParamList;\r
+  CommandPacket->OutTransferLength = UnmapParamListLen;\r
+  CommandPacket->CdbLength         = 0xA;\r
+  CommandPacket->DataDirection     = EFI_SCSI_DATA_OUT;\r
+  //\r
+  // Fill Cdb for UNMAP Command\r
+  //\r
+  Cdb    = CommandPacket->Cdb;\r
+  Cdb[0] = EFI_SCSI_OP_UNMAP;\r
+  WriteUnaligned16 ((UINT16 *)&Cdb[7], SwapBytes16 (UnmapParamListLen));\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    //\r
+    // Non-blocking UNMAP request\r
+    //\r
+    Status = gBS->CreateEvent (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_NOTIFY,\r
+                    ScsiDiskAsyncUnmapNotify,\r
+                    EraseBlkReq,\r
+                    &AsyncUnmapEvent\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      ReturnStatus = EFI_DEVICE_ERROR;\r
+      goto Done;\r
+    }\r
+\r
+    OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+    InsertTailList (&ScsiDiskDevice->AsyncTaskQueue, &EraseBlkReq->Link);\r
+    gBS->RestoreTPL (OldTpl);\r
+\r
+    EraseBlkReq->Token = Token;\r
+\r
+    Status = ScsiIo->ExecuteScsiCommand (\r
+                       ScsiIo,\r
+                       CommandPacket,\r
+                       AsyncUnmapEvent\r
+                       );\r
+    if (EFI_ERROR (Status)) {\r
+      ReturnStatus = EFI_DEVICE_ERROR;\r
+\r
+      OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+      RemoveEntryList (&EraseBlkReq->Link);\r
+      gBS->RestoreTPL (OldTpl);\r
+\r
+      goto Done;\r
+    } else {\r
+      //\r
+      // Directly return if the non-blocking UNMAP request is queued.\r
+      //\r
+      return EFI_SUCCESS;\r
+    }\r
+  } else {\r
+    //\r
+    // Blocking UNMAP request\r
+    //\r
+    Status = ScsiIo->ExecuteScsiCommand (\r
+                       ScsiIo,\r
+                       CommandPacket,\r
+                       NULL\r
+                       );\r
+    if (EFI_ERROR (Status)) {\r
+      ReturnStatus = EFI_DEVICE_ERROR;\r
+      goto Done;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Only blocking UNMAP request will reach here.\r
+  //\r
+  Status = CheckHostAdapterStatus (CommandPacket->HostAdapterStatus);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "ScsiDiskUnmap: Host adapter indicating error status 0x%x.\n",\r
+      CommandPacket->HostAdapterStatus\r
+      ));\r
+\r
+    ReturnStatus = EFI_DEVICE_ERROR;\r
+    goto Done;\r
+  }\r
+\r
+  Status = CheckTargetStatus (CommandPacket->TargetStatus);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "ScsiDiskUnmap: Target indicating error status 0x%x.\n",\r
+      CommandPacket->HostAdapterStatus\r
+      ));\r
+\r
+    ReturnStatus = EFI_DEVICE_ERROR;\r
+    goto Done;\r
+  }\r
+\r
+Done:\r
+  if (EraseBlkReq != NULL) {\r
+    if (EraseBlkReq->CommandPacket.Cdb != NULL) {\r
+      FreePool (EraseBlkReq->CommandPacket.Cdb);\r
+    }\r
+\r
+    FreePool (EraseBlkReq);\r
+  }\r
+\r
+  if (UnmapParamList != NULL) {\r
+    FreePool (UnmapParamList);\r
+  }\r
+\r
+  if (AsyncUnmapEvent != NULL) {\r
+    gBS->CloseEvent (AsyncUnmapEvent);\r
+  }\r
+\r
+  return ReturnStatus;\r
+}\r
+\r
+/**\r
+  Erase a specified number of device blocks.\r
+\r
+  @param[in]       This           Indicates a pointer to the calling context.\r
+  @param[in]       MediaId        The media ID that the erase request is for.\r
+  @param[in]       Lba            The starting logical block address to be\r
+                                  erased. The caller is responsible for erasing\r
+                                  only legitimate locations.\r
+  @param[in, out]  Token          A pointer to the token associated with the\r
+                                  transaction.\r
+  @param[in]       Size           The size in bytes to be erased. This must be\r
+                                  a multiple of the physical block size of the\r
+                                  device.\r
+\r
+  @retval EFI_SUCCESS             The erase request was queued if Event is not\r
+                                  NULL. The data was erased correctly to the\r
+                                  device if the Event is NULL.to the device.\r
+  @retval EFI_WRITE_PROTECTED     The device cannot be erased due to write\r
+                                  protection.\r
+  @retval EFI_DEVICE_ERROR        The device reported an error while attempting\r
+                                  to perform the erase operation.\r
+  @retval EFI_INVALID_PARAMETER   The erase request contains LBAs that are not\r
+                                  valid.\r
+  @retval EFI_NO_MEDIA            There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED       The MediaId is not for the current media.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskEraseBlocks (\r
+  IN     EFI_ERASE_BLOCK_PROTOCOL  *This,\r
+  IN     UINT32                    MediaId,\r
+  IN     EFI_LBA                   Lba,\r
+  IN OUT EFI_ERASE_BLOCK_TOKEN     *Token,\r
+  IN     UINTN                     Size\r
+  )\r
+{\r
+  SCSI_DISK_DEV       *ScsiDiskDevice;\r
+  EFI_BLOCK_IO_MEDIA  *Media;\r
+  EFI_STATUS          Status;\r
+  UINTN               BlockSize;\r
+  UINTN               NumberOfBlocks;\r
+  BOOLEAN             MediaChange;\r
+  EFI_TPL             OldTpl;\r
+\r
+  MediaChange    = FALSE;\r
+  OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);\r
+  ScsiDiskDevice = SCSI_DISK_DEV_FROM_ERASEBLK (This);\r
+\r
+  if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
+    Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+    if (EFI_ERROR (Status)) {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto Done;\r
+    }\r
+\r
+    if (MediaChange) {\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIoProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo,\r
+             &ScsiDiskDevice->BlkIo\r
+             );\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIo2ProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo2,\r
+             &ScsiDiskDevice->BlkIo2\r
+             );\r
+      if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiEraseBlockProtocolGuid,\r
+               &ScsiDiskDevice->EraseBlock,\r
+               &ScsiDiskDevice->EraseBlock\r
+               );\r
+      }\r
+\r
+      if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiStorageSecurityCommandProtocolGuid,\r
+               &ScsiDiskDevice->StorageSecurity,\r
+               &ScsiDiskDevice->StorageSecurity\r
+               );\r
+      }\r
+\r
+      Status = EFI_MEDIA_CHANGED;\r
+      goto Done;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Get the intrinsic block size\r
+  //\r
+  Media = ScsiDiskDevice->BlkIo.Media;\r
+\r
+  if (!(Media->MediaPresent)) {\r
+    Status = EFI_NO_MEDIA;\r
+    goto Done;\r
+  }\r
+\r
+  if (MediaId != Media->MediaId) {\r
+    Status = EFI_MEDIA_CHANGED;\r
+    goto Done;\r
+  }\r
+\r
+  if (Media->ReadOnly) {\r
+    Status = EFI_WRITE_PROTECTED;\r
+    goto Done;\r
+  }\r
+\r
+  if (Size == 0) {\r
+    if ((Token != NULL) && (Token->Event != NULL)) {\r
+      Token->TransactionStatus = EFI_SUCCESS;\r
+      gBS->SignalEvent (Token->Event);\r
+    }\r
+\r
+    Status = EFI_SUCCESS;\r
+    goto Done;\r
+  }\r
+\r
+  BlockSize = Media->BlockSize;\r
+  if ((Size % BlockSize) != 0) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto Done;\r
+  }\r
+\r
+  NumberOfBlocks = Size / BlockSize;\r
+  if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto Done;\r
+  }\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Status = ScsiDiskUnmap (ScsiDiskDevice, Lba, NumberOfBlocks, Token);\r
+  } else {\r
+    Status = ScsiDiskUnmap (ScsiDiskDevice, Lba, NumberOfBlocks, NULL);\r
+  }\r
+\r
+Done:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Send a security protocol command to a device that receives data and/or the result\r
+  of one or more commands sent by SendData.\r
+\r
+  The ReceiveData function sends a security protocol command to the given MediaId.\r
+  The security protocol command sent is defined by SecurityProtocolId and contains\r
+  the security protocol specific data SecurityProtocolSpecificData. The function\r
+  returns the data from the security protocol command in PayloadBuffer.\r
+\r
+  For devices supporting the SCSI command set, the security protocol command is sent\r
+  using the SECURITY PROTOCOL IN command defined in SPC-4.\r
+\r
+  If PayloadBufferSize is too small to store the available data from the security\r
+  protocol command, the function shall copy PayloadBufferSize bytes into the\r
+  PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.\r
+\r
+  If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,\r
+  the function shall return EFI_INVALID_PARAMETER.\r
+\r
+  If the given MediaId does not support security protocol commands, the function shall\r
+  return EFI_UNSUPPORTED. If there is no media in the device, the function returns\r
+  EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device,\r
+  the function returns EFI_MEDIA_CHANGED.\r
+\r
+  If the security protocol fails to complete within the Timeout period, the function\r
+  shall return EFI_TIMEOUT.\r
+\r
+  If the security protocol command completes without an error, the function shall\r
+  return EFI_SUCCESS. If the security protocol command completes with an error, the\r
+  function shall return EFI_DEVICE_ERROR.\r
+\r
+  @param  This                         Indicates a pointer to the calling context.\r
+  @param  MediaId                      ID of the medium to receive data from.\r
+  @param  Timeout                      The timeout, in 100ns units, to use for the execution\r
+                                       of the security protocol command. A Timeout value of 0\r
+                                       means that this function will wait indefinitely for the\r
+                                       security protocol command to execute. If Timeout is greater\r
+                                       than zero, then this function will return EFI_TIMEOUT if the\r
+                                       time required to execute the receive data command is greater than Timeout.\r
+  @param  SecurityProtocolId           The value of the "Security Protocol" parameter of\r
+                                       the security protocol command to be sent.\r
+  @param  SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter\r
+                                       of the security protocol command to be sent.\r
+  @param  PayloadBufferSize            Size in bytes of the payload data buffer.\r
+  @param  PayloadBuffer                A pointer to a destination buffer to store the security\r
+                                       protocol command specific payload data for the security\r
+                                       protocol command. The caller is responsible for having\r
+                                       either implicit or explicit ownership of the buffer.\r
+  @param  PayloadTransferSize          A pointer to a buffer to store the size in bytes of the\r
+                                       data written to the payload data buffer.\r
+\r
+  @retval EFI_SUCCESS                  The security protocol command completed successfully.\r
+  @retval EFI_WARN_BUFFER_TOO_SMALL    The PayloadBufferSize was too small to store the available\r
+                                       data from the device. The PayloadBuffer contains the truncated data.\r
+  @retval EFI_UNSUPPORTED              The given MediaId does not support security protocol commands.\r
+  @retval EFI_DEVICE_ERROR             The security protocol command completed with an error.\r
+  @retval EFI_NO_MEDIA                 There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED            The MediaId is not for the current media.\r
+  @retval EFI_INVALID_PARAMETER        The PayloadBuffer or PayloadTransferSize is NULL and\r
+                                       PayloadBufferSize is non-zero.\r
+  @retval EFI_TIMEOUT                  A timeout occurred while waiting for the security\r
+                                       protocol command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskReceiveData (\r
+  IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL  *This,\r
+  IN UINT32                                 MediaId   OPTIONAL,\r
+  IN UINT64                                 Timeout,\r
+  IN UINT8                                  SecurityProtocolId,\r
+  IN UINT16                                 SecurityProtocolSpecificData,\r
+  IN UINTN                                  PayloadBufferSize,\r
+  OUT VOID                                  *PayloadBuffer,\r
+  OUT UINTN                                 *PayloadTransferSize\r
+  )\r
+{\r
+  SCSI_DISK_DEV       *ScsiDiskDevice;\r
+  EFI_BLOCK_IO_MEDIA  *Media;\r
+  EFI_STATUS          Status;\r
+  BOOLEAN             MediaChange;\r
+  EFI_TPL             OldTpl;\r
+  UINT8               SenseDataLength;\r
+  UINT8               HostAdapterStatus;\r
+  UINT8               TargetStatus;\r
+  VOID                *AlignedBuffer;\r
+  BOOLEAN             AlignedBufferAllocated;\r
+\r
+  AlignedBuffer          = NULL;\r
+  MediaChange            = FALSE;\r
+  AlignedBufferAllocated = FALSE;\r
+  OldTpl                 = gBS->RaiseTPL (TPL_CALLBACK);\r
+  ScsiDiskDevice         = SCSI_DISK_DEV_FROM_STORSEC (This);\r
+  Media                  = ScsiDiskDevice->BlkIo.Media;\r
+\r
+  SenseDataLength = (UINT8)(ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
+\r
+  if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
+    Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+    if (EFI_ERROR (Status)) {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto Done;\r
+    }\r
+\r
+    if (MediaChange) {\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIoProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo,\r
+             &ScsiDiskDevice->BlkIo\r
+             );\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIo2ProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo2,\r
+             &ScsiDiskDevice->BlkIo2\r
+             );\r
+      if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiEraseBlockProtocolGuid,\r
+               &ScsiDiskDevice->EraseBlock,\r
+               &ScsiDiskDevice->EraseBlock\r
+               );\r
+      }\r
+\r
+      if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiStorageSecurityCommandProtocolGuid,\r
+               &ScsiDiskDevice->StorageSecurity,\r
+               &ScsiDiskDevice->StorageSecurity\r
+               );\r
+      }\r
+\r
+      if (Media->MediaPresent) {\r
+        Status = EFI_MEDIA_CHANGED;\r
+      } else {\r
+        Status = EFI_NO_MEDIA;\r
+      }\r
+\r
+      goto Done;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Validate Media\r
+  //\r
+  if (!(Media->MediaPresent)) {\r
+    Status = EFI_NO_MEDIA;\r
+    goto Done;\r
+  }\r
+\r
+  if ((MediaId != 0) && (MediaId != Media->MediaId)) {\r
+    Status = EFI_MEDIA_CHANGED;\r
+    goto Done;\r
+  }\r
+\r
+  if (PayloadBufferSize != 0) {\r
+    if ((PayloadBuffer == NULL) || (PayloadTransferSize == NULL)) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto Done;\r
+    }\r
+\r
+    if ((ScsiDiskDevice->ScsiIo->IoAlign > 1) && !IS_ALIGNED (PayloadBuffer, ScsiDiskDevice->ScsiIo->IoAlign)) {\r
+      AlignedBuffer = AllocateAlignedBuffer (ScsiDiskDevice, PayloadBufferSize);\r
+      if (AlignedBuffer == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto Done;\r
+      }\r
+\r
+      ZeroMem (AlignedBuffer, PayloadBufferSize);\r
+      AlignedBufferAllocated = TRUE;\r
+    } else {\r
+      AlignedBuffer = PayloadBuffer;\r
+    }\r
+  }\r
+\r
+  Status = ScsiSecurityProtocolInCommand (\r
+             ScsiDiskDevice->ScsiIo,\r
+             Timeout,\r
+             ScsiDiskDevice->SenseData,\r
+             &SenseDataLength,\r
+             &HostAdapterStatus,\r
+             &TargetStatus,\r
+             SecurityProtocolId,\r
+             SecurityProtocolSpecificData,\r
+             FALSE,\r
+             PayloadBufferSize,\r
+             AlignedBuffer,\r
+             PayloadTransferSize\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  if (AlignedBufferAllocated) {\r
+    CopyMem (PayloadBuffer, AlignedBuffer, PayloadBufferSize);\r
+  }\r
+\r
+  if (PayloadBufferSize < *PayloadTransferSize) {\r
+    Status = EFI_WARN_BUFFER_TOO_SMALL;\r
+    goto Done;\r
+  }\r
+\r
+  Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  Status = CheckTargetStatus (TargetStatus);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Done;\r
+  }\r
+\r
+Done:\r
+  if (AlignedBufferAllocated) {\r
+    ZeroMem (AlignedBuffer, PayloadBufferSize);\r
+    FreeAlignedBuffer (AlignedBuffer, PayloadBufferSize);\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Send a security protocol command to a device.\r
+\r
+  The SendData function sends a security protocol command containing the payload\r
+  PayloadBuffer to the given MediaId. The security protocol command sent is\r
+  defined by SecurityProtocolId and contains the security protocol specific data\r
+  SecurityProtocolSpecificData. If the underlying protocol command requires a\r
+  specific padding for the command payload, the SendData function shall add padding\r
+  bytes to the command payload to satisfy the padding requirements.\r
+\r
+  For devices supporting the SCSI command set, the security protocol command is sent\r
+  using the SECURITY PROTOCOL OUT command defined in SPC-4.\r
+\r
+  If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall\r
+  return EFI_INVALID_PARAMETER.\r
+\r
+  If the given MediaId does not support security protocol commands, the function\r
+  shall return EFI_UNSUPPORTED. If there is no media in the device, the function\r
+  returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the\r
+  device, the function returns EFI_MEDIA_CHANGED.\r
+\r
+  If the security protocol fails to complete within the Timeout period, the function\r
+  shall return EFI_TIMEOUT.\r
+\r
+  If the security protocol command completes without an error, the function shall return\r
+  EFI_SUCCESS. If the security protocol command completes with an error, the function\r
+  shall return EFI_DEVICE_ERROR.\r
+\r
+  @param  This                         Indicates a pointer to the calling context.\r
+  @param  MediaId                      ID of the medium to receive data from.\r
+  @param  Timeout                      The timeout, in 100ns units, to use for the execution\r
+                                       of the security protocol command. A Timeout value of 0\r
+                                       means that this function will wait indefinitely for the\r
+                                       security protocol command to execute. If Timeout is greater\r
+                                       than zero, then this function will return EFI_TIMEOUT if the\r
+                                       time required to execute the receive data command is greater than Timeout.\r
+  @param  SecurityProtocolId           The value of the "Security Protocol" parameter of\r
+                                       the security protocol command to be sent.\r
+  @param  SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter\r
+                                       of the security protocol command to be sent.\r
+  @param  PayloadBufferSize            Size in bytes of the payload data buffer.\r
+  @param  PayloadBuffer                A pointer to a destination buffer to store the security\r
+                                       protocol command specific payload data for the security\r
+                                       protocol command.\r
+\r
+  @retval EFI_SUCCESS                  The security protocol command completed successfully.\r
+  @retval EFI_UNSUPPORTED              The given MediaId does not support security protocol commands.\r
+  @retval EFI_DEVICE_ERROR             The security protocol command completed with an error.\r
+  @retval EFI_NO_MEDIA                 There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED            The MediaId is not for the current media.\r
+  @retval EFI_INVALID_PARAMETER        The PayloadBuffer is NULL and PayloadBufferSize is non-zero.\r
+  @retval EFI_TIMEOUT                  A timeout occurred while waiting for the security\r
+                                       protocol command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskSendData (\r
+  IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL  *This,\r
+  IN UINT32                                 MediaId   OPTIONAL,\r
+  IN UINT64                                 Timeout,\r
+  IN UINT8                                  SecurityProtocolId,\r
+  IN UINT16                                 SecurityProtocolSpecificData,\r
+  IN UINTN                                  PayloadBufferSize,\r
+  OUT VOID                                  *PayloadBuffer\r
+  )\r
+{\r
+  SCSI_DISK_DEV       *ScsiDiskDevice;\r
+  EFI_BLOCK_IO_MEDIA  *Media;\r
+  EFI_STATUS          Status;\r
+  BOOLEAN             MediaChange;\r
+  EFI_TPL             OldTpl;\r
+  UINT8               SenseDataLength;\r
+  UINT8               HostAdapterStatus;\r
+  UINT8               TargetStatus;\r
+  VOID                *AlignedBuffer;\r
+  BOOLEAN             AlignedBufferAllocated;\r
+\r
+  AlignedBuffer          = NULL;\r
+  MediaChange            = FALSE;\r
+  AlignedBufferAllocated = FALSE;\r
+  OldTpl                 = gBS->RaiseTPL (TPL_CALLBACK);\r
+  ScsiDiskDevice         = SCSI_DISK_DEV_FROM_STORSEC (This);\r
+  Media                  = ScsiDiskDevice->BlkIo.Media;\r
+\r
+  SenseDataLength = (UINT8)(ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
+\r
+  if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
+    Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+    if (EFI_ERROR (Status)) {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto Done;\r
+    }\r
+\r
+    if (MediaChange) {\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIoProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo,\r
+             &ScsiDiskDevice->BlkIo\r
+             );\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIo2ProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo2,\r
+             &ScsiDiskDevice->BlkIo2\r
+             );\r
+      if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiEraseBlockProtocolGuid,\r
+               &ScsiDiskDevice->EraseBlock,\r
+               &ScsiDiskDevice->EraseBlock\r
+               );\r
+      }\r
+\r
+      if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+        gBS->ReinstallProtocolInterface (\r
+               ScsiDiskDevice->Handle,\r
+               &gEfiStorageSecurityCommandProtocolGuid,\r
+               &ScsiDiskDevice->StorageSecurity,\r
+               &ScsiDiskDevice->StorageSecurity\r
+               );\r
+      }\r
+\r
+      if (Media->MediaPresent) {\r
+        Status = EFI_MEDIA_CHANGED;\r
+      } else {\r
+        Status = EFI_NO_MEDIA;\r
+      }\r
+\r
+      goto Done;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Validate Media\r
+  //\r
+  if (!(Media->MediaPresent)) {\r
+    Status = EFI_NO_MEDIA;\r
+    goto Done;\r
+  }\r
+\r
+  if ((MediaId != 0) && (MediaId != Media->MediaId)) {\r
+    Status = EFI_MEDIA_CHANGED;\r
+    goto Done;\r
+  }\r
+\r
+  if (Media->ReadOnly) {\r
+    Status = EFI_WRITE_PROTECTED;\r
+    goto Done;\r
+  }\r
+\r
+  if (PayloadBufferSize != 0) {\r
+    if (PayloadBuffer == NULL) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto Done;\r
+    }\r
+\r
+    if ((ScsiDiskDevice->ScsiIo->IoAlign > 1) && !IS_ALIGNED (PayloadBuffer, ScsiDiskDevice->ScsiIo->IoAlign)) {\r
+      AlignedBuffer = AllocateAlignedBuffer (ScsiDiskDevice, PayloadBufferSize);\r
+      if (AlignedBuffer == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto Done;\r
+      }\r
+\r
+      CopyMem (AlignedBuffer, PayloadBuffer, PayloadBufferSize);\r
+      AlignedBufferAllocated = TRUE;\r
+    } else {\r
+      AlignedBuffer = PayloadBuffer;\r
+    }\r
+  }\r
+\r
+  Status = ScsiSecurityProtocolOutCommand (\r
+             ScsiDiskDevice->ScsiIo,\r
+             Timeout,\r
+             ScsiDiskDevice->SenseData,\r
+             &SenseDataLength,\r
+             &HostAdapterStatus,\r
+             &TargetStatus,\r
+             SecurityProtocolId,\r
+             SecurityProtocolSpecificData,\r
+             FALSE,\r
+             PayloadBufferSize,\r
+             AlignedBuffer\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  Status = CheckTargetStatus (TargetStatus);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Done;\r
+  }\r
+\r
+Done:\r
+  if (AlignedBufferAllocated) {\r
+    ZeroMem (AlignedBuffer, PayloadBufferSize);\r
+    FreeAlignedBuffer (AlignedBuffer, PayloadBufferSize);\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Detect Device and read out capacity ,if error occurs, parse the sense key.\r
+\r
+  @param  ScsiDiskDevice    The pointer of SCSI_DISK_DEV\r
+  @param  MustReadCapacity  The flag about reading device capacity\r
+  @param  MediaChange       The pointer of flag indicates if media has changed\r
+\r
+  @retval EFI_DEVICE_ERROR  Indicates that error occurs\r
+  @retval EFI_SUCCESS       Successfully to detect media\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskDetectMedia (\r
+  IN   SCSI_DISK_DEV  *ScsiDiskDevice,\r
+  IN   BOOLEAN        MustReadCapacity,\r
+  OUT  BOOLEAN        *MediaChange\r
+  )\r
+{\r
+  EFI_STATUS           Status;\r
+  EFI_SCSI_SENSE_DATA  *SenseData;\r
+  UINTN                NumberOfSenseKeys;\r
+  BOOLEAN              NeedRetry;\r
+  BOOLEAN              NeedReadCapacity;\r
+  UINT8                Retry;\r
+  UINT8                MaxRetry;\r
+  EFI_BLOCK_IO_MEDIA   OldMedia;\r
+  UINTN                Action;\r
+  EFI_EVENT            TimeoutEvt;\r
+\r
+  Status            = EFI_SUCCESS;\r
+  SenseData         = NULL;\r
+  NumberOfSenseKeys = 0;\r
+  Retry             = 0;\r
+  MaxRetry          = 3;\r
+  Action            = ACTION_NO_ACTION;\r
+  NeedReadCapacity  = FALSE;\r
+  *MediaChange      = FALSE;\r
+  TimeoutEvt        = NULL;\r
+\r
+  CopyMem (&OldMedia, ScsiDiskDevice->BlkIo.Media, sizeof (OldMedia));\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &TimeoutEvt\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->SetTimer (TimeoutEvt, TimerRelative, EFI_TIMER_PERIOD_SECONDS (120));\r
+  if (EFI_ERROR (Status)) {\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // Sending Test_Unit cmd to poll device status.\r
+  // If the sense data shows the drive is not ready or reset before, we need poll the device status again.\r
+  // We limit the upper boundary to 120 seconds.\r
+  //\r
+  while (EFI_ERROR (gBS->CheckEvent (TimeoutEvt))) {\r
+    Status = ScsiDiskTestUnitReady (\r
+               ScsiDiskDevice,\r
+               &NeedRetry,\r
+               &SenseData,\r
+               &NumberOfSenseKeys\r
+               );\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = DetectMediaParsingSenseKeys (\r
+                 ScsiDiskDevice,\r
+                 SenseData,\r
+                 NumberOfSenseKeys,\r
+                 &Action\r
+                 );\r
+      if (EFI_ERROR (Status)) {\r
+        goto EXIT;\r
+      } else if (Action == ACTION_RETRY_COMMAND_LATER) {\r
+        continue;\r
+      } else {\r
+        break;\r
+      }\r
+    } else {\r
+      Retry++;\r
+      if (!NeedRetry || (Retry >= MaxRetry)) {\r
+        goto EXIT;\r
+      }\r
+    }\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // ACTION_NO_ACTION: need not read capacity\r
   // other action code: need read capacity\r
   //\r
-  if (Action == ACTION_NO_ACTION) {\r
-    NeedReadCapacity = FALSE;\r
-  } else {\r
-    NeedReadCapacity = TRUE;\r
+  if (Action == ACTION_READ_CAPACITY) {\r
+    NeedReadCapacity = TRUE;\r
+  }\r
+\r
+  //\r
+  // READ_CAPACITY command is not supported by any of the UFS WLUNs.\r
+  //\r
+  if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_WLUN) {\r
+    NeedReadCapacity                          = FALSE;\r
+    MustReadCapacity                          = FALSE;\r
+    ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE;\r
+  }\r
+\r
+  //\r
+  // either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE,\r
+  // retrieve capacity via Read Capacity command\r
+  //\r
+  if (NeedReadCapacity || MustReadCapacity) {\r
+    //\r
+    // retrieve media information\r
+    //\r
+    for (Retry = 0; Retry < MaxRetry; Retry++) {\r
+      Status = ScsiDiskReadCapacity (\r
+                 ScsiDiskDevice,\r
+                 &NeedRetry,\r
+                 &SenseData,\r
+                 &NumberOfSenseKeys\r
+                 );\r
+      if (!EFI_ERROR (Status)) {\r
+        //\r
+        // analyze sense key to action\r
+        //\r
+        Status = DetectMediaParsingSenseKeys (\r
+                   ScsiDiskDevice,\r
+                   SenseData,\r
+                   NumberOfSenseKeys,\r
+                   &Action\r
+                   );\r
+        if (EFI_ERROR (Status)) {\r
+          //\r
+          // if Status is error, it may indicate crisis error,\r
+          // so return without retry.\r
+          //\r
+          goto EXIT;\r
+        } else if (Action == ACTION_RETRY_COMMAND_LATER) {\r
+          Retry = 0;\r
+          continue;\r
+        } else {\r
+          break;\r
+        }\r
+      } else {\r
+        Retry++;\r
+        if (!NeedRetry || (Retry >= MaxRetry)) {\r
+          goto EXIT;\r
+        }\r
+      }\r
+    }\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto EXIT;\r
+    }\r
+  }\r
+\r
+  if (ScsiDiskDevice->BlkIo.Media->MediaId != OldMedia.MediaId) {\r
+    //\r
+    // Media change information got from the device\r
+    //\r
+    *MediaChange = TRUE;\r
+  }\r
+\r
+  if (ScsiDiskDevice->BlkIo.Media->ReadOnly != OldMedia.ReadOnly) {\r
+    *MediaChange                          = TRUE;\r
+    ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
+  }\r
+\r
+  if (ScsiDiskDevice->BlkIo.Media->BlockSize != OldMedia.BlockSize) {\r
+    *MediaChange                          = TRUE;\r
+    ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
+  }\r
+\r
+  if (ScsiDiskDevice->BlkIo.Media->LastBlock != OldMedia.LastBlock) {\r
+    *MediaChange                          = TRUE;\r
+    ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
+  }\r
+\r
+  if (ScsiDiskDevice->BlkIo.Media->MediaPresent != OldMedia.MediaPresent) {\r
+    if (ScsiDiskDevice->BlkIo.Media->MediaPresent) {\r
+      //\r
+      // when change from no media to media present, reset the MediaId to 1.\r
+      //\r
+      ScsiDiskDevice->BlkIo.Media->MediaId = 1;\r
+    } else {\r
+      //\r
+      // when no media, reset the MediaId to zero.\r
+      //\r
+      ScsiDiskDevice->BlkIo.Media->MediaId = 0;\r
+    }\r
+\r
+    *MediaChange = TRUE;\r
+  }\r
+\r
+EXIT:\r
+  if (TimeoutEvt != NULL) {\r
+    gBS->CloseEvent (TimeoutEvt);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Send out Inquiry command to Device.\r
+\r
+  @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV\r
+  @param  NeedRetry       Indicates if needs try again when error happens\r
+\r
+  @retval  EFI_DEVICE_ERROR  Indicates that error occurs\r
+  @retval  EFI_SUCCESS       Successfully to detect media\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskInquiryDevice (\r
+  IN OUT  SCSI_DISK_DEV  *ScsiDiskDevice,\r
+  OUT  BOOLEAN           *NeedRetry\r
+  )\r
+{\r
+  UINT32                                 InquiryDataLength;\r
+  UINT8                                  SenseDataLength;\r
+  UINT8                                  HostAdapterStatus;\r
+  UINT8                                  TargetStatus;\r
+  EFI_SCSI_SENSE_DATA                    *SenseDataArray;\r
+  UINTN                                  NumberOfSenseKeys;\r
+  EFI_STATUS                             Status;\r
+  UINT8                                  MaxRetry;\r
+  UINT8                                  Index;\r
+  EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE  *SupportedVpdPages;\r
+  EFI_SCSI_BLOCK_LIMITS_VPD_PAGE         *BlockLimits;\r
+  UINTN                                  PageLength;\r
+\r
+  InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA);\r
+  SenseDataLength   = 0;\r
+\r
+  Status = ScsiInquiryCommand (\r
+             ScsiDiskDevice->ScsiIo,\r
+             SCSI_DISK_TIMEOUT,\r
+             NULL,\r
+             &SenseDataLength,\r
+             &HostAdapterStatus,\r
+             &TargetStatus,\r
+             (VOID *)&(ScsiDiskDevice->InquiryData),\r
+             &InquiryDataLength,\r
+             FALSE\r
+             );\r
+  //\r
+  // no need to check HostAdapterStatus and TargetStatus\r
+  //\r
+  if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
+    ParseInquiryData (ScsiDiskDevice);\r
+\r
+    if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) {\r
+      //\r
+      // Check whether the device supports Block Limits VPD page (0xB0)\r
+      //\r
+      SupportedVpdPages = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
+      if (SupportedVpdPages == NULL) {\r
+        *NeedRetry = FALSE;\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+\r
+      ZeroMem (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
+      InquiryDataLength = sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE);\r
+      SenseDataLength   = 0;\r
+      Status            = ScsiInquiryCommandEx (\r
+                            ScsiDiskDevice->ScsiIo,\r
+                            SCSI_DISK_TIMEOUT,\r
+                            NULL,\r
+                            &SenseDataLength,\r
+                            &HostAdapterStatus,\r
+                            &TargetStatus,\r
+                            (VOID *)SupportedVpdPages,\r
+                            &InquiryDataLength,\r
+                            TRUE,\r
+                            EFI_SCSI_PAGE_CODE_SUPPORTED_VPD\r
+                            );\r
+      if (!EFI_ERROR (Status)) {\r
+        PageLength = (SupportedVpdPages->PageLength2 << 8)\r
+                     |  SupportedVpdPages->PageLength1;\r
+\r
+        //\r
+        // Sanity checks for coping with broken devices\r
+        //\r
+        if (PageLength > sizeof SupportedVpdPages->SupportedVpdPageList) {\r
+          DEBUG ((\r
+            DEBUG_WARN,\r
+            "%a: invalid PageLength (%u) in Supported VPD Pages page\n",\r
+            __FUNCTION__,\r
+            (UINT32)PageLength\r
+            ));\r
+          PageLength = 0;\r
+        }\r
+\r
+        if ((PageLength > 0) &&\r
+            (SupportedVpdPages->SupportedVpdPageList[0] !=\r
+             EFI_SCSI_PAGE_CODE_SUPPORTED_VPD))\r
+        {\r
+          DEBUG ((\r
+            DEBUG_WARN,\r
+            "%a: Supported VPD Pages page doesn't start with code 0x%02x\n",\r
+            __FUNCTION__,\r
+            EFI_SCSI_PAGE_CODE_SUPPORTED_VPD\r
+            ));\r
+          PageLength = 0;\r
+        }\r
+\r
+        //\r
+        // Locate the code for the Block Limits VPD page\r
+        //\r
+        for (Index = 0; Index < PageLength; Index++) {\r
+          //\r
+          // Sanity check\r
+          //\r
+          if ((Index > 0) &&\r
+              (SupportedVpdPages->SupportedVpdPageList[Index] <=\r
+               SupportedVpdPages->SupportedVpdPageList[Index - 1]))\r
+          {\r
+            DEBUG ((\r
+              DEBUG_WARN,\r
+              "%a: non-ascending code in Supported VPD Pages page @ %u\n",\r
+              __FUNCTION__,\r
+              Index\r
+              ));\r
+            Index      = 0;\r
+            PageLength = 0;\r
+            break;\r
+          }\r
+\r
+          if (SupportedVpdPages->SupportedVpdPageList[Index] == EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD) {\r
+            break;\r
+          }\r
+        }\r
+\r
+        //\r
+        // Query the Block Limits VPD page\r
+        //\r
+        if (Index < PageLength) {\r
+          BlockLimits = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));\r
+          if (BlockLimits == NULL) {\r
+            FreeAlignedBuffer (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
+            *NeedRetry = FALSE;\r
+            return EFI_DEVICE_ERROR;\r
+          }\r
+\r
+          ZeroMem (BlockLimits, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));\r
+          InquiryDataLength = sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE);\r
+          SenseDataLength   = 0;\r
+          Status            = ScsiInquiryCommandEx (\r
+                                ScsiDiskDevice->ScsiIo,\r
+                                SCSI_DISK_TIMEOUT,\r
+                                NULL,\r
+                                &SenseDataLength,\r
+                                &HostAdapterStatus,\r
+                                &TargetStatus,\r
+                                (VOID *)BlockLimits,\r
+                                &InquiryDataLength,\r
+                                TRUE,\r
+                                EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD\r
+                                );\r
+          if (!EFI_ERROR (Status)) {\r
+            ScsiDiskDevice->BlkIo.Media->OptimalTransferLengthGranularity =\r
+              (BlockLimits->OptimalTransferLengthGranularity2 << 8) |\r
+              BlockLimits->OptimalTransferLengthGranularity1;\r
+\r
+            ScsiDiskDevice->UnmapInfo.MaxLbaCnt =\r
+              (BlockLimits->MaximumUnmapLbaCount4 << 24) |\r
+              (BlockLimits->MaximumUnmapLbaCount3 << 16) |\r
+              (BlockLimits->MaximumUnmapLbaCount2 << 8)  |\r
+              BlockLimits->MaximumUnmapLbaCount1;\r
+            ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt =\r
+              (BlockLimits->MaximumUnmapBlockDescriptorCount4 << 24) |\r
+              (BlockLimits->MaximumUnmapBlockDescriptorCount3 << 16) |\r
+              (BlockLimits->MaximumUnmapBlockDescriptorCount2 << 8)  |\r
+              BlockLimits->MaximumUnmapBlockDescriptorCount1;\r
+            ScsiDiskDevice->EraseBlock.EraseLengthGranularity =\r
+              (BlockLimits->OptimalUnmapGranularity4 << 24) |\r
+              (BlockLimits->OptimalUnmapGranularity3 << 16) |\r
+              (BlockLimits->OptimalUnmapGranularity2 << 8)  |\r
+              BlockLimits->OptimalUnmapGranularity1;\r
+            if (BlockLimits->UnmapGranularityAlignmentValid != 0) {\r
+              ScsiDiskDevice->UnmapInfo.GranularityAlignment =\r
+                (BlockLimits->UnmapGranularityAlignment4 << 24) |\r
+                (BlockLimits->UnmapGranularityAlignment3 << 16) |\r
+                (BlockLimits->UnmapGranularityAlignment2 << 8)  |\r
+                BlockLimits->UnmapGranularityAlignment1;\r
+            }\r
+\r
+            if (ScsiDiskDevice->EraseBlock.EraseLengthGranularity == 0) {\r
+              //\r
+              // A value of 0 indicates that the optimal unmap granularity is\r
+              // not reported.\r
+              //\r
+              ScsiDiskDevice->EraseBlock.EraseLengthGranularity = 1;\r
+            }\r
+\r
+            ScsiDiskDevice->BlockLimitsVpdSupported = TRUE;\r
+          }\r
+\r
+          FreeAlignedBuffer (BlockLimits, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));\r
+        }\r
+      }\r
+\r
+      FreeAlignedBuffer (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
+    }\r
+  }\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    return EFI_SUCCESS;\r
+  } else if (Status == EFI_NOT_READY) {\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // go ahead to check HostAdapterStatus and TargetStatus\r
+  // (EFI_TIMEOUT, EFI_DEVICE_ERROR)\r
+  //\r
+\r
+  Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+  if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    //\r
+    // reset the scsi channel\r
+    //\r
+    ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Status = CheckTargetStatus (TargetStatus);\r
+  if (Status == EFI_NOT_READY) {\r
+    //\r
+    // reset the scsi device\r
+    //\r
+    ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // if goes here, meant ScsiInquiryCommand() failed.\r
+  // if ScsiDiskRequestSenseKeys() succeeds at last,\r
+  // better retry ScsiInquiryCommand(). (by setting *NeedRetry = TRUE)\r
+  //\r
+  MaxRetry = 3;\r
+  for (Index = 0; Index < MaxRetry; Index++) {\r
+    Status = ScsiDiskRequestSenseKeys (\r
+               ScsiDiskDevice,\r
+               NeedRetry,\r
+               &SenseDataArray,\r
+               &NumberOfSenseKeys,\r
+               TRUE\r
+               );\r
+    if (!EFI_ERROR (Status)) {\r
+      *NeedRetry = TRUE;\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    if (!*NeedRetry) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
+  // set *NeedRetry = FALSE to avoid the outside caller try again.\r
+  //\r
+  *NeedRetry = FALSE;\r
+  return EFI_DEVICE_ERROR;\r
+}\r
+\r
+/**\r
+  To test device.\r
+\r
+  When Test Unit Ready command succeeds, retrieve Sense Keys via Request Sense;\r
+  When Test Unit Ready command encounters any error caused by host adapter or\r
+  target, return error without retrieving Sense Keys.\r
+\r
+  @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV\r
+  @param  NeedRetry          The pointer of flag indicates try again\r
+  @param  SenseDataArray     The pointer of an array of sense data\r
+  @param  NumberOfSenseKeys  The pointer of the number of sense data array\r
+\r
+  @retval EFI_DEVICE_ERROR   Indicates that error occurs\r
+  @retval EFI_SUCCESS        Successfully to test unit\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskTestUnitReady (\r
+  IN  SCSI_DISK_DEV        *ScsiDiskDevice,\r
+  OUT BOOLEAN              *NeedRetry,\r
+  OUT EFI_SCSI_SENSE_DATA  **SenseDataArray,\r
+  OUT UINTN                *NumberOfSenseKeys\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT8       SenseDataLength;\r
+  UINT8       HostAdapterStatus;\r
+  UINT8       TargetStatus;\r
+  UINT8       Index;\r
+  UINT8       MaxRetry;\r
+\r
+  SenseDataLength    = (UINT8)(ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
+  *NumberOfSenseKeys = 0;\r
+\r
+  //\r
+  // Parameter 3 and 4: do not require sense data, retrieve it when needed.\r
+  //\r
+  Status = ScsiTestUnitReadyCommand (\r
+             ScsiDiskDevice->ScsiIo,\r
+             SCSI_DISK_TIMEOUT,\r
+             ScsiDiskDevice->SenseData,\r
+             &SenseDataLength,\r
+             &HostAdapterStatus,\r
+             &TargetStatus\r
+             );\r
+  //\r
+  // no need to check HostAdapterStatus and TargetStatus\r
+  //\r
+  if (Status == EFI_NOT_READY) {\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // go ahead to check HostAdapterStatus and TargetStatus(in case of EFI_DEVICE_ERROR)\r
+  //\r
+\r
+  Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+  if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    //\r
+    // reset the scsi channel\r
+    //\r
+    ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Status = CheckTargetStatus (TargetStatus);\r
+  if (Status == EFI_NOT_READY) {\r
+    //\r
+    // reset the scsi device\r
+    //\r
+    ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (SenseDataLength != 0) {\r
+    *NumberOfSenseKeys = SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA);\r
+    *SenseDataArray    = ScsiDiskDevice->SenseData;\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  MaxRetry = 3;\r
+  for (Index = 0; Index < MaxRetry; Index++) {\r
+    Status = ScsiDiskRequestSenseKeys (\r
+               ScsiDiskDevice,\r
+               NeedRetry,\r
+               SenseDataArray,\r
+               NumberOfSenseKeys,\r
+               FALSE\r
+               );\r
+    if (!EFI_ERROR (Status)) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    if (!*NeedRetry) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
+  // set *NeedRetry = FALSE to avoid the outside caller try again.\r
+  //\r
+  *NeedRetry = FALSE;\r
+  return EFI_DEVICE_ERROR;\r
+}\r
+\r
+/**\r
+  Parsing Sense Keys which got from request sense command.\r
+\r
+  @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV\r
+  @param  SenseData          The pointer of EFI_SCSI_SENSE_DATA\r
+  @param  NumberOfSenseKeys  The number of sense key\r
+  @param  Action             The pointer of action which indicates what is need to do next\r
+\r
+  @retval EFI_DEVICE_ERROR   Indicates that error occurs\r
+  @retval EFI_SUCCESS        Successfully to complete the parsing\r
+\r
+**/\r
+EFI_STATUS\r
+DetectMediaParsingSenseKeys (\r
+  OUT  SCSI_DISK_DEV        *ScsiDiskDevice,\r
+  IN   EFI_SCSI_SENSE_DATA  *SenseData,\r
+  IN   UINTN                NumberOfSenseKeys,\r
+  OUT  UINTN                *Action\r
+  )\r
+{\r
+  BOOLEAN  RetryLater;\r
+\r
+  //\r
+  // Default is to read capacity, unless..\r
+  //\r
+  *Action = ACTION_READ_CAPACITY;\r
+\r
+  if (NumberOfSenseKeys == 0) {\r
+    if (ScsiDiskDevice->BlkIo.Media->MediaPresent == TRUE) {\r
+      *Action = ACTION_NO_ACTION;\r
+    }\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (!ScsiDiskHaveSenseKey (SenseData, NumberOfSenseKeys)) {\r
+    //\r
+    // No Sense Key returned from last submitted command\r
+    //\r
+    if (ScsiDiskDevice->BlkIo.Media->MediaPresent == TRUE) {\r
+      *Action = ACTION_NO_ACTION;\r
+    }\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (ScsiDiskIsNoMedia (SenseData, NumberOfSenseKeys)) {\r
+    ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;\r
+    ScsiDiskDevice->BlkIo.Media->LastBlock    = 0;\r
+    *Action                                   = ACTION_NO_ACTION;\r
+    DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskIsNoMedia\n"));\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {\r
+    ScsiDiskDevice->BlkIo.Media->MediaId++;\r
+    DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskIsMediaChange!\n"));\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (ScsiDiskIsResetBefore (SenseData, NumberOfSenseKeys)) {\r
+    *Action = ACTION_RETRY_COMMAND_LATER;\r
+    DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskIsResetBefore!\n"));\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {\r
+    DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskIsMediaError\n"));\r
+    *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {\r
+    DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskIsHardwareError\n"));\r
+    *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) {\r
+    if (RetryLater) {\r
+      *Action = ACTION_RETRY_COMMAND_LATER;\r
+      DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskDriveNotReady!\n"));\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    *Action = ACTION_NO_ACTION;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;\r
+  DEBUG ((DEBUG_VERBOSE, "ScsiDisk: Sense Key = 0x%x ASC = 0x%x!\n", SenseData->Sense_Key, SenseData->Addnl_Sense_Code));\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Send read capacity command to device and get the device parameter.\r
+\r
+  @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV\r
+  @param  NeedRetry          The pointer of flag indicates if need a retry\r
+  @param  SenseDataArray     The pointer of an array of sense data\r
+  @param  NumberOfSenseKeys  The number of sense key\r
+\r
+  @retval EFI_DEVICE_ERROR   Indicates that error occurs\r
+  @retval EFI_SUCCESS        Successfully to read capacity or sense data is received.\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskReadCapacity (\r
+  IN  OUT  SCSI_DISK_DEV    *ScsiDiskDevice,\r
+  OUT  BOOLEAN              *NeedRetry,\r
+  OUT  EFI_SCSI_SENSE_DATA  **SenseDataArray,\r
+  OUT  UINTN                *NumberOfSenseKeys\r
+  )\r
+{\r
+  UINT8                          HostAdapterStatus;\r
+  UINT8                          TargetStatus;\r
+  EFI_STATUS                     CommandStatus;\r
+  EFI_STATUS                     Status;\r
+  UINT8                          Index;\r
+  UINT8                          MaxRetry;\r
+  UINT8                          SenseDataLength;\r
+  UINT32                         DataLength10;\r
+  UINT32                         DataLength16;\r
+  EFI_SCSI_DISK_CAPACITY_DATA    *CapacityData10;\r
+  EFI_SCSI_DISK_CAPACITY_DATA16  *CapacityData16;\r
+\r
+  CapacityData10 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
+  if (CapacityData10 == NULL) {\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  CapacityData16 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
+  if (CapacityData16 == NULL) {\r
+    FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  SenseDataLength = 0;\r
+  DataLength10    = sizeof (EFI_SCSI_DISK_CAPACITY_DATA);\r
+  DataLength16    = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16);\r
+  ZeroMem (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
+  ZeroMem (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
+\r
+  *NumberOfSenseKeys = 0;\r
+  *NeedRetry         = FALSE;\r
+\r
+  //\r
+  // submit Read Capacity(10) Command. If it returns capacity of FFFFFFFFh,\r
+  // 16 byte command should be used to access large hard disk >2TB\r
+  //\r
+  CommandStatus = ScsiReadCapacityCommand (\r
+                    ScsiDiskDevice->ScsiIo,\r
+                    SCSI_DISK_TIMEOUT,\r
+                    NULL,\r
+                    &SenseDataLength,\r
+                    &HostAdapterStatus,\r
+                    &TargetStatus,\r
+                    (VOID *)CapacityData10,\r
+                    &DataLength10,\r
+                    FALSE\r
+                    );\r
+\r
+  ScsiDiskDevice->Cdb16Byte = FALSE;\r
+  if ((!EFI_ERROR (CommandStatus)) && (CapacityData10->LastLba3 == 0xff) && (CapacityData10->LastLba2 == 0xff) &&\r
+      (CapacityData10->LastLba1 == 0xff) && (CapacityData10->LastLba0 == 0xff))\r
+  {\r
+    //\r
+    // use Read Capacity (16), Read (16) and Write (16) next when hard disk size > 2TB\r
+    //\r
+    ScsiDiskDevice->Cdb16Byte = TRUE;\r
+    //\r
+    // submit Read Capacity(16) Command to get parameter LogicalBlocksPerPhysicalBlock\r
+    // and LowestAlignedLba\r
+    //\r
+    CommandStatus = ScsiReadCapacity16Command (\r
+                      ScsiDiskDevice->ScsiIo,\r
+                      SCSI_DISK_TIMEOUT,\r
+                      NULL,\r
+                      &SenseDataLength,\r
+                      &HostAdapterStatus,\r
+                      &TargetStatus,\r
+                      (VOID *)CapacityData16,\r
+                      &DataLength16,\r
+                      FALSE\r
+                      );\r
+  }\r
+\r
+  //\r
+  // no need to check HostAdapterStatus and TargetStatus\r
+  //\r
+  if (CommandStatus == EFI_SUCCESS) {\r
+    GetMediaInfo (ScsiDiskDevice, CapacityData10, CapacityData16);\r
+    FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
+    FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
+  FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
+\r
+  if (CommandStatus == EFI_NOT_READY) {\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if ((CommandStatus == EFI_INVALID_PARAMETER) || (CommandStatus == EFI_UNSUPPORTED)) {\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // go ahead to check HostAdapterStatus and TargetStatus\r
+  // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
+  //\r
+\r
+  Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+  if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    //\r
+    // reset the scsi channel\r
+    //\r
+    ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Status = CheckTargetStatus (TargetStatus);\r
+  if (Status == EFI_NOT_READY) {\r
+    //\r
+    // reset the scsi device\r
+    //\r
+    ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // if goes here, meant ScsiReadCapacityCommand() failed.\r
+  // if ScsiDiskRequestSenseKeys() succeeds at last,\r
+  // better retry ScsiReadCapacityCommand(). (by setting *NeedRetry = TRUE)\r
+  //\r
+  MaxRetry = 3;\r
+  for (Index = 0; Index < MaxRetry; Index++) {\r
+    Status = ScsiDiskRequestSenseKeys (\r
+               ScsiDiskDevice,\r
+               NeedRetry,\r
+               SenseDataArray,\r
+               NumberOfSenseKeys,\r
+               TRUE\r
+               );\r
+    if (!EFI_ERROR (Status)) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    if (!*NeedRetry) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
+  // set *NeedRetry = FALSE to avoid the outside caller try again.\r
+  //\r
+  *NeedRetry = FALSE;\r
+  return EFI_DEVICE_ERROR;\r
+}\r
+\r
+/**\r
+  Check the HostAdapter status and re-interpret it in EFI_STATUS.\r
+\r
+  @param  HostAdapterStatus  Host Adapter status\r
+\r
+  @retval  EFI_SUCCESS       Host adapter is OK.\r
+  @retval  EFI_TIMEOUT       Timeout.\r
+  @retval  EFI_NOT_READY     Adapter NOT ready.\r
+  @retval  EFI_DEVICE_ERROR  Adapter device error.\r
+\r
+**/\r
+EFI_STATUS\r
+CheckHostAdapterStatus (\r
+  IN UINT8  HostAdapterStatus\r
+  )\r
+{\r
+  switch (HostAdapterStatus) {\r
+    case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK:\r
+      return EFI_SUCCESS;\r
+\r
+    case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT:\r
+    case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT:\r
+    case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND:\r
+      return EFI_TIMEOUT;\r
+\r
+    case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT:\r
+    case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR:\r
+    case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED:\r
+    case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN:\r
+    case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET:\r
+      return EFI_NOT_READY;\r
+\r
+    case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE:\r
+    case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR:\r
+      return EFI_DEVICE_ERROR;\r
+\r
+    default:\r
+      return EFI_SUCCESS;\r
+  }\r
+}\r
+\r
+/**\r
+  Check the target status and re-interpret it in EFI_STATUS.\r
+\r
+  @param  TargetStatus  Target status\r
+\r
+  @retval EFI_NOT_READY       Device is NOT ready.\r
+  @retval EFI_DEVICE_ERROR\r
+  @retval EFI_SUCCESS\r
+\r
+**/\r
+EFI_STATUS\r
+CheckTargetStatus (\r
+  IN  UINT8  TargetStatus\r
+  )\r
+{\r
+  switch (TargetStatus) {\r
+    case EFI_EXT_SCSI_STATUS_TARGET_GOOD:\r
+    case EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION:\r
+    case EFI_EXT_SCSI_STATUS_TARGET_CONDITION_MET:\r
+      return EFI_SUCCESS;\r
+\r
+    case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE:\r
+    case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE_CONDITION_MET:\r
+    case EFI_EXT_SCSI_STATUS_TARGET_BUSY:\r
+    case EFI_EXT_SCSI_STATUS_TARGET_TASK_SET_FULL:\r
+      return EFI_NOT_READY;\r
+\r
+    case EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT:\r
+      return EFI_DEVICE_ERROR;\r
+\r
+    default:\r
+      return EFI_SUCCESS;\r
+  }\r
+}\r
+\r
+/**\r
+  Retrieve all sense keys from the device.\r
+\r
+  When encountering error during the process, if retrieve sense keys before\r
+  error encountered, it returns the sense keys with return status set to EFI_SUCCESS,\r
+  and NeedRetry set to FALSE; otherwise, return the proper return status.\r
+\r
+  @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV\r
+  @param  NeedRetry          The pointer of flag indicates if need a retry\r
+  @param  SenseDataArray     The pointer of an array of sense data\r
+  @param  NumberOfSenseKeys  The number of sense key\r
+  @param  AskResetIfError    The flag indicates if need reset when error occurs\r
+\r
+  @retval EFI_DEVICE_ERROR   Indicates that error occurs\r
+  @retval EFI_SUCCESS        Successfully to request sense key\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskRequestSenseKeys (\r
+  IN  OUT  SCSI_DISK_DEV    *ScsiDiskDevice,\r
+  OUT  BOOLEAN              *NeedRetry,\r
+  OUT  EFI_SCSI_SENSE_DATA  **SenseDataArray,\r
+  OUT  UINTN                *NumberOfSenseKeys,\r
+  IN       BOOLEAN          AskResetIfError\r
+  )\r
+{\r
+  EFI_SCSI_SENSE_DATA  *PtrSenseData;\r
+  UINT8                SenseDataLength;\r
+  BOOLEAN              SenseReq;\r
+  EFI_STATUS           Status;\r
+  EFI_STATUS           FallStatus;\r
+  UINT8                HostAdapterStatus;\r
+  UINT8                TargetStatus;\r
+\r
+  FallStatus      = EFI_SUCCESS;\r
+  SenseDataLength = (UINT8)sizeof (EFI_SCSI_SENSE_DATA);\r
+\r
+  ZeroMem (\r
+    ScsiDiskDevice->SenseData,\r
+    sizeof (EFI_SCSI_SENSE_DATA) * (ScsiDiskDevice->SenseDataNumber)\r
+    );\r
+\r
+  *NumberOfSenseKeys = 0;\r
+  *SenseDataArray    = ScsiDiskDevice->SenseData;\r
+  Status             = EFI_SUCCESS;\r
+  PtrSenseData       = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_SENSE_DATA));\r
+  if (PtrSenseData == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  for (SenseReq = TRUE; SenseReq;) {\r
+    ZeroMem (PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA));\r
+    Status = ScsiRequestSenseCommand (\r
+               ScsiDiskDevice->ScsiIo,\r
+               SCSI_DISK_TIMEOUT,\r
+               PtrSenseData,\r
+               &SenseDataLength,\r
+               &HostAdapterStatus,\r
+               &TargetStatus\r
+               );\r
+    if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
+      FallStatus = EFI_SUCCESS;\r
+    } else if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+      *NeedRetry = TRUE;\r
+      FallStatus = EFI_DEVICE_ERROR;\r
+    } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
+      *NeedRetry = FALSE;\r
+      FallStatus = EFI_DEVICE_ERROR;\r
+    } else if (Status == EFI_DEVICE_ERROR) {\r
+      if (AskResetIfError) {\r
+        ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+      }\r
+\r
+      FallStatus = EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    if (EFI_ERROR (FallStatus)) {\r
+      if (*NumberOfSenseKeys != 0) {\r
+        *NeedRetry = FALSE;\r
+        Status     = EFI_SUCCESS;\r
+        goto EXIT;\r
+      } else {\r
+        Status = EFI_DEVICE_ERROR;\r
+        goto EXIT;\r
+      }\r
+    }\r
+\r
+    CopyMem (ScsiDiskDevice->SenseData + *NumberOfSenseKeys, PtrSenseData, SenseDataLength);\r
+    (*NumberOfSenseKeys) += 1;\r
+\r
+    //\r
+    // no more sense key or number of sense keys exceeds predefined,\r
+    // skip the loop.\r
+    //\r
+    if ((PtrSenseData->Sense_Key == EFI_SCSI_SK_NO_SENSE) ||\r
+        (*NumberOfSenseKeys == ScsiDiskDevice->SenseDataNumber))\r
+    {\r
+      SenseReq = FALSE;\r
+    }\r
+  }\r
+\r
+EXIT:\r
+  FreeAlignedBuffer (PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA));\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Get information from media read capacity command.\r
+\r
+  @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV\r
+  @param  Capacity10      The pointer of EFI_SCSI_DISK_CAPACITY_DATA\r
+  @param  Capacity16      The pointer of EFI_SCSI_DISK_CAPACITY_DATA16\r
+\r
+**/\r
+VOID\r
+GetMediaInfo (\r
+  IN OUT SCSI_DISK_DEV                  *ScsiDiskDevice,\r
+  IN     EFI_SCSI_DISK_CAPACITY_DATA    *Capacity10,\r
+  IN     EFI_SCSI_DISK_CAPACITY_DATA16  *Capacity16\r
+  )\r
+{\r
+  UINT8  *Ptr;\r
+\r
+  if (!ScsiDiskDevice->Cdb16Byte) {\r
+    ScsiDiskDevice->BlkIo.Media->LastBlock =  ((UINT32)Capacity10->LastLba3 << 24) |\r
+                                             (Capacity10->LastLba2 << 16) |\r
+                                             (Capacity10->LastLba1 << 8)  |\r
+                                             Capacity10->LastLba0;\r
+\r
+    ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity10->BlockSize3 << 24) |\r
+                                             (Capacity10->BlockSize2 << 16) |\r
+                                             (Capacity10->BlockSize1 << 8)  |\r
+                                             Capacity10->BlockSize0;\r
+    ScsiDiskDevice->BlkIo.Media->LowestAlignedLba              = 0;\r
+    ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock = 0;\r
+    if (!ScsiDiskDevice->BlockLimitsVpdSupported) {\r
+      ScsiDiskDevice->UnmapInfo.MaxLbaCnt = (UINT32)ScsiDiskDevice->BlkIo.Media->LastBlock;\r
+    }\r
+  } else {\r
+    Ptr    = (UINT8 *)&ScsiDiskDevice->BlkIo.Media->LastBlock;\r
+    *Ptr++ = Capacity16->LastLba0;\r
+    *Ptr++ = Capacity16->LastLba1;\r
+    *Ptr++ = Capacity16->LastLba2;\r
+    *Ptr++ = Capacity16->LastLba3;\r
+    *Ptr++ = Capacity16->LastLba4;\r
+    *Ptr++ = Capacity16->LastLba5;\r
+    *Ptr++ = Capacity16->LastLba6;\r
+    *Ptr   = Capacity16->LastLba7;\r
+\r
+    ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity16->BlockSize3 << 24) |\r
+                                             (Capacity16->BlockSize2 << 16) |\r
+                                             (Capacity16->BlockSize1 << 8)  |\r
+                                             Capacity16->BlockSize0;\r
+\r
+    ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = (Capacity16->LowestAlignLogic2 << 8) |\r
+                                                    Capacity16->LowestAlignLogic1;\r
+    ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock = (1 << Capacity16->LogicPerPhysical);\r
+    if (!ScsiDiskDevice->BlockLimitsVpdSupported) {\r
+      if (ScsiDiskDevice->BlkIo.Media->LastBlock > (UINT32)-1) {\r
+        ScsiDiskDevice->UnmapInfo.MaxLbaCnt = (UINT32)-1;\r
+      } else {\r
+        ScsiDiskDevice->UnmapInfo.MaxLbaCnt = (UINT32)ScsiDiskDevice->BlkIo.Media->LastBlock;\r
+      }\r
+    }\r
+  }\r
+\r
+  ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE;\r
+}\r
+\r
+/**\r
+  Parse Inquiry data.\r
+\r
+  @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV\r
+\r
+**/\r
+VOID\r
+ParseInquiryData (\r
+  IN OUT SCSI_DISK_DEV  *ScsiDiskDevice\r
+  )\r
+{\r
+  ScsiDiskDevice->FixedDevice               = (BOOLEAN)((ScsiDiskDevice->InquiryData.Rmb == 1) ? 0 : 1);\r
+  ScsiDiskDevice->BlkIoMedia.RemovableMedia = (BOOLEAN)(!ScsiDiskDevice->FixedDevice);\r
+}\r
+\r
+/**\r
+  Read sector from SCSI Disk.\r
+\r
+  @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV\r
+  @param  Buffer          The buffer to fill in the read out data\r
+  @param  Lba             Logic block address\r
+  @param  NumberOfBlocks  The number of blocks to read\r
+\r
+  @retval EFI_DEVICE_ERROR  Indicates a device error.\r
+  @retval EFI_SUCCESS       Operation is successful.\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskReadSectors (\r
+  IN   SCSI_DISK_DEV  *ScsiDiskDevice,\r
+  OUT  VOID           *Buffer,\r
+  IN   EFI_LBA        Lba,\r
+  IN   UINTN          NumberOfBlocks\r
+  )\r
+{\r
+  UINTN       BlocksRemaining;\r
+  UINT8       *PtrBuffer;\r
+  UINT32      BlockSize;\r
+  UINT32      ByteCount;\r
+  UINT32      MaxBlock;\r
+  UINT32      SectorCount;\r
+  UINT32      NextSectorCount;\r
+  UINT64      Timeout;\r
+  EFI_STATUS  Status;\r
+  UINT8       Index;\r
+  UINT8       MaxRetry;\r
+  BOOLEAN     NeedRetry;\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  BlocksRemaining = NumberOfBlocks;\r
+  BlockSize       = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+\r
+  //\r
+  // limit the data bytes that can be transferred by one Read(10) or Read(16) Command\r
+  //\r
+  if (!ScsiDiskDevice->Cdb16Byte) {\r
+    MaxBlock = 0xFFFF;\r
+  } else {\r
+    MaxBlock = 0xFFFFFFFF;\r
+  }\r
+\r
+  PtrBuffer = Buffer;\r
+\r
+  while (BlocksRemaining > 0) {\r
+    if (BlocksRemaining <= MaxBlock) {\r
+      if (!ScsiDiskDevice->Cdb16Byte) {\r
+        SectorCount = (UINT16)BlocksRemaining;\r
+      } else {\r
+        SectorCount = (UINT32)BlocksRemaining;\r
+      }\r
+    } else {\r
+      SectorCount = MaxBlock;\r
+    }\r
+\r
+    ByteCount = SectorCount * BlockSize;\r
+    //\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |   ATA Transfer Mode    |  Transfer Rate  |  SCSI Interface  |  Transfer Rate  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 0       |  3.3Mbytes/sec  |     SCSI-1       |    5Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 1       |  5.2Mbytes/sec  |    Fast SCSI     |   10Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 2       |  8.3Mbytes/sec  |  Fast-Wide SCSI  |   20Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 3       | 11.1Mbytes/sec  |    Ultra SCSI    |   20Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 4       | 16.6Mbytes/sec  |  Ultra Wide SCSI |   40Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Single-word DMA Mode 0 |  2.1Mbytes/sec  |    Ultra2 SCSI   |   40Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Single-word DMA Mode 1 |  4.2Mbytes/sec  | Ultra2 Wide SCSI |   80Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Single-word DMA Mode 2 |  8.4Mbytes/sec  |    Ultra3 SCSI   |  160Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Multi-word DMA Mode 0  |  4.2Mbytes/sec  |  Ultra-320 SCSI  |  320Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Multi-word DMA Mode 1  | 13.3Mbytes/sec  |  Ultra-640 SCSI  |  640Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    //\r
+    // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices, we have to use\r
+    // the lowest transfer rate to calculate the possible maximum timeout value for each operation.\r
+    // From the above table, we could know 2.1Mbytes per second is lowest one.\r
+    // The timeout value is rounded up to nearest integer and here an additional 30s is added\r
+    // to follow ATA spec in which it mentioned that the device may take up to 30s to respond\r
+    // commands in the Standby/Idle mode.\r
+    //\r
+    Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);\r
+\r
+    MaxRetry = 2;\r
+    for (Index = 0; Index < MaxRetry; Index++) {\r
+      if (!ScsiDiskDevice->Cdb16Byte) {\r
+        Status = ScsiDiskRead10 (\r
+                   ScsiDiskDevice,\r
+                   &NeedRetry,\r
+                   Timeout,\r
+                   PtrBuffer,\r
+                   &ByteCount,\r
+                   (UINT32)Lba,\r
+                   SectorCount\r
+                   );\r
+      } else {\r
+        Status = ScsiDiskRead16 (\r
+                   ScsiDiskDevice,\r
+                   &NeedRetry,\r
+                   Timeout,\r
+                   PtrBuffer,\r
+                   &ByteCount,\r
+                   Lba,\r
+                   SectorCount\r
+                   );\r
+      }\r
+\r
+      if (!EFI_ERROR (Status)) {\r
+        break;\r
+      }\r
+\r
+      if (!NeedRetry) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+\r
+      //\r
+      // We need to retry. However, if ScsiDiskRead10() or ScsiDiskRead16() has\r
+      // lowered ByteCount on output, we must make sure that we lower\r
+      // SectorCount accordingly. SectorCount will be encoded in the CDB, and\r
+      // it is invalid to request more sectors in the CDB than the entire\r
+      // transfer (ie. ByteCount) can carry.\r
+      //\r
+      // In addition, ByteCount is only expected to go down, or stay unchanged.\r
+      // Therefore we don't need to update Timeout: the original timeout should\r
+      // accommodate shorter transfers too.\r
+      //\r
+      NextSectorCount = ByteCount / BlockSize;\r
+      if (NextSectorCount < SectorCount) {\r
+        SectorCount = NextSectorCount;\r
+        //\r
+        // Account for any rounding down.\r
+        //\r
+        ByteCount = SectorCount * BlockSize;\r
+      }\r
+    }\r
+\r
+    if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    //\r
+    // actual transferred sectors\r
+    //\r
+    SectorCount = ByteCount / BlockSize;\r
+\r
+    Lba             += SectorCount;\r
+    PtrBuffer        = PtrBuffer + SectorCount * BlockSize;\r
+    BlocksRemaining -= SectorCount;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Write sector to SCSI Disk.\r
+\r
+  @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV\r
+  @param  Buffer          The buffer of data to be written into SCSI Disk\r
+  @param  Lba             Logic block address\r
+  @param  NumberOfBlocks  The number of blocks to read\r
+\r
+  @retval EFI_DEVICE_ERROR  Indicates a device error.\r
+  @retval EFI_SUCCESS       Operation is successful.\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskWriteSectors (\r
+  IN  SCSI_DISK_DEV  *ScsiDiskDevice,\r
+  IN  VOID           *Buffer,\r
+  IN  EFI_LBA        Lba,\r
+  IN  UINTN          NumberOfBlocks\r
+  )\r
+{\r
+  UINTN       BlocksRemaining;\r
+  UINT8       *PtrBuffer;\r
+  UINT32      BlockSize;\r
+  UINT32      ByteCount;\r
+  UINT32      MaxBlock;\r
+  UINT32      SectorCount;\r
+  UINT32      NextSectorCount;\r
+  UINT64      Timeout;\r
+  EFI_STATUS  Status;\r
+  UINT8       Index;\r
+  UINT8       MaxRetry;\r
+  BOOLEAN     NeedRetry;\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  BlocksRemaining = NumberOfBlocks;\r
+  BlockSize       = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+\r
+  //\r
+  // limit the data bytes that can be transferred by one Read(10) or Read(16) Command\r
+  //\r
+  if (!ScsiDiskDevice->Cdb16Byte) {\r
+    MaxBlock = 0xFFFF;\r
+  } else {\r
+    MaxBlock = 0xFFFFFFFF;\r
+  }\r
+\r
+  PtrBuffer = Buffer;\r
+\r
+  while (BlocksRemaining > 0) {\r
+    if (BlocksRemaining <= MaxBlock) {\r
+      if (!ScsiDiskDevice->Cdb16Byte) {\r
+        SectorCount = (UINT16)BlocksRemaining;\r
+      } else {\r
+        SectorCount = (UINT32)BlocksRemaining;\r
+      }\r
+    } else {\r
+      SectorCount = MaxBlock;\r
+    }\r
+\r
+    ByteCount = SectorCount * BlockSize;\r
+    //\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |   ATA Transfer Mode    |  Transfer Rate  |  SCSI Interface  |  Transfer Rate  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 0       |  3.3Mbytes/sec  |     SCSI-1       |    5Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 1       |  5.2Mbytes/sec  |    Fast SCSI     |   10Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 2       |  8.3Mbytes/sec  |  Fast-Wide SCSI  |   20Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 3       | 11.1Mbytes/sec  |    Ultra SCSI    |   20Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 4       | 16.6Mbytes/sec  |  Ultra Wide SCSI |   40Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Single-word DMA Mode 0 |  2.1Mbytes/sec  |    Ultra2 SCSI   |   40Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Single-word DMA Mode 1 |  4.2Mbytes/sec  | Ultra2 Wide SCSI |   80Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Single-word DMA Mode 2 |  8.4Mbytes/sec  |    Ultra3 SCSI   |  160Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Multi-word DMA Mode 0  |  4.2Mbytes/sec  |  Ultra-320 SCSI  |  320Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Multi-word DMA Mode 1  | 13.3Mbytes/sec  |  Ultra-640 SCSI  |  640Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    //\r
+    // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices, we have to use\r
+    // the lowest transfer rate to calculate the possible maximum timeout value for each operation.\r
+    // From the above table, we could know 2.1Mbytes per second is lowest one.\r
+    // The timeout value is rounded up to nearest integer and here an additional 30s is added\r
+    // to follow ATA spec in which it mentioned that the device may take up to 30s to respond\r
+    // commands in the Standby/Idle mode.\r
+    //\r
+    Timeout  = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);\r
+    MaxRetry = 2;\r
+    for (Index = 0; Index < MaxRetry; Index++) {\r
+      if (!ScsiDiskDevice->Cdb16Byte) {\r
+        Status = ScsiDiskWrite10 (\r
+                   ScsiDiskDevice,\r
+                   &NeedRetry,\r
+                   Timeout,\r
+                   PtrBuffer,\r
+                   &ByteCount,\r
+                   (UINT32)Lba,\r
+                   SectorCount\r
+                   );\r
+      } else {\r
+        Status = ScsiDiskWrite16 (\r
+                   ScsiDiskDevice,\r
+                   &NeedRetry,\r
+                   Timeout,\r
+                   PtrBuffer,\r
+                   &ByteCount,\r
+                   Lba,\r
+                   SectorCount\r
+                   );\r
+      }\r
+\r
+      if (!EFI_ERROR (Status)) {\r
+        break;\r
+      }\r
+\r
+      if (!NeedRetry) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+\r
+      //\r
+      // We need to retry. However, if ScsiDiskWrite10() or ScsiDiskWrite16()\r
+      // has lowered ByteCount on output, we must make sure that we lower\r
+      // SectorCount accordingly. SectorCount will be encoded in the CDB, and\r
+      // it is invalid to request more sectors in the CDB than the entire\r
+      // transfer (ie. ByteCount) can carry.\r
+      //\r
+      // In addition, ByteCount is only expected to go down, or stay unchanged.\r
+      // Therefore we don't need to update Timeout: the original timeout should\r
+      // accommodate shorter transfers too.\r
+      //\r
+      NextSectorCount = ByteCount / BlockSize;\r
+      if (NextSectorCount < SectorCount) {\r
+        SectorCount = NextSectorCount;\r
+        //\r
+        // Account for any rounding down.\r
+        //\r
+        ByteCount = SectorCount * BlockSize;\r
+      }\r
+    }\r
+\r
+    if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    //\r
+    // actual transferred sectors\r
+    //\r
+    SectorCount = ByteCount / BlockSize;\r
+\r
+    Lba             += SectorCount;\r
+    PtrBuffer        = PtrBuffer + SectorCount * BlockSize;\r
+    BlocksRemaining -= SectorCount;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Asynchronously read sector from SCSI Disk.\r
+\r
+  @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV.\r
+  @param  Buffer          The buffer to fill in the read out data.\r
+  @param  Lba             Logic block address.\r
+  @param  NumberOfBlocks  The number of blocks to read.\r
+  @param  Token           A pointer to the token associated with the\r
+                          non-blocking read request.\r
+\r
+  @retval EFI_INVALID_PARAMETER  Token is NULL or Token->Event is NULL.\r
+  @retval EFI_DEVICE_ERROR       Indicates a device error.\r
+  @retval EFI_SUCCESS            Operation is successful.\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskAsyncReadSectors (\r
+  IN   SCSI_DISK_DEV        *ScsiDiskDevice,\r
+  OUT  VOID                 *Buffer,\r
+  IN   EFI_LBA              Lba,\r
+  IN   UINTN                NumberOfBlocks,\r
+  IN   EFI_BLOCK_IO2_TOKEN  *Token\r
+  )\r
+{\r
+  UINTN                BlocksRemaining;\r
+  UINT8                *PtrBuffer;\r
+  UINT32               BlockSize;\r
+  UINT32               ByteCount;\r
+  UINT32               MaxBlock;\r
+  UINT32               SectorCount;\r
+  UINT64               Timeout;\r
+  SCSI_BLKIO2_REQUEST  *BlkIo2Req;\r
+  EFI_STATUS           Status;\r
+  EFI_TPL              OldTpl;\r
+\r
+  if ((Token == NULL) || (Token->Event == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  BlkIo2Req = AllocateZeroPool (sizeof (SCSI_BLKIO2_REQUEST));\r
+  if (BlkIo2Req == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  BlkIo2Req->Token = Token;\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+  InsertTailList (&ScsiDiskDevice->AsyncTaskQueue, &BlkIo2Req->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  InitializeListHead (&BlkIo2Req->ScsiRWQueue);\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  BlocksRemaining = NumberOfBlocks;\r
+  BlockSize       = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+\r
+  //\r
+  // Limit the data bytes that can be transferred by one Read(10) or Read(16)\r
+  // Command\r
+  //\r
+  if (!ScsiDiskDevice->Cdb16Byte) {\r
+    MaxBlock = 0xFFFF;\r
+  } else {\r
+    MaxBlock = 0xFFFFFFFF;\r
+  }\r
+\r
+  PtrBuffer = Buffer;\r
+\r
+  while (BlocksRemaining > 0) {\r
+    if (BlocksRemaining <= MaxBlock) {\r
+      if (!ScsiDiskDevice->Cdb16Byte) {\r
+        SectorCount = (UINT16)BlocksRemaining;\r
+      } else {\r
+        SectorCount = (UINT32)BlocksRemaining;\r
+      }\r
+    } else {\r
+      SectorCount = MaxBlock;\r
+    }\r
+\r
+    ByteCount = SectorCount * BlockSize;\r
+    //\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |   ATA Transfer Mode    |  Transfer Rate  |  SCSI Interface  |  Transfer Rate  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 0       |  3.3Mbytes/sec  |     SCSI-1       |    5Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 1       |  5.2Mbytes/sec  |    Fast SCSI     |   10Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 2       |  8.3Mbytes/sec  |  Fast-Wide SCSI  |   20Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 3       | 11.1Mbytes/sec  |    Ultra SCSI    |   20Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 4       | 16.6Mbytes/sec  |  Ultra Wide SCSI |   40Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Single-word DMA Mode 0 |  2.1Mbytes/sec  |    Ultra2 SCSI   |   40Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Single-word DMA Mode 1 |  4.2Mbytes/sec  | Ultra2 Wide SCSI |   80Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Single-word DMA Mode 2 |  8.4Mbytes/sec  |    Ultra3 SCSI   |  160Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Multi-word DMA Mode 0  |  4.2Mbytes/sec  |  Ultra-320 SCSI  |  320Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Multi-word DMA Mode 1  | 13.3Mbytes/sec  |  Ultra-640 SCSI  |  640Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    //\r
+    // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices,\r
+    // we have to use the lowest transfer rate to calculate the possible\r
+    // maximum timeout value for each operation.\r
+    // From the above table, we could know 2.1Mbytes per second is lowest one.\r
+    // The timeout value is rounded up to nearest integer and here an additional\r
+    // 30s is added to follow ATA spec in which it mentioned that the device\r
+    // may take up to 30s to respond commands in the Standby/Idle mode.\r
+    //\r
+    Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);\r
+\r
+    if (!ScsiDiskDevice->Cdb16Byte) {\r
+      Status = ScsiDiskAsyncRead10 (\r
+                 ScsiDiskDevice,\r
+                 Timeout,\r
+                 0,\r
+                 PtrBuffer,\r
+                 ByteCount,\r
+                 (UINT32)Lba,\r
+                 SectorCount,\r
+                 BlkIo2Req,\r
+                 Token\r
+                 );\r
+    } else {\r
+      Status = ScsiDiskAsyncRead16 (\r
+                 ScsiDiskDevice,\r
+                 Timeout,\r
+                 0,\r
+                 PtrBuffer,\r
+                 ByteCount,\r
+                 Lba,\r
+                 SectorCount,\r
+                 BlkIo2Req,\r
+                 Token\r
+                 );\r
+    }\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // Some devices will return EFI_DEVICE_ERROR or EFI_TIMEOUT when the data\r
+      // length of a SCSI I/O command is too large.\r
+      // In this case, we retry sending the SCSI command with a data length\r
+      // half of its previous value.\r
+      //\r
+      if ((Status == EFI_DEVICE_ERROR) || (Status == EFI_TIMEOUT)) {\r
+        if ((MaxBlock > 1) && (SectorCount > 1)) {\r
+          MaxBlock = MIN (MaxBlock, SectorCount) >> 1;\r
+          continue;\r
+        }\r
+      }\r
+\r
+      OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+      if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {\r
+        //\r
+        // Free the SCSI_BLKIO2_REQUEST structure only when there is no other\r
+        // SCSI sub-task running. Otherwise, it will be freed in the callback\r
+        // function ScsiDiskNotify().\r
+        //\r
+        RemoveEntryList (&BlkIo2Req->Link);\r
+        FreePool (BlkIo2Req);\r
+        BlkIo2Req = NULL;\r
+        gBS->RestoreTPL (OldTpl);\r
+\r
+        //\r
+        // It is safe to return error status to the caller, since there is no\r
+        // previous SCSI sub-task executing.\r
+        //\r
+        Status = EFI_DEVICE_ERROR;\r
+        goto Done;\r
+      } else {\r
+        gBS->RestoreTPL (OldTpl);\r
+\r
+        //\r
+        // There are previous SCSI commands still running, EFI_SUCCESS should\r
+        // be returned to make sure that the caller does not free resources\r
+        // still using by these SCSI commands.\r
+        //\r
+        Status = EFI_SUCCESS;\r
+        goto Done;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Sectors submitted for transfer\r
+    //\r
+    SectorCount = ByteCount / BlockSize;\r
+\r
+    Lba             += SectorCount;\r
+    PtrBuffer        = PtrBuffer + SectorCount * BlockSize;\r
+    BlocksRemaining -= SectorCount;\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+Done:\r
+  if (BlkIo2Req != NULL) {\r
+    BlkIo2Req->LastScsiRW = TRUE;\r
+\r
+    OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+    if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {\r
+      RemoveEntryList (&BlkIo2Req->Link);\r
+      FreePool (BlkIo2Req);\r
+      BlkIo2Req = NULL;\r
+\r
+      gBS->SignalEvent (Token->Event);\r
+    }\r
+\r
+    gBS->RestoreTPL (OldTpl);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Asynchronously write sector to SCSI Disk.\r
+\r
+  @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV.\r
+  @param  Buffer          The buffer of data to be written into SCSI Disk.\r
+  @param  Lba             Logic block address.\r
+  @param  NumberOfBlocks  The number of blocks to read.\r
+  @param  Token           A pointer to the token associated with the\r
+                          non-blocking read request.\r
+\r
+  @retval EFI_INVALID_PARAMETER  Token is NULL or Token->Event is NULL\r
+  @retval EFI_DEVICE_ERROR  Indicates a device error.\r
+  @retval EFI_SUCCESS       Operation is successful.\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskAsyncWriteSectors (\r
+  IN  SCSI_DISK_DEV        *ScsiDiskDevice,\r
+  IN  VOID                 *Buffer,\r
+  IN  EFI_LBA              Lba,\r
+  IN  UINTN                NumberOfBlocks,\r
+  IN  EFI_BLOCK_IO2_TOKEN  *Token\r
+  )\r
+{\r
+  UINTN                BlocksRemaining;\r
+  UINT8                *PtrBuffer;\r
+  UINT32               BlockSize;\r
+  UINT32               ByteCount;\r
+  UINT32               MaxBlock;\r
+  UINT32               SectorCount;\r
+  UINT64               Timeout;\r
+  SCSI_BLKIO2_REQUEST  *BlkIo2Req;\r
+  EFI_STATUS           Status;\r
+  EFI_TPL              OldTpl;\r
+\r
+  if ((Token == NULL) || (Token->Event == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  BlkIo2Req = AllocateZeroPool (sizeof (SCSI_BLKIO2_REQUEST));\r
+  if (BlkIo2Req == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  BlkIo2Req->Token = Token;\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+  InsertTailList (&ScsiDiskDevice->AsyncTaskQueue, &BlkIo2Req->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  InitializeListHead (&BlkIo2Req->ScsiRWQueue);\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  BlocksRemaining = NumberOfBlocks;\r
+  BlockSize       = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+\r
+  //\r
+  // Limit the data bytes that can be transferred by one Read(10) or Read(16)\r
+  // Command\r
+  //\r
+  if (!ScsiDiskDevice->Cdb16Byte) {\r
+    MaxBlock = 0xFFFF;\r
+  } else {\r
+    MaxBlock = 0xFFFFFFFF;\r
+  }\r
+\r
+  PtrBuffer = Buffer;\r
+\r
+  while (BlocksRemaining > 0) {\r
+    if (BlocksRemaining <= MaxBlock) {\r
+      if (!ScsiDiskDevice->Cdb16Byte) {\r
+        SectorCount = (UINT16)BlocksRemaining;\r
+      } else {\r
+        SectorCount = (UINT32)BlocksRemaining;\r
+      }\r
+    } else {\r
+      SectorCount = MaxBlock;\r
+    }\r
+\r
+    ByteCount = SectorCount * BlockSize;\r
+    //\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |   ATA Transfer Mode    |  Transfer Rate  |  SCSI Interface  |  Transfer Rate  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 0       |  3.3Mbytes/sec  |     SCSI-1       |    5Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 1       |  5.2Mbytes/sec  |    Fast SCSI     |   10Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 2       |  8.3Mbytes/sec  |  Fast-Wide SCSI  |   20Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 3       | 11.1Mbytes/sec  |    Ultra SCSI    |   20Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // |       PIO Mode 4       | 16.6Mbytes/sec  |  Ultra Wide SCSI |   40Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Single-word DMA Mode 0 |  2.1Mbytes/sec  |    Ultra2 SCSI   |   40Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Single-word DMA Mode 1 |  4.2Mbytes/sec  | Ultra2 Wide SCSI |   80Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Single-word DMA Mode 2 |  8.4Mbytes/sec  |    Ultra3 SCSI   |  160Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Multi-word DMA Mode 0  |  4.2Mbytes/sec  |  Ultra-320 SCSI  |  320Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    // | Multi-word DMA Mode 1  | 13.3Mbytes/sec  |  Ultra-640 SCSI  |  640Mbytes/sec  |\r
+    // |------------------------|-----------------|------------------|-----------------|\r
+    //\r
+    // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices,\r
+    // we have to use the lowest transfer rate to calculate the possible\r
+    // maximum timeout value for each operation.\r
+    // From the above table, we could know 2.1Mbytes per second is lowest one.\r
+    // The timeout value is rounded up to nearest integer and here an additional\r
+    // 30s is added to follow ATA spec in which it mentioned that the device\r
+    // may take up to 30s to respond commands in the Standby/Idle mode.\r
+    //\r
+    Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);\r
+\r
+    if (!ScsiDiskDevice->Cdb16Byte) {\r
+      Status = ScsiDiskAsyncWrite10 (\r
+                 ScsiDiskDevice,\r
+                 Timeout,\r
+                 0,\r
+                 PtrBuffer,\r
+                 ByteCount,\r
+                 (UINT32)Lba,\r
+                 SectorCount,\r
+                 BlkIo2Req,\r
+                 Token\r
+                 );\r
+    } else {\r
+      Status = ScsiDiskAsyncWrite16 (\r
+                 ScsiDiskDevice,\r
+                 Timeout,\r
+                 0,\r
+                 PtrBuffer,\r
+                 ByteCount,\r
+                 Lba,\r
+                 SectorCount,\r
+                 BlkIo2Req,\r
+                 Token\r
+                 );\r
+    }\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // Some devices will return EFI_DEVICE_ERROR or EFI_TIMEOUT when the data\r
+      // length of a SCSI I/O command is too large.\r
+      // In this case, we retry sending the SCSI command with a data length\r
+      // half of its previous value.\r
+      //\r
+      if ((Status == EFI_DEVICE_ERROR) || (Status == EFI_TIMEOUT)) {\r
+        if ((MaxBlock > 1) && (SectorCount > 1)) {\r
+          MaxBlock = MIN (MaxBlock, SectorCount) >> 1;\r
+          continue;\r
+        }\r
+      }\r
+\r
+      OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+      if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {\r
+        //\r
+        // Free the SCSI_BLKIO2_REQUEST structure only when there is no other\r
+        // SCSI sub-task running. Otherwise, it will be freed in the callback\r
+        // function ScsiDiskNotify().\r
+        //\r
+        RemoveEntryList (&BlkIo2Req->Link);\r
+        FreePool (BlkIo2Req);\r
+        BlkIo2Req = NULL;\r
+        gBS->RestoreTPL (OldTpl);\r
+\r
+        //\r
+        // It is safe to return error status to the caller, since there is no\r
+        // previous SCSI sub-task executing.\r
+        //\r
+        Status = EFI_DEVICE_ERROR;\r
+        goto Done;\r
+      } else {\r
+        gBS->RestoreTPL (OldTpl);\r
+\r
+        //\r
+        // There are previous SCSI commands still running, EFI_SUCCESS should\r
+        // be returned to make sure that the caller does not free resources\r
+        // still using by these SCSI commands.\r
+        //\r
+        Status = EFI_SUCCESS;\r
+        goto Done;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Sectors submitted for transfer\r
+    //\r
+    SectorCount = ByteCount / BlockSize;\r
+\r
+    Lba             += SectorCount;\r
+    PtrBuffer        = PtrBuffer + SectorCount * BlockSize;\r
+    BlocksRemaining -= SectorCount;\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+Done:\r
+  if (BlkIo2Req != NULL) {\r
+    BlkIo2Req->LastScsiRW = TRUE;\r
+\r
+    OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+    if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {\r
+      RemoveEntryList (&BlkIo2Req->Link);\r
+      FreePool (BlkIo2Req);\r
+      BlkIo2Req = NULL;\r
+\r
+      gBS->SignalEvent (Token->Event);\r
+    }\r
+\r
+    gBS->RestoreTPL (OldTpl);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Submit Read(10) command.\r
+\r
+  @param  ScsiDiskDevice     The pointer of ScsiDiskDevice\r
+  @param  NeedRetry          The pointer of flag indicates if needs retry if error happens\r
+  @param  Timeout            The time to complete the command\r
+  @param  DataBuffer         The buffer to fill with the read out data\r
+  @param  DataLength         The length of buffer\r
+  @param  StartLba           The start logic block address\r
+  @param  SectorCount        The number of blocks to read\r
+\r
+  @return  EFI_STATUS is returned by calling ScsiRead10Command().\r
+**/\r
+EFI_STATUS\r
+ScsiDiskRead10 (\r
+  IN     SCSI_DISK_DEV  *ScsiDiskDevice,\r
+  OUT BOOLEAN           *NeedRetry,\r
+  IN     UINT64         Timeout,\r
+  OUT UINT8             *DataBuffer,\r
+  IN OUT UINT32         *DataLength,\r
+  IN     UINT32         StartLba,\r
+  IN     UINT32         SectorCount\r
+  )\r
+{\r
+  UINT8       SenseDataLength;\r
+  EFI_STATUS  Status;\r
+  EFI_STATUS  ReturnStatus;\r
+  UINT8       HostAdapterStatus;\r
+  UINT8       TargetStatus;\r
+  UINTN       Action;\r
+\r
+  //\r
+  // Implement a backoff algorithm to resolve some compatibility issues that\r
+  // some SCSI targets or ATAPI devices couldn't correctly response reading/writing\r
+  // big data in a single operation.\r
+  // This algorithm will at first try to execute original request. If the request fails\r
+  // with media error sense data or else, it will reduce the transfer length to half and\r
+  // try again till the operation succeeds or fails with one sector transfer length.\r
+  //\r
+BackOff:\r
+  *NeedRetry      = FALSE;\r
+  Action          = ACTION_NO_ACTION;\r
+  SenseDataLength = (UINT8)(ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
+  ReturnStatus    = ScsiRead10Command (\r
+                      ScsiDiskDevice->ScsiIo,\r
+                      Timeout,\r
+                      ScsiDiskDevice->SenseData,\r
+                      &SenseDataLength,\r
+                      &HostAdapterStatus,\r
+                      &TargetStatus,\r
+                      DataBuffer,\r
+                      DataLength,\r
+                      StartLba,\r
+                      SectorCount\r
+                      );\r
+\r
+  if ((ReturnStatus == EFI_NOT_READY) || (ReturnStatus == EFI_BAD_BUFFER_SIZE)) {\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {\r
+    *NeedRetry = FALSE;\r
+    return ReturnStatus;\r
   }\r
 \r
   //\r
-  // either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE,\r
-  // retrieve capacity via Read Capacity command\r
+  // go ahead to check HostAdapterStatus and TargetStatus\r
+  // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
   //\r
-  if (NeedReadCapacity || MustReadCapacity) {\r
+  Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+  if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
     //\r
-    // retrieve media information\r
+    // reset the scsi channel\r
     //\r
-    MaxRetry = 3;\r
-    for (Index = 0; Index < MaxRetry; Index++) {\r
+    ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
 \r
-      ReadCapacityStatus = ScsiDiskReadCapacity (\r
-                            ScsiDiskDevice,\r
-                            &NeedRetry,\r
-                            &SenseData,\r
-                            &NumberOfSenseKeys\r
-                            );\r
-      if (EFI_ERROR (ReadCapacityStatus) && !NeedRetry) {\r
+  Status = CheckTargetStatus (TargetStatus);\r
+  if (Status == EFI_NOT_READY) {\r
+    //\r
+    // reset the scsi device\r
+    //\r
+    ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {\r
+    DEBUG ((DEBUG_ERROR, "ScsiDiskRead10: Check Condition happened!\n"));\r
+    DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);\r
+    if (Action == ACTION_RETRY_COMMAND_LATER) {\r
+      *NeedRetry = TRUE;\r
+      return EFI_DEVICE_ERROR;\r
+    } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
+      if (SectorCount <= 1) {\r
+        //\r
+        // Jump out if the operation still fails with one sector transfer length.\r
+        //\r
+        *NeedRetry = FALSE;\r
         return EFI_DEVICE_ERROR;\r
       }\r
+\r
       //\r
-      // analyze sense key to action\r
-      //\r
-      Status = DetectMediaParsingSenseKeys (\r
-                ScsiDiskDevice,\r
-                SenseData,\r
-                NumberOfSenseKeys,\r
-                &Action\r
-                );\r
-      //\r
-      // if Status is error, it may indicate crisis error,\r
-      // so return without retry.\r
+      // Try again with half length if the sense data shows we need to retry.\r
       //\r
-      if (EFI_ERROR (Status)) {\r
-        return Status;\r
-      }\r
+      SectorCount >>= 1;\r
+      *DataLength   = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+      goto BackOff;\r
+    } else {\r
+      *NeedRetry = FALSE;\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
 \r
-      switch (Action) {\r
-      case ACTION_NO_ACTION:\r
-        //\r
-        // no retry\r
-        //\r
-        Index = MaxRetry;\r
-        break;\r
+  return ReturnStatus;\r
+}\r
 \r
-      case ACTION_RETRY_COMMAND_LATER:\r
-        //\r
-        // retry the ReadCapacity later and continuously, until the condition\r
-        // no longer emerges.\r
-        // stall time is 100000us, or say 0.1 second.\r
-        //\r
-        gBS->Stall (100000);\r
-        Index = 0;\r
-        break;\r
+/**\r
+  Submit Write(10) Command.\r
 \r
-      default:\r
+  @param  ScsiDiskDevice     The pointer of ScsiDiskDevice\r
+  @param  NeedRetry          The pointer of flag indicates if needs retry if error happens\r
+  @param  Timeout            The time to complete the command\r
+  @param  DataBuffer         The buffer to fill with the read out data\r
+  @param  DataLength         The length of buffer\r
+  @param  StartLba           The start logic block address\r
+  @param  SectorCount        The number of blocks to write\r
+\r
+  @return  EFI_STATUS is returned by calling ScsiWrite10Command().\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskWrite10 (\r
+  IN     SCSI_DISK_DEV  *ScsiDiskDevice,\r
+  OUT BOOLEAN           *NeedRetry,\r
+  IN     UINT64         Timeout,\r
+  IN     UINT8          *DataBuffer,\r
+  IN OUT UINT32         *DataLength,\r
+  IN     UINT32         StartLba,\r
+  IN     UINT32         SectorCount\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  EFI_STATUS  ReturnStatus;\r
+  UINT8       SenseDataLength;\r
+  UINT8       HostAdapterStatus;\r
+  UINT8       TargetStatus;\r
+  UINTN       Action;\r
+\r
+  //\r
+  // Implement a backoff algorithm to resolve some compatibility issues that\r
+  // some SCSI targets or ATAPI devices couldn't correctly response reading/writing\r
+  // big data in a single operation.\r
+  // This algorithm will at first try to execute original request. If the request fails\r
+  // with media error sense data or else, it will reduce the transfer length to half and\r
+  // try again till the operation succeeds or fails with one sector transfer length.\r
+  //\r
+BackOff:\r
+  *NeedRetry      = FALSE;\r
+  Action          = ACTION_NO_ACTION;\r
+  SenseDataLength = (UINT8)(ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
+  ReturnStatus    = ScsiWrite10Command (\r
+                      ScsiDiskDevice->ScsiIo,\r
+                      Timeout,\r
+                      ScsiDiskDevice->SenseData,\r
+                      &SenseDataLength,\r
+                      &HostAdapterStatus,\r
+                      &TargetStatus,\r
+                      DataBuffer,\r
+                      DataLength,\r
+                      StartLba,\r
+                      SectorCount\r
+                      );\r
+  if ((ReturnStatus == EFI_NOT_READY) || (ReturnStatus == EFI_BAD_BUFFER_SIZE)) {\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {\r
+    *NeedRetry = FALSE;\r
+    return ReturnStatus;\r
+  }\r
+\r
+  //\r
+  // go ahead to check HostAdapterStatus and TargetStatus\r
+  // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
+  //\r
+  Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+  if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    //\r
+    // reset the scsi channel\r
+    //\r
+    ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Status = CheckTargetStatus (TargetStatus);\r
+  if (Status == EFI_NOT_READY) {\r
+    //\r
+    // reset the scsi device\r
+    //\r
+    ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {\r
+    DEBUG ((DEBUG_ERROR, "ScsiDiskWrite10: Check Condition happened!\n"));\r
+    DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);\r
+    if (Action == ACTION_RETRY_COMMAND_LATER) {\r
+      *NeedRetry = TRUE;\r
+      return EFI_DEVICE_ERROR;\r
+    } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
+      if (SectorCount <= 1) {\r
         //\r
-        // other cases, just retry the command\r
+        // Jump out if the operation still fails with one sector transfer length.\r
         //\r
-        break;\r
+        *NeedRetry = FALSE;\r
+        return EFI_DEVICE_ERROR;\r
       }\r
-    }\r
 \r
-    if ((Index == MaxRetry) && EFI_ERROR (ReadCapacityStatus)) {\r
+      //\r
+      // Try again with half length if the sense data shows we need to retry.\r
+      //\r
+      SectorCount >>= 1;\r
+      *DataLength   = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+      goto BackOff;\r
+    } else {\r
+      *NeedRetry = FALSE;\r
       return EFI_DEVICE_ERROR;\r
     }\r
   }\r
 \r
-  if (ScsiDiskDevice->BlkIo.Media->MediaId != OldMedia.MediaId) {\r
-    //\r
-    // Media change information got from the device\r
-    //\r
-    *MediaChange = TRUE;\r
-  }\r
+  return ReturnStatus;\r
+}\r
 \r
-  if (ScsiDiskDevice->BlkIo.Media->ReadOnly != OldMedia.ReadOnly) {\r
-    *MediaChange = TRUE;\r
-    ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
+/**\r
+  Submit Read(16) command.\r
+\r
+  @param  ScsiDiskDevice     The pointer of ScsiDiskDevice\r
+  @param  NeedRetry          The pointer of flag indicates if needs retry if error happens\r
+  @param  Timeout            The time to complete the command\r
+  @param  DataBuffer         The buffer to fill with the read out data\r
+  @param  DataLength         The length of buffer\r
+  @param  StartLba           The start logic block address\r
+  @param  SectorCount        The number of blocks to read\r
+\r
+  @return  EFI_STATUS is returned by calling ScsiRead16Command().\r
+**/\r
+EFI_STATUS\r
+ScsiDiskRead16 (\r
+  IN     SCSI_DISK_DEV  *ScsiDiskDevice,\r
+  OUT BOOLEAN           *NeedRetry,\r
+  IN     UINT64         Timeout,\r
+  OUT UINT8             *DataBuffer,\r
+  IN OUT UINT32         *DataLength,\r
+  IN     UINT64         StartLba,\r
+  IN     UINT32         SectorCount\r
+  )\r
+{\r
+  UINT8       SenseDataLength;\r
+  EFI_STATUS  Status;\r
+  EFI_STATUS  ReturnStatus;\r
+  UINT8       HostAdapterStatus;\r
+  UINT8       TargetStatus;\r
+  UINTN       Action;\r
+\r
+  //\r
+  // Implement a backoff algorithm to resolve some compatibility issues that\r
+  // some SCSI targets or ATAPI devices couldn't correctly response reading/writing\r
+  // big data in a single operation.\r
+  // This algorithm will at first try to execute original request. If the request fails\r
+  // with media error sense data or else, it will reduce the transfer length to half and\r
+  // try again till the operation succeeds or fails with one sector transfer length.\r
+  //\r
+BackOff:\r
+  *NeedRetry      = FALSE;\r
+  Action          = ACTION_NO_ACTION;\r
+  SenseDataLength = (UINT8)(ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
+  ReturnStatus    = ScsiRead16Command (\r
+                      ScsiDiskDevice->ScsiIo,\r
+                      Timeout,\r
+                      ScsiDiskDevice->SenseData,\r
+                      &SenseDataLength,\r
+                      &HostAdapterStatus,\r
+                      &TargetStatus,\r
+                      DataBuffer,\r
+                      DataLength,\r
+                      StartLba,\r
+                      SectorCount\r
+                      );\r
+  if ((ReturnStatus == EFI_NOT_READY) || (ReturnStatus == EFI_BAD_BUFFER_SIZE)) {\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {\r
+    *NeedRetry = FALSE;\r
+    return ReturnStatus;\r
   }\r
 \r
-  if (ScsiDiskDevice->BlkIo.Media->BlockSize != OldMedia.BlockSize) {\r
-    *MediaChange = TRUE;\r
-    ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
+  //\r
+  // go ahead to check HostAdapterStatus and TargetStatus\r
+  // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
+  //\r
+  Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+  if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    //\r
+    // reset the scsi channel\r
+    //\r
+    ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
   }\r
 \r
-  if (ScsiDiskDevice->BlkIo.Media->LastBlock != OldMedia.LastBlock) {\r
-    *MediaChange = TRUE;\r
-    ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
+  Status = CheckTargetStatus (TargetStatus);\r
+  if (Status == EFI_NOT_READY) {\r
+    //\r
+    // reset the scsi device\r
+    //\r
+    ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    *NeedRetry = FALSE;\r
+    return EFI_DEVICE_ERROR;\r
   }\r
 \r
-  if (ScsiDiskDevice->BlkIo.Media->MediaPresent != OldMedia.MediaPresent) {\r
-    if (ScsiDiskDevice->BlkIo.Media->MediaPresent) {\r
+  if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {\r
+    DEBUG ((DEBUG_ERROR, "ScsiDiskRead16: Check Condition happened!\n"));\r
+    DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);\r
+    if (Action == ACTION_RETRY_COMMAND_LATER) {\r
+      *NeedRetry = TRUE;\r
+      return EFI_DEVICE_ERROR;\r
+    } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
+      if (SectorCount <= 1) {\r
+        //\r
+        // Jump out if the operation still fails with one sector transfer length.\r
+        //\r
+        *NeedRetry = FALSE;\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+\r
       //\r
-      // when change from no media to media present, reset the MediaId to 1.\r
+      // Try again with half length if the sense data shows we need to retry.\r
       //\r
-      ScsiDiskDevice->BlkIo.Media->MediaId = 1;\r
+      SectorCount >>= 1;\r
+      *DataLength   = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+      goto BackOff;\r
     } else {\r
-      //\r
-      // when no media, reset the MediaId to zero.\r
-      //\r
-      ScsiDiskDevice->BlkIo.Media->MediaId = 0;\r
+      *NeedRetry = FALSE;\r
+      return EFI_DEVICE_ERROR;\r
     }\r
-\r
-    *MediaChange = TRUE;\r
   }\r
 \r
-  return EFI_SUCCESS;\r
+  return ReturnStatus;\r
 }\r
 \r
-\r
 /**\r
-  Send out Inquiry command to Device.\r
+  Submit Write(16) Command.\r
 \r
-  @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV\r
-  @param  NeedRetry       Indicates if needs try again when error happens\r
+  @param  ScsiDiskDevice     The pointer of ScsiDiskDevice\r
+  @param  NeedRetry          The pointer of flag indicates if needs retry if error happens\r
+  @param  Timeout            The time to complete the command\r
+  @param  DataBuffer         The buffer to fill with the read out data\r
+  @param  DataLength         The length of buffer\r
+  @param  StartLba           The start logic block address\r
+  @param  SectorCount        The number of blocks to write\r
 \r
-  @retval  EFI_DEVICE_ERROR  Indicates that error occurs\r
-  @retval  EFI_SUCCESS       Successfully to detect media\r
+  @return  EFI_STATUS is returned by calling ScsiWrite16Command().\r
 \r
 **/\r
 EFI_STATUS\r
-ScsiDiskInquiryDevice (\r
-  IN OUT  SCSI_DISK_DEV   *ScsiDiskDevice,\r
-     OUT  BOOLEAN         *NeedRetry\r
+ScsiDiskWrite16 (\r
+  IN     SCSI_DISK_DEV  *ScsiDiskDevice,\r
+  OUT BOOLEAN           *NeedRetry,\r
+  IN     UINT64         Timeout,\r
+  IN     UINT8          *DataBuffer,\r
+  IN OUT UINT32         *DataLength,\r
+  IN     UINT64         StartLba,\r
+  IN     UINT32         SectorCount\r
   )\r
 {\r
-  UINT32              InquiryDataLength;\r
-  UINT8               SenseDataLength;\r
-  UINT8               HostAdapterStatus;\r
-  UINT8               TargetStatus;\r
-  EFI_SCSI_SENSE_DATA *SenseDataArray;\r
-  UINTN               NumberOfSenseKeys;\r
-  EFI_STATUS          Status;\r
-  UINT8               MaxRetry;\r
-  UINT8               Index;\r
+  EFI_STATUS  Status;\r
+  EFI_STATUS  ReturnStatus;\r
+  UINT8       SenseDataLength;\r
+  UINT8       HostAdapterStatus;\r
+  UINT8       TargetStatus;\r
+  UINTN       Action;\r
 \r
-  InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA);\r
-  SenseDataLength   = 0;\r
+  //\r
+  // Implement a backoff algorithm to resolve some compatibility issues that\r
+  // some SCSI targets or ATAPI devices couldn't correctly response reading/writing\r
+  // big data in a single operation.\r
+  // This algorithm will at first try to execute original request. If the request fails\r
+  // with media error sense data or else, it will reduce the transfer length to half and\r
+  // try again till the operation succeeds or fails with one sector transfer length.\r
+  //\r
+BackOff:\r
+  *NeedRetry      = FALSE;\r
+  Action          = ACTION_NO_ACTION;\r
+  SenseDataLength = (UINT8)(ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
+  ReturnStatus    = ScsiWrite16Command (\r
+                      ScsiDiskDevice->ScsiIo,\r
+                      Timeout,\r
+                      ScsiDiskDevice->SenseData,\r
+                      &SenseDataLength,\r
+                      &HostAdapterStatus,\r
+                      &TargetStatus,\r
+                      DataBuffer,\r
+                      DataLength,\r
+                      StartLba,\r
+                      SectorCount\r
+                      );\r
+  if ((ReturnStatus == EFI_NOT_READY) || (ReturnStatus == EFI_BAD_BUFFER_SIZE)) {\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {\r
+    *NeedRetry = FALSE;\r
+    return ReturnStatus;\r
+  }\r
 \r
-  Status = ScsiInquiryCommand (\r
-            ScsiDiskDevice->ScsiIo,\r
-            EFI_TIMER_PERIOD_SECONDS (1),\r
-            NULL,\r
-            &SenseDataLength,\r
-            &HostAdapterStatus,\r
-            &TargetStatus,\r
-            (VOID *) &(ScsiDiskDevice->InquiryData),\r
-            &InquiryDataLength,\r
-            FALSE\r
-            );\r
-    //\r
-    // no need to check HostAdapterStatus and TargetStatus\r
+  //\r
+  // go ahead to check HostAdapterStatus and TargetStatus\r
+  // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
+  //\r
+  Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+  if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+    *NeedRetry = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    //\r
+    // reset the scsi channel\r
     //\r
-  if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
-     ParseInquiryData (ScsiDiskDevice);\r
-     return EFI_SUCCESS;\r
\r
-   } else if (Status == EFI_NOT_READY) {\r
-     *NeedRetry = TRUE;\r
-     return EFI_DEVICE_ERROR;\r
\r
-   } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
-     *NeedRetry = FALSE;\r
-     return EFI_DEVICE_ERROR;\r
-   }\r
-   //\r
-   // go ahead to check HostAdapterStatus and TargetStatus\r
-   // (EFI_TIMEOUT, EFI_DEVICE_ERROR)\r
-   //\r
\r
-   Status = CheckHostAdapterStatus (HostAdapterStatus);\r
-   if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
-     *NeedRetry = TRUE;\r
-     return EFI_DEVICE_ERROR;\r
-   } else if (Status == EFI_DEVICE_ERROR) {\r
-      //\r
-      // reset the scsi channel\r
-      //\r
     ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
     *NeedRetry = FALSE;\r
     return EFI_DEVICE_ERROR;\r
@@ -926,1277 +4660,1760 @@ ScsiDiskInquiryDevice (
     ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
     *NeedRetry = TRUE;\r
     return EFI_DEVICE_ERROR;\r
-\r
   } else if (Status == EFI_DEVICE_ERROR) {\r
     *NeedRetry = FALSE;\r
     return EFI_DEVICE_ERROR;\r
   }\r
-  \r
-  //\r
-  // if goes here, meant SubmitInquiryCommand() failed.\r
-  // if ScsiDiskRequestSenseKeys() succeeds at last,\r
-  // better retry SubmitInquiryCommand(). (by setting *NeedRetry = TRUE)\r
-  //\r
-  MaxRetry = 3;\r
-  for (Index = 0; Index < MaxRetry; Index++) {\r
-    Status = ScsiDiskRequestSenseKeys (\r
-              ScsiDiskDevice,\r
-              NeedRetry,\r
-              &SenseDataArray,\r
-              &NumberOfSenseKeys,\r
-              TRUE\r
-              );\r
-    if (!EFI_ERROR (Status)) {\r
+\r
+  if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {\r
+    DEBUG ((DEBUG_ERROR, "ScsiDiskWrite16: Check Condition happened!\n"));\r
+    DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);\r
+    if (Action == ACTION_RETRY_COMMAND_LATER) {\r
       *NeedRetry = TRUE;\r
       return EFI_DEVICE_ERROR;\r
-    }\r
+    } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
+      if (SectorCount <= 1) {\r
+        //\r
+        // Jump out if the operation still fails with one sector transfer length.\r
+        //\r
+        *NeedRetry = FALSE;\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
 \r
-    if (!*NeedRetry) {\r
+      //\r
+      // Try again with half length if the sense data shows we need to retry.\r
+      //\r
+      SectorCount >>= 1;\r
+      *DataLength   = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+      goto BackOff;\r
+    } else {\r
+      *NeedRetry = FALSE;\r
       return EFI_DEVICE_ERROR;\r
     }\r
   }\r
-  //\r
-  // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
-  // set *NeedRetry = FALSE to avoid the outside caller try again.\r
-  //\r
-  *NeedRetry = FALSE;\r
-  return EFI_DEVICE_ERROR;\r
+\r
+  return ReturnStatus;\r
 }\r
 \r
 /**\r
-  To test deivice.\r
-\r
-  When Test Unit Ready command succeeds, retrieve Sense Keys via Request Sense;\r
-  When Test Unit Ready command encounters any error caused by host adapter or\r
-  target, return error without retrieving Sense Keys.\r
-\r
-  @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV\r
-  @param  NeedRetry          The pointer of flag indicates try again\r
-  @param  SenseDataArray     The pointer of an array of sense data\r
-  @param  NumberOfSenseKeys  The pointer of the number of sense data array\r
+  Internal helper notify function in which determine whether retry of a SCSI\r
+  Read/Write command is needed and signal the event passed from Block I/O(2) if\r
+  the SCSI I/O operation completes.\r
 \r
-  @retval EFI_DEVICE_ERROR   Indicates that error occurs\r
-  @retval EFI_SUCCESS        Successfully to test unit\r
+  @param  Event    The instance of EFI_EVENT.\r
+  @param  Context  The parameter passed in.\r
 \r
 **/\r
-EFI_STATUS\r
-ScsiDiskTestUnitReady (\r
-  IN  SCSI_DISK_DEV         *ScsiDiskDevice,\r
-  OUT BOOLEAN               *NeedRetry,\r
-  OUT EFI_SCSI_SENSE_DATA   **SenseDataArray,\r
-  OUT UINTN                 *NumberOfSenseKeys\r
+VOID\r
+EFIAPI\r
+ScsiDiskNotify (\r
+  IN  EFI_EVENT  Event,\r
+  IN  VOID       *Context\r
   )\r
 {\r
-  EFI_STATUS  Status;\r
-  UINT8       SenseDataLength;\r
-  UINT8       HostAdapterStatus;\r
-  UINT8       TargetStatus;\r
-  UINT8       Index;\r
-  UINT8       MaxRetry;\r
+  EFI_STATUS             Status;\r
+  SCSI_ASYNC_RW_REQUEST  *Request;\r
+  SCSI_DISK_DEV          *ScsiDiskDevice;\r
+  EFI_BLOCK_IO2_TOKEN    *Token;\r
+  UINTN                  Action;\r
+  UINT32                 OldDataLength;\r
+  UINT32                 OldSectorCount;\r
+  UINT8                  MaxRetry;\r
+\r
+  gBS->CloseEvent (Event);\r
+\r
+  Request        = (SCSI_ASYNC_RW_REQUEST *)Context;\r
+  ScsiDiskDevice = Request->ScsiDiskDevice;\r
+  Token          = Request->BlkIo2Req->Token;\r
+  OldDataLength  = Request->DataLength;\r
+  OldSectorCount = Request->SectorCount;\r
+  MaxRetry       = 2;\r
 \r
-  SenseDataLength     = 0;\r
-  *NumberOfSenseKeys  = 0;\r
-\r
-  //\r
-  // Parameter 3 and 4: do not require sense data, retrieve it when needed.\r
-  //\r
-  Status = ScsiTestUnitReadyCommand (\r
-            ScsiDiskDevice->ScsiIo,\r
-            EFI_TIMER_PERIOD_SECONDS (1),\r
-            NULL,\r
-            &SenseDataLength,\r
-            &HostAdapterStatus,\r
-            &TargetStatus\r
-            );\r
   //\r
-  // no need to check HostAdapterStatus and TargetStatus\r
+  // If previous sub-tasks already fails, no need to process this sub-task.\r
   //\r
-  if (Status == EFI_NOT_READY) {\r
-    *NeedRetry = TRUE;\r
-    return EFI_DEVICE_ERROR;\r
-\r
-  } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
-    *NeedRetry = FALSE;\r
-    return EFI_DEVICE_ERROR;\r
+  if (Token->TransactionStatus != EFI_SUCCESS) {\r
+    goto Exit;\r
   }\r
+\r
   //\r
-  // go ahead to check HostAdapterStatus and TargetStatus(in case of EFI_DEVICE_ERROR)\r
+  // Check HostAdapterStatus and TargetStatus\r
+  // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
   //\r
-\r
-  Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+  Status = CheckHostAdapterStatus (Request->HostAdapterStatus);\r
   if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
-    *NeedRetry = TRUE;\r
-    return EFI_DEVICE_ERROR;\r
-\r
+    if (++Request->TimesRetry > MaxRetry) {\r
+      Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+      goto Exit;\r
+    } else {\r
+      goto Retry;\r
+    }\r
   } else if (Status == EFI_DEVICE_ERROR) {\r
     //\r
     // reset the scsi channel\r
     //\r
     ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
-    *NeedRetry = FALSE;\r
-    return EFI_DEVICE_ERROR;\r
+    Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+    goto Exit;\r
   }\r
 \r
-  Status = CheckTargetStatus (TargetStatus);\r
+  Status = CheckTargetStatus (Request->TargetStatus);\r
   if (Status == EFI_NOT_READY) {\r
     //\r
     // reset the scsi device\r
     //\r
     ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
-    *NeedRetry = TRUE;\r
-    return EFI_DEVICE_ERROR;\r
-\r
+    if (++Request->TimesRetry > MaxRetry) {\r
+      Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+      goto Exit;\r
+    } else {\r
+      goto Retry;\r
+    }\r
   } else if (Status == EFI_DEVICE_ERROR) {\r
-    *NeedRetry = FALSE;\r
-    return EFI_DEVICE_ERROR;\r
+    Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+    goto Exit;\r
   }\r
 \r
-  MaxRetry = 3;\r
-  for (Index = 0; Index < MaxRetry; Index++) {\r
-    Status = ScsiDiskRequestSenseKeys (\r
-              ScsiDiskDevice,\r
-              NeedRetry,\r
-              SenseDataArray,\r
-              NumberOfSenseKeys,\r
-              FALSE\r
-              );\r
-    if (!EFI_ERROR (Status)) {\r
-      return EFI_SUCCESS;\r
-    }\r
+  if (Request->TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) {\r
+    DEBUG ((DEBUG_ERROR, "ScsiDiskNotify: Check Condition happened!\n"));\r
+\r
+    DetectMediaParsingSenseKeys (\r
+      ScsiDiskDevice,\r
+      Request->SenseData,\r
+      Request->SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA),\r
+      &Action\r
+      );\r
+    if (Action == ACTION_RETRY_COMMAND_LATER) {\r
+      if (++Request->TimesRetry > MaxRetry) {\r
+        Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+        goto Exit;\r
+      } else {\r
+        goto Retry;\r
+      }\r
+    } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
+      if (Request->SectorCount <= 1) {\r
+        //\r
+        // Jump out if the operation still fails with one sector transfer\r
+        // length.\r
+        //\r
+        Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+        goto Exit;\r
+      }\r
 \r
-    if (!*NeedRetry) {\r
-      return EFI_DEVICE_ERROR;\r
+      //\r
+      // Try again with two half length request if the sense data shows we need\r
+      // to retry.\r
+      //\r
+      Request->SectorCount >>= 1;\r
+      Request->DataLength    = Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+      Request->TimesRetry    = 0;\r
+\r
+      goto Retry;\r
+    } else {\r
+      Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+      goto Exit;\r
     }\r
   }\r
+\r
   //\r
-  // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
-  // set *NeedRetry = FALSE to avoid the outside caller try again.\r
+  // This sub-task succeeds, no need to retry.\r
   //\r
-  *NeedRetry = FALSE;\r
-  return EFI_DEVICE_ERROR;\r
+  goto Exit;\r
+\r
+Retry:\r
+  if (Request->InBuffer != NULL) {\r
+    //\r
+    // SCSI read command\r
+    //\r
+    if (!ScsiDiskDevice->Cdb16Byte) {\r
+      Status = ScsiDiskAsyncRead10 (\r
+                 ScsiDiskDevice,\r
+                 Request->Timeout,\r
+                 Request->TimesRetry,\r
+                 Request->InBuffer,\r
+                 Request->DataLength,\r
+                 (UINT32)Request->StartLba,\r
+                 Request->SectorCount,\r
+                 Request->BlkIo2Req,\r
+                 Token\r
+                 );\r
+    } else {\r
+      Status = ScsiDiskAsyncRead16 (\r
+                 ScsiDiskDevice,\r
+                 Request->Timeout,\r
+                 Request->TimesRetry,\r
+                 Request->InBuffer,\r
+                 Request->DataLength,\r
+                 Request->StartLba,\r
+                 Request->SectorCount,\r
+                 Request->BlkIo2Req,\r
+                 Token\r
+                 );\r
+    }\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+      goto Exit;\r
+    } else if (OldSectorCount != Request->SectorCount) {\r
+      //\r
+      // Original sub-task will be split into two new sub-tasks with smaller\r
+      // DataLength\r
+      //\r
+      if (!ScsiDiskDevice->Cdb16Byte) {\r
+        Status = ScsiDiskAsyncRead10 (\r
+                   ScsiDiskDevice,\r
+                   Request->Timeout,\r
+                   0,\r
+                   Request->InBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,\r
+                   OldDataLength - Request->DataLength,\r
+                   (UINT32)Request->StartLba + Request->SectorCount,\r
+                   OldSectorCount - Request->SectorCount,\r
+                   Request->BlkIo2Req,\r
+                   Token\r
+                   );\r
+      } else {\r
+        Status = ScsiDiskAsyncRead16 (\r
+                   ScsiDiskDevice,\r
+                   Request->Timeout,\r
+                   0,\r
+                   Request->InBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,\r
+                   OldDataLength - Request->DataLength,\r
+                   Request->StartLba + Request->SectorCount,\r
+                   OldSectorCount - Request->SectorCount,\r
+                   Request->BlkIo2Req,\r
+                   Token\r
+                   );\r
+      }\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+        goto Exit;\r
+      }\r
+    }\r
+  } else {\r
+    //\r
+    // SCSI write command\r
+    //\r
+    if (!ScsiDiskDevice->Cdb16Byte) {\r
+      Status = ScsiDiskAsyncWrite10 (\r
+                 ScsiDiskDevice,\r
+                 Request->Timeout,\r
+                 Request->TimesRetry,\r
+                 Request->OutBuffer,\r
+                 Request->DataLength,\r
+                 (UINT32)Request->StartLba,\r
+                 Request->SectorCount,\r
+                 Request->BlkIo2Req,\r
+                 Token\r
+                 );\r
+    } else {\r
+      Status = ScsiDiskAsyncWrite16 (\r
+                 ScsiDiskDevice,\r
+                 Request->Timeout,\r
+                 Request->TimesRetry,\r
+                 Request->OutBuffer,\r
+                 Request->DataLength,\r
+                 Request->StartLba,\r
+                 Request->SectorCount,\r
+                 Request->BlkIo2Req,\r
+                 Token\r
+                 );\r
+    }\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+      goto Exit;\r
+    } else if (OldSectorCount != Request->SectorCount) {\r
+      //\r
+      // Original sub-task will be split into two new sub-tasks with smaller\r
+      // DataLength\r
+      //\r
+      if (!ScsiDiskDevice->Cdb16Byte) {\r
+        Status = ScsiDiskAsyncWrite10 (\r
+                   ScsiDiskDevice,\r
+                   Request->Timeout,\r
+                   0,\r
+                   Request->OutBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,\r
+                   OldDataLength - Request->DataLength,\r
+                   (UINT32)Request->StartLba + Request->SectorCount,\r
+                   OldSectorCount - Request->SectorCount,\r
+                   Request->BlkIo2Req,\r
+                   Token\r
+                   );\r
+      } else {\r
+        Status = ScsiDiskAsyncWrite16 (\r
+                   ScsiDiskDevice,\r
+                   Request->Timeout,\r
+                   0,\r
+                   Request->OutBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,\r
+                   OldDataLength - Request->DataLength,\r
+                   Request->StartLba + Request->SectorCount,\r
+                   OldSectorCount - Request->SectorCount,\r
+                   Request->BlkIo2Req,\r
+                   Token\r
+                   );\r
+      }\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+        goto Exit;\r
+      }\r
+    }\r
+  }\r
+\r
+Exit:\r
+  RemoveEntryList (&Request->Link);\r
+  if ((IsListEmpty (&Request->BlkIo2Req->ScsiRWQueue)) &&\r
+      (Request->BlkIo2Req->LastScsiRW))\r
+  {\r
+    //\r
+    // The last SCSI R/W command of a BlockIo2 request completes\r
+    //\r
+    RemoveEntryList (&Request->BlkIo2Req->Link);\r
+    FreePool (Request->BlkIo2Req);  // Should be freed only once\r
+    gBS->SignalEvent (Token->Event);\r
+  }\r
+\r
+  FreePool (Request->SenseData);\r
+  FreePool (Request);\r
 }\r
 \r
 /**\r
-  Parsing Sense Keys which got from request sense command.\r
+  Submit Async Read(10) command.\r
+\r
+  @param  ScsiDiskDevice     The pointer of ScsiDiskDevice.\r
+  @param  Timeout            The time to complete the command.\r
+  @param  TimesRetry         The number of times the command has been retried.\r
+  @param  DataBuffer         The buffer to fill with the read out data.\r
+  @param  DataLength         The length of buffer.\r
+  @param  StartLba           The start logic block address.\r
+  @param  SectorCount        The number of blocks to read.\r
+  @param  BlkIo2Req          The upstream BlockIo2 request.\r
+  @param  Token              The pointer to the token associated with the\r
+                             non-blocking read request.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a\r
+                                lack of resources.\r
+  @return others                Status returned by calling\r
+                                ScsiRead10CommandEx().\r
 \r
-  @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV\r
-  @param  SenseData          The pointer of EFI_SCSI_SENSE_DATA\r
-  @param  NumberOfSenseKeys  The number of sense key  \r
-  @param  Action             The pointer of action which indicates what is need to do next\r
+**/\r
+EFI_STATUS\r
+ScsiDiskAsyncRead10 (\r
+  IN     SCSI_DISK_DEV        *ScsiDiskDevice,\r
+  IN     UINT64               Timeout,\r
+  IN     UINT8                TimesRetry,\r
+  OUT UINT8                   *DataBuffer,\r
+  IN     UINT32               DataLength,\r
+  IN     UINT32               StartLba,\r
+  IN     UINT32               SectorCount,\r
+  IN OUT SCSI_BLKIO2_REQUEST  *BlkIo2Req,\r
+  IN     EFI_BLOCK_IO2_TOKEN  *Token\r
+  )\r
+{\r
+  EFI_STATUS             Status;\r
+  SCSI_ASYNC_RW_REQUEST  *Request;\r
+  EFI_EVENT              AsyncIoEvent;\r
+  EFI_TPL                OldTpl;\r
 \r
-  @retval EFI_DEVICE_ERROR   Indicates that error occurs\r
-  @retval EFI_SUCCESS        Successfully to complete the parsing\r
+  AsyncIoEvent = NULL;\r
+\r
+  Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
+  if (Request == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+  InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  Request->SenseDataLength = (UINT8)(6 * sizeof (EFI_SCSI_SENSE_DATA));\r
+  Request->SenseData       = AllocateZeroPool (Request->SenseDataLength);\r
+  if (Request->SenseData == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ErrorExit;\r
+  }\r
+\r
+  Request->ScsiDiskDevice = ScsiDiskDevice;\r
+  Request->Timeout        = Timeout;\r
+  Request->TimesRetry     = TimesRetry;\r
+  Request->InBuffer       = DataBuffer;\r
+  Request->DataLength     = DataLength;\r
+  Request->StartLba       = StartLba;\r
+  Request->SectorCount    = SectorCount;\r
+  Request->BlkIo2Req      = BlkIo2Req;\r
+\r
+  //\r
+  // Create Event\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  ScsiDiskNotify,\r
+                  Request,\r
+                  &AsyncIoEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ErrorExit;\r
+  }\r
+\r
+  Status = ScsiRead10CommandEx (\r
+             ScsiDiskDevice->ScsiIo,\r
+             Request->Timeout,\r
+             Request->SenseData,\r
+             &Request->SenseDataLength,\r
+             &Request->HostAdapterStatus,\r
+             &Request->TargetStatus,\r
+             Request->InBuffer,\r
+             &Request->DataLength,\r
+             (UINT32)Request->StartLba,\r
+             Request->SectorCount,\r
+             AsyncIoEvent\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ErrorExit;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ErrorExit:\r
+  if (AsyncIoEvent != NULL) {\r
+    gBS->CloseEvent (AsyncIoEvent);\r
+  }\r
+\r
+  if (Request != NULL) {\r
+    if (Request->SenseData != NULL) {\r
+      FreePool (Request->SenseData);\r
+    }\r
+\r
+    OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+    RemoveEntryList (&Request->Link);\r
+    gBS->RestoreTPL (OldTpl);\r
+\r
+    FreePool (Request);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Submit Async Write(10) command.\r
+\r
+  @param  ScsiDiskDevice     The pointer of ScsiDiskDevice.\r
+  @param  Timeout            The time to complete the command.\r
+  @param  TimesRetry         The number of times the command has been retried.\r
+  @param  DataBuffer         The buffer contains the data to write.\r
+  @param  DataLength         The length of buffer.\r
+  @param  StartLba           The start logic block address.\r
+  @param  SectorCount        The number of blocks to write.\r
+  @param  BlkIo2Req          The upstream BlockIo2 request.\r
+  @param  Token              The pointer to the token associated with the\r
+                             non-blocking read request.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a\r
+                                lack of resources.\r
+  @return others                Status returned by calling\r
+                                ScsiWrite10CommandEx().\r
 \r
 **/\r
 EFI_STATUS\r
-DetectMediaParsingSenseKeys (\r
-  OUT  SCSI_DISK_DEV           *ScsiDiskDevice,\r
-  IN   EFI_SCSI_SENSE_DATA     *SenseData,\r
-  IN   UINTN                   NumberOfSenseKeys,\r
-  OUT  UINTN                   *Action\r
+ScsiDiskAsyncWrite10 (\r
+  IN     SCSI_DISK_DEV        *ScsiDiskDevice,\r
+  IN     UINT64               Timeout,\r
+  IN     UINT8                TimesRetry,\r
+  IN     UINT8                *DataBuffer,\r
+  IN     UINT32               DataLength,\r
+  IN     UINT32               StartLba,\r
+  IN     UINT32               SectorCount,\r
+  IN OUT SCSI_BLKIO2_REQUEST  *BlkIo2Req,\r
+  IN     EFI_BLOCK_IO2_TOKEN  *Token\r
   )\r
 {\r
-  BOOLEAN RetryLater;\r
+  EFI_STATUS             Status;\r
+  SCSI_ASYNC_RW_REQUEST  *Request;\r
+  EFI_EVENT              AsyncIoEvent;\r
+  EFI_TPL                OldTpl;\r
+\r
+  AsyncIoEvent = NULL;\r
+\r
+  Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
+  if (Request == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+  InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  Request->SenseDataLength = (UINT8)(6 * sizeof (EFI_SCSI_SENSE_DATA));\r
+  Request->SenseData       = AllocateZeroPool (Request->SenseDataLength);\r
+  if (Request->SenseData == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ErrorExit;\r
+  }\r
+\r
+  Request->ScsiDiskDevice = ScsiDiskDevice;\r
+  Request->Timeout        = Timeout;\r
+  Request->TimesRetry     = TimesRetry;\r
+  Request->OutBuffer      = DataBuffer;\r
+  Request->DataLength     = DataLength;\r
+  Request->StartLba       = StartLba;\r
+  Request->SectorCount    = SectorCount;\r
+  Request->BlkIo2Req      = BlkIo2Req;\r
 \r
   //\r
-  // Default is to read capacity, unless..\r
+  // Create Event\r
   //\r
-  *Action = ACTION_READ_CAPACITY;\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  ScsiDiskNotify,\r
+                  Request,\r
+                  &AsyncIoEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ErrorExit;\r
+  }\r
 \r
-  if (NumberOfSenseKeys == 0) {\r
-    *Action = ACTION_NO_ACTION;\r
-    return EFI_SUCCESS;\r
+  Status = ScsiWrite10CommandEx (\r
+             ScsiDiskDevice->ScsiIo,\r
+             Request->Timeout,\r
+             Request->SenseData,\r
+             &Request->SenseDataLength,\r
+             &Request->HostAdapterStatus,\r
+             &Request->TargetStatus,\r
+             Request->OutBuffer,\r
+             &Request->DataLength,\r
+             (UINT32)Request->StartLba,\r
+             Request->SectorCount,\r
+             AsyncIoEvent\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ErrorExit;\r
   }\r
 \r
-  if (!ScsiDiskHaveSenseKey (SenseData, NumberOfSenseKeys)) {\r
-    //\r
-    // No Sense Key returned from last submitted command\r
-    //\r
-    *Action = ACTION_NO_ACTION;\r
-    return EFI_SUCCESS;\r
+  return EFI_SUCCESS;\r
+\r
+ErrorExit:\r
+  if (AsyncIoEvent != NULL) {\r
+    gBS->CloseEvent (AsyncIoEvent);\r
   }\r
 \r
-  if (ScsiDiskIsNoMedia (SenseData, NumberOfSenseKeys)) {\r
-    ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;\r
-    ScsiDiskDevice->BlkIo.Media->LastBlock    = 0;\r
-    *Action = ACTION_NO_ACTION;\r
-    return EFI_SUCCESS;\r
+  if (Request != NULL) {\r
+    if (Request->SenseData != NULL) {\r
+      FreePool (Request->SenseData);\r
+    }\r
+\r
+    OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+    RemoveEntryList (&Request->Link);\r
+    gBS->RestoreTPL (OldTpl);\r
+\r
+    FreePool (Request);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Submit Async Read(16) command.\r
+\r
+  @param  ScsiDiskDevice     The pointer of ScsiDiskDevice.\r
+  @param  Timeout            The time to complete the command.\r
+  @param  TimesRetry         The number of times the command has been retried.\r
+  @param  DataBuffer         The buffer to fill with the read out data.\r
+  @param  DataLength         The length of buffer.\r
+  @param  StartLba           The start logic block address.\r
+  @param  SectorCount        The number of blocks to read.\r
+  @param  BlkIo2Req          The upstream BlockIo2 request.\r
+  @param  Token              The pointer to the token associated with the\r
+                             non-blocking read request.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a\r
+                                lack of resources.\r
+  @return others                Status returned by calling\r
+                                ScsiRead16CommandEx().\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskAsyncRead16 (\r
+  IN     SCSI_DISK_DEV        *ScsiDiskDevice,\r
+  IN     UINT64               Timeout,\r
+  IN     UINT8                TimesRetry,\r
+  OUT UINT8                   *DataBuffer,\r
+  IN     UINT32               DataLength,\r
+  IN     UINT64               StartLba,\r
+  IN     UINT32               SectorCount,\r
+  IN OUT SCSI_BLKIO2_REQUEST  *BlkIo2Req,\r
+  IN     EFI_BLOCK_IO2_TOKEN  *Token\r
+  )\r
+{\r
+  EFI_STATUS             Status;\r
+  SCSI_ASYNC_RW_REQUEST  *Request;\r
+  EFI_EVENT              AsyncIoEvent;\r
+  EFI_TPL                OldTpl;\r
+\r
+  AsyncIoEvent = NULL;\r
+\r
+  Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
+  if (Request == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+  InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  Request->SenseDataLength = (UINT8)(6 * sizeof (EFI_SCSI_SENSE_DATA));\r
+  Request->SenseData       = AllocateZeroPool (Request->SenseDataLength);\r
+  if (Request->SenseData == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ErrorExit;\r
+  }\r
+\r
+  Request->ScsiDiskDevice = ScsiDiskDevice;\r
+  Request->Timeout        = Timeout;\r
+  Request->TimesRetry     = TimesRetry;\r
+  Request->InBuffer       = DataBuffer;\r
+  Request->DataLength     = DataLength;\r
+  Request->StartLba       = StartLba;\r
+  Request->SectorCount    = SectorCount;\r
+  Request->BlkIo2Req      = BlkIo2Req;\r
+\r
+  //\r
+  // Create Event\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  ScsiDiskNotify,\r
+                  Request,\r
+                  &AsyncIoEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ErrorExit;\r
   }\r
 \r
-  if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {\r
-    ScsiDiskDevice->BlkIo.Media->MediaId++;\r
-    return EFI_SUCCESS;\r
+  Status = ScsiRead16CommandEx (\r
+             ScsiDiskDevice->ScsiIo,\r
+             Request->Timeout,\r
+             Request->SenseData,\r
+             &Request->SenseDataLength,\r
+             &Request->HostAdapterStatus,\r
+             &Request->TargetStatus,\r
+             Request->InBuffer,\r
+             &Request->DataLength,\r
+             Request->StartLba,\r
+             Request->SectorCount,\r
+             AsyncIoEvent\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ErrorExit;\r
   }\r
 \r
-  if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {\r
-    ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;\r
-    ScsiDiskDevice->BlkIo.Media->LastBlock    = 0;\r
-    return EFI_DEVICE_ERROR;\r
-  }\r
+  return EFI_SUCCESS;\r
 \r
-  if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {\r
-    return EFI_DEVICE_ERROR;\r
+ErrorExit:\r
+  if (AsyncIoEvent != NULL) {\r
+    gBS->CloseEvent (AsyncIoEvent);\r
   }\r
 \r
-  if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) {\r
-    if (RetryLater) {\r
-      *Action = ACTION_RETRY_COMMAND_LATER;\r
-      return EFI_SUCCESS;\r
+  if (Request != NULL) {\r
+    if (Request->SenseData != NULL) {\r
+      FreePool (Request->SenseData);\r
     }\r
 \r
-    return EFI_DEVICE_ERROR;\r
+    OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+    RemoveEntryList (&Request->Link);\r
+    gBS->RestoreTPL (OldTpl);\r
+\r
+    FreePool (Request);\r
   }\r
 \r
-  return EFI_SUCCESS;\r
+  return Status;\r
 }\r
 \r
-\r
 /**\r
-  Send read capacity command to device and get the device parameter.\r
-\r
-  @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV\r
-  @param  NeedRetry          The pointer of flag indicates if need a retry\r
-  @param  SenseDataArray     The pointer of an array of sense data\r
-  @param  NumberOfSenseKeys  The number of sense key\r
-\r
-  @retval EFI_DEVICE_ERROR   Indicates that error occurs\r
-  @retval EFI_SUCCESS        Successfully to read capacity\r
+  Submit Async Write(16) command.\r
+\r
+  @param  ScsiDiskDevice     The pointer of ScsiDiskDevice.\r
+  @param  Timeout            The time to complete the command.\r
+  @param  TimesRetry         The number of times the command has been retried.\r
+  @param  DataBuffer         The buffer contains the data to write.\r
+  @param  DataLength         The length of buffer.\r
+  @param  StartLba           The start logic block address.\r
+  @param  SectorCount        The number of blocks to write.\r
+  @param  BlkIo2Req          The upstream BlockIo2 request.\r
+  @param  Token              The pointer to the token associated with the\r
+                             non-blocking read request.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a\r
+                                lack of resources.\r
+  @return others                Status returned by calling\r
+                                ScsiWrite16CommandEx().\r
 \r
 **/\r
 EFI_STATUS\r
-ScsiDiskReadCapacity (\r
-  IN  OUT  SCSI_DISK_DEV           *ScsiDiskDevice,\r
-      OUT  BOOLEAN                 *NeedRetry,\r
-      OUT  EFI_SCSI_SENSE_DATA     **SenseDataArray,\r
-      OUT  UINTN                   *NumberOfSenseKeys\r
+ScsiDiskAsyncWrite16 (\r
+  IN     SCSI_DISK_DEV        *ScsiDiskDevice,\r
+  IN     UINT64               Timeout,\r
+  IN     UINT8                TimesRetry,\r
+  IN     UINT8                *DataBuffer,\r
+  IN     UINT32               DataLength,\r
+  IN     UINT64               StartLba,\r
+  IN     UINT32               SectorCount,\r
+  IN OUT SCSI_BLKIO2_REQUEST  *BlkIo2Req,\r
+  IN     EFI_BLOCK_IO2_TOKEN  *Token\r
   )\r
 {\r
-  EFI_SCSI_DISK_CAPACITY_DATA CapacityData;\r
-  UINT32                      DataLength;\r
-  UINT8                       HostAdapterStatus;\r
-  UINT8                       TargetStatus;\r
-  EFI_STATUS                  CommandStatus;\r
-  EFI_STATUS                  Status;\r
-  UINT8                       Index;\r
-  UINT8                       MaxRetry;\r
-  UINT8                       SenseDataLength;\r
+  EFI_STATUS             Status;\r
+  SCSI_ASYNC_RW_REQUEST  *Request;\r
+  EFI_EVENT              AsyncIoEvent;\r
+  EFI_TPL                OldTpl;\r
 \r
-  SenseDataLength = 0;\r
-  ZeroMem (&CapacityData, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
-  DataLength          = sizeof (EFI_SCSI_DISK_CAPACITY_DATA);\r
+  AsyncIoEvent = NULL;\r
 \r
-  *NumberOfSenseKeys  = 0;\r
-  *NeedRetry          = FALSE;\r
-  //\r
-  // submit Read Capacity Command. in this call,not request sense data\r
-  //\r
-  CommandStatus = ScsiReadCapacityCommand (\r
-                    ScsiDiskDevice->ScsiIo,\r
-                    EFI_TIMER_PERIOD_SECONDS (1),\r
-                    NULL,\r
-                    &SenseDataLength,\r
-                    &HostAdapterStatus,\r
-                    &TargetStatus,\r
-                    (VOID *) &CapacityData,\r
-                    &DataLength,\r
-                    FALSE\r
-                    );\r
-  //\r
-    // no need to check HostAdapterStatus and TargetStatus\r
-    //\r
-   if (CommandStatus == EFI_SUCCESS) {\r
-     GetMediaInfo (ScsiDiskDevice, &CapacityData);\r
-     return EFI_SUCCESS;\r
\r
-   } else if (CommandStatus == EFI_NOT_READY) {\r
-     *NeedRetry = TRUE;\r
-     return EFI_DEVICE_ERROR;\r
\r
-   } else if ((CommandStatus == EFI_INVALID_PARAMETER) || (CommandStatus == EFI_UNSUPPORTED)) {\r
-     *NeedRetry = FALSE;\r
-     return EFI_DEVICE_ERROR;\r
-   }\r
-   //\r
-   // go ahead to check HostAdapterStatus and TargetStatus\r
-   // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
-   //\r
\r
-   Status = CheckHostAdapterStatus (HostAdapterStatus);\r
-   if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
-     *NeedRetry = TRUE;\r
-     return EFI_DEVICE_ERROR;\r
\r
-   } else if (Status == EFI_DEVICE_ERROR) {\r
-    //\r
-    // reset the scsi channel\r
-    //\r
-    ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
-    *NeedRetry = FALSE;\r
-    return EFI_DEVICE_ERROR;\r
+  Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
+  if (Request == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
   }\r
 \r
-  Status = CheckTargetStatus (TargetStatus);\r
-  if (Status == EFI_NOT_READY) {\r
-    //\r
-    // reset the scsi device\r
-    //\r
-    ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
-    *NeedRetry = TRUE;\r
-    return EFI_DEVICE_ERROR;\r
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+  InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
 \r
-  } else if (Status == EFI_DEVICE_ERROR) {\r
-    *NeedRetry = FALSE;\r
-    return EFI_DEVICE_ERROR;\r
+  Request->SenseDataLength = (UINT8)(6 * sizeof (EFI_SCSI_SENSE_DATA));\r
+  Request->SenseData       = AllocateZeroPool (Request->SenseDataLength);\r
+  if (Request->SenseData == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ErrorExit;\r
   }\r
-  \r
+\r
+  Request->ScsiDiskDevice = ScsiDiskDevice;\r
+  Request->Timeout        = Timeout;\r
+  Request->TimesRetry     = TimesRetry;\r
+  Request->OutBuffer      = DataBuffer;\r
+  Request->DataLength     = DataLength;\r
+  Request->StartLba       = StartLba;\r
+  Request->SectorCount    = SectorCount;\r
+  Request->BlkIo2Req      = BlkIo2Req;\r
+\r
   //\r
-  // if goes here, meant SubmitReadCapacityCommand() failed.\r
-  // if ScsiDiskRequestSenseKeys() succeeds at last,\r
-  // better retry SubmitReadCapacityCommand(). (by setting *NeedRetry = TRUE)\r
+  // Create Event\r
   //\r
-  MaxRetry = 3;\r
-  for (Index = 0; Index < MaxRetry; Index++) {\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  ScsiDiskNotify,\r
+                  Request,\r
+                  &AsyncIoEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ErrorExit;\r
+  }\r
 \r
-    Status = ScsiDiskRequestSenseKeys (\r
-              ScsiDiskDevice,\r
-              NeedRetry,\r
-              SenseDataArray,\r
-              NumberOfSenseKeys,\r
-              TRUE\r
-              );\r
-    if (!EFI_ERROR (Status)) {\r
-      *NeedRetry = TRUE;\r
-      return EFI_DEVICE_ERROR;\r
-    }\r
+  Status = ScsiWrite16CommandEx (\r
+             ScsiDiskDevice->ScsiIo,\r
+             Request->Timeout,\r
+             Request->SenseData,\r
+             &Request->SenseDataLength,\r
+             &Request->HostAdapterStatus,\r
+             &Request->TargetStatus,\r
+             Request->OutBuffer,\r
+             &Request->DataLength,\r
+             Request->StartLba,\r
+             Request->SectorCount,\r
+             AsyncIoEvent\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ErrorExit;\r
+  }\r
 \r
-    if (!*NeedRetry) {\r
-      return EFI_DEVICE_ERROR;\r
+  return EFI_SUCCESS;\r
+\r
+ErrorExit:\r
+  if (AsyncIoEvent != NULL) {\r
+    gBS->CloseEvent (AsyncIoEvent);\r
+  }\r
+\r
+  if (Request != NULL) {\r
+    if (Request->SenseData != NULL) {\r
+      FreePool (Request->SenseData);\r
     }\r
+\r
+    OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+    RemoveEntryList (&Request->Link);\r
+    gBS->RestoreTPL (OldTpl);\r
+\r
+    FreePool (Request);\r
   }\r
-  //\r
-  // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
-  // set *NeedRetry = FALSE to avoid the outside caller try again.\r
-  //\r
-  *NeedRetry = FALSE;\r
-  return EFI_DEVICE_ERROR;\r
+\r
+  return Status;\r
 }\r
 \r
 /**\r
-  Check the HostAdapter status and re-interpret it in EFI_STATUS.\r
-\r
-  @param  HostAdapterStatus  Host Adapter status\r
+  Check sense key to find if media presents.\r
 \r
-  @retval  EFI_SUCCESS       Host adapter is OK.\r
-  @retval  EFI_TIMEOUT       Timeout.\r
-  @retval  EFI_NOT_READY     Adapter NOT ready.\r
-  @retval  EFI_DEVICE_ERROR  Adapter device error.\r
+  @param  SenseData   The pointer of EFI_SCSI_SENSE_DATA\r
+  @param  SenseCounts The number of sense key\r
 \r
+  @retval TRUE    NOT any media\r
+  @retval FALSE   Media presents\r
 **/\r
-EFI_STATUS\r
-CheckHostAdapterStatus (\r
-  IN UINT8   HostAdapterStatus\r
+BOOLEAN\r
+ScsiDiskIsNoMedia (\r
+  IN  EFI_SCSI_SENSE_DATA  *SenseData,\r
+  IN  UINTN                SenseCounts\r
   )\r
 {\r
-  switch (HostAdapterStatus) {\r
-  case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK:\r
-    return EFI_SUCCESS;\r
-\r
-  case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT:\r
-  case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT:\r
-  case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND:\r
-    return EFI_TIMEOUT;\r
+  EFI_SCSI_SENSE_DATA  *SensePtr;\r
+  UINTN                Index;\r
+  BOOLEAN              IsNoMedia;\r
 \r
-  case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT:\r
-  case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR:\r
-  case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED:\r
-  case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN:\r
-  case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET:\r
-    return EFI_NOT_READY;\r
+  IsNoMedia = FALSE;\r
+  SensePtr  = SenseData;\r
 \r
-  case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE:\r
-  case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR:\r
-    return EFI_DEVICE_ERROR;\r
+  for (Index = 0; Index < SenseCounts; Index++) {\r
+    //\r
+    // Sense Key is EFI_SCSI_SK_NOT_READY (0x2),\r
+    // Additional Sense Code is ASC_NO_MEDIA (0x3A)\r
+    //\r
+    if ((SensePtr->Sense_Key == EFI_SCSI_SK_NOT_READY) &&\r
+        (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA))\r
+    {\r
+      IsNoMedia = TRUE;\r
+    }\r
 \r
-  default:\r
-    return EFI_SUCCESS;\r
+    SensePtr++;\r
   }\r
-}\r
 \r
+  return IsNoMedia;\r
+}\r
 \r
 /**\r
-  Check the target status and re-interpret it in EFI_STATUS.\r
+  Parse sense key.\r
 \r
-  @param  TargetStatus  Target status\r
+  @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA\r
+  @param  SenseCounts  The number of sense key\r
 \r
-  @retval EFI_NOT_READY       Device is NOT ready.\r
-  @retval EFI_DEVICE_ERROR \r
-  @retval EFI_SUCCESS\r
+  @retval TRUE   Error\r
+  @retval FALSE  NOT error\r
 \r
 **/\r
-EFI_STATUS\r
-CheckTargetStatus (\r
-  IN  UINT8   TargetStatus\r
+BOOLEAN\r
+ScsiDiskIsMediaError (\r
+  IN  EFI_SCSI_SENSE_DATA  *SenseData,\r
+  IN  UINTN                SenseCounts\r
   )\r
 {\r
-  switch (TargetStatus) {\r
-  case EFI_EXT_SCSI_STATUS_TARGET_GOOD:\r
-  case EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION:\r
-  case EFI_EXT_SCSI_STATUS_TARGET_CONDITION_MET:\r
-    return EFI_SUCCESS;\r
+  EFI_SCSI_SENSE_DATA  *SensePtr;\r
+  UINTN                Index;\r
+  BOOLEAN              IsError;\r
 \r
-  case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE:\r
-  case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE_CONDITION_MET:\r
-  case EFI_EXT_SCSI_STATUS_TARGET_BUSY:\r
-  case EFI_EXT_SCSI_STATUS_TARGET_TASK_SET_FULL:\r
-    return EFI_NOT_READY;\r
+  IsError  = FALSE;\r
+  SensePtr = SenseData;\r
 \r
-  case EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT:\r
-    return EFI_DEVICE_ERROR;\r
-    break;\r
+  for (Index = 0; Index < SenseCounts; Index++) {\r
+    switch (SensePtr->Sense_Key) {\r
+      case EFI_SCSI_SK_MEDIUM_ERROR:\r
+        //\r
+        // Sense Key is EFI_SCSI_SK_MEDIUM_ERROR (0x3)\r
+        //\r
+        switch (SensePtr->Addnl_Sense_Code) {\r
+          //\r
+          // fall through\r
+          //\r
+          case EFI_SCSI_ASC_MEDIA_ERR1:\r
 \r
-  default:\r
-    return EFI_SUCCESS;\r
-  }\r
-}\r
+          //\r
+          // fall through\r
+          //\r
+          case EFI_SCSI_ASC_MEDIA_ERR2:\r
 \r
+          //\r
+          // fall through\r
+          //\r
+          case EFI_SCSI_ASC_MEDIA_ERR3:\r
+          case EFI_SCSI_ASC_MEDIA_ERR4:\r
+            IsError = TRUE;\r
+            break;\r
 \r
-/**\r
-  Retrieve all sense keys from the device.\r
+          default:\r
+            break;\r
+        }\r
 \r
-  When encountering error during the process, if retrieve sense keys before\r
-  error encounterred, it returns the sense keys with return status set to EFI_SUCCESS,\r
-  and NeedRetry set to FALSE; otherwize, return the proper return status.\r
+        break;\r
 \r
-  @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV\r
-  @param  NeedRetry          The pointer of flag indicates if need a retry\r
-  @param  SenseDataArray     The pointer of an array of sense data\r
-  @param  NumberOfSenseKeys  The number of sense key\r
-  @param  AskResetIfError    The flag indicates if need reset when error occurs\r
+      case EFI_SCSI_SK_NOT_READY:\r
+        //\r
+        // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
+        //\r
+        switch (SensePtr->Addnl_Sense_Code) {\r
+          //\r
+          // Additional Sense Code is ASC_MEDIA_UPSIDE_DOWN (0x6)\r
+          //\r
+          case EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN:\r
+            IsError = TRUE;\r
+            break;\r
 \r
-  @retval EFI_DEVICE_ERROR   Indicates that error occurs\r
-  @retval EFI_SUCCESS        Successfully to request sense key\r
+          default:\r
+            break;\r
+        }\r
 \r
-**/\r
-EFI_STATUS\r
-ScsiDiskRequestSenseKeys (\r
-  IN  OUT  SCSI_DISK_DEV           *ScsiDiskDevice,\r
-      OUT  BOOLEAN                 *NeedRetry,\r
-      OUT  EFI_SCSI_SENSE_DATA     **SenseDataArray,\r
-      OUT  UINTN                   *NumberOfSenseKeys,\r
-  IN       BOOLEAN                 AskResetIfError\r
-  )\r
-{\r
-  EFI_SCSI_SENSE_DATA *PtrSenseData;\r
-  UINT8               SenseDataLength;\r
-  BOOLEAN             SenseReq;\r
-  EFI_STATUS          Status;\r
-  EFI_STATUS          FallStatus;\r
-  UINT8               HostAdapterStatus;\r
-  UINT8               TargetStatus;\r
+        break;\r
 \r
-  FallStatus      = EFI_SUCCESS;\r
-  SenseDataLength = sizeof (EFI_SCSI_SENSE_DATA);\r
+      default:\r
+        break;\r
+    }\r
 \r
-  ZeroMem (\r
-    ScsiDiskDevice->SenseData,\r
-    sizeof (EFI_SCSI_SENSE_DATA) * (ScsiDiskDevice->SenseDataNumber)\r
-    );\r
+    SensePtr++;\r
+  }\r
 \r
-  *NumberOfSenseKeys  = 0;\r
-  *SenseDataArray     = ScsiDiskDevice->SenseData;\r
-  PtrSenseData        = ScsiDiskDevice->SenseData;\r
+  return IsError;\r
+}\r
 \r
-  for (SenseReq = TRUE; SenseReq;) {\r
-    Status = ScsiRequestSenseCommand (\r
-              ScsiDiskDevice->ScsiIo,\r
-              EFI_TIMER_PERIOD_SECONDS (2),\r
-              PtrSenseData,\r
-              &SenseDataLength,\r
-              &HostAdapterStatus,\r
-              &TargetStatus\r
-              );\r
-     if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
-        FallStatus = EFI_SUCCESS;\r
-  \r
-     } else if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
-       *NeedRetry  = TRUE;\r
-       FallStatus  = EFI_DEVICE_ERROR;\r
\r
-     } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
-       *NeedRetry  = FALSE;\r
-       FallStatus  = EFI_DEVICE_ERROR;\r
\r
-     } else if (Status == EFI_DEVICE_ERROR) {\r
-        if (AskResetIfError) {\r
-          ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
-        }\r
-  \r
-        FallStatus = EFI_DEVICE_ERROR;\r
-    }\r
+/**\r
+  Check sense key to find if hardware error happens.\r
 \r
-    if (EFI_ERROR (FallStatus)) {\r
-      if (*NumberOfSenseKeys != 0) {\r
-        *NeedRetry = FALSE;\r
-        return EFI_SUCCESS;\r
-      } else {\r
-        return EFI_DEVICE_ERROR;\r
-      }\r
-    }\r
+  @param  SenseData     The pointer of EFI_SCSI_SENSE_DATA\r
+  @param  SenseCounts   The number of sense key\r
 \r
-    (*NumberOfSenseKeys) += 1;\r
+  @retval TRUE  Hardware error exits.\r
+  @retval FALSE NO error.\r
+\r
+**/\r
+BOOLEAN\r
+ScsiDiskIsHardwareError (\r
+  IN  EFI_SCSI_SENSE_DATA  *SenseData,\r
+  IN  UINTN                SenseCounts\r
+  )\r
+{\r
+  EFI_SCSI_SENSE_DATA  *SensePtr;\r
+  UINTN                Index;\r
+  BOOLEAN              IsError;\r
 \r
+  IsError  = FALSE;\r
+  SensePtr = SenseData;\r
+\r
+  for (Index = 0; Index < SenseCounts; Index++) {\r
     //\r
-    // no more sense key or number of sense keys exceeds predefined,\r
-    // skip the loop.\r
+    // Sense Key is EFI_SCSI_SK_HARDWARE_ERROR (0x4)\r
     //\r
-    if ((PtrSenseData->Sense_Key == EFI_SCSI_SK_NO_SENSE) || \r
-        (*NumberOfSenseKeys == ScsiDiskDevice->SenseDataNumber)) {\r
-      SenseReq = FALSE;\r
+    if (SensePtr->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) {\r
+      IsError = TRUE;\r
     }\r
-    PtrSenseData += 1;\r
+\r
+    SensePtr++;\r
   }\r
-  return EFI_SUCCESS;\r
-}\r
 \r
+  return IsError;\r
+}\r
 \r
 /**\r
-  Get information from media read capacity command.\r
+  Check sense key to find if media has changed.\r
 \r
-  @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV\r
-  @param  Capacity        The pointer of EFI_SCSI_DISK_CAPACITY_DATA\r
+  @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA\r
+  @param  SenseCounts  The number of sense key\r
 \r
+  @retval TRUE   Media is changed.\r
+  @retval FALSE  Media is NOT changed.\r
 **/\r
-VOID\r
-GetMediaInfo (\r
-  IN  OUT  SCSI_DISK_DEV                 *ScsiDiskDevice,\r
-  IN       EFI_SCSI_DISK_CAPACITY_DATA   *Capacity\r
+BOOLEAN\r
+ScsiDiskIsMediaChange (\r
+  IN  EFI_SCSI_SENSE_DATA  *SenseData,\r
+  IN  UINTN                SenseCounts\r
   )\r
 {\r
-  ScsiDiskDevice->BlkIo.Media->LastBlock =  (Capacity->LastLba3 << 24) |\r
-                                            (Capacity->LastLba2 << 16) |\r
-                                            (Capacity->LastLba1 << 8)  |\r
-                                             Capacity->LastLba0;\r
+  EFI_SCSI_SENSE_DATA  *SensePtr;\r
+  UINTN                Index;\r
+  BOOLEAN              IsMediaChanged;\r
 \r
-  ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE;\r
-  ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity->BlockSize3 << 24) |\r
-                                           (Capacity->BlockSize2 << 16) | \r
-                                           (Capacity->BlockSize1 << 8)  |\r
-                                            Capacity->BlockSize0;\r
-  if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) {\r
-    ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;\r
-  }\r
+  IsMediaChanged = FALSE;\r
+  SensePtr       = SenseData;\r
+\r
+  for (Index = 0; Index < SenseCounts; Index++) {\r
+    //\r
+    // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6),\r
+    // Additional sense code is EFI_SCSI_ASC_MEDIA_CHANGE (0x28)\r
+    //\r
+    if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
+        (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE))\r
+    {\r
+      IsMediaChanged = TRUE;\r
+    }\r
 \r
-  if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_CDROM) {\r
-    ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;\r
+    SensePtr++;\r
   }\r
-}\r
-\r
-/**\r
-  Parse Inquiry data.\r
-\r
-  @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV\r
 \r
-**/\r
-VOID\r
-ParseInquiryData (\r
-  IN OUT SCSI_DISK_DEV   *ScsiDiskDevice\r
-  )\r
-{\r
-  ScsiDiskDevice->FixedDevice               = (BOOLEAN) (ScsiDiskDevice->InquiryData.RMB ? 0 : 1);\r
-  ScsiDiskDevice->BlkIoMedia.RemovableMedia = (BOOLEAN) (!ScsiDiskDevice->FixedDevice);\r
+  return IsMediaChanged;\r
 }\r
 \r
 /**\r
-  Read sector from SCSI Disk.\r
+  Check sense key to find if reset happens.\r
 \r
-  @param  ScsiDiskDevice  The poiniter of SCSI_DISK_DEV\r
-  @param  Buffer          The buffer to fill in the read out data\r
-  @param  Lba             Logic block address\r
-  @param  NumberOfBlocks  The number of blocks to read\r
+  @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA\r
+  @param  SenseCounts  The number of sense key\r
 \r
-  @retval EFI_DEVICE_ERROR  Indicates a device error.\r
-  @retval EFI_SUCCESS       Operation is successful.\r
+  @retval TRUE  It is reset before.\r
+  @retval FALSE It is NOT reset before.\r
 \r
 **/\r
-EFI_STATUS\r
-ScsiDiskReadSectors (\r
-  IN   SCSI_DISK_DEV     *ScsiDiskDevice,\r
-  OUT  VOID              *Buffer,\r
-  IN   EFI_LBA           Lba,\r
-  IN   UINTN             NumberOfBlocks\r
+BOOLEAN\r
+ScsiDiskIsResetBefore (\r
+  IN  EFI_SCSI_SENSE_DATA  *SenseData,\r
+  IN  UINTN                SenseCounts\r
   )\r
 {\r
-  UINTN               BlocksRemaining;\r
-  UINT32              Lba32;\r
-  UINT8               *PtrBuffer;\r
-  UINT32              BlockSize;\r
-  UINT32              ByteCount;\r
-  UINT32              MaxBlock;\r
-  UINT32              SectorCount;\r
-  UINT64              Timeout;\r
-  EFI_STATUS          Status;\r
-  UINT8               Index;\r
-  UINT8               MaxRetry;\r
-  BOOLEAN             NeedRetry;\r
-  EFI_SCSI_SENSE_DATA *SenseData;\r
-  UINTN               NumberOfSenseKeys;\r
+  EFI_SCSI_SENSE_DATA  *SensePtr;\r
+  UINTN                Index;\r
+  BOOLEAN              IsResetBefore;\r
 \r
-  SenseData         = NULL;\r
-  NumberOfSenseKeys = 0;\r
+  IsResetBefore = FALSE;\r
+  SensePtr      = SenseData;\r
 \r
-  Status            = EFI_SUCCESS;\r
+  for (Index = 0; Index < SenseCounts; Index++) {\r
+    //\r
+    // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6)\r
+    // Additional Sense Code is EFI_SCSI_ASC_RESET (0x29)\r
+    //\r
+    if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
+        (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_RESET))\r
+    {\r
+      IsResetBefore = TRUE;\r
+    }\r
 \r
-  BlocksRemaining   = NumberOfBlocks;\r
-  BlockSize         = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
-  //\r
-  // limit the data bytes that can be transferred by one Read(10) Command\r
-  //\r
-  MaxBlock  = 65536;\r
+    SensePtr++;\r
+  }\r
 \r
-  PtrBuffer = Buffer;\r
-  Lba32     = (UINT32) Lba;\r
+  return IsResetBefore;\r
+}\r
 \r
-  while (BlocksRemaining > 0) {\r
+/**\r
+  Check sense key to find if the drive is ready.\r
 \r
-    if (BlocksRemaining <= MaxBlock) {\r
+  @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA\r
+  @param  SenseCounts  The number of sense key\r
+  @param  RetryLater   The flag means if need a retry\r
 \r
-      SectorCount = (UINT16) BlocksRemaining;\r
-    } else {\r
+  @retval TRUE  Drive is ready.\r
+  @retval FALSE Drive is NOT ready.\r
 \r
-      SectorCount = MaxBlock;\r
-    }\r
+**/\r
+BOOLEAN\r
+ScsiDiskIsDriveReady (\r
+  IN  EFI_SCSI_SENSE_DATA  *SenseData,\r
+  IN  UINTN                SenseCounts,\r
+  OUT BOOLEAN              *RetryLater\r
+  )\r
+{\r
+  EFI_SCSI_SENSE_DATA  *SensePtr;\r
+  UINTN                Index;\r
+  BOOLEAN              IsReady;\r
 \r
-    ByteCount = SectorCount * BlockSize;\r
-    Timeout   = EFI_TIMER_PERIOD_SECONDS (2);\r
+  IsReady     = TRUE;\r
+  *RetryLater = FALSE;\r
+  SensePtr    = SenseData;\r
 \r
-    MaxRetry  = 2;\r
-    for (Index = 0; Index < MaxRetry; Index++) {\r
+  for (Index = 0; Index < SenseCounts; Index++) {\r
+    switch (SensePtr->Sense_Key) {\r
+      case EFI_SCSI_SK_NOT_READY:\r
+        //\r
+        // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
+        //\r
+        switch (SensePtr->Addnl_Sense_Code) {\r
+          case EFI_SCSI_ASC_NOT_READY:\r
+            //\r
+            // Additional Sense Code is EFI_SCSI_ASC_NOT_READY (0x4)\r
+            //\r
+            switch (SensePtr->Addnl_Sense_Code_Qualifier) {\r
+              case EFI_SCSI_ASCQ_IN_PROGRESS:\r
+                //\r
+                // Additional Sense Code Qualifier is\r
+                // EFI_SCSI_ASCQ_IN_PROGRESS (0x1)\r
+                //\r
+                IsReady     = FALSE;\r
+                *RetryLater = TRUE;\r
+                break;\r
+\r
+              default:\r
+                IsReady     = FALSE;\r
+                *RetryLater = FALSE;\r
+                break;\r
+            }\r
+\r
+            break;\r
+\r
+          default:\r
+            break;\r
+        }\r
 \r
-      Status = ScsiDiskRead10 (\r
-                ScsiDiskDevice,\r
-                &NeedRetry,\r
-                &SenseData,\r
-                &NumberOfSenseKeys,\r
-                Timeout,\r
-                PtrBuffer,\r
-                &ByteCount,\r
-                Lba32,\r
-                SectorCount\r
-                );\r
-      if (!EFI_ERROR (Status)) {\r
         break;\r
-      }\r
-\r
-      if (!NeedRetry) {\r
-        return EFI_DEVICE_ERROR;\r
-      }\r
-\r
-    }\r
 \r
-    if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
-      return EFI_DEVICE_ERROR;\r
+      default:\r
+        break;\r
     }\r
 \r
-    //\r
-    // actual transferred sectors\r
-    //\r
-    SectorCount = ByteCount / BlockSize;\r
-\r
-    Lba32 += SectorCount;\r
-    PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
-    BlocksRemaining -= SectorCount;\r
+    SensePtr++;\r
   }\r
 \r
-  return EFI_SUCCESS;\r
+  return IsReady;\r
 }\r
 \r
 /**\r
-  Write sector to SCSI Disk.\r
+  Check sense key to find if it has sense key.\r
 \r
-  @param  ScsiDiskDevice  The poiniter of SCSI_DISK_DEV\r
-  @param  Buffer          The buffer of data to be written into SCSI Disk\r
-  @param  Lba             Logic block address\r
-  @param  NumberOfBlocks  The number of blocks to read\r
+  @param  SenseData   - The pointer of EFI_SCSI_SENSE_DATA\r
+  @param  SenseCounts - The number of sense key\r
 \r
-  @retval EFI_DEVICE_ERROR  Indicates a device error.\r
-  @retval EFI_SUCCESS       Operation is successful.\r
+  @retval TRUE  It has sense key.\r
+  @retval FALSE It has NOT any sense key.\r
 \r
 **/\r
-EFI_STATUS\r
-ScsiDiskWriteSectors (\r
-  IN  SCSI_DISK_DEV     *ScsiDiskDevice,\r
-  IN  VOID              *Buffer,\r
-  IN  EFI_LBA           Lba,\r
-  IN  UINTN             NumberOfBlocks\r
+BOOLEAN\r
+ScsiDiskHaveSenseKey (\r
+  IN  EFI_SCSI_SENSE_DATA  *SenseData,\r
+  IN  UINTN                SenseCounts\r
   )\r
 {\r
-  UINTN               BlocksRemaining;\r
-  UINT32              Lba32;\r
-  UINT8               *PtrBuffer;\r
-  UINT32              BlockSize;\r
-  UINT32              ByteCount;\r
-  UINT32              MaxBlock;\r
-  UINT32              SectorCount;\r
-  UINT64              Timeout;\r
-  EFI_STATUS          Status;\r
-  UINT8               Index;\r
-  UINT8               MaxRetry;\r
-  BOOLEAN             NeedRetry;\r
-  EFI_SCSI_SENSE_DATA *SenseData;\r
-  UINTN               NumberOfSenseKeys;\r
-\r
-  SenseData         = NULL;\r
-  NumberOfSenseKeys = 0;\r
-\r
-  Status            = EFI_SUCCESS;\r
-\r
-  BlocksRemaining   = NumberOfBlocks;\r
-  BlockSize         = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
-  //\r
-  // limit the data bytes that can be transferred by one Write(10) Command\r
-  //\r
-  MaxBlock  = 65536;\r
-\r
-  PtrBuffer = Buffer;\r
-  Lba32     = (UINT32) Lba;\r
-\r
-  while (BlocksRemaining > 0) {\r
+  EFI_SCSI_SENSE_DATA  *SensePtr;\r
+  UINTN                Index;\r
+  BOOLEAN              HaveSenseKey;\r
 \r
-    if (BlocksRemaining <= MaxBlock) {\r
-\r
-      SectorCount = (UINT16) BlocksRemaining;\r
-    } else {\r
-\r
-      SectorCount = MaxBlock;\r
-    }\r
-\r
-    ByteCount = SectorCount * BlockSize;\r
-    Timeout   = EFI_TIMER_PERIOD_SECONDS (2);\r
-    MaxRetry  = 2;\r
-    for (Index = 0; Index < MaxRetry; Index++) {\r
-      Status = ScsiDiskWrite10 (\r
-                ScsiDiskDevice,\r
-                &NeedRetry,\r
-                &SenseData,\r
-                &NumberOfSenseKeys,\r
-                Timeout,\r
-                PtrBuffer,\r
-                &ByteCount,\r
-                Lba32,\r
-                SectorCount\r
-                );\r
-      if (!EFI_ERROR (Status)) {\r
-        break;\r
-      }\r
+  if (SenseCounts == 0) {\r
+    HaveSenseKey = FALSE;\r
+  } else {\r
+    HaveSenseKey = TRUE;\r
+  }\r
 \r
-      if (!NeedRetry) {\r
-        return EFI_DEVICE_ERROR;\r
-      }\r
-    }\r
+  SensePtr = SenseData;\r
 \r
-    if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
-      return EFI_DEVICE_ERROR;\r
-    }\r
+  for (Index = 0; Index < SenseCounts; Index++) {\r
     //\r
-    // actual transferred sectors\r
+    // Sense Key is SK_NO_SENSE (0x0)\r
     //\r
-    SectorCount = ByteCount / BlockSize;\r
+    if ((SensePtr->Sense_Key == EFI_SCSI_SK_NO_SENSE) &&\r
+        (Index == 0))\r
+    {\r
+      HaveSenseKey = FALSE;\r
+    }\r
 \r
-    Lba32 += SectorCount;\r
-    PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
-    BlocksRemaining -= SectorCount;\r
+    SensePtr++;\r
   }\r
 \r
-  return EFI_SUCCESS;\r
+  return HaveSenseKey;\r
 }\r
 \r
-\r
 /**\r
-  Sumbmit Read command.\r
+  Release resource about disk device.\r
 \r
-  @param  ScsiDiskDevice     The pointer of ScsiDiskDevice\r
-  @param  NeedRetry          The pointer of flag indicates if needs retry if error happens\r
-  @param  SenseDataArray     NOT used yet in this function\r
-  @param  NumberOfSenseKeys  The number of sense key\r
-  @param  Timeout            The time to complete the command\r
-  @param  DataBuffer         The buffer to fill with the read out data\r
-  @param  DataLength         The length of buffer\r
-  @param  StartLba           The start logic block address\r
-  @param  SectorSize         The size of sector\r
+  @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV\r
 \r
-  @return  EFI_STATUS is returned by calling ScsiRead10Command().\r
 **/\r
-EFI_STATUS\r
-ScsiDiskRead10 (\r
-  IN     SCSI_DISK_DEV         *ScsiDiskDevice,\r
-     OUT BOOLEAN               *NeedRetry,\r
-     OUT EFI_SCSI_SENSE_DATA   **SenseDataArray,   OPTIONAL\r
-     OUT UINTN                 *NumberOfSenseKeys,\r
-  IN     UINT64                Timeout,\r
-     OUT UINT8                 *DataBuffer,\r
-  IN OUT UINT32                *DataLength,\r
-  IN     UINT32                StartLba,\r
-  IN     UINT32                SectorSize\r
+VOID\r
+ReleaseScsiDiskDeviceResources (\r
+  IN  SCSI_DISK_DEV  *ScsiDiskDevice\r
   )\r
 {\r
-  UINT8       SenseDataLength;\r
-  EFI_STATUS  Status;\r
-  UINT8       HostAdapterStatus;\r
-  UINT8       TargetStatus;\r
+  if (ScsiDiskDevice == NULL) {\r
+    return;\r
+  }\r
 \r
-  *NeedRetry          = FALSE;\r
-  *NumberOfSenseKeys  = 0;\r
-  SenseDataLength     = 0;\r
-  Status = ScsiRead10Command (\r
-            ScsiDiskDevice->ScsiIo,\r
-            Timeout,\r
-            NULL,\r
-            &SenseDataLength,\r
-            &HostAdapterStatus,\r
-            &TargetStatus,\r
-            DataBuffer,\r
-            DataLength,\r
-            StartLba,\r
-            SectorSize\r
-            );\r
-  return Status;\r
-}\r
+  if (ScsiDiskDevice->SenseData != NULL) {\r
+    FreePool (ScsiDiskDevice->SenseData);\r
+    ScsiDiskDevice->SenseData = NULL;\r
+  }\r
+\r
+  if (ScsiDiskDevice->ControllerNameTable != NULL) {\r
+    FreeUnicodeStringTable (ScsiDiskDevice->ControllerNameTable);\r
+    ScsiDiskDevice->ControllerNameTable = NULL;\r
+  }\r
+\r
+  FreePool (ScsiDiskDevice);\r
 \r
+  ScsiDiskDevice = NULL;\r
+}\r
 \r
 /**\r
-  Submit Write Command.\r
+  Determine if Block Io & Block Io2 should be produced.\r
 \r
-  @param  ScsiDiskDevice     The pointer of ScsiDiskDevice\r
-  @param  NeedRetry          The pointer of flag indicates if needs retry if error happens\r
-  @param  SenseDataArray     NOT used yet in this function\r
-  @param  NumberOfSenseKeys  The number of sense key\r
-  @param  Timeout            The time to complete the command\r
-  @param  DataBuffer         The buffer to fill with the read out data\r
-  @param  DataLength         The length of buffer\r
-  @param  StartLba           The start logic block address\r
-  @param  SectorSize         The size of sector\r
 \r
-  @return  EFI_STATUS is returned by calling ScsiWrite10Command().\r
+  @param  ChildHandle  Child Handle to retrieve Parent information.\r
+\r
+  @retval  TRUE    Should produce Block Io & Block Io2.\r
+  @retval  FALSE   Should not produce Block Io & Block Io2.\r
 \r
 **/\r
-EFI_STATUS\r
-ScsiDiskWrite10 (\r
-  IN     SCSI_DISK_DEV         *ScsiDiskDevice,\r
-     OUT BOOLEAN               *NeedRetry,\r
-     OUT EFI_SCSI_SENSE_DATA   **SenseDataArray,   OPTIONAL\r
-     OUT UINTN                 *NumberOfSenseKeys,\r
-  IN     UINT64                Timeout,\r
-  IN     UINT8                 *DataBuffer,\r
-  IN OUT UINT32                *DataLength,\r
-  IN     UINT32                StartLba,\r
-  IN     UINT32                SectorSize\r
+BOOLEAN\r
+DetermineInstallBlockIo (\r
+  IN  EFI_HANDLE  ChildHandle\r
   )\r
 {\r
-  EFI_STATUS  Status;\r
-  UINT8       SenseDataLength;\r
-  UINT8       HostAdapterStatus;\r
-  UINT8       TargetStatus;\r
+  EFI_SCSI_PASS_THRU_PROTOCOL      *ScsiPassThru;\r
+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *ExtScsiPassThru;\r
 \r
-  *NeedRetry          = FALSE;\r
-  *NumberOfSenseKeys  = 0;\r
-  SenseDataLength     = 0;\r
-  Status = ScsiWrite10Command (\r
-            ScsiDiskDevice->ScsiIo,\r
-            Timeout,\r
-            NULL,\r
-            &SenseDataLength,\r
-            &HostAdapterStatus,\r
-            &TargetStatus,\r
-            DataBuffer,\r
-            DataLength,\r
-            StartLba,\r
-            SectorSize\r
-            );\r
-  return Status;\r
-}\r
+  //\r
+  // Firstly, check if ExtScsiPassThru Protocol parent handle exists. If existence,\r
+  // check its attribute, logic or physical.\r
+  //\r
+  ExtScsiPassThru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiExtScsiPassThruProtocolGuid, ChildHandle);\r
+  if (ExtScsiPassThru != NULL) {\r
+    if ((ExtScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) {\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Secondly, check if ScsiPassThru Protocol parent handle exists. If existence,\r
+  // check its attribute, logic or physical.\r
+  //\r
+  ScsiPassThru = (EFI_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiScsiPassThruProtocolGuid, ChildHandle);\r
+  if (ScsiPassThru != NULL) {\r
+    if ((ScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) {\r
+      return TRUE;\r
+    }\r
+  }\r
 \r
+  return FALSE;\r
+}\r
 \r
 /**\r
-  Check sense key to find if media presents.\r
+  Search protocol database and check to see if the protocol\r
+  specified by ProtocolGuid is present on a ControllerHandle and opened by\r
+  ChildHandle with an attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.\r
+  If the ControllerHandle is found, then the protocol specified by ProtocolGuid\r
+  will be opened on it.\r
 \r
-  @param  SenseData   The pointer of EFI_SCSI_SENSE_DATA\r
-  @param  SenseCounts The number of sense key\r
 \r
-  @retval TRUE    NOT any media\r
-  @retval FALSE   Media presents\r
+  @param  ProtocolGuid   ProtocolGuid pointer.\r
+  @param  ChildHandle    Child Handle to retrieve Parent information.\r
+\r
 **/\r
-BOOLEAN\r
-ScsiDiskIsNoMedia (\r
-  IN  EFI_SCSI_SENSE_DATA   *SenseData,\r
-  IN  UINTN                 SenseCounts\r
+VOID *\r
+EFIAPI\r
+GetParentProtocol (\r
+  IN  EFI_GUID    *ProtocolGuid,\r
+  IN  EFI_HANDLE  ChildHandle\r
   )\r
 {\r
-  EFI_SCSI_SENSE_DATA *SensePtr;\r
-  UINTN               Index;\r
-  BOOLEAN             IsNoMedia;\r
+  UINTN       Index;\r
+  UINTN       HandleCount;\r
+  VOID        *Interface;\r
+  EFI_STATUS  Status;\r
+  EFI_HANDLE  *HandleBuffer;\r
 \r
-  IsNoMedia = FALSE;\r
-  SensePtr  = SenseData;\r
+  //\r
+  // Retrieve the list of all handles from the handle database\r
+  //\r
+  Status = gBS->LocateHandleBuffer (\r
+                  ByProtocol,\r
+                  ProtocolGuid,\r
+                  NULL,\r
+                  &HandleCount,\r
+                  &HandleBuffer\r
+                  );\r
 \r
-  for (Index = 0; Index < SenseCounts; Index++) {\r
-    //\r
-    // Sense Key is EFI_SCSI_SK_NOT_READY (0x2),\r
-    // Additional Sense Code is ASC_NO_MEDIA (0x3A)\r
-    //\r
-    if ((SensePtr->Sense_Key == EFI_SCSI_SK_NOT_READY) &&\r
-        (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA)) {\r
-      IsNoMedia = TRUE;\r
+  if (EFI_ERROR (Status)) {\r
+    return NULL;\r
+  }\r
+\r
+  //\r
+  // Iterate to find who is parent handle that is opened with ProtocolGuid by ChildHandle\r
+  //\r
+  for (Index = 0; Index < HandleCount; Index++) {\r
+    Status = EfiTestChildHandle (HandleBuffer[Index], ChildHandle, ProtocolGuid);\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = gBS->HandleProtocol (HandleBuffer[Index], ProtocolGuid, (VOID **)&Interface);\r
+      if (!EFI_ERROR (Status)) {\r
+        gBS->FreePool (HandleBuffer);\r
+        return Interface;\r
+      }\r
     }\r
-    SensePtr++;\r
   }\r
 \r
-  return IsNoMedia;\r
+  gBS->FreePool (HandleBuffer);\r
+  return NULL;\r
 }\r
 \r
-\r
 /**\r
-  Parse sense key.\r
+  Determine if EFI Erase Block Protocol should be produced.\r
 \r
-  @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA\r
-  @param  SenseCounts  The number of sense key\r
+  @param   ScsiDiskDevice    The pointer of SCSI_DISK_DEV.\r
+  @param   ChildHandle       Handle of device.\r
 \r
-  @retval TRUE   Error\r
-  @retval FALSE  NOT error\r
+  @retval  TRUE    Should produce EFI Erase Block Protocol.\r
+  @retval  FALSE   Should not produce EFI Erase Block Protocol.\r
 \r
 **/\r
 BOOLEAN\r
-ScsiDiskIsMediaError (\r
-  IN  EFI_SCSI_SENSE_DATA   *SenseData,\r
-  IN  UINTN                 SenseCounts\r
+DetermineInstallEraseBlock (\r
+  IN  SCSI_DISK_DEV  *ScsiDiskDevice,\r
+  IN  EFI_HANDLE     ChildHandle\r
   )\r
 {\r
-  EFI_SCSI_SENSE_DATA *SensePtr;\r
-  UINTN               Index;\r
-  BOOLEAN             IsError;\r
-\r
-  IsError   = FALSE;\r
-  SensePtr  = SenseData;\r
+  UINT8                          HostAdapterStatus;\r
+  UINT8                          TargetStatus;\r
+  EFI_STATUS                     CommandStatus;\r
+  EFI_STATUS                     Status;\r
+  BOOLEAN                        UfsDevice;\r
+  BOOLEAN                        RetVal;\r
+  EFI_DEVICE_PATH_PROTOCOL       *DevicePathNode;\r
+  UINT8                          SenseDataLength;\r
+  UINT32                         DataLength16;\r
+  EFI_SCSI_DISK_CAPACITY_DATA16  *CapacityData16;\r
+\r
+  UfsDevice      = FALSE;\r
+  RetVal         = TRUE;\r
+  CapacityData16 = NULL;\r
 \r
-  for (Index = 0; Index < SenseCounts; Index++) {\r
+  //\r
+  // UNMAP command is not supported by any of the UFS WLUNs.\r
+  //\r
+  if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_WLUN) {\r
+    RetVal = FALSE;\r
+    goto Done;\r
+  }\r
 \r
-    switch (SensePtr->Sense_Key) {\r
+  Status = gBS->HandleProtocol (\r
+                  ChildHandle,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  (VOID **)&DevicePathNode\r
+                  );\r
+  //\r
+  // Device Path protocol must be installed on the device handle.\r
+  //\r
+  ASSERT_EFI_ERROR (Status);\r
 \r
-    case EFI_SCSI_SK_MEDIUM_ERROR:\r
-      //\r
-      // Sense Key is EFI_SCSI_SK_MEDIUM_ERROR (0x3)\r
-      //\r
-      switch (SensePtr->Addnl_Sense_Code) {\r
+  while (!IsDevicePathEndType (DevicePathNode)) {\r
+    //\r
+    // For now, only support Erase Block Protocol on UFS devices.\r
+    //\r
+    if ((DevicePathNode->Type == MESSAGING_DEVICE_PATH) &&\r
+        (DevicePathNode->SubType == MSG_UFS_DP))\r
+    {\r
+      UfsDevice = TRUE;\r
+      break;\r
+    }\r
 \r
-      //\r
-      // fall through\r
-      //\r
-      case EFI_SCSI_ASC_MEDIA_ERR1:\r
+    DevicePathNode = NextDevicePathNode (DevicePathNode);\r
+  }\r
 \r
-      //\r
-      // fall through\r
-      //\r
-      case EFI_SCSI_ASC_MEDIA_ERR2:\r
+  if (!UfsDevice) {\r
+    RetVal = FALSE;\r
+    goto Done;\r
+  }\r
 \r
-      //\r
-      // fall through\r
-      //\r
-      case EFI_SCSI_ASC_MEDIA_ERR3:\r
-      case EFI_SCSI_ASC_MEDIA_ERR4:\r
-        IsError = TRUE;\r
-        break;\r
+  //\r
+  // Check whether the erase functionality is enabled on the UFS device.\r
+  //\r
+  CapacityData16 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
+  if (CapacityData16 == NULL) {\r
+    RetVal = FALSE;\r
+    goto Done;\r
+  }\r
 \r
-      default:\r
-        break;\r
-      }\r
+  SenseDataLength = 0;\r
+  DataLength16    = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16);\r
+  ZeroMem (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
 \r
-      break;\r
+  CommandStatus = ScsiReadCapacity16Command (\r
+                    ScsiDiskDevice->ScsiIo,\r
+                    SCSI_DISK_TIMEOUT,\r
+                    NULL,\r
+                    &SenseDataLength,\r
+                    &HostAdapterStatus,\r
+                    &TargetStatus,\r
+                    (VOID *)CapacityData16,\r
+                    &DataLength16,\r
+                    FALSE\r
+                    );\r
 \r
-    case EFI_SCSI_SK_NOT_READY:\r
-      //\r
-      // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
-      //\r
-      switch (SensePtr->Addnl_Sense_Code) {\r
-      //\r
-      // Additional Sense Code is ASC_MEDIA_UPSIDE_DOWN (0x6)\r
-      //\r
-      case EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN:\r
-        IsError = TRUE;\r
-        break;\r
+  if (CommandStatus == EFI_SUCCESS) {\r
+    //\r
+    // Universal Flash Storage (UFS) Version 2.0\r
+    // Section 11.3.9.2\r
+    // Bits TPE and TPRZ should both be set to enable the erase feature on UFS.\r
+    //\r
+    if (((CapacityData16->LowestAlignLogic2 & BIT7) == 0) ||\r
+        ((CapacityData16->LowestAlignLogic2 & BIT6) == 0))\r
+    {\r
+      DEBUG ((\r
+        DEBUG_VERBOSE,\r
+        "ScsiDisk EraseBlock: Either TPE or TPRZ is not set: 0x%x.\n",\r
+        CapacityData16->LowestAlignLogic2\r
+        ));\r
+\r
+      RetVal = FALSE;\r
+      goto Done;\r
+    }\r
+  } else {\r
+    DEBUG ((\r
+      DEBUG_VERBOSE,\r
+      "ScsiDisk EraseBlock: ReadCapacity16 failed with status %r.\n",\r
+      CommandStatus\r
+      ));\r
 \r
-      default:\r
-        break;\r
-      }\r
-      break;\r
+    RetVal = FALSE;\r
+    goto Done;\r
+  }\r
 \r
-    default:\r
-      break;\r
-    }\r
+  //\r
+  // Check whether the UFS device server implements the UNMAP command.\r
+  //\r
+  if ((ScsiDiskDevice->UnmapInfo.MaxLbaCnt == 0) ||\r
+      (ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt == 0))\r
+  {\r
+    DEBUG ((\r
+      DEBUG_VERBOSE,\r
+      "ScsiDisk EraseBlock: The device server does not implement the UNMAP command.\n"\r
+      ));\r
+\r
+    RetVal = FALSE;\r
+    goto Done;\r
+  }\r
 \r
-    SensePtr++;\r
+Done:\r
+  if (CapacityData16 != NULL) {\r
+    FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
   }\r
 \r
-  return IsError;\r
+  return RetVal;\r
 }\r
 \r
-\r
 /**\r
-  Check sense key to find if hardware error happens.\r
+  Determine if EFI Storage Security Command Protocol should be produced.\r
 \r
-  @param  SenseData     The pointer of EFI_SCSI_SENSE_DATA\r
-  @param  SenseCounts   The number of sense key\r
+  @param   ScsiDiskDevice    The pointer of SCSI_DISK_DEV.\r
+  @param   ChildHandle       Handle of device.\r
 \r
-  @retval TRUE  Hardware error exits.\r
-  @retval FALSE NO error.\r
+  @retval  TRUE    Should produce EFI Storage Security Command Protocol.\r
+  @retval  FALSE   Should not produce EFI Storage Security Command Protocol.\r
 \r
 **/\r
 BOOLEAN\r
-ScsiDiskIsHardwareError (\r
-  IN  EFI_SCSI_SENSE_DATA   *SenseData,\r
-  IN  UINTN                 SenseCounts\r
+DetermineInstallStorageSecurity (\r
+  IN  SCSI_DISK_DEV  *ScsiDiskDevice,\r
+  IN  EFI_HANDLE     ChildHandle\r
   )\r
 {\r
-  EFI_SCSI_SENSE_DATA *SensePtr;\r
-  UINTN               Index;\r
-  BOOLEAN             IsError;\r
-\r
-  IsError   = FALSE;\r
-  SensePtr  = SenseData;\r
+  EFI_STATUS                Status;\r
+  UFS_DEVICE_PATH           *UfsDevice;\r
+  BOOLEAN                   RetVal;\r
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePathNode;\r
+\r
+  UfsDevice = NULL;\r
+  RetVal    = TRUE;\r
+\r
+  Status = gBS->HandleProtocol (\r
+                  ChildHandle,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  (VOID **)&DevicePathNode\r
+                  );\r
+  //\r
+  // Device Path protocol must be installed on the device handle.\r
+  //\r
+  ASSERT_EFI_ERROR (Status);\r
 \r
-  for (Index = 0; Index < SenseCounts; Index++) {\r
-    \r
+  while (!IsDevicePathEndType (DevicePathNode)) {\r
     //\r
-    // Sense Key is EFI_SCSI_SK_HARDWARE_ERROR (0x4)\r
+    // For now, only support Storage Security Command Protocol on UFS devices.\r
     //\r
-    if (SensePtr->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) {\r
-      IsError = TRUE;\r
+    if ((DevicePathNode->Type == MESSAGING_DEVICE_PATH) &&\r
+        (DevicePathNode->SubType == MSG_UFS_DP))\r
+    {\r
+      UfsDevice = (UFS_DEVICE_PATH *)DevicePathNode;\r
+      break;\r
     }\r
 \r
-    SensePtr++;\r
+    DevicePathNode = NextDevicePathNode (DevicePathNode);\r
   }\r
 \r
-  return IsError;\r
-}\r
+  if (UfsDevice == NULL) {\r
+    RetVal = FALSE;\r
+    goto Done;\r
+  }\r
+\r
+  if (UfsDevice->Lun != UFS_WLUN_RPMB) {\r
+    RetVal = FALSE;\r
+  }\r
 \r
+Done:\r
+  return RetVal;\r
+}\r
 \r
 /**\r
-  Check sense key to find if media has changed.\r
+  Provides inquiry information for the controller type.\r
 \r
-  @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA\r
-  @param  SenseCounts  The number of sense key\r
+  This function is used by the IDE bus driver to get inquiry data.  Data format\r
+  of Identify data is defined by the Interface GUID.\r
+\r
+  @param[in]      This              Pointer to the EFI_DISK_INFO_PROTOCOL instance.\r
+  @param[in, out] InquiryData       Pointer to a buffer for the inquiry data.\r
+  @param[in, out] InquiryDataSize   Pointer to the value for the inquiry data size.\r
+\r
+  @retval EFI_SUCCESS            The command was accepted without any errors.\r
+  @retval EFI_NOT_FOUND          Device does not support this data class\r
+  @retval EFI_DEVICE_ERROR       Error reading InquiryData from device\r
+  @retval EFI_BUFFER_TOO_SMALL   InquiryDataSize not big enough\r
 \r
-  @retval TRUE   Media is changed.\r
-  @retval FALSE  Medit is NOT changed.\r
 **/\r
-BOOLEAN\r
-ScsiDiskIsMediaChange (\r
-  IN  EFI_SCSI_SENSE_DATA   *SenseData,\r
-  IN  UINTN                 SenseCounts\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskInfoInquiry (\r
+  IN     EFI_DISK_INFO_PROTOCOL  *This,\r
+  IN OUT VOID                    *InquiryData,\r
+  IN OUT UINT32                  *InquiryDataSize\r
   )\r
 {\r
-  EFI_SCSI_SENSE_DATA *SensePtr;\r
-  UINTN               Index;\r
-  BOOLEAN             IsMediaChanged;\r
+  EFI_STATUS     Status;\r
+  SCSI_DISK_DEV  *ScsiDiskDevice;\r
 \r
-  IsMediaChanged  = FALSE;\r
-  SensePtr        = SenseData;\r
-\r
-  for (Index = 0; Index < SenseCounts; Index++) {\r
-    //\r
-    // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6),\r
-    // Additional sense code is EFI_SCSI_ASC_MEDIA_CHANGE (0x28)\r
-    //\r
-    if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
-        (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE)) {\r
-      IsMediaChanged = TRUE;\r
-    }\r
+  ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
 \r
-    SensePtr++;\r
+  Status = EFI_BUFFER_TOO_SMALL;\r
+  if (*InquiryDataSize >= sizeof (ScsiDiskDevice->InquiryData)) {\r
+    Status = EFI_SUCCESS;\r
+    CopyMem (InquiryData, &ScsiDiskDevice->InquiryData, sizeof (ScsiDiskDevice->InquiryData));\r
   }\r
 \r
-  return IsMediaChanged;\r
+  *InquiryDataSize = sizeof (ScsiDiskDevice->InquiryData);\r
+  return Status;\r
 }\r
 \r
 /**\r
-  Check sense key to find if reset happens.\r
+  Provides identify information for the controller type.\r
 \r
-  @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA\r
-  @param  SenseCounts  The number of sense key\r
+  This function is used by the IDE bus driver to get identify data.  Data format\r
+  of Identify data is defined by the Interface GUID.\r
 \r
-  @retval TRUE  It is reset before.\r
-  @retval FALSE It is NOT reset before.\r
+  @param[in]      This              Pointer to the EFI_DISK_INFO_PROTOCOL\r
+                                    instance.\r
+  @param[in, out] IdentifyData      Pointer to a buffer for the identify data.\r
+  @param[in, out] IdentifyDataSize  Pointer to the value for the identify data\r
+                                    size.\r
+\r
+  @retval EFI_SUCCESS            The command was accepted without any errors.\r
+  @retval EFI_NOT_FOUND          Device does not support this data class\r
+  @retval EFI_DEVICE_ERROR       Error reading IdentifyData from device\r
+  @retval EFI_BUFFER_TOO_SMALL   IdentifyDataSize not big enough\r
 \r
 **/\r
-BOOLEAN\r
-ScsiDiskIsResetBefore (\r
-  IN  EFI_SCSI_SENSE_DATA   *SenseData,\r
-  IN  UINTN                 SenseCounts\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskInfoIdentify (\r
+  IN     EFI_DISK_INFO_PROTOCOL  *This,\r
+  IN OUT VOID                    *IdentifyData,\r
+  IN OUT UINT32                  *IdentifyDataSize\r
   )\r
 {\r
-  EFI_SCSI_SENSE_DATA *SensePtr;\r
-  UINTN               Index;\r
-  BOOLEAN             IsResetBefore;\r
-\r
-  IsResetBefore = FALSE;\r
-  SensePtr      = SenseData;\r
+  EFI_STATUS     Status;\r
+  SCSI_DISK_DEV  *ScsiDiskDevice;\r
 \r
-  for (Index = 0; Index < SenseCounts; Index++) {\r
-    \r
+  if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid) || CompareGuid (&This->Interface, &gEfiDiskInfoUfsInterfaceGuid)) {\r
     //\r
-    // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6)\r
-    // Additional Sense Code is EFI_SCSI_ASC_RESET (0x29)\r
+    // Physical SCSI bus does not support this data class.\r
     //\r
-    if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
-        (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_RESET)) {\r
-      IsResetBefore = TRUE;\r
-    }\r
+    return EFI_NOT_FOUND;\r
+  }\r
 \r
-    SensePtr++;\r
+  ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
+\r
+  Status = EFI_BUFFER_TOO_SMALL;\r
+  if (*IdentifyDataSize >= sizeof (ScsiDiskDevice->IdentifyData)) {\r
+    Status = EFI_SUCCESS;\r
+    CopyMem (IdentifyData, &ScsiDiskDevice->IdentifyData, sizeof (ScsiDiskDevice->IdentifyData));\r
   }\r
 \r
-  return IsResetBefore;\r
+  *IdentifyDataSize = sizeof (ScsiDiskDevice->IdentifyData);\r
+  return Status;\r
 }\r
 \r
 /**\r
-  Check sense key to find if the drive is ready.\r
+  Provides sense data information for the controller type.\r
 \r
-  @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA\r
-  @param  SenseCounts  The number of sense key\r
-  @param  RetryLater   The flag means if need a retry \r
+  This function is used by the IDE bus driver to get sense data.\r
+  Data format of Sense data is defined by the Interface GUID.\r
 \r
-  @retval TRUE  Drive is ready.\r
-  @retval FALSE Drive is NOT ready.\r
+  @param[in]      This              Pointer to the EFI_DISK_INFO_PROTOCOL instance.\r
+  @param[in, out] SenseData         Pointer to the SenseData.\r
+  @param[in, out] SenseDataSize     Size of SenseData in bytes.\r
+  @param[out]     SenseDataNumber   Pointer to the value for the sense data size.\r
+\r
+  @retval EFI_SUCCESS            The command was accepted without any errors.\r
+  @retval EFI_NOT_FOUND          Device does not support this data class.\r
+  @retval EFI_DEVICE_ERROR       Error reading SenseData from device.\r
+  @retval EFI_BUFFER_TOO_SMALL   SenseDataSize not big enough.\r
 \r
 **/\r
-BOOLEAN\r
-ScsiDiskIsDriveReady (\r
-  IN  EFI_SCSI_SENSE_DATA   *SenseData,\r
-  IN  UINTN                 SenseCounts,\r
-  OUT BOOLEAN               *RetryLater\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskInfoSenseData (\r
+  IN     EFI_DISK_INFO_PROTOCOL  *This,\r
+  IN OUT VOID                    *SenseData,\r
+  IN OUT UINT32                  *SenseDataSize,\r
+  OUT    UINT8                   *SenseDataNumber\r
   )\r
 {\r
-  EFI_SCSI_SENSE_DATA *SensePtr;\r
-  UINTN               Index;\r
-  BOOLEAN             IsReady;\r
-\r
-  IsReady     = TRUE;\r
-  *RetryLater = FALSE;\r
-  SensePtr    = SenseData;\r
-\r
-  for (Index = 0; Index < SenseCounts; Index++) {\r
-\r
-    switch (SensePtr->Sense_Key) {\r
+  return EFI_NOT_FOUND;\r
+}\r
 \r
-    case EFI_SCSI_SK_NOT_READY:\r
-      //\r
-      // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
-      //\r
-      switch (SensePtr->Addnl_Sense_Code) {\r
-      case EFI_SCSI_ASC_NOT_READY:\r
-        //\r
-        // Additional Sense Code is EFI_SCSI_ASC_NOT_READY (0x4)\r
-        //\r
-        switch (SensePtr->Addnl_Sense_Code_Qualifier) {\r
-        case EFI_SCSI_ASCQ_IN_PROGRESS:\r
-          //\r
-          // Additional Sense Code Qualifier is\r
-          // EFI_SCSI_ASCQ_IN_PROGRESS (0x1)\r
-          //\r
-          IsReady     = FALSE;\r
-          *RetryLater = TRUE;\r
-          break;\r
+/**\r
+  This function is used by the IDE bus driver to get controller information.\r
 \r
-        default:\r
-          IsReady     = FALSE;\r
-          *RetryLater = FALSE;\r
-          break;\r
-        }\r
-        break;\r
+  @param[in]  This         Pointer to the EFI_DISK_INFO_PROTOCOL instance.\r
+  @param[out] IdeChannel   Pointer to the Ide Channel number.  Primary or secondary.\r
+  @param[out] IdeDevice    Pointer to the Ide Device number.  Master or slave.\r
 \r
-      default:\r
-        break;\r
-      }\r
-      break;\r
+  @retval EFI_SUCCESS       IdeChannel and IdeDevice are valid.\r
+  @retval EFI_UNSUPPORTED   This is not an IDE device.\r
 \r
-    default:\r
-      break;\r
-    }\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskInfoWhichIde (\r
+  IN  EFI_DISK_INFO_PROTOCOL  *This,\r
+  OUT UINT32                  *IdeChannel,\r
+  OUT UINT32                  *IdeDevice\r
+  )\r
+{\r
+  SCSI_DISK_DEV  *ScsiDiskDevice;\r
 \r
-    SensePtr++;\r
+  if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid) || CompareGuid (&This->Interface, &gEfiDiskInfoUfsInterfaceGuid)) {\r
+    //\r
+    // This is not an IDE physical device.\r
+    //\r
+    return EFI_UNSUPPORTED;\r
   }\r
 \r
-  return IsReady;\r
+  ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
+  *IdeChannel    = ScsiDiskDevice->Channel;\r
+  *IdeDevice     = ScsiDiskDevice->Device;\r
+\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
-  Check sense key to find if it has sense key.\r
+  Issues ATA IDENTIFY DEVICE command to identify ATAPI device.\r
 \r
-  @param  SenseData   - The pointer of EFI_SCSI_SENSE_DATA\r
-  @param  SenseCounts - The number of sense key\r
+  This function tries to fill 512-byte ATAPI_IDENTIFY_DATA for ATAPI device to\r
+  implement Identify() interface for DiskInfo protocol. The ATA command is sent\r
+  via SCSI Request Packet.\r
 \r
-  @retval TRUE  It has sense key.\r
-  @retval FALSE It has NOT any sense key.\r
+  @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV\r
+\r
+  @retval EFI_SUCCESS     The ATAPI device identify data were retrieved successfully.\r
+  @retval others          Some error occurred during the identification that ATAPI device.\r
 \r
 **/\r
-BOOLEAN\r
-ScsiDiskHaveSenseKey (\r
-  IN  EFI_SCSI_SENSE_DATA   *SenseData,\r
-  IN  UINTN                 SenseCounts\r
+EFI_STATUS\r
+AtapiIdentifyDevice (\r
+  IN OUT SCSI_DISK_DEV  *ScsiDiskDevice\r
   )\r
 {\r
-  EFI_SCSI_SENSE_DATA *SensePtr;\r
-  UINTN               Index;\r
-  BOOLEAN             HaveSenseKey;\r
-\r
-  if (SenseCounts == 0) {\r
-    HaveSenseKey = FALSE;\r
-  } else {\r
-    HaveSenseKey = TRUE;\r
-  }\r
+  EFI_SCSI_IO_SCSI_REQUEST_PACKET  CommandPacket;\r
+  UINT8                            Cdb[6];\r
 \r
-  SensePtr = SenseData;\r
-\r
-  for (Index = 0; Index < SenseCounts; Index++) {\r
-    \r
-    //\r
-    // Sense Key is SK_NO_SENSE (0x0)\r
-    //\r
-    if ((SensePtr->Sense_Key == EFI_SCSI_SK_NO_SENSE) &&\r
-        (Index == 0)) {\r
-      HaveSenseKey = FALSE;\r
-    }\r
+  //\r
+  // Initialize SCSI REQUEST_PACKET and 6-byte Cdb\r
+  //\r
+  ZeroMem (&CommandPacket, sizeof (CommandPacket));\r
+  ZeroMem (Cdb, sizeof (Cdb));\r
 \r
-    SensePtr++;\r
-  }\r
+  Cdb[0]                         = ATA_CMD_IDENTIFY_DEVICE;\r
+  CommandPacket.Timeout          = SCSI_DISK_TIMEOUT;\r
+  CommandPacket.Cdb              = Cdb;\r
+  CommandPacket.CdbLength        = (UINT8)sizeof (Cdb);\r
+  CommandPacket.InDataBuffer     = &ScsiDiskDevice->IdentifyData;\r
+  CommandPacket.InTransferLength = sizeof (ScsiDiskDevice->IdentifyData);\r
 \r
-  return HaveSenseKey;\r
+  return ScsiDiskDevice->ScsiIo->ExecuteScsiCommand (ScsiDiskDevice->ScsiIo, &CommandPacket, NULL);\r
 }\r
 \r
 /**\r
-  Release resource about disk device.\r
+  Initialize the installation of DiskInfo protocol.\r
 \r
-  @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV\r
+  This function prepares for the installation of DiskInfo protocol on the child handle.\r
+  By default, it installs DiskInfo protocol with SCSI interface GUID. If it further\r
+  detects that the physical device is an ATAPI/AHCI device, it then updates interface GUID\r
+  to be IDE/AHCI interface GUID.\r
+\r
+  @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV.\r
+  @param  ChildHandle     Child handle to install DiskInfo protocol.\r
 \r
 **/\r
 VOID\r
-ReleaseScsiDiskDeviceResources (\r
-  IN  SCSI_DISK_DEV   *ScsiDiskDevice\r
+InitializeInstallDiskInfo (\r
+  IN  SCSI_DISK_DEV  *ScsiDiskDevice,\r
+  IN  EFI_HANDLE     ChildHandle\r
   )\r
 {\r
-  if (ScsiDiskDevice == NULL) {\r
-    return ;\r
-  }\r
-\r
-  if (ScsiDiskDevice->SenseData != NULL) {\r
-    gBS->FreePool (ScsiDiskDevice->SenseData);\r
-    ScsiDiskDevice->SenseData = NULL;\r
-  }\r
+  EFI_STATUS                Status;\r
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePathNode;\r
+  EFI_DEVICE_PATH_PROTOCOL  *ChildDevicePathNode;\r
+  ATAPI_DEVICE_PATH         *AtapiDevicePath;\r
+  SATA_DEVICE_PATH          *SataDevicePath;\r
+  UINTN                     IdentifyRetry;\r
+\r
+  Status = gBS->HandleProtocol (ChildHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathNode);\r
+  //\r
+  // Device Path protocol must be installed on the device handle.\r
+  //\r
+  ASSERT_EFI_ERROR (Status);\r
+  //\r
+  // Copy the DiskInfo protocol template.\r
+  //\r
+  CopyMem (&ScsiDiskDevice->DiskInfo, &gScsiDiskInfoProtocolTemplate, sizeof (gScsiDiskInfoProtocolTemplate));\r
+\r
+  while (!IsDevicePathEnd (DevicePathNode)) {\r
+    ChildDevicePathNode = NextDevicePathNode (DevicePathNode);\r
+    if ((DevicePathType (DevicePathNode) == HARDWARE_DEVICE_PATH) &&\r
+        (DevicePathSubType (DevicePathNode) == HW_PCI_DP) &&\r
+        (DevicePathType (ChildDevicePathNode) == MESSAGING_DEVICE_PATH) &&\r
+        ((DevicePathSubType (ChildDevicePathNode) == MSG_ATAPI_DP) ||\r
+         (DevicePathSubType (ChildDevicePathNode) == MSG_SATA_DP)))\r
+    {\r
+      IdentifyRetry = 3;\r
+      do {\r
+        //\r
+        // Issue ATA Identify Device Command via SCSI command, which is required to publish DiskInfo protocol\r
+        // with IDE/AHCI interface GUID.\r
+        //\r
+        Status = AtapiIdentifyDevice (ScsiDiskDevice);\r
+        if (!EFI_ERROR (Status)) {\r
+          if (DevicePathSubType (ChildDevicePathNode) == MSG_ATAPI_DP) {\r
+            //\r
+            // We find the valid ATAPI device path\r
+            //\r
+            AtapiDevicePath         = (ATAPI_DEVICE_PATH *)ChildDevicePathNode;\r
+            ScsiDiskDevice->Channel = AtapiDevicePath->PrimarySecondary;\r
+            ScsiDiskDevice->Device  = AtapiDevicePath->SlaveMaster;\r
+            //\r
+            // Update the DiskInfo.Interface to IDE interface GUID for the physical ATAPI device.\r
+            //\r
+            CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoIdeInterfaceGuid);\r
+          } else {\r
+            //\r
+            // We find the valid SATA device path\r
+            //\r
+            SataDevicePath          = (SATA_DEVICE_PATH *)ChildDevicePathNode;\r
+            ScsiDiskDevice->Channel = SataDevicePath->HBAPortNumber;\r
+            ScsiDiskDevice->Device  = SataDevicePath->PortMultiplierPortNumber;\r
+            //\r
+            // Update the DiskInfo.Interface to AHCI interface GUID for the physical AHCI device.\r
+            //\r
+            CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoAhciInterfaceGuid);\r
+          }\r
+\r
+          return;\r
+        }\r
+      } while (--IdentifyRetry > 0);\r
+    } else if ((DevicePathType (ChildDevicePathNode) == MESSAGING_DEVICE_PATH) &&\r
+               (DevicePathSubType (ChildDevicePathNode) == MSG_UFS_DP))\r
+    {\r
+      CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoUfsInterfaceGuid);\r
+      break;\r
+    }\r
 \r
-  if (ScsiDiskDevice->ControllerNameTable != NULL) {\r
-    FreeUnicodeStringTable (ScsiDiskDevice->ControllerNameTable);\r
-    ScsiDiskDevice->ControllerNameTable = NULL;\r
+    DevicePathNode = ChildDevicePathNode;\r
   }\r
 \r
-  gBS->FreePool (ScsiDiskDevice);\r
-\r
-  ScsiDiskDevice = NULL;\r
+  return;\r
 }\r