/** @file | |
USB Mass Storage Driver that manages USB Mass Storage Device and produces Block I/O Protocol. | |
Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "UsbMass.h" | |
#define USB_MASS_TRANSPORT_COUNT 3 | |
// | |
// Array of USB transport interfaces. | |
// | |
USB_MASS_TRANSPORT *mUsbMassTransport[USB_MASS_TRANSPORT_COUNT] = { | |
&mUsbCbi0Transport, | |
&mUsbCbi1Transport, | |
&mUsbBotTransport, | |
}; | |
EFI_DRIVER_BINDING_PROTOCOL gUSBMassDriverBinding = { | |
USBMassDriverBindingSupported, | |
USBMassDriverBindingStart, | |
USBMassDriverBindingStop, | |
0x11, | |
NULL, | |
NULL | |
}; | |
/** | |
Reset the block device. | |
This function implements EFI_BLOCK_IO_PROTOCOL.Reset(). | |
It resets the block device hardware. | |
ExtendedVerification is ignored in this implementation. | |
@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 | |
EFIAPI | |
UsbMassReset ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN BOOLEAN ExtendedVerification | |
) | |
{ | |
USB_MASS_DEVICE *UsbMass; | |
EFI_TPL OldTpl; | |
EFI_STATUS Status; | |
// | |
// 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); | |
Status = UsbMass->Transport->Reset (UsbMass->Context, ExtendedVerification); | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
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, | |
IN EFI_LBA Lba, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
USB_MASS_DEVICE *UsbMass; | |
EFI_BLOCK_IO_MEDIA *Media; | |
EFI_STATUS Status; | |
EFI_TPL OldTpl; | |
UINTN TotalBlock; | |
// | |
// 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; | |
// | |
// 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 (Media->RemovableMedia) { | |
Status = UsbBootDetectMedia (UsbMass); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
} | |
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; | |
} | |
// | |
// BufferSize must be a multiple of the intrinsic block size of the device. | |
// | |
if ((BufferSize % Media->BlockSize) != 0) { | |
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) { | |
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); | |
} | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "UsbMassReadBlocks: UsbBootReadBlocks (%r) -> Reset\n", Status)); | |
UsbMassReset (This, TRUE); | |
} | |
ON_EXIT: | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
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, | |
IN EFI_LBA Lba, | |
IN UINTN BufferSize, | |
IN VOID *Buffer | |
) | |
{ | |
USB_MASS_DEVICE *UsbMass; | |
EFI_BLOCK_IO_MEDIA *Media; | |
EFI_STATUS Status; | |
EFI_TPL OldTpl; | |
UINTN TotalBlock; | |
// | |
// 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; | |
// | |
// 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 (Media->RemovableMedia) { | |
Status = UsbBootDetectMedia (UsbMass); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
} | |
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; | |
} | |
// | |
// BufferSize must be a multiple of the intrinsic block size of the device. | |
// | |
if ((BufferSize % Media->BlockSize) != 0) { | |
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) { | |
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. | |
// | |
if (UsbMass->Cdb16Byte) { | |
Status = UsbBootReadWriteBlocks16 (UsbMass, TRUE, Lba, TotalBlock, Buffer); | |
} else { | |
Status = UsbBootReadWriteBlocks (UsbMass, TRUE, (UINT32)Lba, TotalBlock, Buffer); | |
} | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "UsbMassWriteBlocks: UsbBootWriteBlocks (%r) -> Reset\n", Status)); | |
UsbMassReset (This, TRUE); | |
} | |
ON_EXIT: | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Flushes all modified data to a physical block device. | |
This function implements EFI_BLOCK_IO_PROTOCOL.FlushBlocks(). | |
USB mass storage device doesn't support write cache, | |
so return EFI_SUCCESS directly. | |
@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 | |
) | |
{ | |
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; | |
} | |
/** | |
Initialize the USB Mass Storage transport. | |
This function tries to find the matching USB Mass Storage transport | |
protocol for USB device. If found, initializes the matching transport. | |
@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 | |
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; | |
UINT8 Index; | |
EFI_STATUS Status; | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiUsbIoProtocolGuid, | |
(VOID **)&UsbIo, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
Status = EFI_UNSUPPORTED; | |
// | |
// 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]; | |
if (Interface.InterfaceProtocol == (*Transport)->Protocol) { | |
Status = (*Transport)->Init (UsbIo, Context); | |
break; | |
} | |
} | |
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 | |
); | |
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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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; | |
} | |
/** | |
Check whether the controller is a supported USB mass storage. | |
@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 supports this controller. | |
@retval other This device isn't supported. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
USBMassDriverBindingSupported ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
EFI_USB_IO_PROTOCOL *UsbIo; | |
EFI_USB_INTERFACE_DESCRIPTOR Interface; | |
USB_MASS_TRANSPORT *Transport; | |
EFI_STATUS Status; | |
UINTN Index; | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiUsbIoProtocolGuid, | |
(VOID **)&UsbIo, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Get the interface descriptor to check the USB class and find a transport | |
// protocol handler. | |
// | |
Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
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 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) { | |
Status = Transport->Init (UsbIo, NULL); | |
break; | |
} | |
} | |
ON_EXIT: | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiUsbIoProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
return Status; | |
} | |
/** | |
Starts the USB mass storage device with this driver. | |
This function consumes USB I/O Protocol, initializes 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); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_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 ((DEBUG_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 | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "USBMassDriverBindingStart: OpenDevicePathProtocol By Driver (%r)\n", Status)); | |
goto Exit; | |
} | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiUsbIoProtocolGuid, | |
(VOID **)&UsbIo, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_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 ((DEBUG_ERROR, "USBMassDriverBindingStart: UsbMassInitMultiLun (%r) with Maxlun=%d\n", Status, MaxLun)); | |
} | |
} | |
Exit: | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Stop controlling the device. | |
@param This The USB mass storage driver binding | |
@param Controller The device controller controlled by the driver. | |
@param NumberOfChildren The number of children of this device | |
@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 | |
**/ | |
EFI_STATUS | |
EFIAPI | |
USBMassDriverBindingStop ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN UINTN NumberOfChildren, | |
IN EFI_HANDLE *ChildHandleBuffer | |
) | |
{ | |
EFI_STATUS Status; | |
USB_MASS_DEVICE *UsbMass; | |
EFI_USB_IO_PROTOCOL *UsbIo; | |
EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
UINTN Index; | |
BOOLEAN AllChildrenStopped; | |
// | |
// 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). | |
// | |
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 ((DEBUG_INFO, "Success to stop multi-lun root handle\n")); | |
return EFI_SUCCESS; | |
} | |
// | |
// 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->Transport->CleanUp (UsbMass->Context); | |
FreePool (UsbMass); | |
DEBUG ((DEBUG_INFO, "Success to stop non-multi-lun root handle\n")); | |
return EFI_SUCCESS; | |
} | |
// | |
// 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. | |
// | |
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 ((DEBUG_ERROR, "Fail to stop No.%d multi-lun child handle when opening blockio\n", (UINT32)Index)); | |
continue; | |
} | |
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 ((DEBUG_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); | |
} | |
} | |
if (!AllChildrenStopped) { | |
return EFI_DEVICE_ERROR; | |
} | |
DEBUG ((DEBUG_INFO, "Success to stop all %d multi-lun children handles\n", (UINT32)NumberOfChildren)); | |
return EFI_SUCCESS; | |
} | |
/** | |
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. | |
@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 | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Install driver binding protocol | |
// | |
Status = EfiLibInstallDriverBindingComponentName2 ( | |
ImageHandle, | |
SystemTable, | |
&gUSBMassDriverBinding, | |
ImageHandle, | |
&gUsbMassStorageComponentName, | |
&gUsbMassStorageComponentName2 | |
); | |
ASSERT_EFI_ERROR (Status); | |
return EFI_SUCCESS; | |
} |