]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassImpl.c
index 71ccfa9d10c2b34008af2c262b0292a42d164042..8c27e18cdb8754151221d8a00ed943267a9953a2 100644 (file)
 /** @file\r
+  USB Mass Storage Driver that manages USB Mass Storage Device and produces Block I/O Protocol.\r
 \r
-Copyright (c) 2007, Intel Corporation\r
-All rights reserved. This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution.  The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
-\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
-\r
-Module Name:\r
-\r
-  UsbMassImpl.c\r
-\r
-Abstract:\r
-\r
-  The implementation of USB mass storage class device driver.\r
-  The command set supported is "USB Mass Storage Specification\r
-  for Bootability".\r
-\r
-Revision History\r
-\r
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
-#include "UsbMassImpl.h"\r
+#include "UsbMass.h"\r
 \r
+#define USB_MASS_TRANSPORT_COUNT    3\r
 //\r
-// The underlying transport protocol. CBI support isn't included\r
-// in the current build. It is being obseleted by the standard\r
-// body. If you want to enable it, remove the if directive here,\r
-// then add the UsbMassCbi.c/.h to the driver's inf file.\r
+// Array of USB transport interfaces.\r
 //\r
-STATIC\r
-USB_MASS_TRANSPORT *mUsbMassTransport[] = {\r
+USB_MASS_TRANSPORT *mUsbMassTransport[USB_MASS_TRANSPORT_COUNT] = {\r
   &mUsbCbi0Transport,\r
   &mUsbCbi1Transport,\r
   &mUsbBotTransport,\r
+};\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gUSBMassDriverBinding = {\r
+  USBMassDriverBindingSupported,\r
+  USBMassDriverBindingStart,\r
+  USBMassDriverBindingStop,\r
+  0x11,\r
+  NULL,\r
   NULL\r
 };\r
 \r
 /**\r
-  Retrieve the media parameters such as disk gemotric for the\r
-  device's BLOCK IO protocol.\r
-\r
-  @param  UsbMass                The USB mass storage device\r
-\r
-  @retval EFI_SUCCESS            The media parameters is updated successfully.\r
-  @retval Others                 Failed to get the media parameters.\r
-\r
-**/\r
-EFI_STATUS\r
-UsbMassInitMedia (\r
-  IN USB_MASS_DEVICE          *UsbMass\r
-  )\r
-{\r
-  EFI_BLOCK_IO_MEDIA          *Media;\r
-  EFI_STATUS                  Status;\r
-  UINTN                       Index;\r
-\r
-  Media = &UsbMass->BlockIoMedia;\r
-\r
-  //\r
-  // Initialize the MediaPrsent/ReadOnly and others to the default.\r
-  // We are not forced to get it right at this time, check UEFI2.0\r
-  // spec for more information:\r
-  //\r
-  // MediaPresent: This field shows the media present status as\r
-  //               of the most recent ReadBlocks or WriteBlocks call.\r
-  //\r
-  // ReadOnly    : This field shows the read-only status as of the\r
-  //               recent WriteBlocks call.\r
-  //\r
-  // but remember to update MediaId/MediaPresent/ReadOnly status\r
-  // after ReadBlocks and WriteBlocks\r
-  //\r
-  Media->MediaPresent     = FALSE;\r
-  Media->LogicalPartition = FALSE;\r
-  Media->ReadOnly         = FALSE;\r
-  Media->WriteCaching     = FALSE;\r
-  Media->IoAlign          = 0;\r
-  Media->MediaId          = 1;\r
-\r
-  //\r
-  // Some device may spend several seconds before it is ready.\r
-  // Try several times before giving up. Wait 5s at most.\r
-  //\r
-  Status = EFI_SUCCESS;\r
-\r
-  for (Index = 0; Index < USB_BOOT_INIT_MEDIA_RETRY; Index++) {\r
-\r
-    Status = UsbBootGetParams (UsbMass);\r
-    if ((Status != EFI_MEDIA_CHANGED)\r
-        && (Status != EFI_NOT_READY)\r
-        && (Status != EFI_TIMEOUT)) {\r
-      break;\r
-    }\r
-\r
-    Status = UsbBootIsUnitReady (UsbMass);\r
-    if (EFI_ERROR (Status)) {\r
-      gBS->Stall (USB_BOOT_RETRY_UNIT_READY_STALL * (Index + 1));\r
-    }\r
+  Reset the block device.\r
 \r
-  }\r
+  This function implements EFI_BLOCK_IO_PROTOCOL.Reset().\r
+  It resets the block device hardware.\r
+  ExtendedVerification is ignored in this implementation.\r
 \r
-  return Status;\r
-}\r
-\r
-\r
-/**\r
-  Reset the block device. ExtendedVerification is ignored for this.\r
+  @param  This                   Indicates a pointer to the calling context.\r
+  @param  ExtendedVerification   Indicates that the driver may perform a more exhaustive\r
+                                 verification operation of the device during reset.\r
 \r
-  @param  This                   The BLOCK IO protocol\r
-  @param  ExtendedVerification   Whether to execute extended verfication.\r
-\r
-  @retval EFI_SUCCESS            The device is successfully resetted.\r
-  @retval Others                 Failed to reset the device.\r
+  @retval EFI_SUCCESS            The block device was reset.\r
+  @retval EFI_DEVICE_ERROR       The block device is not functioning correctly and could not be reset.\r
 \r
 **/\r
 EFI_STATUS\r
@@ -129,9 +53,13 @@ UsbMassReset (
   EFI_TPL         OldTpl;\r
   EFI_STATUS      Status;\r
 \r
-  OldTpl  = gBS->RaiseTPL (USB_MASS_TPL);\r
+  //\r
+  // Raise TPL to TPL_CALLBACK to serialize all its operations\r
+  // to protect shared data structures.\r
+  //\r
+  OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);\r
 \r
-  UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This);\r
+  UsbMass = USB_MASS_DEVICE_FROM_BLOCK_IO (This);\r
   Status  = UsbMass->Transport->Reset (UsbMass->Context, ExtendedVerification);\r
 \r
   gBS->RestoreTPL (OldTpl);\r
@@ -139,24 +67,28 @@ UsbMassReset (
   return Status;\r
 }\r
 \r
