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