]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c
MdeModulePkg ScsiDiskDxe: Add BlockIO2 Support
[mirror_edk2.git] / MdeModulePkg / Bus / Scsi / ScsiDiskDxe / ScsiDisk.c
index 282e9c283228865c97ee97b722ca35242a4358b5..eb50889943760b2d3814ab8bff7b1c06cd9a4100 100644 (file)
@@ -239,7 +239,13 @@ ScsiDiskDriverBindingStart (
   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->Handle               = Controller;\r
+  InitializeListHead (&ScsiDiskDevice->BlkIo2Queue);\r
 \r
   ScsiIo->GetDeviceType (ScsiIo, &(ScsiDiskDevice->DeviceType));\r
   switch (ScsiDiskDevice->DeviceType) {\r
@@ -300,7 +306,8 @@ ScsiDiskDriverBindingStart (
   Status = ScsiDiskDetectMedia (ScsiDiskDevice, MustReadCapacity, &Temp);\r
   if (!EFI_ERROR (Status)) {\r
     //\r
-    // Determine if Block IO should be produced on this controller handle\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
@@ -308,6 +315,8 @@ ScsiDiskDriverBindingStart (
                       &Controller,\r
                       &gEfiBlockIoProtocolGuid,\r
                       &ScsiDiskDevice->BlkIo,\r
+                      &gEfiBlockIo2ProtocolGuid,\r
+                      &ScsiDiskDevice->BlkIo2,\r
                       &gEfiDiskInfoProtocolGuid,\r
                       &ScsiDiskDevice->DiskInfo,\r
                       NULL\r
@@ -390,11 +399,19 @@ ScsiDiskDriverBindingStop (
     return Status;\r
   }\r
 \r
-  ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (BlkIo);\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->BlkIo2Queue));\r
+\r
   Status = gBS->UninstallMultipleProtocolInterfaces (\r
                   Controller,\r
                   &gEfiBlockIoProtocolGuid,\r
                   &ScsiDiskDevice->BlkIo,\r
+                  &gEfiBlockIo2ProtocolGuid,\r
+                  &ScsiDiskDevice->BlkIo2,\r
                   &gEfiDiskInfoProtocolGuid,\r
                   &ScsiDiskDevice->DiskInfo,\r
                   NULL\r
@@ -443,7 +460,7 @@ ScsiDiskReset (
 \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
@@ -505,7 +522,7 @@ ScsiDiskReadBlocks (
 \r
   MediaChange    = FALSE;\r
   OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);\r
-  ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);\r
+  ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This);\r
 \r
   if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
 \r
@@ -522,6 +539,12 @@ ScsiDiskReadBlocks (
             &ScsiDiskDevice->BlkIo,\r
             &ScsiDiskDevice->BlkIo\r
             );\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIo2ProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo2,\r
+             &ScsiDiskDevice->BlkIo2\r
+             );\r
       Status = EFI_MEDIA_CHANGED;\r
       goto Done;\r
     }\r
@@ -623,7 +646,7 @@ ScsiDiskWriteBlocks (
 \r
   MediaChange    = FALSE;\r
   OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);\r
-  ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);\r
+  ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This);\r
 \r
   if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
 \r
@@ -640,6 +663,12 @@ ScsiDiskWriteBlocks (
             &ScsiDiskDevice->BlkIo,\r
             &ScsiDiskDevice->BlkIo\r
             );\r
+      gBS->ReinstallProtocolInterface (\r
+             ScsiDiskDevice->Handle,\r
+             &gEfiBlockIo2ProtocolGuid,\r
+             &ScsiDiskDevice->BlkIo2,\r
+             &ScsiDiskDevice->BlkIo2\r
+             );\r
       Status = EFI_MEDIA_CHANGED;\r
       goto Done;\r
     }\r
@@ -725,6 +754,392 @@ ScsiDiskFlushBlocks (
 }\r
 \r
 \r