-\r
 /**\r
-  Read some blocks of data from the block device.\r
-\r
-  @param  This                   The Block IO protocol\r
-  @param  MediaId                The media's ID of the device for current request\r
-  @param  Lba                    The start block number\r
-  @param  BufferSize             The size of buffer to read data in\r
-  @param  Buffer                 The buffer to read data to\r
-\r
-  @retval EFI_SUCCESS            The data is successfully read\r
-  @retval EFI_NO_MEDIA           Media isn't present\r
-  @retval EFI_MEDIA_CHANGED      The device media has been changed, that is,\r
-                                 MediaId changed\r
-  @retval EFI_INVALID_PARAMETER  Some parameters are invalid, such as Buffer is\r
-                                 NULL.\r
-  @retval EFI_BAD_BUFFER_SIZE    The buffer size isn't a multiple of media's block\r
-                                 size,  or overflow the last block number.\r
+  Reads the requested number of blocks from the device.\r
+\r
+  This function implements EFI_BLOCK_IO_PROTOCOL.ReadBlocks().\r
+  It reads the requested number of blocks from the device.\r
+  All the blocks are read, or an error is returned.\r
+\r
+  @param  This                   Indicates a pointer to the calling context.\r
+  @param  MediaId                The media ID that the read request is for.\r
+  @param  Lba                    The starting logical block address to read from on the device.\r
+  @param  BufferSize             The size of the Buffer in bytes.\r
+                                 This must be a multiple of the intrinsic block size of the device.\r
+  @param  Buffer                 A pointer to the destination buffer for the data. The caller is\r
+                                 responsible for either having implicit or explicit ownership of the buffer.\r
+\r
+  @retval EFI_SUCCESS            The data was read correctly from the device.\r
+  @retval EFI_DEVICE_ERROR       The device reported an error while attempting 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 the intrinsic block size of the device.\r
+  @retval EFI_INVALID_PARAMETER  The read request contains LBAs that are not valid,\r
+                                 or the buffer is not on proper alignment.\r
 \r
 **/\r
 EFI_STATUS\r
@@ -175,34 +107,48 @@ UsbMassReadBlocks (
   EFI_TPL             OldTpl;\r
   UINTN               TotalBlock;\r
 \r
-  OldTpl  = gBS->RaiseTPL (USB_MASS_TPL);\r
-  UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This);\r
-  Media   = &UsbMass->BlockIoMedia;\r
-\r
   //\r
-  // First, validate the parameters\r
+  // Raise TPL to TPL_CALLBACK to serialize all its operations\r
+  // to protect shared data structures.\r
   //\r
-  if ((Buffer == NULL) || (BufferSize == 0)) {\r
-    Status = EFI_INVALID_PARAMETER;\r
-    goto ON_EXIT;\r
-  }\r
+  OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);\r
+  UsbMass = USB_MASS_DEVICE_FROM_BLOCK_IO (This);\r
+  Media   = &UsbMass->BlockIoMedia;\r
 \r
   //\r
   // If it is a removable media, such as CD-Rom or Usb-Floppy,\r
-  // need to detect the media before each rw. While some of\r
+  // need to detect the media before each read/write. While some of\r
   // Usb-Flash is marked as removable media.\r
   //\r
