1217 lines
34 KiB
C
1217 lines
34 KiB
C
/** @file
|
|
Implementation of the USB mass storage Control/Bulk/Interrupt transpor.
|
|
Notice: it is being obseleted by the standard body in favor of the BOT
|
|
(Bulk-Only Transport).
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2012 - 2018, 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 "UsbMassCbi.h"
|
|
|
|
#ifndef MDEPKG_NDEBUG
|
|
UINTN mUsbCbiInfo = EFI_D_INFO;
|
|
UINTN mUsbCbiError = EFI_D_ERROR;
|
|
#endif
|
|
|
|
//
|
|
// Function prototypes
|
|
//
|
|
STATIC
|
|
EFI_STATUS
|
|
UsbCbi0Init (
|
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
|
IN EFI_HANDLE Controller,
|
|
OUT VOID **Context
|
|
);
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
UsbCbi1Init (
|
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
|
IN EFI_HANDLE Controller,
|
|
OUT VOID **Context
|
|
);
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbMassBotDriverEntryPoint (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
);
|
|
|
|
STATIC EFI_GUID mUsbMassTransportProtocolGuid = USB_MASS_TRANSPORT_PROTOCOL_GUID;
|
|
STATIC EFI_USB_CORE_PROTOCOL *mUsbCore;
|
|
STATIC BOOLEAN mInSmram;
|
|
STATIC USB_MASS_TRANSPORT_PROTOCOL *mUsbCbi0Transport;
|
|
STATIC USB_MASS_TRANSPORT_PROTOCOL *mUsbCbi1Transport;
|
|
|
|
/**
|
|
|
|
Register protocol for this driver.
|
|
|
|
@param ImageHandle EFI_HANDLE
|
|
@param SystemTable EFI_SYSTEM_TABLE pointer
|
|
|
|
@retval EFI_SUCCESS Driver loaded
|
|
@retval other Driver not loaded
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbMassCbiDriverEntryPoint (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE Handle;
|
|
UINTN Index;
|
|
UINTN HandleCount;
|
|
EFI_HANDLE *HandleBuffer;
|
|
USB_MASS_TRANSPORT_PROTOCOL *Transport;
|
|
//
|
|
// Locate UsbCore protocol
|
|
//
|
|
Status = gBS->LocateProtocol (
|
|
&gEfiUsbCoreProtocolGuid,
|
|
NULL,
|
|
(VOID **)&mUsbCore
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
//
|
|
// Check the phase of instance
|
|
//
|
|
mUsbCore->IsInSmm (&mInSmram);
|
|
if (!mInSmram) {
|
|
//
|
|
// Now in boot service, install instance
|
|
//
|
|
Status = mUsbCore->AllocateBuffer (
|
|
sizeof (USB_MASS_TRANSPORT_PROTOCOL) * 2,
|
|
ALIGNMENT_32,
|
|
(VOID **)&mUsbCbi0Transport
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
mUsbCbi0Transport->Protocol = USB_MASS_STORE_CBI0;
|
|
mUsbCbi0Transport->Init = UsbCbi0Init;
|
|
mUsbCbi0Transport->ExecCommand = UsbCbiExecCommand;
|
|
mUsbCbi0Transport->Reset = UsbCbiResetDevice;
|
|
mUsbCbi0Transport->Fini = UsbCbiFini;
|
|
mUsbCbi1Transport = mUsbCbi0Transport + 1;
|
|
mUsbCbi1Transport->Protocol = USB_MASS_STORE_CBI1;
|
|
mUsbCbi1Transport->Init = UsbCbi1Init;
|
|
mUsbCbi1Transport->ExecCommand = UsbCbiExecCommand;
|
|
mUsbCbi1Transport->Reset = UsbCbiResetDevice;
|
|
mUsbCbi1Transport->Fini = UsbCbiFini;
|
|
//
|
|
// Pass in a NULL to install to a new handle
|
|
//
|
|
Handle = NULL;
|
|
Status = gBS->InstallProtocolInterface (
|
|
&Handle,
|
|
&mUsbMassTransportProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
mUsbCbi0Transport
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
Handle = NULL;
|
|
Status = gBS->InstallProtocolInterface (
|
|
&Handle,
|
|
&mUsbMassTransportProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
mUsbCbi1Transport
|
|
);
|
|
//
|
|
// Register module in DXE instance
|
|
//
|
|
mUsbCore->ModuleRegistration (ImageHandle);
|
|
} else {
|
|
//
|
|
// SMM instance, disable DEBUG message out
|
|
//
|
|
#ifndef MDEPKG_NDEBUG
|
|
mUsbCbiInfo = 0;
|
|
mUsbCbiError = 0;
|
|
#endif
|
|
//
|
|
// Now in SMM, get instance installed by boot service
|
|
//
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&mUsbMassTransportProtocolGuid,
|
|
NULL,
|
|
&HandleCount,
|
|
&HandleBuffer
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
Transport = NULL;
|
|
for (Index = 0; Index < HandleCount; Index ++) {
|
|
Status = gBS->HandleProtocol (
|
|
HandleBuffer[Index],
|
|
&mUsbMassTransportProtocolGuid,
|
|
(VOID **)&Transport
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
continue;
|
|
}
|
|
//
|
|
// Setup appropriate variable in the Smm region
|
|
//
|
|
if (Transport->Protocol == USB_MASS_STORE_CBI0) {
|
|
mUsbCbi0Transport = Transport;
|
|
} else if (Transport->Protocol == USB_MASS_STORE_CBI1) {
|
|
mUsbCbi1Transport = Transport;
|
|
}
|
|
}
|
|
gBS->FreePool (HandleBuffer);
|
|
//
|
|
// Setup UsbCore for use by SMM instance
|
|
//
|
|
mUsbCore->AddressConvert (
|
|
SMM_ADDRESS,
|
|
(VOID*)(UINTN)mUsbCore,
|
|
(VOID**)&mUsbCore
|
|
);
|
|
//
|
|
// Register module in SMM instance
|
|
//
|
|
mUsbCore->ModuleRegistration (ImageHandle);
|
|
//
|
|
// Setup Smm address convert table for Smm security policy
|
|
//
|
|
mUsbCore->InsertAddressConvertTable (
|
|
ACT_FUNCTION_POINTER,
|
|
&mUsbCbi0Transport->Init,
|
|
4
|
|
);
|
|
mUsbCore->InsertAddressConvertTable (
|
|
ACT_FUNCTION_POINTER,
|
|
&mUsbCbi1Transport->Init,
|
|
4
|
|
);
|
|
mUsbCore->InsertAddressConvertTable (
|
|
ACT_INSTANCE_BODY,
|
|
mUsbCbi0Transport,
|
|
sizeof (USB_MASS_TRANSPORT_PROTOCOL) * 2
|
|
);
|
|
mUsbCore->InsertAddressConvertTable (
|
|
ACT_INSTANCE_POINTER,
|
|
&mUsbCbi0Transport,
|
|
1
|
|
);
|
|
mUsbCore->InsertAddressConvertTable (
|
|
ACT_INSTANCE_POINTER,
|
|
&mUsbCbi1Transport,
|
|
1
|
|
);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
This function is used to manage a USB device with a control transfer pipe.
|
|
|
|
@param This Indicates calling context.
|
|
@param Request A pointer to the USB device request that will be sent to
|
|
the USB device.
|
|
@param Direction Indicates the data direction.
|
|
@param Data A pointer to the buffer of data that will be transmitted
|
|
to USB device or received from USB device.
|
|
@param Timeout Indicates the transfer should be completed within this time
|
|
frame.
|
|
@param DataLength The size, in bytes, of the data buffer specified by Data.
|
|
@param Status A pointer to the result of the USB transfer.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_INVALID_PARAMETER
|
|
@retval EFI_OUT_OF_RESOURCES
|
|
@retval EFI_TIMEOUT
|
|
@retval EFI_DEVICE_ERROR
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbControlTransfer (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
IN EFI_USB_DEVICE_REQUEST *Request,
|
|
IN EFI_USB_DATA_DIRECTION Direction,
|
|
IN UINT32 Timeout,
|
|
IN OUT VOID *Data, OPTIONAL
|
|
IN UINTN DataLength, OPTIONAL
|
|
OUT UINT32 *Status
|
|
)
|
|
{
|
|
EFI_USB_IO_CONTROL_TRANSFER UsbControlTransferFunc = This->UsbControlTransfer;
|
|
|
|
if (mInSmram) {
|
|
mUsbCore->AddressConvert (
|
|
SMM_ADDRESS,
|
|
(VOID*)(UINTN)UsbControlTransferFunc,
|
|
(VOID**)&UsbControlTransferFunc
|
|
);
|
|
}
|
|
return UsbControlTransferFunc (
|
|
This,
|
|
Request,
|
|
Direction,
|
|
Timeout,
|
|
Data,
|
|
DataLength,
|
|
Status
|
|
);
|
|
}
|
|
|
|
/**
|
|
|
|
This function is used to manage a USB device with the bulk transfer pipe.
|
|
|
|
@param This Indicates calling context.
|
|
@param DeviceEndpoint The destination USB device endpoint to which the device
|
|
request is being sent.
|
|
@param Data A pointer to the buffer of data that will be transmitted
|
|
to USB device or received from USB device.
|
|
@param DataLength On input, the size, in bytes, of the data buffer
|
|
specified by Data. On output, the number of bytes that
|
|
were actually transferred.
|
|
@param Timeout Indicates the transfer should be completed within this
|
|
time frame.
|
|
@param Status This parameter indicates the USB transfer status.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_INVALID_PARAMETER
|
|
@retval EFI_OUT_OF_RESOURCES
|
|
@retval EFI_TIMEOUT
|
|
@retval EFI_DEVICE_ERROR
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbBulkTransfer (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
IN UINT8 DeviceEndpoint,
|
|
IN OUT VOID *Data,
|
|
IN OUT UINTN *DataLength,
|
|
IN UINTN Timeout,
|
|
OUT UINT32 *Status
|
|
)
|
|
{
|
|
EFI_USB_IO_BULK_TRANSFER UsbBulkTransferFunc = This->UsbBulkTransfer;
|
|
|
|
if (mInSmram) {
|
|
mUsbCore->AddressConvert (
|
|
SMM_ADDRESS,
|
|
(VOID*)(UINTN)UsbBulkTransferFunc,
|
|
(VOID**)&UsbBulkTransferFunc
|
|
);
|
|
}
|
|
return UsbBulkTransferFunc (
|
|
This,
|
|
DeviceEndpoint,
|
|
Data,
|
|
DataLength,
|
|
Timeout,
|
|
Status
|
|
);
|
|
}
|
|
|
|
/*++
|
|
|
|
Usb Sync Interrupt Transfer
|
|
|
|
@param This Indicates calling context.
|
|
@param DeviceEndpoint The destination USB device endpoint to which the device
|
|
request is being sent.
|
|
@param Data A pointer to the buffer of data that will be transmitted
|
|
to USB device or received from USB device.
|
|
@param DataLength On input, the size, in bytes, of the data buffer
|
|
specified by Data. On output, the number of bytes that
|
|
were actually transferred.
|
|
@param Timeout Indicates the transfer should be completed within this
|
|
time frame.
|
|
@param Status This parameter indicates the USB transfer status.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_INVALID_PARAMETER
|
|
@retval EFI_OUT_OF_RESOURCES
|
|
@retval EFI_TIMEOUT
|
|
@retval EFI_DEVICE_ERROR
|
|
|
|
--*/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbSyncInterruptTransfer (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
IN UINT8 DeviceEndpoint,
|
|
IN OUT VOID *Data,
|
|
IN OUT UINTN *DataLength,
|
|
IN UINTN Timeout,
|
|
OUT UINT32 *Status
|
|
)
|
|
{
|
|
EFI_USB_IO_SYNC_INTERRUPT_TRANSFER UsbSyncInterruptTransferFunc = This->UsbSyncInterruptTransfer;
|
|
|
|
if (mInSmram) {
|
|
mUsbCore->AddressConvert (
|
|
SMM_ADDRESS,
|
|
(VOID*)(UINTN)UsbSyncInterruptTransferFunc,
|
|
(VOID**)&UsbSyncInterruptTransferFunc
|
|
);
|
|
}
|
|
return UsbSyncInterruptTransferFunc (
|
|
This,
|
|
DeviceEndpoint,
|
|
Data,
|
|
DataLength,
|
|
Timeout,
|
|
Status
|
|
);
|
|
}
|
|
|
|
/**
|
|
|
|
Retrieves the interface Descriptor for that controller.
|
|
|
|
@param This Indicates the calling context.
|
|
@param InterfaceDescriptor A pointer to the caller allocated USB interface
|
|
Descriptor.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_INVALID_PARAMETER
|
|
@retval EFI_NOT_FOUND
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbGetInterfaceDescriptor (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
OUT EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDescriptor
|
|
)
|
|
{
|
|
EFI_USB_IO_GET_INTERFACE_DESCRIPTOR UsbGetInterfaceDescriptorFunc = This->UsbGetInterfaceDescriptor;
|
|
|
|
if (mInSmram) {
|
|
mUsbCore->AddressConvert (
|
|
SMM_ADDRESS,
|
|
(VOID*)(UINTN)UsbGetInterfaceDescriptorFunc,
|
|
(VOID**)&UsbGetInterfaceDescriptorFunc
|
|
);
|
|
}
|
|
return UsbGetInterfaceDescriptorFunc (
|
|
This,
|
|
InterfaceDescriptor
|
|
);
|
|
}
|
|
|
|
/**
|
|
|
|
Retrieves the endpoint Descriptor for a given endpoint.
|
|
|
|
@param This Indicates the calling context.
|
|
@param EndpointIndex Indicates which endpoint descriptor to retrieve.
|
|
The valid range is 0..15.
|
|
@param EndpointDescriptor A pointer to the caller allocated USB Endpoint
|
|
Descriptor of a USB controller.
|
|
|
|
@retval EFI_SUCCESS The endpoint descriptor was retrieved successfully.
|
|
@retval EFI_INVALID_PARAMETER EndpointIndex is not valid.
|
|
EndpointDescriptor is NULL.
|
|
@retval EFI_NOT_FOUND The endpoint descriptor cannot be found.
|
|
The device may not be correctly configured.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbGetEndpointDescriptor (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
IN UINT8 EndpointIndex,
|
|
OUT EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDescriptor
|
|
)
|
|
{
|
|
EFI_USB_IO_GET_ENDPOINT_DESCRIPTOR UsbGetEndpointDescriptorFunc = This->UsbGetEndpointDescriptor;
|
|
|
|
if (mInSmram) {
|
|
mUsbCore->AddressConvert (
|
|
SMM_ADDRESS,
|
|
(VOID*)(UINTN)UsbGetEndpointDescriptorFunc,
|
|
(VOID**)&UsbGetEndpointDescriptorFunc
|
|
);
|
|
}
|
|
return UsbGetEndpointDescriptorFunc (
|
|
This,
|
|
EndpointIndex,
|
|
EndpointDescriptor
|
|
);
|
|
}
|
|
|
|
/**
|
|
|
|
Initialize the USB mass storage class CBI transport protocol.
|
|
If Context isn't NULL, it will save its context in it.
|
|
|
|
@param UsbIo The USB IO to use
|
|
@param Controller The device controller
|
|
@param Context The variable to save context in
|
|
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory
|
|
@retval EFI_UNSUPPORTED The device isn't supported
|
|
@retval EFI_SUCCESS The CBI protocol is initialized.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
UsbCbiInit (
|
|
IN UINT8 InterfaceProtocol,
|
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
|
IN EFI_HANDLE Controller,
|
|
OUT VOID **Context OPTIONAL
|
|
)
|
|
{
|
|
USB_CBI_PROTOCOL *UsbCbi;
|
|
EFI_USB_INTERFACE_DESCRIPTOR *Interface;
|
|
EFI_USB_ENDPOINT_DESCRIPTOR EndPoint;
|
|
EFI_STATUS Status;
|
|
UINT8 Index;
|
|
|
|
//
|
|
// Allocate the CBI context
|
|
//
|
|
Status = mUsbCore->AllocateBuffer(
|
|
sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR),
|
|
ALIGNMENT_32,
|
|
(VOID **)&UsbCbi
|
|
);
|
|
|
|
if (UsbCbi == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
UsbCbi->UsbIo = UsbIo;
|
|
//
|
|
// Get the interface descriptor and validate that it is a USB mass
|
|
// storage class CBI interface.
|
|
//
|
|
Status = UsbGetInterfaceDescriptor (UsbIo, &UsbCbi->Interface);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Interface = &UsbCbi->Interface;
|
|
//
|
|
// Locate and save the bulk-in, bulk-out, and interrupt endpoint
|
|
//
|
|
for (Index = 0; Index < Interface->NumEndpoints; Index++) {
|
|
Status = UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
if (USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) {
|
|
//
|
|
// Use the first Bulk-In and Bulk-Out endpoints
|
|
//
|
|
if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) &&
|
|
(UsbCbi->BulkInEndpoint == NULL)) {
|
|
|
|
UsbCbi->BulkInEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1);
|
|
*UsbCbi->BulkInEndpoint = EndPoint;
|
|
}
|
|
|
|
if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) &&
|
|
(UsbCbi->BulkOutEndpoint == NULL)) {
|
|
|
|
UsbCbi->BulkOutEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1) + 1;
|
|
*UsbCbi->BulkOutEndpoint = EndPoint;
|
|
}
|
|
|
|
} else if (USB_IS_INTERRUPT_ENDPOINT (EndPoint.Attributes)) {
|
|
//
|
|
// Use the first interrupt endpoint if it is CBI0
|
|
//
|
|
if ((InterfaceProtocol == USB_MASS_STORE_CBI0) &&
|
|
(UsbCbi->InterruptEndpoint == NULL)) {
|
|
|
|
UsbCbi->InterruptEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1) + 2;
|
|
*UsbCbi->InterruptEndpoint = EndPoint;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((UsbCbi->BulkInEndpoint == NULL)
|
|
|| (UsbCbi->BulkOutEndpoint == NULL)
|
|
|| ((InterfaceProtocol == USB_MASS_STORE_CBI0)
|
|
&& (UsbCbi->InterruptEndpoint == NULL))) {
|
|
Status = EFI_UNSUPPORTED;
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
if (Context != NULL) {
|
|
//
|
|
// Setup the data for UsbLegacy
|
|
//
|
|
CopyMem(
|
|
&UsbCbi->Transport,
|
|
(InterfaceProtocol == USB_MASS_STORE_CBI0) ? mUsbCbi0Transport : mUsbCbi1Transport,
|
|
sizeof(USB_MASS_TRANSPORT_PROTOCOL)
|
|
);
|
|
//
|
|
// Setup Smm address convert table for Smm security policy
|
|
//
|
|
mUsbCore->InsertAddressConvertTable (
|
|
ACT_FUNCTION_POINTER,
|
|
&UsbCbi->Transport.Init,
|
|
4
|
|
);
|
|
mUsbCore->InsertAddressConvertTable (
|
|
ACT_INSTANCE_BODY,
|
|
UsbCbi,
|
|
sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)
|
|
);
|
|
mUsbCore->InsertAddressConvertTable (
|
|
ACT_INSTANCE_POINTER,
|
|
&UsbCbi->BulkInEndpoint,
|
|
1
|
|
);
|
|
mUsbCore->InsertAddressConvertTable (
|
|
ACT_INSTANCE_POINTER,
|
|
&UsbCbi->BulkOutEndpoint,
|
|
1
|
|
);
|
|
mUsbCore->InsertAddressConvertTable (
|
|
ACT_INSTANCE_POINTER,
|
|
&UsbCbi->InterruptEndpoint,
|
|
1
|
|
);
|
|
mUsbCore->InsertAddressConvertTable (
|
|
ACT_INSTANCE_POINTER,
|
|
&UsbCbi->UsbIo,
|
|
1
|
|
);
|
|
*Context = UsbCbi;
|
|
} else {
|
|
//
|
|
// Free the instance due to the calling purpose is just to check is the device matches the protocol
|
|
//
|
|
Status = EFI_SUCCESS;
|
|
goto ON_ERROR;
|
|
}
|
|
return EFI_SUCCESS;
|
|
|
|
ON_ERROR:
|
|
mUsbCore->FreeBuffer (
|
|
sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR),
|
|
UsbCbi
|
|
);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
Initialize the USB mass storage class CBI transport protocol.
|
|
If Context isn't NULL, it will save its context in it.
|
|
|
|
@param UsbIo The USB IO to use
|
|
@param Controller The device controller
|
|
@param Context The variable to save context in
|
|
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory
|
|
@retval EFI_UNSUPPORTED The device isn't supported
|
|
@retval EFI_SUCCESS The CBI protocol is initialized.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
UsbCbi0Init (
|
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
|
IN EFI_HANDLE Controller,
|
|
OUT VOID **Context OPTIONAL
|
|
)
|
|
{
|
|
return UsbCbiInit(
|
|
USB_MASS_STORE_CBI0,
|
|
UsbIo,
|
|
Controller,
|
|
Context
|
|
);
|
|
}
|
|
|
|
/**
|
|
|
|
Initialize the USB mass storage class CBI transport protocol.
|
|
If Context isn't NULL, it will save its context in it.
|
|
|
|
@param UsbIo The USB IO to use
|
|
@param Controller The device controller
|
|
@param Context The variable to save context in
|
|
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory
|
|
@retval EFI_UNSUPPORTED The device isn't supported
|
|
@retval EFI_SUCCESS The CBI protocol is initialized.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
UsbCbi1Init (
|
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
|
IN EFI_HANDLE Controller,
|
|
OUT VOID **Context OPTIONAL
|
|
)
|
|
{
|
|
return UsbCbiInit(
|
|
USB_MASS_STORE_CBI1,
|
|
UsbIo,
|
|
Controller,
|
|
Context
|
|
);
|
|
}
|
|
|
|
/**
|
|
|
|
Send the command to the device using class specific control transfer.
|
|
|
|
@param UsbCbi The USB CBI protocol
|
|
@param Cmd The high level command to transfer to device
|
|
@param CmdLen The length of the command
|
|
@param Timeout The time to wait the command to finish
|
|
|
|
@retval EFI_SUCCESS The command is transferred to device
|
|
@retval Others The command failed to transfer to device
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
UsbCbiSendCommand (
|
|
IN USB_CBI_PROTOCOL *UsbCbi,
|
|
IN UINT8 *Cmd,
|
|
IN UINT8 CmdLen,
|
|
IN UINT32 Timeout
|
|
)
|
|
{
|
|
EFI_USB_DEVICE_REQUEST Request;
|
|
EFI_STATUS Status;
|
|
UINT32 TransStatus;
|
|
UINTN DataLen;
|
|
INTN Retry;
|
|
|
|
//
|
|
// Fill in the device request, CBI use the "Accept Device-Specific
|
|
// Cmd" (ADSC) class specific request to send commands
|
|
//
|
|
Request.RequestType = 0x21;
|
|
Request.Request = 0;
|
|
Request.Value = 0;
|
|
Request.Index = UsbCbi->Interface.InterfaceNumber;
|
|
Request.Length = CmdLen;
|
|
|
|
Status = EFI_SUCCESS;
|
|
Timeout = Timeout / USB_MASS_STALL_1_MS;
|
|
|
|
for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) {
|
|
//
|
|
// Check is device been detached
|
|
//
|
|
if (mUsbCore->CheckDeviceDetached(UsbCbi->UsbIo) == EFI_SUCCESS) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
//
|
|
// Use the UsbIo to send the command to the device
|
|
//
|
|
TransStatus = 0;
|
|
DataLen = CmdLen;
|
|
|
|
Status = UsbControlTransfer (
|
|
UsbCbi->UsbIo,
|
|
&Request,
|
|
EfiUsbDataOut,
|
|
Timeout,
|
|
Cmd,
|
|
DataLen,
|
|
&TransStatus
|
|
);
|
|
//
|
|
// The device can fail the command by STALL the control endpoint.
|
|
// It can delay the command by NAK the data or status stage, this
|
|
// is a "class-specific exemption to the USB specification". Retry
|
|
// if the command is NAKed.
|
|
//
|
|
if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
Transfer data between the device and host. The CBI contains three phase,
|
|
command, data, and status. This is data phase.
|
|
|
|
@param UsbCbi The USB CBI device
|
|
@param DataDir The direction of the data transfer
|
|
@param Data The buffer to hold the data
|
|
@param TransLen The expected transfer length
|
|
@param Timeout The time to wait the command to execute
|
|
|
|
@retval EFI_SUCCESS The data transfer succeeded
|
|
@retval Others Failed to transfer all the data
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
UsbCbiDataTransfer (
|
|
IN USB_CBI_PROTOCOL *UsbCbi,
|
|
IN EFI_USB_DATA_DIRECTION DataDir,
|
|
IN OUT UINT8 *Data,
|
|
IN OUT UINTN *TransLen,
|
|
IN UINT32 Timeout
|
|
)
|
|
{
|
|
EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint;
|
|
EFI_STATUS Status;
|
|
UINT32 TransStatus;
|
|
UINTN Remain;
|
|
UINTN Increment;
|
|
UINT8 *Next;
|
|
UINTN Retry;
|
|
|
|
//
|
|
// It's OK if no data to transfer
|
|
//
|
|
if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {
|
|
//
|
|
// Stall 0.5ms for specific device compatibility(0.3ms minimum)
|
|
//
|
|
mUsbCore->Stall(500);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Select the endpoint then issue the transfer
|
|
//
|
|
if (DataDir == EfiUsbDataIn) {
|
|
Endpoint = UsbCbi->BulkInEndpoint;
|
|
} else {
|
|
Endpoint = UsbCbi->BulkOutEndpoint;
|
|
}
|
|
|
|
Next = Data;
|
|
Remain = *TransLen;
|
|
Retry = 0;
|
|
Status = EFI_SUCCESS;
|
|
Timeout = Timeout / USB_MASS_STALL_1_MS;
|
|
|
|
//
|
|
// Transfer the data, if the device returns NAK, retry it.
|
|
//
|
|
while (Remain > 0) {
|
|
//
|
|
// Check is device been detached
|
|
//
|
|
if (mUsbCore->CheckDeviceDetached(UsbCbi->UsbIo) == EFI_SUCCESS) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
TransStatus = 0;
|
|
|
|
if (Remain > (UINTN) USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize) {
|
|
Increment = USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize;
|
|
} else {
|
|
Increment = Remain;
|
|
}
|
|
|
|
Status = UsbBulkTransfer (
|
|
UsbCbi->UsbIo,
|
|
Endpoint->EndpointAddress,
|
|
Next,
|
|
&Increment,
|
|
Timeout,
|
|
&TransStatus
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
if (TransStatus == EFI_USB_ERR_NAK) {
|
|
//
|
|
// The device can NAK the host if either the data/buffer isn't
|
|
// aviable or the command is in-progress. The data can be partly
|
|
// transferred. The transfer is aborted if several succssive data
|
|
// transfer commands are NAKed.
|
|
//
|
|
if (Increment == 0) {
|
|
if (++Retry > USB_CBI_MAX_RETRY) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
} else {
|
|
Next += Increment;
|
|
Remain -= Increment;
|
|
Retry = 0;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The device can fail the command by STALL the bulk endpoint.
|
|
// Clear the stall if that is the case.
|
|
//
|
|
if (TransStatus == EFI_USB_ERR_STALL) {
|
|
mUsbCore->UsbClearDeviceFeature(
|
|
UsbCbi->UsbIo,
|
|
USB_TARGET_ENDPOINT,
|
|
USB_FEATURE_ENDPOINT_HALT,
|
|
Endpoint->EndpointAddress,
|
|
&TransStatus
|
|
);
|
|
}
|
|
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Next += Increment;
|
|
Remain -= Increment;
|
|
}
|
|
|
|
ON_EXIT:
|
|
*TransLen -= Remain;
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
Get the result of high level command execution from interrupt
|
|
endpoint. This function returns the USB transfer status, and
|
|
put the high level command execution result in Result.
|
|
|
|
@param UsbCbi The USB CBI protocol
|
|
@param Timeout The time to wait the command to execute
|
|
@param Result Result status
|
|
|
|
@retval EFI_SUCCESS The high level command execution result is retrieved in Result.
|
|
@retval Others Failed to retrieve the result.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
UsbCbiGetStatus (
|
|
IN USB_CBI_PROTOCOL *UsbCbi,
|
|
IN UINT32 Timeout,
|
|
OUT USB_CBI_STATUS *Result
|
|
)
|
|
{
|
|
UINTN Len;
|
|
UINT8 Endpoint;
|
|
EFI_STATUS Status;
|
|
UINT32 TransStatus;
|
|
INTN Retry;
|
|
|
|
Endpoint = UsbCbi->InterruptEndpoint->EndpointAddress;
|
|
Status = EFI_SUCCESS;
|
|
Timeout = Timeout / USB_MASS_STALL_1_MS;
|
|
|
|
//
|
|
// Attemp to the read the result from interrupt endpoint
|
|
//
|
|
for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) {
|
|
//
|
|
// Check is device been detached
|
|
//
|
|
if (mUsbCore->CheckDeviceDetached(UsbCbi->UsbIo) == EFI_SUCCESS) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
TransStatus = 0;
|
|
Len = sizeof (USB_CBI_STATUS);
|
|
|
|
Status = UsbSyncInterruptTransfer (
|
|
UsbCbi->UsbIo,
|
|
Endpoint,
|
|
Result,
|
|
&Len,
|
|
Timeout,
|
|
&TransStatus
|
|
);
|
|
//
|
|
// The CBI can NAK the interrupt endpoint if the command is in-progress.
|
|
//
|
|
if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
Execute USB mass storage command through the CBI0/CBI1 transport protocol
|
|
|
|
@param Context The USB CBI device
|
|
@param Cmd The command to transfer to device
|
|
@param CmdLen The length of the command
|
|
@param DataDir The direction of data transfer
|
|
@param Data The buffer to hold the data
|
|
@param DataLen The length of the buffer
|
|
@param Timeout The time to wait
|
|
@param CmdStatus The result of the command execution
|
|
|
|
@retval EFI_SUCCESS The command is executed OK and result in CmdStatus.
|
|
@retval EFI_DEVICE_ERROR Failed to execute the command
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
UsbCbiExecCommand (
|
|
IN VOID *Context,
|
|
IN VOID *Cmd,
|
|
IN UINT8 CmdLen,
|
|
IN EFI_USB_DATA_DIRECTION DataDir,
|
|
IN VOID *Data,
|
|
IN UINT32 DataLen,
|
|
IN UINT32 Timeout,
|
|
OUT UINT32 *CmdStatus
|
|
)
|
|
{
|
|
USB_CBI_PROTOCOL *UsbCbi;
|
|
USB_CBI_STATUS Result;
|
|
EFI_STATUS Status;
|
|
UINTN TransLen;
|
|
|
|
*CmdStatus = USB_MASS_CMD_SUCCESS;
|
|
UsbCbi = (USB_CBI_PROTOCOL *) Context;
|
|
|
|
//
|
|
// Send the command to the device. Return immediately if device
|
|
// rejects the command.
|
|
//
|
|
Status = UsbCbiSendCommand (UsbCbi, Cmd, CmdLen, Timeout);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((mUsbCbiError, "UsbCbiExecCommand: UsbCbiSendCommand (%r)\n",Status));
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Transfer the data, return this status if no interrupt endpoint
|
|
// is used to report the transfer status.
|
|
//
|
|
TransLen = (UINTN) DataLen;
|
|
|
|
Status = UsbCbiDataTransfer (UsbCbi, DataDir, Data, &TransLen, Timeout);
|
|
if (EFI_ERROR (Status) || UsbCbi->InterruptEndpoint == NULL) {
|
|
DEBUG ((mUsbCbiError, "UsbCbiExecCommand: UsbCbiDataTransfer (%r)\n",Status));
|
|
return Status;
|
|
}
|
|
|
|
if (UsbCbi->Transport.Protocol == USB_MASS_STORE_CBI1) {
|
|
//
|
|
// No completion interrupt required
|
|
//
|
|
return EFI_SUCCESS;
|
|
}
|
|
//
|
|
// Get the status for CBI0, if that succeeds, interpret the result
|
|
//
|
|
Status = UsbCbiGetStatus (UsbCbi, Timeout, &Result);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((mUsbCbiError, "UsbCbiExecCommand: UsbCbiGetStatus (%r)\n",Status));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (UsbCbi->Interface.InterfaceSubClass == USB_MASS_STORE_UFI) {
|
|
//
|
|
// For UFI device, ASC and ASCQ are returned.
|
|
//
|
|
if (Result.Type != 0) {
|
|
*CmdStatus = USB_MASS_CMD_FAIL;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Check page 27, CBI spec 1.1 for vaious reture status.
|
|
//
|
|
switch (Result.Value & 0x03) {
|
|
case 0x00:
|
|
//
|
|
// Pass
|
|
//
|
|
*CmdStatus = USB_MASS_CMD_SUCCESS;
|
|
break;
|
|
|
|
case 0x02:
|
|
//
|
|
// Phase Error, response with reset. Fall through to Fail.
|
|
//
|
|
UsbCbiResetDevice (UsbCbi, FALSE);
|
|
*CmdStatus = USB_MASS_CMD_PHASE_ERROR;
|
|
break;
|
|
|
|
case 0x01:
|
|
//
|
|
// Fail
|
|
//
|
|
*CmdStatus = USB_MASS_CMD_FAIL;
|
|
break;
|
|
|
|
case 0x03:
|
|
//
|
|
// Persistent Fail, need to send REQUEST SENSE.
|
|
//
|
|
*CmdStatus = USB_MASS_CMD_PERSISTENT;
|
|
break;
|
|
}
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Call the Usb mass storage class transport protocol to
|
|
reset the device. The reset is defined as a Non-Data
|
|
command. Don't use UsbCbiExecCommand to send the command
|
|
to device because that may introduce recursive loop.
|
|
|
|
@param Context The USB CBI device protocol
|
|
|
|
@retval EFI_SUCCESS the device is reset
|
|
@retval Others Failed to reset the device
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
UsbCbiResetDevice (
|
|
IN VOID *Context,
|
|
IN BOOLEAN ExtendedVerification
|
|
)
|
|
{
|
|
UINT8 ResetCmd[USB_CBI_RESET_CMD_LEN];
|
|
USB_CBI_PROTOCOL *UsbCbi;
|
|
USB_CBI_STATUS Result;
|
|
EFI_STATUS Status;
|
|
UINT32 Timeout;
|
|
UINT32 ClearResult;
|
|
|
|
UsbCbi = (USB_CBI_PROTOCOL *) Context;
|
|
|
|
//
|
|
// Check is device been detached
|
|
//
|
|
if (mUsbCore->CheckDeviceDetached(UsbCbi->UsbIo) == EFI_SUCCESS) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
//
|
|
// Fill in the reset command.
|
|
//
|
|
SetMem (ResetCmd, USB_CBI_RESET_CMD_LEN, 0xFF);
|
|
|
|
ResetCmd[0] = 0x1D;
|
|
ResetCmd[1] = 0x04;
|
|
Timeout = USB_CBI_RESET_TIMEOUT / USB_MASS_STALL_1_MS;
|
|
|
|
//
|
|
// Send the command to the device. Don't use UsbCbiExecCommand here.
|
|
//
|
|
Status = UsbCbiSendCommand (UsbCbi, ResetCmd, USB_CBI_RESET_CMD_LEN, Timeout);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Just retrieve the status and ignore that. Then stall
|
|
// 50ms to wait it complete
|
|
//
|
|
UsbCbiGetStatus (UsbCbi, Timeout, &Result);
|
|
mUsbCore->Stall (50 * 1000);
|
|
|
|
//
|
|
// Clear the Bulk-In and Bulk-Out stall condition and
|
|
// init data toggle.
|
|
//
|
|
mUsbCore->UsbClearDeviceFeature(
|
|
UsbCbi->UsbIo,
|
|
USB_TARGET_ENDPOINT,
|
|
USB_FEATURE_ENDPOINT_HALT,
|
|
UsbCbi->BulkInEndpoint->EndpointAddress,
|
|
&ClearResult
|
|
);
|
|
mUsbCore->UsbClearDeviceFeature(
|
|
UsbCbi->UsbIo,
|
|
USB_TARGET_ENDPOINT,
|
|
USB_FEATURE_ENDPOINT_HALT,
|
|
UsbCbi->BulkOutEndpoint->EndpointAddress,
|
|
&ClearResult
|
|
);
|
|
return Status;
|
|
}
|
|
|
|
/*++
|
|
|
|
Clean up the CBI protocol's resource
|
|
|
|
@param Context The CBI protocol
|
|
|
|
@retval EFI_SUCCESS The resource is cleaned up.
|
|
|
|
--*/
|
|
STATIC
|
|
EFI_STATUS
|
|
UsbCbiFini (
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
USB_CBI_PROTOCOL *UsbCbi;
|
|
|
|
UsbCbi = (USB_CBI_PROTOCOL *) Context;
|
|
mUsbCore->FreeBuffer (
|
|
sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR),
|
|
UsbCbi
|
|
);
|
|
//
|
|
// Remove Smm address convert table
|
|
//
|
|
mUsbCore->RemoveAddressConvertTable (
|
|
ACT_FUNCTION_POINTER,
|
|
&UsbCbi->Transport.Init
|
|
);
|
|
mUsbCore->RemoveAddressConvertTable (
|
|
ACT_INSTANCE_BODY,
|
|
UsbCbi
|
|
);
|
|
mUsbCore->RemoveAddressConvertTable (
|
|
ACT_INSTANCE_POINTER,
|
|
&UsbCbi->BulkInEndpoint
|
|
);
|
|
mUsbCore->RemoveAddressConvertTable (
|
|
ACT_INSTANCE_POINTER,
|
|
&UsbCbi->BulkOutEndpoint
|
|
);
|
|
mUsbCore->RemoveAddressConvertTable (
|
|
ACT_INSTANCE_POINTER,
|
|
&UsbCbi->InterruptEndpoint
|
|
);
|
|
mUsbCore->RemoveAddressConvertTable (
|
|
ACT_INSTANCE_POINTER,
|
|
&UsbCbi->UsbIo
|
|
);
|
|
return EFI_SUCCESS;
|
|
}
|