1407 lines
43 KiB
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;
|
|
} |