-  //\r
-  if (Media->RemovableMedia == TRUE) {\r
+  if (Media->RemovableMedia) {\r
     Status = UsbBootDetectMedia (UsbMass);\r
     if (EFI_ERROR (Status)) {\r
-      DEBUG ((EFI_D_ERROR, "UsbMassReadBlocks: UsbBootDetectMedia (%r)\n", Status));\r
       goto ON_EXIT;\r
     }\r
   }\r
 \r
+  if (!(Media->MediaPresent)) {\r
+    Status = EFI_NO_MEDIA;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (MediaId != Media->MediaId) {\r
+    Status = EFI_MEDIA_CHANGED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (BufferSize == 0) {\r
+    Status = EFI_SUCCESS;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (Buffer == NULL) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+\r
   //\r
-  // Make sure BlockSize and LBA is consistent with BufferSize\r
+  // BufferSize must be a multiple of the intrinsic block size of the device.\r
   //\r
   if ((BufferSize % Media->BlockSize) != 0) {\r
     Status = EFI_BAD_BUFFER_SIZE;\r
@@ -211,22 +157,20 @@ UsbMassReadBlocks (
 \r
   TotalBlock = BufferSize / Media->BlockSize;\r
 \r
+  //\r
+  // Make sure the range to read is valid.\r
+  //\r
   if (Lba + TotalBlock - 1 > Media->LastBlock) {\r
     Status = EFI_INVALID_PARAMETER;\r
     goto ON_EXIT;\r
   }\r
 \r
-  if (!(Media->MediaPresent)) {\r
-    Status = EFI_NO_MEDIA;\r
-    goto ON_EXIT;\r
-  }\r
-\r
-  if (MediaId != Media->MediaId) {\r
-    Status = EFI_MEDIA_CHANGED;\r
-    goto ON_EXIT;\r
+  if (UsbMass->Cdb16Byte) {\r
+    Status = UsbBootReadWriteBlocks16 (UsbMass, FALSE, Lba, TotalBlock, Buffer);\r
+  } else {\r
+    Status = UsbBootReadWriteBlocks (UsbMass, FALSE, (UINT32) Lba, TotalBlock, Buffer);\r
   }\r
 \r
-  Status = UsbBootReadBlocks (UsbMass, (UINT32) Lba, TotalBlock, Buffer);\r
   if (EFI_ERROR (Status)) {\r
     DEBUG ((EFI_D_ERROR, "UsbMassReadBlocks: UsbBootReadBlocks (%r) -> Reset\n", Status));\r
     UsbMassReset (This, TRUE);\r
@@ -239,22 +183,28 @@ ON_EXIT:
 \r
 \r
 /**\r
-  Write some blocks of data to the block device.\r
-\r
-  @param  This                   The Block IO protocol\r
-  @param  MediaId                The media's ID of the device for current request\r
-  @param  Lba                    The start block number\r
-  @param  BufferSize             The size of buffer to write data to\r
-  @param  Buffer                 The buffer to write data to\r
-\r
-  @retval EFI_SUCCESS            The data is successfully written\r
-  @retval EFI_NO_MEDIA           Media isn't present\r
-  @retval EFI_MEDIA_CHANGED      The device media has been changed, that is,\r
-                                 MediaId changed\r
-  @retval EFI_INVALID_PARAMETER  Some parameters are invalid, such as Buffer is\r
-                                 NULL.\r
-  @retval EFI_BAD_BUFFER_SIZE    The buffer size isn't a multiple of media's block\r
-                                 size,\r
+  Writes a specified number of blocks to the device.\r
+\r
+  This function implements EFI_BLOCK_IO_PROTOCOL.WriteBlocks().\r
+  It writes a specified number of blocks to the device.\r
+  All blocks are written, or an error is returned.\r
+\r
+  @param  This                   Indicates a pointer to the calling context.\r
+  @param  MediaId                The media ID that the write request is for.\r
+  @param  Lba                    The starting logical block address to be written.\r
+  @param  BufferSize             The size of the Buffer in bytes.\r
+                                 This must be a multiple of the intrinsic block size of the device.\r
+  @param  Buffer                 Pointer to the source buffer for the 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 to perform the write operation.\r
+  @retval EFI_BAD_BUFFER_SIZE    The BufferSize parameter is not a multiple of the intrinsic\r
+                                 block size of the device.\r
+  @retval EFI_INVALID_PARAMETER  The write request contains LBAs that are not valid,\r
+                                 or the buffer is not on proper alignment.\r
 \r
 **/\r
 EFI_STATUS\r
@@ -273,54 +223,61 @@ UsbMassWriteBlocks (
   EFI_TPL             OldTpl;\r
   UINTN               TotalBlock;\r
 \r
-  OldTpl  = gBS->RaiseTPL (USB_MASS_TPL);\r
-  UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This);\r
-  Media   = &UsbMass->BlockIoMedia;\r
-\r
   //\r
-  // First, validate the parameters\r
+  // Raise TPL to TPL_CALLBACK to serialize all its operations\r
+  // to protect shared data structures.\r
   //\r
-  if ((Buffer == NULL) || (BufferSize == 0)) {\r
-    Status = EFI_INVALID_PARAMETER;\r
-    goto ON_EXIT;\r
-  }\r
+  OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);\r
+  UsbMass = USB_MASS_DEVICE_FROM_BLOCK_IO (This);\r
+  Media   = &UsbMass->BlockIoMedia;\r
 \r
   //\r
   // If it is a removable media, such as CD-Rom or Usb-Floppy,\r
-  // need to detect the media before each rw. While some of\r
-  // Usb-Flash is marked as removable media.\r
-  //\r
+  // need to detect the media before each read/write. Some of\r
+  // USB Flash is marked as removable media.\r
   //\r
-  if (Media->RemovableMedia == TRUE) {\r
+  if (Media->RemovableMedia) {\r
     Status = UsbBootDetectMedia (UsbMass);\r
     if (EFI_ERROR (Status)) {\r
-      DEBUG ((EFI_D_ERROR, "UsbMassWriteBlocks: UsbBootDetectMedia (%r)\n", Status));\r
       goto ON_EXIT;\r
     }\r
   }\r
 \r
-  //\r
-  // Make sure BlockSize and LBA is consistent with BufferSize\r
-  //\r
-  if ((BufferSize % Media->BlockSize) != 0) {\r
-    Status = EFI_BAD_BUFFER_SIZE;\r
+  if (!(Media->MediaPresent)) {\r
+    Status = EFI_NO_MEDIA;\r
     goto ON_EXIT;\r
   }\r
 \r
-  TotalBlock = BufferSize / Media->BlockSize;\r
+  if (MediaId != Media->MediaId) {\r
+    Status = EFI_MEDIA_CHANGED;\r
+    goto ON_EXIT;\r
+  }\r
 \r
-  if (Lba + TotalBlock - 1 > Media->LastBlock) {\r
+  if (BufferSize == 0) {\r
+    Status = EFI_SUCCESS;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (Buffer == NULL) {\r
     Status = EFI_INVALID_PARAMETER;\r
     goto ON_EXIT;\r
   }\r
 \r
-  if (!(Media->MediaPresent)) {\r
-    Status = EFI_NO_MEDIA;\r
+  //\r
+  // BufferSize must be a multiple of the intrinsic block size of the device.\r
+  //\r
+  if ((BufferSize % Media->BlockSize) != 0) {\r
+    Status = EFI_BAD_BUFFER_SIZE;\r
     goto ON_EXIT;\r
   }\r
 \r
-  if (MediaId != Media->MediaId) {\r
-    Status = EFI_MEDIA_CHANGED;\r
+  TotalBlock = BufferSize / Media->BlockSize;\r
+\r
+  //\r
+  // Make sure the range to write is valid.\r
+  //\r
+  if (Lba + TotalBlock - 1 > Media->LastBlock) {\r
+    Status = EFI_INVALID_PARAMETER;\r
     goto ON_EXIT;\r
   }\r
 \r
@@ -328,7 +285,12 @@ UsbMassWriteBlocks (
   // Try to write the data even the device is marked as ReadOnly,\r
   // and clear the status should the write succeed.\r
   //\r
-  Status = UsbBootWriteBlocks (UsbMass, (UINT32) Lba, TotalBlock, Buffer);\r
+  if (UsbMass->Cdb16Byte) {\r
+    Status = UsbBootReadWriteBlocks16 (UsbMass, TRUE, Lba, TotalBlock, Buffer);\r
+  } else {\r
+    Status = UsbBootReadWriteBlocks (UsbMass, TRUE, (UINT32) Lba, TotalBlock, Buffer);\r
+  }\r
+\r
   if (EFI_ERROR (Status)) {\r
     DEBUG ((EFI_D_ERROR, "UsbMassWriteBlocks: UsbBootWriteBlocks (%r) -> Reset\n", Status));\r
     UsbMassReset (This, TRUE);\r
@@ -339,14 +301,18 @@ ON_EXIT:
   return Status;\r
 }\r
 \r
-\r
 /**\r
-  Flush the cached writes to disks. USB mass storage device doesn't\r
-  support write cache, so return EFI_SUCCESS directly.\r
+  Flushes all modified data to a physical block device.\r
+\r
+  This function implements EFI_BLOCK_IO_PROTOCOL.FlushBlocks().\r
+  USB mass storage device doesn't support write cache,\r
+  so return EFI_SUCCESS directly.\r
 \r
-  @param  This                   The BLOCK IO protocol\r
+  @param  This                   Indicates a pointer to the calling context.\r
 \r
-  @retval EFI_SUCCESS            Always returns success\r
+  @retval EFI_SUCCESS            All outstanding data were written correctly to the device.\r
+  @retval EFI_DEVICE_ERROR       The device reported an error while attempting to write data.\r
+  @retval EFI_NO_MEDIA           There is no media in the device.\r
 \r
 **/\r
 EFI_STATUS\r
@@ -358,36 +324,79 @@ UsbMassFlushBlocks (
   return EFI_SUCCESS;\r
 }\r
 \r
+/**\r
+  Initialize the media parameter data for EFI_BLOCK_IO_MEDIA of Block I/O Protocol.\r
+\r
+  @param  UsbMass                The USB mass storage device\r
+\r
+  @retval EFI_SUCCESS            The media parameters are updated successfully.\r
+  @retval Others                 Failed to get the media parameters.\r
+\r
+**/\r
+EFI_STATUS\r
+UsbMassInitMedia (\r
+  IN USB_MASS_DEVICE          *UsbMass\r
+  )\r
+{\r
+  EFI_BLOCK_IO_MEDIA          *Media;\r
+  EFI_STATUS                  Status;\r
+\r
+  Media = &UsbMass->BlockIoMedia;\r
+\r
+  //\r
+  // Fields of EFI_BLOCK_IO_MEDIA are defined in UEFI 2.0 spec,\r
+  // section for Block I/O Protocol.\r
+  //\r
+  Media->MediaPresent     = FALSE;\r
+  Media->LogicalPartition = FALSE;\r
+  Media->ReadOnly         = FALSE;\r
+  Media->WriteCaching     = FALSE;\r
+  Media->IoAlign          = 0;\r
+  Media->MediaId          = 1;\r
+\r
+  Status = UsbBootGetParams (UsbMass);\r
+  DEBUG ((DEBUG_INFO, "UsbMassInitMedia: UsbBootGetParams (%r)\n", Status));\r
+  if (Status == EFI_MEDIA_CHANGED) {\r
+    //\r
+    // Some USB storage devices may report MEDIA_CHANGED sense key when hot-plugged.\r
+    // Treat it as SUCCESS\r
+    //\r
+    Status = EFI_SUCCESS;\r
+  }\r
+  return Status;\r
+}\r
 \r
 /**\r
-  Check whether the controller is a supported USB mass storage.\r
+  Initilize the USB Mass Storage transport.\r
+\r
+  This function tries to find the matching USB Mass Storage transport\r
+  protocol for USB device. If found, initializes the matching transport.\r
 \r
-  @param  This                   The USB mass driver's driver binding.\r
-  @param  Controller             The device to test against.\r
-  @param  RemainingDevicePath    The remaining device path\r
+  @param  This            The USB mass driver's driver binding.\r
+  @param  Controller      The device to test.\r
+  @param  Transport       The pointer to pointer to USB_MASS_TRANSPORT.\r
+  @param  Context         The parameter for USB_MASS_DEVICE.Context.\r
+  @param  MaxLun          Get the MaxLun if is BOT dev.\r
 \r
-  @retval EFI_SUCCESS            This device is a supported USB mass storage.\r
-  @retval EFI_UNSUPPORTED        The device isn't supported\r
-  @retval Others                 Some error happened.\r
+  @retval EFI_SUCCESS     The initialization is successful.\r
+  @retval EFI_UNSUPPORTED No matching transport protocol is found.\r
+  @retval Others          Failed to initialize dev.\r
 \r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
-USBMassDriverBindingSupported (\r
-  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
-  IN EFI_HANDLE                   Controller,\r
-  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath\r
+UsbMassInitTransport (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   Controller,\r
+  OUT USB_MASS_TRANSPORT           **Transport,\r
+  OUT VOID                         **Context,\r
+  OUT UINT8                        *MaxLun\r
   )\r
 {\r
   EFI_USB_IO_PROTOCOL           *UsbIo;\r
   EFI_USB_INTERFACE_DESCRIPTOR  Interface;\r
-  USB_MASS_TRANSPORT            *Transport;\r
+  UINT8                         Index;\r
   EFI_STATUS                    Status;\r
-  INTN                          Index;\r
 \r
-  //\r
-  // Check whether the controlelr support USB_IO\r
-  //\r
   Status = gBS->OpenProtocol (\r
                   Controller,\r
                   &gEfiUsbIoProtocolGuid,\r
@@ -396,14 +405,11 @@ USBMassDriverBindingSupported (
                   Controller,\r
                   EFI_OPEN_PROTOCOL_BY_DRIVER\r
                   );\r
+\r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
-  //\r
-  // Get the interface to check the USB class and find a transport\r
-  // protocol handler.\r
-  //\r
   Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);\r
   if (EFI_ERROR (Status)) {\r
     goto ON_EXIT;\r
@@ -411,48 +417,293 @@ USBMassDriverBindingSupported (
 \r
   Status = EFI_UNSUPPORTED;\r
 \r
-  if (Interface.InterfaceClass != USB_MASS_STORE_CLASS) {\r
-    goto ON_EXIT;\r
-  }\r
+  //\r
+  // Traverse the USB_MASS_TRANSPORT arrary and try to find the\r
+  // matching transport protocol.\r
+  // If not found, return EFI_UNSUPPORTED.\r
+  // If found, execute USB_MASS_TRANSPORT.Init() to initialize the transport context.\r
+  //\r
+  for (Index = 0; Index < USB_MASS_TRANSPORT_COUNT; Index++) {\r
+    *Transport = mUsbMassTransport[Index];\r
 \r
-  for (Index = 0; mUsbMassTransport[Index] != NULL; Index++) {\r
-    Transport = mUsbMassTransport[Index];\r
-    if (Interface.InterfaceProtocol == Transport->Protocol) {\r
-      Status = Transport->Init (UsbIo, Controller, NULL);\r
+    if (Interface.InterfaceProtocol == (*Transport)->Protocol) {\r
+      Status  = (*Transport)->Init (UsbIo, Context);\r
       break;\r
     }\r
   }\r
 \r
-  DEBUG ((EFI_D_INFO, "Found a USB mass store device %r\n", Status));\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // For BOT device, try to get its max LUN.\r
+  // If max LUN is 0, then it is a non-lun device.\r
+  // Otherwise, it is a multi-lun device.\r
+  //\r
+  if ((*Transport)->Protocol == USB_MASS_STORE_BOT) {\r
+    (*Transport)->GetMaxLun (*Context, MaxLun);\r
+  }\r
 \r
 ON_EXIT:\r
   gBS->CloseProtocol (\r
-        Controller,\r
-        &gEfiUsbIoProtocolGuid,\r
-        This->DriverBindingHandle,\r
-        Controller\r
-        );\r
+         Controller,\r
+         &gEfiUsbIoProtocolGuid,\r
+         This->DriverBindingHandle,\r
+         Controller\r
+         );\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Initialize data for device that supports multiple LUNSs.\r
+\r
+  @param  This                 The Driver Binding Protocol instance.\r
+  @param  Controller           The device to initialize.\r
+  @param  Transport            Pointer to USB_MASS_TRANSPORT.\r
+  @param  Context              Parameter for USB_MASS_DEVICE.Context.\r
+  @param  DevicePath           The remaining device path.\r
+  @param  MaxLun               The max LUN number.\r
+\r
+  @retval EFI_SUCCESS          At least one LUN is initialized successfully.\r
+  @retval EFI_NOT_FOUND        Fail to initialize any of multiple LUNs.\r
+\r
+**/\r
+EFI_STATUS\r
+UsbMassInitMultiLun (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL   *This,\r
+  IN EFI_HANDLE                    Controller,\r
+  IN USB_MASS_TRANSPORT            *Transport,\r
+  IN VOID                          *Context,\r
+  IN EFI_DEVICE_PATH_PROTOCOL      *DevicePath,\r
+  IN UINT8                         MaxLun\r
+  )\r
+{\r
+  USB_MASS_DEVICE                  *UsbMass;\r
+  EFI_USB_IO_PROTOCOL              *UsbIo;\r
+  DEVICE_LOGICAL_UNIT_DEVICE_PATH  LunNode;\r
+  UINT8                            Index;\r
+  EFI_STATUS                       Status;\r
+  EFI_STATUS                       ReturnStatus;\r
+\r
+  ASSERT (MaxLun > 0);\r
+  ReturnStatus = EFI_NOT_FOUND;\r
+\r
+  for (Index = 0; Index <= MaxLun; Index++) {\r
+\r
+    DEBUG ((EFI_D_INFO, "UsbMassInitMultiLun: Start to initialize No.%d logic unit\n", Index));\r
+\r
+    UsbIo   = NULL;\r
+    UsbMass = AllocateZeroPool (sizeof (USB_MASS_DEVICE));\r
+    ASSERT (UsbMass != NULL);\r
+\r
+    UsbMass->Signature            = USB_MASS_SIGNATURE;\r
+    UsbMass->UsbIo                = UsbIo;\r
+    UsbMass->BlockIo.Media        = &UsbMass->BlockIoMedia;\r
+    UsbMass->BlockIo.Reset        = UsbMassReset;\r
+    UsbMass->BlockIo.ReadBlocks   = UsbMassReadBlocks;\r
+    UsbMass->BlockIo.WriteBlocks  = UsbMassWriteBlocks;\r
+    UsbMass->BlockIo.FlushBlocks  = UsbMassFlushBlocks;\r
+    UsbMass->OpticalStorage       = FALSE;\r
+    UsbMass->Transport            = Transport;\r
+    UsbMass->Context              = Context;\r
+    UsbMass->Lun                  = Index;\r
+\r
+    //\r
+    // Initialize the media parameter data for EFI_BLOCK_IO_MEDIA of Block I/O Protocol.\r
+    //\r
+    Status = UsbMassInitMedia (UsbMass);\r
+    if ((EFI_ERROR (Status)) && (Status != EFI_NO_MEDIA)) {\r
+      DEBUG ((EFI_D_ERROR, "UsbMassInitMultiLun: UsbMassInitMedia (%r)\n", Status));\r
+      FreePool (UsbMass);\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Create a device path node for device logic unit, and append it.\r
+    //\r
+    LunNode.Header.Type    = MESSAGING_DEVICE_PATH;\r
+    LunNode.Header.SubType = MSG_DEVICE_LOGICAL_UNIT_DP;\r
+    LunNode.Lun            = UsbMass->Lun;\r
+\r
+    SetDevicePathNodeLength (&LunNode.Header, sizeof (LunNode));\r
+\r
+    UsbMass->DevicePath = AppendDevicePathNode (DevicePath, &LunNode.Header);\r
+\r
+    if (UsbMass->DevicePath == NULL) {\r
+      DEBUG ((EFI_D_ERROR, "UsbMassInitMultiLun: failed to create device logic unit device path\n"));\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      FreePool (UsbMass);\r
+      continue;\r
+    }\r
+\r
+    InitializeDiskInfo (UsbMass);\r
+\r
+    //\r
+    // Create a new handle for each LUN, and install Block I/O Protocol and Device Path Protocol.\r
+    //\r
+    Status = gBS->InstallMultipleProtocolInterfaces (\r
+                    &UsbMass->Controller,\r
+                    &gEfiDevicePathProtocolGuid,\r
+                    UsbMass->DevicePath,\r
+                    &gEfiBlockIoProtocolGuid,\r
+                    &UsbMass->BlockIo,\r
+                    &gEfiDiskInfoProtocolGuid,\r
+                    &UsbMass->DiskInfo,\r
+                    NULL\r
+                    );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "UsbMassInitMultiLun: InstallMultipleProtocolInterfaces (%r)\n", Status));\r
+      FreePool (UsbMass->DevicePath);\r
+      FreePool (UsbMass);\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Open USB I/O Protocol by child to setup a parent-child relationship.\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    Controller,\r
+                    &gEfiUsbIoProtocolGuid,\r
+                    (VOID **) &UsbIo,\r
+                    This->DriverBindingHandle,\r
+                    UsbMass->Controller,\r
+                    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                    );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "UsbMassInitMultiLun: OpenUsbIoProtocol By Child (%r)\n", Status));\r
+      gBS->UninstallMultipleProtocolInterfaces (\r
+             &UsbMass->Controller,\r
+             &gEfiDevicePathProtocolGuid,\r
+             UsbMass->DevicePath,\r
+             &gEfiBlockIoProtocolGuid,\r
+             &UsbMass->BlockIo,\r
+             &gEfiDiskInfoProtocolGuid,\r
+             &UsbMass->DiskInfo,\r
+             NULL\r
+             );\r
+      FreePool (UsbMass->DevicePath);\r
+      FreePool (UsbMass);\r
+      continue;\r
+    }\r
+    ReturnStatus = EFI_SUCCESS;\r
+    DEBUG ((EFI_D_INFO, "UsbMassInitMultiLun: Success to initialize No.%d logic unit\n", Index));\r
+  }\r
+\r
+  return ReturnStatus;\r
+}\r
+\r
+/**\r
+  Initialize data for device that does not support multiple LUNSs.\r
+\r
+  @param  This            The Driver Binding Protocol instance.\r
+  @param  Controller      The device to initialize.\r
+  @param  Transport       Pointer to USB_MASS_TRANSPORT.\r
+  @param  Context         Parameter for USB_MASS_DEVICE.Context.\r
+\r
+  @retval EFI_SUCCESS     Initialization succeeds.\r
+  @retval Other           Initialization fails.\r
+\r
+**/\r
+EFI_STATUS\r
+UsbMassInitNonLun (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL   *This,\r
+  IN EFI_HANDLE                    Controller,\r
+  IN USB_MASS_TRANSPORT            *Transport,\r
+  IN VOID                          *Context\r
+  )\r
+{\r
+  USB_MASS_DEVICE             *UsbMass;\r
+  EFI_USB_IO_PROTOCOL         *UsbIo;\r
+  EFI_STATUS                  Status;\r
+\r
+  UsbIo   = NULL;\r
+  UsbMass = AllocateZeroPool (sizeof (USB_MASS_DEVICE));\r
+  ASSERT (UsbMass != NULL);\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiUsbIoProtocolGuid,\r
+                  (VOID **) &UsbIo,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "UsbMassInitNonLun: OpenUsbIoProtocol By Driver (%r)\n", Status));\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  UsbMass->Signature            = USB_MASS_SIGNATURE;\r
+  UsbMass->Controller           = Controller;\r
+  UsbMass->UsbIo                = UsbIo;\r
+  UsbMass->BlockIo.Media        = &UsbMass->BlockIoMedia;\r
+  UsbMass->BlockIo.Reset        = UsbMassReset;\r
+  UsbMass->BlockIo.ReadBlocks   = UsbMassReadBlocks;\r
+  UsbMass->BlockIo.WriteBlocks  = UsbMassWriteBlocks;\r
+  UsbMass->BlockIo.FlushBlocks  = UsbMassFlushBlocks;\r
+  UsbMass->OpticalStorage       = FALSE;\r
+  UsbMass->Transport            = Transport;\r
+  UsbMass->Context              = Context;\r
+\r
+  //\r
+  // Initialize the media parameter data for EFI_BLOCK_IO_MEDIA of Block I/O Protocol.\r
+  //\r
+  Status = UsbMassInitMedia (UsbMass);\r
+  if ((EFI_ERROR (Status)) && (Status != EFI_NO_MEDIA)) {\r
+    DEBUG ((EFI_D_ERROR, "UsbMassInitNonLun: UsbMassInitMedia (%r)\n", Status));\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  InitializeDiskInfo (UsbMass);\r
+\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &Controller,\r
+                  &gEfiBlockIoProtocolGuid,\r
+                  &UsbMass->BlockIo,\r
+                  &gEfiDiskInfoProtocolGuid,\r
+                  &UsbMass->DiskInfo,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
 \r
+ON_ERROR:\r
+  if (UsbMass != NULL) {\r
+    FreePool (UsbMass);\r
+  }\r
+  if (UsbIo != NULL) {\r
+    gBS->CloseProtocol (\r
+           Controller,\r
+           &gEfiUsbIoProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           Controller\r
+           );\r
+  }\r
   return Status;\r
 }\r
 \r
 \r
 /**\r
-  Start the USB mass storage device on the controller. It will\r
-  install a BLOCK_IO protocol on the device if everything is OK.\r
+  Check whether the controller is a supported USB mass storage.\r
 \r
-  @param  This                   The USB mass storage driver binding.\r
-  @param  Controller             The USB mass storage device to start on\r
+  @param  This                   The USB mass storage driver binding protocol.\r
+  @param  Controller             The controller handle to check.\r
   @param  RemainingDevicePath    The remaining device path.\r
 \r
-  @retval EFI_SUCCESS            The driver has started on the device.\r
-  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory\r
-  @retval Others                 Failed to start the driver on the device.\r
+  @retval EFI_SUCCESS            The driver supports this controller.\r
+  @retval other                  This device isn't supported.\r
 \r
 **/\r
 EFI_STATUS\r
 EFIAPI\r
-USBMassDriverBindingStart (\r
+USBMassDriverBindingSupported (\r
   IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
   IN EFI_HANDLE                   Controller,\r
   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath\r
@@ -460,7 +711,6 @@ USBMassDriverBindingStart (
 {\r
   EFI_USB_IO_PROTOCOL           *UsbIo;\r
   EFI_USB_INTERFACE_DESCRIPTOR  Interface;\r
-  USB_MASS_DEVICE               *UsbMass;\r
   USB_MASS_TRANSPORT            *Transport;\r
   EFI_STATUS                    Status;\r
   UINTN                         Index;\r
@@ -473,92 +723,165 @@ USBMassDriverBindingStart (
                   Controller,\r
                   EFI_OPEN_PROTOCOL_BY_DRIVER\r
                   );\r
-\r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
-  UsbMass = AllocateZeroPool (sizeof (USB_MASS_DEVICE));\r
-  if (UsbMass == NULL) {\r
-    return EFI_OUT_OF_RESOURCES;\r
-  }\r
-\r
   //\r
-  // Initialize the transport protocols\r
+  // Get the interface descriptor to check the USB class and find a transport\r
+  // protocol handler.\r
   //\r
   Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);\r
   if (EFI_ERROR (Status)) {\r
-    DEBUG ((EFI_D_ERROR, "USBMassDriverBindingStart: UsbIo->UsbGetInterfaceDescriptor (%r)\n", Status));\r
-    goto ON_ERROR;\r
+    goto ON_EXIT;\r
   }\r
 \r
   Status = EFI_UNSUPPORTED;\r
 \r
-  for (Index = 0; mUsbMassTransport[Index] != NULL; Index++) {\r
-    Transport = mUsbMassTransport[Index];\r
+  if (Interface.InterfaceClass != USB_MASS_STORE_CLASS) {\r
+    goto ON_EXIT;\r
+  }\r
 \r
+  //\r
+  // Traverse the USB_MASS_TRANSPORT arrary and try to find the\r
+  // matching transport method.\r
+  // If not found, return EFI_UNSUPPORTED.\r
+  // If found, execute USB_MASS_TRANSPORT.Init() to initialize the transport context.\r
+  //\r
+  for (Index = 0; Index < USB_MASS_TRANSPORT_COUNT; Index++) {\r
+    Transport = mUsbMassTransport[Index];\r
     if (Interface.InterfaceProtocol == Transport->Protocol) {\r
-      UsbMass->Transport = Transport;\r
-      Status             = Transport->Init (UsbIo, Controller, &UsbMass->Context);\r
+      Status = Transport->Init (UsbIo, NULL);\r
       break;\r
     }\r
   }\r
 \r
-  if (EFI_ERROR (Status)) {\r
-    DEBUG ((EFI_D_ERROR, "USBMassDriverBindingStart: Transport->Init (%r)\n", Status));\r
-    goto ON_ERROR;\r
-  }\r
+ON_EXIT:\r
+  gBS->CloseProtocol (\r
+         Controller,\r
+         &gEfiUsbIoProtocolGuid,\r
+         This->DriverBindingHandle,\r
+         Controller\r
+         );\r
 \r
-  UsbMass->Signature            = USB_MASS_SIGNATURE;\r
-  UsbMass->Controller           = Controller;\r
-  UsbMass->UsbIo                = UsbIo;\r
-  UsbMass->BlockIo.Media        = &UsbMass->BlockIoMedia;\r
-  UsbMass->BlockIo.Reset        = UsbMassReset;\r
-  UsbMass->BlockIo.ReadBlocks   = UsbMassReadBlocks;\r
-  UsbMass->BlockIo.WriteBlocks  = UsbMassWriteBlocks;\r
-  UsbMass->BlockIo.FlushBlocks  = UsbMassFlushBlocks;\r
-  UsbMass->OpticalStorage       = FALSE;\r
+  return Status;\r
+}\r
 \r
-  //\r
-  // Get the storage's parameters, such as last block number.\r
-  // then install the BLOCK_IO\r
-  //\r
-  Status = UsbMassInitMedia (UsbMass);\r
-  if (!EFI_ERROR (Status)) {\r
-    if ((UsbMass->Pdt != USB_PDT_DIRECT_ACCESS) &&\r
-         (UsbMass->Pdt != USB_PDT_CDROM) &&\r
-         (UsbMass->Pdt != USB_PDT_OPTICAL) &&\r
-         (UsbMass->Pdt != USB_PDT_SIMPLE_DIRECT)) {\r
-      DEBUG ((EFI_D_ERROR, "USBMassDriverBindingStart: Found an unsupported peripheral type[%d]\n", UsbMass->Pdt));\r
-      goto ON_ERROR;\r
-    }\r
-  } else if (Status != EFI_NO_MEDIA){\r
-    DEBUG ((EFI_D_ERROR, "USBMassDriverBindingStart: UsbMassInitMedia (%r)\n", Status));\r
-    goto ON_ERROR;\r
-  }\r
+/**\r
+  Starts the USB mass storage device with this driver.\r
+\r
+  This function consumes USB I/O Portocol, intializes USB mass storage device,\r
+  installs Block I/O Protocol, and submits Asynchronous Interrupt\r
+  Transfer to manage the USB mass storage device.\r
+\r
+  @param  This                  The USB mass storage driver binding protocol.\r
+  @param  Controller            The USB mass storage device to start on\r
+  @param  RemainingDevicePath   The remaining device path.\r
+\r
+  @retval EFI_SUCCESS           This driver supports this device.\r
+  @retval EFI_UNSUPPORTED       This driver does not support this device.\r
+  @retval EFI_DEVICE_ERROR      This driver cannot be started due to device Error.\r
+  @retval EFI_OUT_OF_RESOURCES  Can't allocate memory resources.\r
+  @retval EFI_ALREADY_STARTED   This driver has been started.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+USBMassDriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath\r
+  )\r
+{\r
+  USB_MASS_TRANSPORT            *Transport;\r
+  EFI_DEVICE_PATH_PROTOCOL      *DevicePath;\r
+  VOID                          *Context;\r
+  UINT8                         MaxLun;\r
+  EFI_STATUS                    Status;\r
+  EFI_USB_IO_PROTOCOL           *UsbIo;\r
+  EFI_TPL                       OldTpl;\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  Transport = NULL;\r
+  Context   = NULL;\r
+  MaxLun    = 0;\r
+\r
+  Status = UsbMassInitTransport (This, Controller, &Transport, &Context, &MaxLun);\r
 \r
-  Status = gBS->InstallProtocolInterface (\r
-                  &Controller,\r
-                  &gEfiBlockIoProtocolGuid,\r
-                  EFI_NATIVE_INTERFACE,\r
-                  &UsbMass->BlockIo\r
-                  );\r
   if (EFI_ERROR (Status)) {\r
-    goto ON_ERROR;\r
+    DEBUG ((EFI_D_ERROR, "USBMassDriverBindingStart: UsbMassInitTransport (%r)\n", Status));\r
+    goto Exit;\r
   }\r
+  if (MaxLun == 0) {\r
+    //\r
+    // Initialize data for device that does not support multiple LUNSs.\r
+    //\r
+    Status = UsbMassInitNonLun (This, Controller, Transport, Context);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "USBMassDriverBindingStart: UsbMassInitNonLun (%r)\n", Status));\r
+    }\r
+  } else {\r
+    //\r
+    // Open device path to prepare for appending Device Logic Unit node.\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    Controller,\r
+                    &gEfiDevicePathProtocolGuid,\r
+                    (VOID **) &DevicePath,\r
+                    This->DriverBindingHandle,\r
+                    Controller,\r
+                    EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                    );\r
 \r
-  return EFI_SUCCESS;\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "USBMassDriverBindingStart: OpenDevicePathProtocol By Driver (%r)\n", Status));\r
+      goto Exit;\r
+    }\r
 \r
-ON_ERROR:\r
-  gBS->FreePool (UsbMass);\r
+    Status = gBS->OpenProtocol (\r
+                    Controller,\r
+                    &gEfiUsbIoProtocolGuid,\r
+                    (VOID **) &UsbIo,\r
+                    This->DriverBindingHandle,\r
+                    Controller,\r
+                    EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                    );\r
 \r
-  gBS->CloseProtocol (\r
-        Controller,\r
-        &gEfiUsbIoProtocolGuid,\r
-        This->DriverBindingHandle,\r
-        Controller\r
-        );\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "USBMassDriverBindingStart: OpenUsbIoProtocol By Driver (%r)\n", Status));\r
+      gBS->CloseProtocol (\r
+             Controller,\r
+             &gEfiDevicePathProtocolGuid,\r
+             This->DriverBindingHandle,\r
+             Controller\r
+             );\r
+      goto Exit;\r
+    }\r
 \r
+    //\r
+    // Initialize data for device that supports multiple LUNs.\r
+    // EFI_SUCCESS is returned if at least 1 LUN is initialized successfully.\r
+    //\r
+    Status = UsbMassInitMultiLun (This, Controller, Transport, Context, DevicePath, MaxLun);\r
+    if (EFI_ERROR (Status)) {\r
+      gBS->CloseProtocol (\r
+              Controller,\r
+              &gEfiDevicePathProtocolGuid,\r
+              This->DriverBindingHandle,\r
+              Controller\r
+              );\r
+      gBS->CloseProtocol (\r
+              Controller,\r
+              &gEfiUsbIoProtocolGuid,\r
+              This->DriverBindingHandle,\r
+              Controller\r
+              );\r
+      DEBUG ((EFI_D_ERROR, "USBMassDriverBindingStart: UsbMassInitMultiLun (%r) with Maxlun=%d\n", Status, MaxLun));\r
+    }\r
+  }\r
+Exit:\r
+  gBS->RestoreTPL (OldTpl);\r
   return Status;\r
 }\r
 \r
@@ -572,6 +895,8 @@ ON_ERROR:
   @param  ChildHandleBuffer      The buffer of children handle.\r
 \r
   @retval EFI_SUCCESS            The driver stopped from controlling the device.\r
+  @retval EFI_DEVICE_ERROR       The device could not be stopped due to a device error.\r
+  @retval EFI_UNSUPPORTED        Block I/O Protocol is not installed on Controller.\r
   @retval Others                 Failed to stop the driver\r
 \r
 **/\r
@@ -586,85 +911,183 @@ USBMassDriverBindingStop (
 {\r
   EFI_STATUS            Status;\r
   USB_MASS_DEVICE       *UsbMass;\r
+  EFI_USB_IO_PROTOCOL   *UsbIo;\r
   EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
+  UINTN                 Index;\r
+  BOOLEAN               AllChildrenStopped;\r
 \r
   //\r
-  // First, get our context back from the BLOCK_IO\r
+  // This is a bus driver stop function since multi-lun is supported.\r
+  // There are three kinds of device handles that might be passed:\r
+  // 1st is a handle with USB I/O & Block I/O installed (non-multi-lun)\r
+  // 2nd is a handle with Device Path & USB I/O installed (multi-lun root)\r
+  // 3rd is a handle with Device Path & USB I/O & Block I/O installed (multi-lun).\r
   //\r
-  Status = gBS->OpenProtocol (\r
-                  Controller,\r
-                  &gEfiBlockIoProtocolGuid,\r
-                  (VOID **) &BlockIo,\r
-                  This->DriverBindingHandle,\r
-                  Controller,\r
-                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
-                  );\r
+  if (NumberOfChildren == 0) {\r
+    //\r
+    // A handle without any children, might be 1st and 2nd type.\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    Controller,\r
+                    &gEfiBlockIoProtocolGuid,\r
+                    (VOID **) &BlockIo,\r
+                    This->DriverBindingHandle,\r
+                    Controller,\r
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                    );\r
+\r
+    if (EFI_ERROR(Status)) {\r
+      //\r
+      // This is a 2nd type handle(multi-lun root), it needs to close devicepath\r
+      // and usbio protocol.\r
+      //\r
+      gBS->CloseProtocol (\r
+            Controller,\r
+            &gEfiDevicePathProtocolGuid,\r
+            This->DriverBindingHandle,\r
+            Controller\r
+            );\r
+      gBS->CloseProtocol (\r
+            Controller,\r
+            &gEfiUsbIoProtocolGuid,\r
+            This->DriverBindingHandle,\r
+            Controller\r
+            );\r
+      DEBUG ((EFI_D_INFO, "Success to stop multi-lun root handle\n"));\r
+      return EFI_SUCCESS;\r
+    }\r
 \r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
-  }\r
+    //\r
+    // This is a 1st type handle(non-multi-lun), which only needs to uninstall\r
+    // Block I/O Protocol, close USB I/O Protocol and free mass device.\r
+    //\r
+    UsbMass = USB_MASS_DEVICE_FROM_BLOCK_IO (BlockIo);\r
+\r
+    //\r
+    // Uninstall Block I/O protocol from the device handle,\r
+    // then call the transport protocol to stop itself.\r
+    //\r
+    Status = gBS->UninstallMultipleProtocolInterfaces (\r
+                    Controller,\r
+                    &gEfiBlockIoProtocolGuid,\r
+                    &UsbMass->BlockIo,\r
+                    &gEfiDiskInfoProtocolGuid,\r
+                    &UsbMass->DiskInfo,\r
+                    NULL\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
 \r
-  UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (BlockIo);\r
+    gBS->CloseProtocol (\r
+          Controller,\r
+          &gEfiUsbIoProtocolGuid,\r
+          This->DriverBindingHandle,\r
+          Controller\r
+          );\r
+\r
+    UsbMass->Transport->CleanUp (UsbMass->Context);\r
+    FreePool (UsbMass);\r
+\r
+    DEBUG ((EFI_D_INFO, "Success to stop non-multi-lun root handle\n"));\r
+    return EFI_SUCCESS;\r
+  }\r
 \r
   //\r
-  // Uninstall Block I/O protocol from the device handle,\r
-  // then call the transport protocol to stop itself.\r
+  // This is a 3rd type handle(multi-lun), which needs uninstall\r
+  // Block I/O Protocol and Device Path Protocol, close USB I/O Protocol and\r
+  // free mass device for all children.\r
   //\r
-  Status = gBS->UninstallProtocolInterface (\r
-                  Controller,\r
-                  &gEfiBlockIoProtocolGuid,\r
-                  &UsbMass->BlockIo\r
-                  );\r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
-  }\r
+  AllChildrenStopped = TRUE;\r
+\r
+  for (Index = 0; Index < NumberOfChildren; Index++) {\r
+\r
+    Status = gBS->OpenProtocol (\r
+                    ChildHandleBuffer[Index],\r
+                    &gEfiBlockIoProtocolGuid,\r
+                    (VOID **) &BlockIo,\r
+                    This->DriverBindingHandle,\r
+                    Controller,\r
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      AllChildrenStopped = FALSE;\r
+      DEBUG ((EFI_D_ERROR, "Fail to stop No.%d multi-lun child handle when opening blockio\n", (UINT32)Index));\r
+      continue;\r
+    }\r
 \r
-  gBS->CloseProtocol (\r
-        Controller,\r
-        &gEfiUsbIoProtocolGuid,\r
-        This->DriverBindingHandle,\r
-        Controller\r
-        );\r
+    UsbMass = USB_MASS_DEVICE_FROM_BLOCK_IO (BlockIo);\r
+\r
+    gBS->CloseProtocol (\r
+           Controller,\r
+           &gEfiUsbIoProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           ChildHandleBuffer[Index]\r
+           );\r
+\r
+    Status = gBS->UninstallMultipleProtocolInterfaces (\r
+                    ChildHandleBuffer[Index],\r
+                    &gEfiDevicePathProtocolGuid,\r
+                    UsbMass->DevicePath,\r
+                    &gEfiBlockIoProtocolGuid,\r
+                    &UsbMass->BlockIo,\r
+                    &gEfiDiskInfoProtocolGuid,\r
+                    &UsbMass->DiskInfo,\r
+                    NULL\r
+                    );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // Fail to uninstall Block I/O Protocol and Device Path Protocol, so re-open USB I/O Protocol by child.\r
+      //\r
+      AllChildrenStopped = FALSE;\r
+      DEBUG ((EFI_D_ERROR, "Fail to stop No.%d multi-lun child handle when uninstalling blockio and devicepath\n", (UINT32)Index));\r
+\r
+      gBS->OpenProtocol (\r
+             Controller,\r
+             &gEfiUsbIoProtocolGuid,\r
+             (VOID **) &UsbIo,\r
+             This->DriverBindingHandle,\r
+             ChildHandleBuffer[Index],\r
+             EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+             );\r
+    } else {\r
+      //\r
+      // Succeed to stop this multi-lun handle, so go on with next child.\r
+      //\r
+      if (((Index + 1) == NumberOfChildren) && AllChildrenStopped) {\r
+        UsbMass->Transport->CleanUp (UsbMass->Context);\r
+      }\r
+      FreePool (UsbMass);\r
+    }\r
+  }\r
 \r
-  UsbMass->Transport->Fini (UsbMass->Context);\r
-  gBS->FreePool (UsbMass);\r
+  if (!AllChildrenStopped) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
 \r
+  DEBUG ((EFI_D_INFO, "Success to stop all %d multi-lun children handles\n", (UINT32) NumberOfChildren));\r
   return EFI_SUCCESS;\r
 }\r
 \r
-EFI_DRIVER_BINDING_PROTOCOL gUSBMassDriverBinding = {\r
-  USBMassDriverBindingSupported,\r
-  USBMassDriverBindingStart,\r
-  USBMassDriverBindingStop,\r
-  0x11,\r
-  NULL,\r
-  NULL\r
-};\r
+/**\r
+  Entrypoint of USB Mass Storage Driver.\r
+\r
+  This function is the entrypoint of USB Mass Storage Driver. It installs Driver Binding\r
+  Protocol together with Component Name Protocols.\r
 \r
+  @param  ImageHandle       The firmware allocated handle for the EFI image.\r
+  @param  SystemTable       A pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS       The entry point is executed successfully.\r
+\r
+**/\r
 EFI_STATUS\r
 EFIAPI\r
 USBMassStorageEntryPoint (\r
   IN EFI_HANDLE               ImageHandle,\r
   IN EFI_SYSTEM_TABLE         *SystemTable\r
   )\r
-/*++\r
-\r
-Routine Description:\r
-\r
-  The entry point for the driver, which will install the driver binding and\r
-  component name protocol\r
-\r
-Arguments:\r
-\r
-  ImageHandle - The image handle of this driver\r
-  SystemTable - The system table\r
-\r
-Returns:\r
-\r
-  EFI_SUCCESS - the protocols are installed OK\r
-  Others      - Failed to install protocols.\r
-\r
---*/\r
 {\r
   EFI_STATUS  Status;\r
 \r
@@ -679,6 +1102,7 @@ Returns:
              &gUsbMassStorageComponentName,\r
              &gUsbMassStorageComponentName2\r
              );\r
+  ASSERT_EFI_ERROR (Status);\r
 \r
-  return Status;\r
+  return EFI_SUCCESS;\r
 }\r