alder_lake_bios/Insyde/InsydeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.c

1407 lines
43 KiB
C

/** @file
The implementation of USB mass storage class device driver.
The command set supported is "USB Mass Storage Specification
for Bootability".
;******************************************************************************
;* Copyright (c) 2012 - 2021, Insyde Software Corp. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#include "UsbMassImpl.h"
#include <PostCode.h>
UINTN mUsbMscInfo = EFI_D_INFO;
UINTN mUsbMscError = EFI_D_ERROR;
STATIC EFI_GUID mUsbMassTransportProtocolGuid = USB_MASS_TRANSPORT_PROTOCOL_GUID;
/**
Usb Get Maximum LUNs for multiple LUN device
@param UsbIo EFI_USB_IO_PROTOCOL
@param NumOfLun Pointer to MaximumLuns for return
@retval EFI_INVALID_PARAMETER Parameter is error
@retval EFI_SUCCESS Success
@retval EFI_UNSUPPORTED Device has no multiple LUN supported
**/
EFI_STATUS
UsbMassGetMaximumLuns (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN UINT8 InterfaceNumber,
OUT UINT8 *MaximumLuns
)
{
EFI_USB_DEVICE_REQUEST DevReq;
UINT32 RetStatus;
if (UsbIo == NULL) {
return EFI_INVALID_PARAMETER;
}
ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
DevReq.RequestType = USB_DEV_SET_MAX_LUN_REQ_TYPE;
DevReq.Request = USB_DEV_GET_MAX_LUN;
DevReq.Index = InterfaceNumber;
DevReq.Length = 1;
return UsbIo->UsbControlTransfer (
UsbIo,
&DevReq,
EfiUsbDataIn,
1000,
MaximumLuns,
1,
&RetStatus
);
}
/**
Retrieve the media parameters such as disk gemotric for the
device's BLOCK IO protocol.
@param UsbMass The USB mass storage device
@retval EFI_SUCCESS The media parameters is 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;
UINTN Index;
UINTN Retry;
BOOLEAN InquirySuccessed;
BOOLEAN MediaPresent;
Status = EFI_SUCCESS;
InquirySuccessed = FALSE;
MediaPresent = TRUE;
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;
//
// Use the loop to make the device get ready(if available)
//
for (Index = 0; Index < USB_BOOT_INIT_MEDIA_RETRY; Index++) {
//
// Make Inquiry as first UFI command after bunch of control transfer commands
//
if (!InquirySuccessed) {
Status = UsbBootInquiry (UsbMass);
if (!EFI_ERROR (Status)) {
InquirySuccessed = TRUE;
} else {
DEBUG ((mUsbMscError, "UsbBootGetParams: UsbBootInquiry (%r)\n", Status));
if (Status == EFI_NOT_FOUND || Status == EFI_NO_RESPONSE) {
//
// Device been detached or no response
//
break;
}
}
}
//
// Use Test Unit to detect the device ready
//
Status = UsbBootIsUnitReady (UsbMass, USB_BOOT_TEST_UNIT_TIMEOUT);
if (EFI_ERROR (Status)) {
if (Status == EFI_NOT_FOUND || Status == EFI_NO_RESPONSE) {
//
// Device been detached or no response
//
break;
} else if (Status == EFI_NO_MEDIA) {
//
// Clear MediaPresent to ignore unnecessary ReadCapacity
//
MediaPresent = FALSE;
} else {
gBS->Stall (USB_BOOT_UNIT_READY_STALL * (Index + 1));
continue;
}
}
//
// Don't use the Removable bit in inquirydata to test whether the media
// is removable because many flash disks wrongly set this bit.
//
if ((UsbMass->Pdt == USB_PDT_CDROM) || (UsbMass->Pdt == USB_PDT_OPTICAL)) {
//
// CD-Rom or Optical device
//
UsbMass->OpticalStorage = 1;
//
// Default value 2048 Bytes, in case no media present at first time
//
Media->BlockSize = 0x0800;
} else {
//
// Specific defect device workaround
//
if (CompareMem(UsbMass->InquiryData.ProductID, "USB CYBER DISK", 14) == 0) {
//
// Waiting for media ready within 2 seconds
//
for (Retry = 0; Retry < 20; Retry ++) {
Status = UsbBootIsUnitReady (UsbMass, USB_BOOT_GENERAL_CMD_TIMEOUT);
if (Status != EFI_NO_MEDIA) break;
gBS->Stall (100 * USB_MASS_STALL_1_MS);
}
//
// Doesn't issue ModeSense command to this device due to unstable
//
UsbMass->ScsiInterface = 0;
}
if (Media->RemovableMedia &&
CompareMem(UsbMass->InquiryData.VendorID, "IOMEGA", 6) == 0 &&
CompareMem(UsbMass->InquiryData.ProductID, "ZIP", 3) == 0) {
//
// Use START_UNIT command if it is IOMEGA ZIP
//
Status = UsbBootStartUnit (UsbMass);
if (EFI_ERROR (Status)) {
break;
}
}
}
if (MediaPresent) {
if (!UsbMass->OpticalStorage) {
//
// Update ReadOnly bit for non CD-Rom device SCSI devices
//
UsbBootModeSense (UsbMass);
}
//
// Use ReadCapacity to setup media related parameters
//
Status = UsbBootReadCapacity (UsbMass);
}
if (Status != EFI_MEDIA_CHANGED &&
Status != EFI_NOT_READY &&
Status != EFI_TIMEOUT &&
InquirySuccessed) {
break;
}
}
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
EFIAPI
UsbMassReset (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
USB_MASS_DEVICE *UsbMass;
EFI_STATUS Status;
//
// RaiseTPL to preventing the background thread releases the UsbMass through hot-plug
//
EFI_TPL OldTPL = gBS->RaiseTPL (TPL_CALLBACK);
UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This);
Status = UsbMass->Transport->Reset (UsbMass->Context, ExtendedVerification);
//
// RestoreTPL back to original TPL
//
gBS->RestoreTPL(OldTPL);
return Status;
}
/**
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.
**/
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;
UINTN TotalBlock;
//
// RaiseTPL to preventing the background thread releases the UsbMass through hot-plug
//
EFI_TPL OldTPL = gBS->RaiseTPL (TPL_CALLBACK);
UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (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) && Media->MediaPresent) {
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;
}
//
// Make sure BlockSize and LBA is consistent with BufferSize
//
if ((BufferSize % Media->BlockSize) != 0) {
Status = EFI_BAD_BUFFER_SIZE;
goto ON_EXIT;
}
TotalBlock = BufferSize / Media->BlockSize;
if (Lba + TotalBlock - 1 > Media->LastBlock) {
Status = EFI_INVALID_PARAMETER;
goto ON_EXIT;
}
Status = UsbBootReadBlocks (UsbMass, Lba, TotalBlock, Buffer);
if (EFI_ERROR (Status) && Status != EFI_NOT_FOUND) {
DEBUG ((mUsbMscError, "UsbMassReadBlocks: UsbBootReadBlocks (%r) -> Reset\n", Status));
UsbMassReset (This, TRUE);
}
ON_EXIT:
if (Status == EFI_NOT_FOUND) {
//
// As the EFI_NOT_FOUND indicates the device in either unplugged or power failure,
// Change the Status to EFI_NO_MEDIA to make the upper layer code stop the operation
//
Status = EFI_NO_MEDIA;
Media->MediaPresent = FALSE;
}
//
// RestoreTPL back to original TPL
//
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,
**/
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;
UINTN TotalBlock;
//
// RaiseTPL to preventing the background thread releases the UsbMass through hot-plug
//
EFI_TPL OldTPL = gBS->RaiseTPL (TPL_CALLBACK);
UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (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) && Media->MediaPresent) {
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;
}
//
// Make sure BlockSize and LBA is consistent with BufferSize
//
if ((BufferSize % Media->BlockSize) != 0) {
Status = EFI_BAD_BUFFER_SIZE;
goto ON_EXIT;
}
TotalBlock = BufferSize / Media->BlockSize;
if (Lba + TotalBlock - 1 > Media->LastBlock) {
Status = EFI_BAD_BUFFER_SIZE;
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, Lba, TotalBlock, Buffer);
if (EFI_ERROR (Status) && Status != EFI_NOT_FOUND) {
DEBUG ((mUsbMscError, "UsbMassWriteBlocks: UsbBootWriteBlocks (%r) -> Reset\n", Status));
UsbMassReset (This, TRUE);
}
ON_EXIT:
if (Status == EFI_NOT_FOUND) {
//
// As the EFI_NOT_FOUND indicates the device in either unplugged or power failure,
// Change the Status to EFI_NO_MEDIA to make the upper layer code stop the operation
//
Status = EFI_NO_MEDIA;
Media->MediaPresent = FALSE;
}
//
// RestoreTPL back to original TPL
//
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.
@param This The BLOCK IO protocol
@retval EFI_SUCCESS Always returns success
**/
EFI_STATUS
EFIAPI
UsbMassFlushBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This
)
{
return EFI_SUCCESS;
}
/**
Provides inquiry information for the controller type.
This function is used to get inquiry data. Data format
of Identify data is defined by the Interface GUID.
@param This Pointer to the EFI_DISK_INFO_PROTOCOL instance.
@param InquiryData Pointer to a buffer for the inquiry data.
@param InquiryDataSize Pointer to the value for the inquiry data size.
@retval EFI_SUCCESS The command was accepted without any errors.
@retval EFI_NOT_FOUND Device does not support this data class
@retval EFI_DEVICE_ERROR Error reading InquiryData from device
@retval EFI_BUFFER_TOO_SMALL InquiryDataSize not big enough
**/
EFI_STATUS
EFIAPI
UsbMassInquiry (
IN EFI_DISK_INFO_PROTOCOL *This,
IN OUT VOID *InquiryData,
IN OUT UINT32 *InquiryDataSize
)
{
EFI_STATUS Status;
USB_MASS_DEVICE *UsbMass;
UsbMass = USB_MASS_DEVICE_FROM_DISK_INFO (This);
Status = EFI_BUFFER_TOO_SMALL;
if (*InquiryDataSize >= sizeof (USB_BOOT_INQUIRY_DATA)) {
Status = EFI_SUCCESS;
CopyMem (InquiryData, &UsbMass->InquiryData, sizeof (USB_BOOT_INQUIRY_DATA));
}
*InquiryDataSize = sizeof (USB_BOOT_INQUIRY_DATA);
return Status;
}
/**
Provides identify information for the controller type.
This function is used to get identify data. Data format
of Identify data is defined by the Interface GUID.
@param This Pointer to the EFI_DISK_INFO_PROTOCOL instance.
@param IdentifyData Pointer to a buffer for the identify data.
@param IdentifyDataSize Pointer to the value for the identify data size.
@retval EFI_SUCCESS The command was accepted without any errors.
@retval EFI_NOT_FOUND Device does not support this data class
@retval EFI_DEVICE_ERROR Error reading IdentifyData from device
@retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough
**/
EFI_STATUS
EFIAPI
UsbMassIdentify (
IN EFI_DISK_INFO_PROTOCOL *This,
IN OUT VOID *IdentifyData,
IN OUT UINT32 *IdentifyDataSize
)
{
return EFI_NOT_FOUND;
}
/**
Provides sense data information for the controller type.
This function is used to get sense data.
Data format of Sense data is defined by the Interface GUID.
@param This Pointer to the EFI_DISK_INFO_PROTOCOL instance.
@param SenseData Pointer to the SenseData.
@param SenseDataSize Size of SenseData in bytes.
@param SenseDataNumber Pointer to the value for the sense data size.
@retval EFI_SUCCESS The command was accepted without any errors.
@retval EFI_NOT_FOUND Device does not support this data class.
@retval EFI_DEVICE_ERROR Error reading SenseData from device.
@retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough.
**/
EFI_STATUS
EFIAPI
UsbMassSenseData (
IN EFI_DISK_INFO_PROTOCOL *This,
IN OUT VOID *SenseData,
IN OUT UINT32 *SenseDataSize,
OUT UINT8 *SenseDataNumber
)
{
return EFI_NOT_FOUND;
}
/**
This function is used to get controller information.
@param This Pointer to the EFI_DISK_INFO_PROTOCOL instance.
@param IdeChannel Pointer to the Ide Channel number. Primary or secondary.
@param IdeDevice Pointer to the Ide Device number. Master or slave.
@retval EFI_SUCCESS IdeChannel and IdeDevice are valid.
@retval EFI_UNSUPPORTED This is not an IDE device.
**/
EFI_STATUS
EFIAPI
UsbMassWhichIde (
IN EFI_DISK_INFO_PROTOCOL *This,
OUT UINT32 *IdeChannel,
OUT UINT32 *IdeDevice
)
{
return EFI_UNSUPPORTED;
}
/**
Check whether the controller is a supported USB mass storage.
@param This The USB mass driver's driver binding.
@param Controller The device to test against.
@param RemainingDevicePath The remaining device path
@retval EFI_SUCCESS This device is a supported USB mass storage.
@retval EFI_UNSUPPORTED The device isn't supported
@retval Others Some error happened.
**/
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;
EFI_STATUS Status;
USB_MASS_TRANSPORT_PROTOCOL *Transport;
UINTN Index;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
EFI_DEV_PATH_PTR Node;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_USB_CORE_PROTOCOL *UsbCore;
EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor;
//
// Check Device Path
//
if (RemainingDevicePath != NULL) {
Node.DevPath = RemainingDevicePath;
if ((Node.DevPath->Type != MESSAGING_DEVICE_PATH ||
Node.DevPath->SubType != MSG_DEVICE_LOGICAL_UNIT_DP ||
DevicePathNodeLength(Node.DevPath) != sizeof(DEVICE_LOGICAL_UNIT_DEVICE_PATH)) &&
(Node.DevPath->Type != MEDIA_DEVICE_PATH ||
Node.DevPath->SubType != MEDIA_HARDDRIVE_DP ||
DevicePathNodeLength(Node.DevPath) != sizeof(HARDDRIVE_DEVICE_PATH))) {
return EFI_UNSUPPORTED;
}
}
//
// Check whether the controlelr support USB_IO
//
Status = gBS->OpenProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
(VOID **)&UsbIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Get the device path for CheckIgnoredDevice
//
Status = gBS->HandleProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **)&DevicePath
);
if (EFI_ERROR (Status)) {
goto ON_EXIT;
}
//
// Setup the data for UsbLegacy
//
Status = gBS->LocateProtocol (
&gEfiUsbCoreProtocolGuid,
NULL,
(VOID **)&UsbCore
);
if (EFI_ERROR (Status)) {
goto ON_EXIT;
}
if (UsbCore && UsbCore->CheckDeviceDetached(UsbIo) == EFI_SUCCESS) {
//
// Device detached
//
Status = EFI_UNSUPPORTED;
goto ON_EXIT;
}
#if (KEEP_USBIO_FOR_IGNORED_DEVICE == 1)
//
// Filter out the USB devices which in the UsbIgnoreDevice list
//
Status = UsbCore->CheckIgnoredDevice(DevicePath, UsbIo);
if (EFI_ERROR (Status)) {
goto ON_EXIT;
}
#endif
//
// 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;
}
Status = EFI_UNSUPPORTED;
if (Interface.InterfaceClass != USB_MASS_STORE_CLASS &&
Interface.InterfaceClass != USB_VENDOR_SPECIFIC_CLASS) {
goto ON_EXIT;
}
//
// Get VID and PID
//
Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DeviceDescriptor);
if (EFI_ERROR (Status)) {
goto ON_EXIT;
}
//
// Checks vendor-specific devices
//
if (Interface.InterfaceClass == USB_VENDOR_SPECIFIC_CLASS) {
Status = UsbVendorSpecificDevice(
UsbIo,
DeviceDescriptor.IdVendor,
DeviceDescriptor.IdProduct,
&Interface.InterfaceProtocol
);
if (EFI_ERROR (Status)) {
goto ON_EXIT;
}
}
//
// Locates UsbMassTransport Protocol
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&mUsbMassTransportProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR(Status)) {
goto ON_EXIT;
}
Status = EFI_UNSUPPORTED;
for (Index = 0; Index < HandleCount; Index++) {
if (EFI_ERROR (gBS->HandleProtocol (
HandleBuffer[Index],
&mUsbMassTransportProtocolGuid,
(VOID **)&Transport
))) {
continue;
}
if (Interface.InterfaceProtocol == Transport->Protocol) {
Status = EFI_SUCCESS;
break;
}
}
gBS->FreePool (HandleBuffer);
ON_EXIT:
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.
@param This The USB mass storage driver binding.
@param Controller The USB mass storage device to start on
@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.
**/
EFI_STATUS
EFIAPI
UsbMassDriverBindingStart (
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_DEVICE *UsbMass;
USB_MASS_TRANSPORT_PROTOCOL *Transport;
VOID *Context;
EFI_STATUS Status;
UINTN Index;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
EFI_USB_CORE_PROTOCOL *UsbCore;
USB_DEVICE UsbDevice;
EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
UINT8 MaximumLuns;
UINT8 NewDevicePath[sizeof(DEVICE_LOGICAL_UNIT_DEVICE_PATH) + END_DEVICE_PATH_LENGTH];
DEVICE_LOGICAL_UNIT_DEVICE_PATH *LunDevicePath;
EFI_TPL OldTPL;
EFI_USB_LEGACY_PLATFORM_PROTOCOL *LegacyBiosPlatform;
USB_LEGACY_MODIFIERS UsbLegacyPolicy;
EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath;
EFI_HANDLE Handle;
EFI_PCI_IO_PROTOCOL *PciIo;
UINT16 HcLocation;
UINTN Seg;
UINTN Bus;
UINTN Dev;
UINTN Fun;
UINTN Size;
POST_CODE (BDS_CONNECT_USB_DEVICE);
Status = gBS->OpenProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
(VOID **)&UsbIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
UsbMass = NULL;
OldTPL = 0;
//
// Locate the UsbCore for UsbLegacy's InsertUsbDevice
//
UsbCore = NULL;
gBS->LocateProtocol (
&gEfiUsbCoreProtocolGuid,
NULL,
(VOID **)&UsbCore
);
if (EFI_ERROR (Status) || UsbCore == NULL) {
goto ON_ERROR;
}
if (UsbCore && UsbCore->CheckDeviceDetached(UsbIo) == EFI_SUCCESS) {
//
// Device detached
//
goto ON_ERROR;
}
//
// Initialize the transport protocols
//
Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);
if (EFI_ERROR (Status)) {
DEBUG ((mUsbMscError, "USBMassDriverBindingStart: UsbIo->UsbGetInterfaceDescriptor (%r)\n", Status));
goto ON_ERROR;
}
//
// Get VID and PID
//
Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DeviceDescriptor);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
//
// Checks vendor-specific devices
//
if (Interface.InterfaceClass == USB_VENDOR_SPECIFIC_CLASS) {
Status = UsbVendorSpecificDevice(
UsbIo,
DeviceDescriptor.IdVendor,
DeviceDescriptor.IdProduct,
&Interface.InterfaceProtocol
);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
}
//
// Locates UsbMassTransport Protocol
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&mUsbMassTransportProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR(Status)) {
goto ON_ERROR;
}
Status = EFI_UNSUPPORTED;
Transport = NULL;
for (Index = 0; Index < HandleCount; Index++) {
if (EFI_ERROR (gBS->HandleProtocol (
HandleBuffer[Index],
&mUsbMassTransportProtocolGuid,
(VOID **)&Transport
))) {
continue;
}
if (Interface.InterfaceProtocol == Transport->Protocol) {
Status = EFI_SUCCESS;
break;
}
}
gBS->FreePool (HandleBuffer);
if (EFI_ERROR (Status) || Transport == NULL) {
DEBUG ((mUsbMscError, "USBMassDriverBindingStart: Transport->Init (%r)\n", Status));
goto ON_ERROR;
}
//
// Get the device path for new handle
//
DevicePath = NULL;
Status = gBS->HandleProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **)&DevicePath
);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
//
// Get UsbPlatformPolicy to get the Multi-LUN support policy
// Clear UsbLegacyPolicy first (GetUsbPlatformOptions will only capable to "set", can't "clear")
//
ZeroMem(&UsbLegacyPolicy, sizeof(USB_LEGACY_MODIFIERS));
Status = gBS->LocateProtocol (
&gEfiUsbLegacyPlatformProtocolGuid,
NULL,
(VOID **)&LegacyBiosPlatform
);
if (Status == EFI_SUCCESS) {
//
// Get the Usb Legacy Policy
//
LegacyBiosPlatform->GetUsbPlatformOptions(
LegacyBiosPlatform,
&UsbLegacyPolicy
);
}
//
// Get the HC location for legacy BBS
//
HcLocation = 0;
TmpDevicePath = DevicePath;
Status = gBS->LocateDevicePath (
&gEfiUsb3HcProtocolGuid,
&TmpDevicePath,
&Handle
);
if (Status == EFI_SUCCESS) {
Status = gBS->HandleProtocol (
Handle,
&gEfiPciIoProtocolGuid,
(VOID **)&PciIo
);
if (Status == EFI_SUCCESS) {
PciIo->GetLocation (
PciIo,
&Seg,
&Bus,
&Dev,
&Fun
);
HcLocation = (UINT16)((Bus << 8) | (Dev << 3) | Fun);
}
}
//
// Stall 0.2(BOT)/1.2(CBI) second before TestUnit to waiting for spin-up
//
gBS->Stall (((Transport->Protocol == USB_MASS_STORE_BOT) ? 200 : 1200) * USB_MASS_STALL_1_MS);
//
// RaiseTPL to preventing the background thread releases the UsbMass through hot-plug
//
OldTPL = gBS->RaiseTPL (TPL_CALLBACK);
//
// Get maximum LUNs number for Multi-LUN device
//
MaximumLuns = 0;
if (Transport->Protocol == USB_MASS_STORE_BOT && !UsbLegacyPolicy.UsbSupportSingleLunOnly) {
//
// Only BOT devices support LUN
//
UsbMassGetMaximumLuns (UsbIo, Interface.InterfaceNumber, &MaximumLuns);
}
for (Index = 0; Index <= MaximumLuns; Index ++) {
//
// Initial Transport instance
//
Context = NULL;
Status = Transport->Init (UsbIo, Controller, &Context);
//
// Allocate the instance for each BlockIo
//
Status = UsbCore->AllocateBuffer (
sizeof (USB_MASS_DEVICE),
ALIGNMENT_32,
(VOID **)&UsbMass
);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
UsbMass->Signature = USB_MASS_SIGNATURE;
UsbMass->Controller = Controller;
UsbMass->UsbIo = UsbIo;
UsbMass->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION;
UsbMass->BlockIo.Media = &UsbMass->BlockIoMedia;
UsbMass->BlockIo.Reset = UsbMassReset;
UsbMass->BlockIo.ReadBlocks = UsbMassReadBlocks;
UsbMass->BlockIo.WriteBlocks = UsbMassWriteBlocks;
UsbMass->BlockIo.FlushBlocks = UsbMassFlushBlocks;
CopyMem (&UsbMass->DiskInfo.Interface, &gEfiDiskInfoUsbInterfaceGuid, sizeof (EFI_GUID));
UsbMass->DiskInfo.Inquiry = UsbMassInquiry;
UsbMass->DiskInfo.Identify = UsbMassIdentify;
UsbMass->DiskInfo.SenseData = UsbMassSenseData;
UsbMass->DiskInfo.WhichIde = UsbMassWhichIde;
UsbMass->OpticalStorage = 0;
UsbMass->ScsiInterface = (Interface.InterfaceSubClass == USB_MASS_STORE_SCSI) ? 1 : 0;
UsbMass->Transport = Transport;
UsbMass->Context = Context;
UsbMass->Lun = (UINT8)Index;
UsbMass->DevicePath = NULL;
UsbMass->UsbCore = UsbCore;
//
// 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));
if (MaximumLuns == 0) goto ON_ERROR; else continue;
}
} else if (Status != EFI_NO_MEDIA){
DEBUG ((mUsbMscError, "USBMassDriverBindingStart: UsbMassInitMedia (%r)\n", Status));
if (Status == EFI_NOT_FOUND) {
//
// Device has detached or no response, quit immediately
//
Status = EFI_DEVICE_ERROR;
goto ON_ERROR;
}
if (Status == EFI_NO_RESPONSE) {
//
// Serious error occurred, skip the protocol generating and
// block the UsbIo to preventing further unnecessary retry
//
continue;
}
if (MaximumLuns == 0) goto ON_ERROR; else continue;
}
//
// Install BlockIo. For Multi-LUN device should
// create new handle with MSG_DEVICE_LOGICAL_UNIT_DP device path appended
//
if (MaximumLuns == 0) {
//
// Install BlockIo protocol for Single-LUN device
//
Status = gBS->InstallMultipleProtocolInterfaces (
&Controller,
&gEfiBlockIoProtocolGuid,
&UsbMass->BlockIo,
&gEfiDiskInfoProtocolGuid,
&UsbMass->DiskInfo,
NULL
);
} else {
//
// Install child BlockIo protocol for Multi-LUN device. Create new handle with
// MSG_DEVICE_LOGICAL_UNIT_DP device path appended
//
LunDevicePath = (DEVICE_LOGICAL_UNIT_DEVICE_PATH*)NewDevicePath;
LunDevicePath->Header.Type = MESSAGING_DEVICE_PATH;
LunDevicePath->Header.SubType = MSG_DEVICE_LOGICAL_UNIT_DP;
SetDevicePathNodeLength (&LunDevicePath->Header, sizeof (DEVICE_LOGICAL_UNIT_DEVICE_PATH));
LunDevicePath->Lun = (UINT8)Index;
SetDevicePathEndNode ((EFI_DEVICE_PATH_PROTOCOL*)(LunDevicePath + 1));
UsbMass->DevicePath = AppendDevicePath(DevicePath, &LunDevicePath->Header);
//
// Create new handle
//
UsbMass->Controller = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&UsbMass->Controller,
&gEfiDevicePathProtocolGuid,
UsbMass->DevicePath,
&gEfiBlockIoProtocolGuid,
&UsbMass->BlockIo,
&gEfiDiskInfoProtocolGuid,
&UsbMass->DiskInfo,
NULL
);
if (!EFI_ERROR (Status)) {
Status = gBS->OpenProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
(VOID **)&UsbIo,
This->DriverBindingHandle,
UsbMass->Controller,
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
}
}
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
//
// Setup the data for UsbLegacy
//
if (UsbMass->UsbCore) {
//
// Registers a UsbMassStorage Device for UsbLegacy
//
UsbDevice.UsbMassStorage.Type = USB_CORE_USB_MASS_STORAGE;
UsbDevice.UsbMassStorage.UsbIo = UsbIo;
UsbDevice.UsbMassStorage.Transport = (USB_MASS_TRANSPORT_PROTOCOL*)UsbMass->Context;
UsbDevice.UsbMassStorage.Media = &UsbMass->BlockIoMedia;
UsbDevice.UsbMassStorage.MediumType = &UsbMass->MediumType;
UsbDevice.UsbMassStorage.Manufacturer = UsbMass->InquiryData.VendorID;
UsbDevice.UsbMassStorage.Product = UsbMass->InquiryData.ProductID;
UsbDevice.UsbMassStorage.SerialNumber = UsbMass->InquiryData.ProductRevision;
UsbDevice.UsbMassStorage.VendorID = DeviceDescriptor.IdVendor;
UsbDevice.UsbMassStorage.ProductID = DeviceDescriptor.IdProduct;
UsbDevice.UsbMassStorage.HcLocation = HcLocation;
UsbDevice.UsbMassStorage.Lun = UsbMass->Lun;
UsbDevice.UsbMassStorage.InterfaceNumber = Interface.InterfaceNumber;
UsbDevice.UsbMassStorage.HotPlugged = 0;
TmpDevicePath = (UsbMass->DevicePath == NULL) ? DevicePath : UsbMass->DevicePath;
Size = GetDevicePathSize (TmpDevicePath);
Status = UsbCore->AllocateBuffer (
Size,
ALIGNMENT_32,
(VOID **)&UsbDevice.UsbMassStorage.DevicePath
);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
CopyMem (UsbDevice.UsbMassStorage.DevicePath, TmpDevicePath , Size);
Status = UsbMass->UsbCore->InsertUsbDevice(
&UsbDevice
);
if (Status == EFI_ALREADY_STARTED) {
//
// InsertUsbDevice may return EFI_ALREADY_STARTED because
// this device had recorded into BBS. Finish the transport
// allocated by previous Init
//
((USB_MASS_TRANSPORT_PROTOCOL*)UsbMass->Context)->Fini(UsbMass->Context);
//
// Use of the stock context in recorded device list
//
UsbMass->Context = UsbDevice.UsbMassStorage.Transport;
}
}
}
Status = EFI_SUCCESS;
goto ON_EXIT;
ON_ERROR:
if (UsbMass) {
if (UsbMass->Context != NULL && UsbMass->Transport != NULL) {
UsbMass->Transport->Fini (UsbMass->Context);
}
UsbMass->UsbCore->FreeBuffer (
sizeof (USB_MASS_DEVICE),
UsbMass
);
}
if (Status != EFI_DEVICE_ERROR) {
gBS->CloseProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
}
ON_EXIT:
//
// RestoreTPL back to original TPL
//
if (OldTPL) 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 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_BLOCK_IO_PROTOCOL *BlockIo;
EFI_TPL OldTPL;
UINTN Index;
//
// First, get our context back from the BLOCK_IO
//
Status = gBS->OpenProtocol (
Controller,
&gEfiBlockIoProtocolGuid,
(VOID **)&BlockIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
//
// RaiseTPL to preventing the background thread releases the UsbMass through hot-plug
//
OldTPL = gBS->RaiseTPL (TPL_CALLBACK);
if (!EFI_ERROR (Status)) {
//
// For Single-LUN device, the BlockIo and UsbIo are installed in single handle
//
UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (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)) {
goto ON_EXIT;
}
//
// Unregisters a UsbMassStorage Device
//
Status = UsbMass->UsbCore->RemoveUsbDevice(UsbMass->UsbIo);
if (!EFI_ERROR (Status)) {
//
// RemoveUsbDevice may return EFI_ALREADY_STARTED because
// this device had recorded into BBS. Don't finish the transport
// protocol because it may attached again(through hot-plug)
//
UsbMass->Transport->Fini (UsbMass->Context);
}
if (UsbMass->DeviceNameTable) {
FreeUnicodeStringTable (UsbMass->DeviceNameTable);
}
UsbMass->UsbCore->FreeBuffer (
sizeof (USB_MASS_DEVICE),
UsbMass
);
}
//
// Uninstall child handles for Multi-LUN device
//
for (Index = 0; Index < NumberOfChildren; Index ++) {
//
// Uninstall child BlockIo protocol
//
Status = gBS->OpenProtocol (
ChildHandleBuffer[Index],
&gEfiBlockIoProtocolGuid,
(VOID **)&BlockIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (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)) {
goto ON_EXIT;
}
//
// Unregisters a UsbMassStorage Device
//
Status = UsbMass->UsbCore->RemoveUsbDevice(UsbMass->UsbIo);
if (!EFI_ERROR (Status)) {
//
// RemoveUsbDevice may return EFI_ALREADY_STARTED because
// this device had recorded into BBS. Don't finish the transport
// protocol because it may attached again(through hot-plug)
//
UsbMass->Transport->Fini (UsbMass->Context);
}
if (UsbMass->DeviceNameTable) {
FreeUnicodeStringTable (UsbMass->DeviceNameTable);
}
gBS->FreePool (UsbMass->DevicePath);
UsbMass->UsbCore->FreeBuffer (
sizeof (USB_MASS_DEVICE),
UsbMass
);
}
}
gBS->CloseProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
Status = EFI_SUCCESS;
ON_EXIT:
//
// RestoreTPL back to original TPL
//
gBS->RestoreTPL(OldTPL);
return Status;
}
EFI_DRIVER_BINDING_PROTOCOL gUsbMassDriverBinding = {
UsbMassDriverBindingSupported,
UsbMassDriverBindingStart,
UsbMassDriverBindingStop,
0x11,
NULL,
NULL
};
/**
The entry point for the driver, which will install the driver binding and
component name protocol
@param ImageHandle The image handle of this driver
@param SystemTable The system table
@retval EFI_SUCCESS the protocols are installed OK
@retval Others Failed to install protocols.
**/
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
);
return Status;
}