+/**\r
+  Reset SCSI Disk.\r
+\r
+  @param  This                 The pointer of EFI_BLOCK_IO2_PROTOCOL.\r
+  @param  ExtendedVerification The flag about if extend verificate.\r
+\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
+EFIAPI\r
+ScsiDiskResetEx (\r
+  IN  EFI_BLOCK_IO2_PROTOCOL  *This,\r
+  IN  BOOLEAN                 ExtendedVerification\r
+  )\r
+{\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_BLKIO2 (This);\r
+\r
+  Status          = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto Done;\r
+  }\r
+\r
+  if (!ExtendedVerification) {\r
+    goto Done;\r
+  }\r
+\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
+}\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
+\r
+  if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
+\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
+      Status = EFI_MEDIA_CHANGED;\r
+      goto Done;\r
+    }\r
+  }\r
+  //\r
+  // Get the intrinsic block size\r
+  //\r
+  Media           = ScsiDiskDevice->BlkIo2.Media;\r
+  BlockSize       = Media->BlockSize;\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
+\r
+  if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
+\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
+      Status = EFI_MEDIA_CHANGED;\r
+      goto Done;\r
+    }\r
+  }\r
+  //\r
+  // Get the intrinsic block size\r
+  //\r
+  Media           = ScsiDiskDevice->BlkIo2.Media;\r
+  BlockSize       = Media->BlockSize;\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 (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 writing back the\r
+                            data.\r
+  @retval EFI_NO_MEDIA      There is no media in the device.\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
+  //\r
+  // Signal event and return directly.\r
+  //\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Token->TransactionStatus = EFI_SUCCESS;\r
+    gBS->SignalEvent (Token->Event);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
 /**\r
   Detect Device and read out capacity ,if error occurs, parse the sense key.\r
 \r
@@ -2091,6 +2506,328 @@ ScsiDiskWriteSectors (
   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
+\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
+  InsertTailList (&ScsiDiskDevice->BlkIo2Queue, &BlkIo2Req->Link);\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
+\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 timout value is rounded up to nearest integar 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
+                 PtrBuffer,\r
+                 ByteCount,\r
+                 (UINT32) Lba,\r
+                 SectorCount,\r
+                 BlkIo2Req,\r
+                 Token\r
+                 );\r
+    } else {\r
+      Status = ScsiDiskAsyncRead16 (\r
+                 ScsiDiskDevice,\r
+                 Timeout,\r
+                 PtrBuffer,\r
+                 ByteCount,\r
+                 Lba,\r
+                 SectorCount,\r
+                 BlkIo2Req,\r
+                 Token\r
+                 );\r
+    }\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // Free the SCSI_BLKIO2_REQUEST structure only when the first SCSI\r
+      // command fails. Otherwise, it will be freed in the callback function\r
+      // ScsiDiskNotify().\r
+      //\r
+      if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {\r
+        RemoveEntryList (&BlkIo2Req->Link);\r
+        FreePool (BlkIo2Req);\r
+      }\r
+      return EFI_DEVICE_ERROR;\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
+  return EFI_SUCCESS;\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
+\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
+  InsertTailList (&ScsiDiskDevice->BlkIo2Queue, &BlkIo2Req->Link);\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
+\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 timout value is rounded up to nearest integar 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
+                 PtrBuffer,\r
+                 ByteCount,\r
+                 (UINT32) Lba,\r
+                 SectorCount,\r
+                 BlkIo2Req,\r
+                 Token\r
+                 );\r
+    } else {\r
+      Status = ScsiDiskAsyncWrite16 (\r
+                 ScsiDiskDevice,\r
+                 Timeout,\r
+                 PtrBuffer,\r
+                 ByteCount,\r
+                 Lba,\r
+                 SectorCount,\r
+                 BlkIo2Req,\r
+                 Token\r
+                 );\r
+    }\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // Free the SCSI_BLKIO2_REQUEST structure only when the first SCSI\r
+      // command fails. Otherwise, it will be freed in the callback function\r
+      // ScsiDiskNotify().\r
+      //\r
+      if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {\r
+        RemoveEntryList (&BlkIo2Req->Link);\r
+        FreePool (BlkIo2Req);\r
+      }\r
+      return EFI_DEVICE_ERROR;\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
+  return EFI_SUCCESS;\r
+}\r
+\r
 \r
 /**\r
   Submit Read(10) command.\r
@@ -2587,6 +3324,689 @@ BackOff:
 }\r
 \r
 \r
+/**\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
+  @param  Event    The instance of EFI_EVENT.\r
+  @param  Context  The parameter passed in.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+ScsiDiskNotify (\r
+  IN  EFI_EVENT  Event,\r
+  IN  VOID       *Context\r
+  )\r
+{\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
+  //\r
+  // If previous sub-tasks already fails, no need to process this sub-task.\r
+  //\r
+  if (Token->TransactionStatus != EFI_SUCCESS) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Check HostAdapterStatus and TargetStatus\r
+  // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
+  //\r
+  Status = CheckHostAdapterStatus (Request->HostAdapterStatus);\r
+  if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\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
+    Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+    goto Exit;\r
+  }\r
+\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
+    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
+    Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+    goto Exit;\r
+  }\r
+\r
+  if (Request->TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) {\r
+    DEBUG ((EFI_D_ERROR, "ScsiDiskNotify: Check Condition happened!\n"));\r
+\r
+    Status = 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
+      // 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
+  // This sub-task succeeds, no need to retry.\r
+  //\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->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->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
+                   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
+                   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
+      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->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->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
+                   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
+                   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
+      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
+    //\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
+/**\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  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
+**/\r
+EFI_STATUS\r
+ScsiDiskAsyncRead10 (\r
+  IN     SCSI_DISK_DEV         *ScsiDiskDevice,\r
+  IN     UINT64                Timeout,\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
+\r
+  Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
+  if (Request == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\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->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_CALLBACK,\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 (Request != NULL) {\r
+    if (Request->SenseData != NULL) {\r
+      FreePool (Request->SenseData);\r
+    }\r
+\r
+    RemoveEntryList (&Request->Link);\r
+    FreePool (Request);\r
+  }\r
+\r
+  return Status;\r
+}\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  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
+ScsiDiskAsyncWrite10 (\r
+  IN     SCSI_DISK_DEV         *ScsiDiskDevice,\r
+  IN     UINT64                Timeout,\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
+  EFI_STATUS                   Status;\r
+  SCSI_ASYNC_RW_REQUEST        *Request;\r
+  EFI_EVENT                    AsyncIoEvent;\r
+\r
+  Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
+  if (Request == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\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->OutBuffer       = 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_CALLBACK,\r
+                  ScsiDiskNotify,\r
+                  Request,\r
+                  &AsyncIoEvent\r
+                  );\r
+  if (EFI_ERROR(Status)) {\r
+    goto ErrorExit;\r
+  }\r
+\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
+  return EFI_SUCCESS;\r
+\r
+ErrorExit:\r
+  if (Request != NULL) {\r
+    if (Request->SenseData != NULL) {\r
+      FreePool (Request->SenseData);\r
+    }\r
+\r
+    RemoveEntryList (&Request->Link);\r
+    FreePool (Request);\r
+  }\r
+\r
+  return Status;\r
+}\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  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
+     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
+\r
+  Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
+  if (Request == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\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->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_CALLBACK,\r
+                  ScsiDiskNotify,\r
+                  Request,\r
+                  &AsyncIoEvent\r
+                  );\r
+  if (EFI_ERROR(Status)) {\r
+    goto ErrorExit;\r
+  }\r
+\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
+  return EFI_SUCCESS;\r
+\r
+ErrorExit:\r
+  if (Request != NULL) {\r
+    if (Request->SenseData != NULL) {\r
+      FreePool (Request->SenseData);\r
+    }\r
+\r
+    RemoveEntryList (&Request->Link);\r
+    FreePool (Request);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\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  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
+ScsiDiskAsyncWrite16 (\r
+  IN     SCSI_DISK_DEV         *ScsiDiskDevice,\r
+  IN     UINT64                Timeout,\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_STATUS                   Status;\r
+  SCSI_ASYNC_RW_REQUEST        *Request;\r
+  EFI_EVENT                    AsyncIoEvent;\r
+\r
+  Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
+  if (Request == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\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->OutBuffer       = 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_CALLBACK,\r
+                  ScsiDiskNotify,\r
+                  Request,\r
+                  &AsyncIoEvent\r
+                  );\r
+  if (EFI_ERROR(Status)) {\r
+    goto ErrorExit;\r
+  }\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
+  return EFI_SUCCESS;\r
+\r
+ErrorExit:\r
+  if (Request != NULL) {\r
+    if (Request->SenseData != NULL) {\r
+      FreePool (Request->SenseData);\r
+    }\r
+\r
+    RemoveEntryList (&Request->Link);\r
+    FreePool (Request);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
 /**\r
   Check sense key to find if media presents.\r
 \r
@@ -2973,13 +4393,13 @@ ReleaseScsiDiskDeviceResources (
 }\r
 \r
 /**\r
-  Determine if Block Io should be produced.\r
+  Determine if Block Io & Block Io2 should be produced.\r
   \r
 \r
   @param  ChildHandle  Child Handle to retrieve Parent information.\r
   \r
-  @retval  TRUE    Should produce Block Io.\r
-  @retval  FALSE   Should not produce Block Io.\r
+  @retval  TRUE    Should produce Block Io & Block Io2.\r
+  @retval  FALSE   Should not produce Block Io & Block Io2.\r
 \r
 **/  \r
 BOOLEAN\r