mirror of https://github.com/acidanthera/audk.git
1544 lines
44 KiB
C
1544 lines
44 KiB
C
/** @file
|
|
|
|
Usb Bus Driver Binding and Bus IO Protocol.
|
|
|
|
Copyright (c) 2004 - 2017, Intel Corporation. All rights reserved.<BR>
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include "UsbBus.h"
|
|
|
|
EFI_USB_IO_PROTOCOL mUsbIoProtocol = {
|
|
UsbIoControlTransfer,
|
|
UsbIoBulkTransfer,
|
|
UsbIoAsyncInterruptTransfer,
|
|
UsbIoSyncInterruptTransfer,
|
|
UsbIoIsochronousTransfer,
|
|
UsbIoAsyncIsochronousTransfer,
|
|
UsbIoGetDeviceDescriptor,
|
|
UsbIoGetActiveConfigDescriptor,
|
|
UsbIoGetInterfaceDescriptor,
|
|
UsbIoGetEndpointDescriptor,
|
|
UsbIoGetStringDescriptor,
|
|
UsbIoGetSupportedLanguages,
|
|
UsbIoPortReset
|
|
};
|
|
|
|
EFI_DRIVER_BINDING_PROTOCOL mUsbBusDriverBinding = {
|
|
UsbBusControllerDriverSupported,
|
|
UsbBusControllerDriverStart,
|
|
UsbBusControllerDriverStop,
|
|
0xa,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
/**
|
|
USB_IO function to execute a control transfer. This
|
|
function will execute the USB transfer. If transfer
|
|
successes, it will sync the internal state of USB bus
|
|
with device state.
|
|
|
|
@param This The USB_IO instance
|
|
@param Request The control transfer request
|
|
@param Direction Direction for data stage
|
|
@param Timeout The time to wait before timeout
|
|
@param Data The buffer holding the data
|
|
@param DataLength Then length of the data
|
|
@param UsbStatus USB result
|
|
|
|
@retval EFI_INVALID_PARAMETER The parameters are invalid
|
|
@retval EFI_SUCCESS The control transfer succeeded.
|
|
@retval Others Failed to execute the transfer
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbIoControlTransfer (
|
|
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 *UsbStatus
|
|
)
|
|
{
|
|
USB_DEVICE *Dev;
|
|
USB_INTERFACE *UsbIf;
|
|
USB_ENDPOINT_DESC *EpDesc;
|
|
EFI_TPL OldTpl;
|
|
EFI_STATUS Status;
|
|
UINTN RequestedDataLength;
|
|
|
|
if (UsbStatus == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
|
|
|
|
UsbIf = USB_INTERFACE_FROM_USBIO (This);
|
|
Dev = UsbIf->Device;
|
|
|
|
RequestedDataLength = DataLength;
|
|
Status = UsbHcControlTransfer (
|
|
Dev->Bus,
|
|
Dev->Address,
|
|
Dev->Speed,
|
|
Dev->MaxPacket0,
|
|
Request,
|
|
Direction,
|
|
Data,
|
|
&DataLength,
|
|
(UINTN) Timeout,
|
|
&Dev->Translator,
|
|
UsbStatus
|
|
);
|
|
//
|
|
// If the request completed sucessfully and the Direction of the request is
|
|
// EfiUsbDataIn or EfiUsbDataOut, then make sure the actual number of bytes
|
|
// transfered is the same as the number of bytes requested. If a different
|
|
// number of bytes were transfered, then return EFI_DEVICE_ERROR.
|
|
//
|
|
if (!EFI_ERROR (Status)) {
|
|
if (Direction != EfiUsbNoData && DataLength != RequestedDataLength) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto ON_EXIT;
|
|
}
|
|
}
|
|
|
|
if (EFI_ERROR (Status) || (*UsbStatus != EFI_USB_NOERROR)) {
|
|
//
|
|
// Clear TT buffer when CTRL/BULK split transaction failes
|
|
// Clear the TRANSLATOR TT buffer, not parent's buffer
|
|
//
|
|
ASSERT (Dev->Translator.TranslatorHubAddress < Dev->Bus->MaxDevices);
|
|
if (Dev->Translator.TranslatorHubAddress != 0) {
|
|
UsbHubCtrlClearTTBuffer (
|
|
Dev->Bus->Devices[Dev->Translator.TranslatorHubAddress],
|
|
Dev->Translator.TranslatorPortNumber,
|
|
Dev->Address,
|
|
0,
|
|
USB_ENDPOINT_CONTROL
|
|
);
|
|
}
|
|
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Some control transfer will change the device's internal
|
|
// status, such as Set_Configuration and Set_Interface.
|
|
// We must synchronize the bus driver's status with that in
|
|
// device. We ignore the Set_Descriptor request because it's
|
|
// hardly used by any device, especially in pre-boot environment
|
|
//
|
|
|
|
//
|
|
// Reset the endpoint toggle when endpoint stall is cleared
|
|
//
|
|
if ((Request->Request == USB_REQ_CLEAR_FEATURE) &&
|
|
(Request->RequestType == USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD,
|
|
USB_TARGET_ENDPOINT)) &&
|
|
(Request->Value == USB_FEATURE_ENDPOINT_HALT)) {
|
|
|
|
EpDesc = UsbGetEndpointDesc (UsbIf, (UINT8) Request->Index);
|
|
|
|
if (EpDesc != NULL) {
|
|
EpDesc->Toggle = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Select a new configuration. This is a dangerous action. Upper driver
|
|
// should stop use its current UsbIo after calling this driver. The old
|
|
// UsbIo will be uninstalled and new UsbIo be installed. We can't use
|
|
// ReinstallProtocol since interfaces in different configuration may be
|
|
// completely irrelevant.
|
|
//
|
|
if ((Request->Request == USB_REQ_SET_CONFIG) &&
|
|
(Request->RequestType == USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD,
|
|
USB_TARGET_DEVICE))) {
|
|
//
|
|
// Don't re-create the USB interfaces if configuration isn't changed.
|
|
//
|
|
if ((Dev->ActiveConfig != NULL) &&
|
|
(Request->Value == Dev->ActiveConfig->Desc.ConfigurationValue)) {
|
|
|
|
goto ON_EXIT;
|
|
}
|
|
DEBUG ((EFI_D_INFO, "UsbIoControlTransfer: configure changed!!! Do NOT use old UsbIo!!!\n"));
|
|
|
|
if (Dev->ActiveConfig != NULL) {
|
|
UsbRemoveConfig (Dev);
|
|
}
|
|
|
|
if (Request->Value != 0) {
|
|
Status = UsbSelectConfig (Dev, (UINT8) Request->Value);
|
|
}
|
|
|
|
//
|
|
// Exit now, Old USB_IO is invalid now
|
|
//
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// A new alternative setting is selected for the interface.
|
|
// No need to reinstall UsbIo in this case because only
|
|
// underlying communication endpoints are changed. Functionality
|
|
// should remains the same.
|
|
//
|
|
if ((Request->Request == USB_REQ_SET_INTERFACE) &&
|
|
(Request->RequestType == USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD,
|
|
USB_TARGET_INTERFACE)) &&
|
|
(Request->Index == UsbIf->IfSetting->Desc.InterfaceNumber)) {
|
|
|
|
Status = UsbSelectSetting (UsbIf->IfDesc, (UINT8) Request->Value);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
ASSERT (UsbIf->IfDesc->ActiveIndex < USB_MAX_INTERFACE_SETTING);
|
|
UsbIf->IfSetting = UsbIf->IfDesc->Settings[UsbIf->IfDesc->ActiveIndex];
|
|
}
|
|
}
|
|
|
|
ON_EXIT:
|
|
gBS->RestoreTPL (OldTpl);
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Execute a bulk transfer to the device endpoint.
|
|
|
|
@param This The USB IO instance.
|
|
@param Endpoint The device endpoint.
|
|
@param Data The data to transfer.
|
|
@param DataLength The length of the data to transfer.
|
|
@param Timeout Time to wait before timeout.
|
|
@param UsbStatus The result of USB transfer.
|
|
|
|
@retval EFI_SUCCESS The bulk transfer is OK.
|
|
@retval EFI_INVALID_PARAMETER Some parameters are invalid.
|
|
@retval Others Failed to execute transfer, reason returned in
|
|
UsbStatus.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbIoBulkTransfer (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
IN UINT8 Endpoint,
|
|
IN OUT VOID *Data,
|
|
IN OUT UINTN *DataLength,
|
|
IN UINTN Timeout,
|
|
OUT UINT32 *UsbStatus
|
|
)
|
|
{
|
|
USB_DEVICE *Dev;
|
|
USB_INTERFACE *UsbIf;
|
|
USB_ENDPOINT_DESC *EpDesc;
|
|
UINT8 BufNum;
|
|
UINT8 Toggle;
|
|
EFI_TPL OldTpl;
|
|
EFI_STATUS Status;
|
|
|
|
if ((USB_ENDPOINT_ADDR (Endpoint) == 0) || (USB_ENDPOINT_ADDR(Endpoint) > 15) ||
|
|
(UsbStatus == NULL)) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
|
|
|
|
UsbIf = USB_INTERFACE_FROM_USBIO (This);
|
|
Dev = UsbIf->Device;
|
|
|
|
EpDesc = UsbGetEndpointDesc (UsbIf, Endpoint);
|
|
|
|
if ((EpDesc == NULL) || (USB_ENDPOINT_TYPE (&EpDesc->Desc) != USB_ENDPOINT_BULK)) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
BufNum = 1;
|
|
Toggle = EpDesc->Toggle;
|
|
Status = UsbHcBulkTransfer (
|
|
Dev->Bus,
|
|
Dev->Address,
|
|
Endpoint,
|
|
Dev->Speed,
|
|
EpDesc->Desc.MaxPacketSize,
|
|
BufNum,
|
|
&Data,
|
|
DataLength,
|
|
&Toggle,
|
|
Timeout,
|
|
&Dev->Translator,
|
|
UsbStatus
|
|
);
|
|
|
|
EpDesc->Toggle = Toggle;
|
|
|
|
if (EFI_ERROR (Status) || (*UsbStatus != EFI_USB_NOERROR)) {
|
|
//
|
|
// Clear TT buffer when CTRL/BULK split transaction failes.
|
|
// Clear the TRANSLATOR TT buffer, not parent's buffer
|
|
//
|
|
ASSERT (Dev->Translator.TranslatorHubAddress < Dev->Bus->MaxDevices);
|
|
if (Dev->Translator.TranslatorHubAddress != 0) {
|
|
UsbHubCtrlClearTTBuffer (
|
|
Dev->Bus->Devices[Dev->Translator.TranslatorHubAddress],
|
|
Dev->Translator.TranslatorPortNumber,
|
|
Dev->Address,
|
|
0,
|
|
USB_ENDPOINT_BULK
|
|
);
|
|
}
|
|
}
|
|
|
|
ON_EXIT:
|
|
gBS->RestoreTPL (OldTpl);
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Execute a synchronous interrupt transfer.
|
|
|
|
@param This The USB IO instance.
|
|
@param Endpoint The device endpoint.
|
|
@param Data The data to transfer.
|
|
@param DataLength The length of the data to transfer.
|
|
@param Timeout Time to wait before timeout.
|
|
@param UsbStatus The result of USB transfer.
|
|
|
|
@retval EFI_SUCCESS The synchronous interrupt transfer is OK.
|
|
@retval EFI_INVALID_PARAMETER Some parameters are invalid.
|
|
@retval Others Failed to execute transfer, reason returned in
|
|
UsbStatus.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbIoSyncInterruptTransfer (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
IN UINT8 Endpoint,
|
|
IN OUT VOID *Data,
|
|
IN OUT UINTN *DataLength,
|
|
IN UINTN Timeout,
|
|
OUT UINT32 *UsbStatus
|
|
)
|
|
{
|
|
USB_DEVICE *Dev;
|
|
USB_INTERFACE *UsbIf;
|
|
USB_ENDPOINT_DESC *EpDesc;
|
|
EFI_TPL OldTpl;
|
|
UINT8 Toggle;
|
|
EFI_STATUS Status;
|
|
|
|
if ((USB_ENDPOINT_ADDR (Endpoint) == 0) || (USB_ENDPOINT_ADDR(Endpoint) > 15) ||
|
|
(UsbStatus == NULL)) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
|
|
|
|
UsbIf = USB_INTERFACE_FROM_USBIO (This);
|
|
Dev = UsbIf->Device;
|
|
|
|
EpDesc = UsbGetEndpointDesc (UsbIf, Endpoint);
|
|
|
|
if ((EpDesc == NULL) || (USB_ENDPOINT_TYPE (&EpDesc->Desc) != USB_ENDPOINT_INTERRUPT)) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Toggle = EpDesc->Toggle;
|
|
Status = UsbHcSyncInterruptTransfer (
|
|
Dev->Bus,
|
|
Dev->Address,
|
|
Endpoint,
|
|
Dev->Speed,
|
|
EpDesc->Desc.MaxPacketSize,
|
|
Data,
|
|
DataLength,
|
|
&Toggle,
|
|
Timeout,
|
|
&Dev->Translator,
|
|
UsbStatus
|
|
);
|
|
|
|
EpDesc->Toggle = Toggle;
|
|
|
|
ON_EXIT:
|
|
gBS->RestoreTPL (OldTpl);
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Queue a new asynchronous interrupt transfer, or remove the old
|
|
request if (IsNewTransfer == FALSE).
|
|
|
|
@param This The USB_IO instance.
|
|
@param Endpoint The device endpoint.
|
|
@param IsNewTransfer Whether this is a new request, if it's old, remove
|
|
the request.
|
|
@param PollInterval The interval to poll the transfer result, (in ms).
|
|
@param DataLength The length of perodic data transfer.
|
|
@param Callback The function to call periodicaly when transfer is
|
|
ready.
|
|
@param Context The context to the callback.
|
|
|
|
@retval EFI_SUCCESS New transfer is queued or old request is removed.
|
|
@retval EFI_INVALID_PARAMETER Some parameters are invalid.
|
|
@retval Others Failed to queue the new request or remove the old
|
|
request.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbIoAsyncInterruptTransfer (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
IN UINT8 Endpoint,
|
|
IN BOOLEAN IsNewTransfer,
|
|
IN UINTN PollInterval, OPTIONAL
|
|
IN UINTN DataLength, OPTIONAL
|
|
IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, OPTIONAL
|
|
IN VOID *Context OPTIONAL
|
|
)
|
|
{
|
|
USB_DEVICE *Dev;
|
|
USB_INTERFACE *UsbIf;
|
|
USB_ENDPOINT_DESC *EpDesc;
|
|
EFI_TPL OldTpl;
|
|
UINT8 Toggle;
|
|
EFI_STATUS Status;
|
|
|
|
if ((USB_ENDPOINT_ADDR (Endpoint) == 0) || (USB_ENDPOINT_ADDR (Endpoint) > 15)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
|
|
UsbIf = USB_INTERFACE_FROM_USBIO (This);
|
|
Dev = UsbIf->Device;
|
|
|
|
EpDesc = UsbGetEndpointDesc (UsbIf, Endpoint);
|
|
|
|
if ((EpDesc == NULL) || (USB_ENDPOINT_TYPE (&EpDesc->Desc) != USB_ENDPOINT_INTERRUPT)) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Toggle = EpDesc->Toggle;
|
|
Status = UsbHcAsyncInterruptTransfer (
|
|
Dev->Bus,
|
|
Dev->Address,
|
|
Endpoint,
|
|
Dev->Speed,
|
|
EpDesc->Desc.MaxPacketSize,
|
|
IsNewTransfer,
|
|
&Toggle,
|
|
PollInterval,
|
|
DataLength,
|
|
&Dev->Translator,
|
|
Callback,
|
|
Context
|
|
);
|
|
|
|
EpDesc->Toggle = Toggle;
|
|
|
|
ON_EXIT:
|
|
gBS->RestoreTPL (OldTpl);
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Execute a synchronous isochronous transfer.
|
|
|
|
@param This The USB IO instance.
|
|
@param DeviceEndpoint The device endpoint.
|
|
@param Data The data to transfer.
|
|
@param DataLength The length of the data to transfer.
|
|
@param UsbStatus The result of USB transfer.
|
|
|
|
@retval EFI_UNSUPPORTED Currently isochronous transfer isn't supported.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbIoIsochronousTransfer (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
IN UINT8 DeviceEndpoint,
|
|
IN OUT VOID *Data,
|
|
IN UINTN DataLength,
|
|
OUT UINT32 *Status
|
|
)
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
|
|
/**
|
|
Queue an asynchronous isochronous transfer.
|
|
|
|
@param This The USB_IO instance.
|
|
@param DeviceEndpoint The device endpoint.
|
|
@param Data The data to transfer.
|
|
@param DataLength The length of perodic data transfer.
|
|
@param IsochronousCallBack The function to call periodicaly when transfer is
|
|
ready.
|
|
@param Context The context to the callback.
|
|
|
|
@retval EFI_UNSUPPORTED Currently isochronous transfer isn't supported.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbIoAsyncIsochronousTransfer (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
IN UINT8 DeviceEndpoint,
|
|
IN OUT VOID *Data,
|
|
IN UINTN DataLength,
|
|
IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack,
|
|
IN VOID *Context OPTIONAL
|
|
)
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
|
|
/**
|
|
Retrieve the device descriptor of the device.
|
|
|
|
@param This The USB IO instance.
|
|
@param Descriptor The variable to receive the device descriptor.
|
|
|
|
@retval EFI_SUCCESS The device descriptor is returned.
|
|
@retval EFI_INVALID_PARAMETER The parameter is invalid.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbIoGetDeviceDescriptor (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
OUT EFI_USB_DEVICE_DESCRIPTOR *Descriptor
|
|
)
|
|
{
|
|
USB_DEVICE *Dev;
|
|
USB_INTERFACE *UsbIf;
|
|
EFI_TPL OldTpl;
|
|
|
|
if (Descriptor == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
|
|
|
|
UsbIf = USB_INTERFACE_FROM_USBIO (This);
|
|
Dev = UsbIf->Device;
|
|
|
|
CopyMem (Descriptor, &Dev->DevDesc->Desc, sizeof (EFI_USB_DEVICE_DESCRIPTOR));
|
|
|
|
gBS->RestoreTPL (OldTpl);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Return the configuration descriptor of the current active configuration.
|
|
|
|
@param This The USB IO instance.
|
|
@param Descriptor The USB configuration descriptor.
|
|
|
|
@retval EFI_SUCCESS The active configuration descriptor is returned.
|
|
@retval EFI_INVALID_PARAMETER Some parameter is invalid.
|
|
@retval EFI_NOT_FOUND Currently no active configuration is selected.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbIoGetActiveConfigDescriptor (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
OUT EFI_USB_CONFIG_DESCRIPTOR *Descriptor
|
|
)
|
|
{
|
|
USB_DEVICE *Dev;
|
|
USB_INTERFACE *UsbIf;
|
|
EFI_STATUS Status;
|
|
EFI_TPL OldTpl;
|
|
|
|
if (Descriptor == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
|
|
|
|
UsbIf = USB_INTERFACE_FROM_USBIO (This);
|
|
Dev = UsbIf->Device;
|
|
|
|
if (Dev->ActiveConfig == NULL) {
|
|
Status = EFI_NOT_FOUND;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
CopyMem (Descriptor, &(Dev->ActiveConfig->Desc), sizeof (EFI_USB_CONFIG_DESCRIPTOR));
|
|
|
|
ON_EXIT:
|
|
gBS->RestoreTPL (OldTpl);
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Retrieve the active interface setting descriptor for this USB IO instance.
|
|
|
|
@param This The USB IO instance.
|
|
@param Descriptor The variable to receive active interface setting.
|
|
|
|
@retval EFI_SUCCESS The active interface setting is returned.
|
|
@retval EFI_INVALID_PARAMETER Some parameter is invalid.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbIoGetInterfaceDescriptor (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
OUT EFI_USB_INTERFACE_DESCRIPTOR *Descriptor
|
|
)
|
|
{
|
|
USB_INTERFACE *UsbIf;
|
|
EFI_TPL OldTpl;
|
|
|
|
if (Descriptor == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
|
|
|
|
UsbIf = USB_INTERFACE_FROM_USBIO (This);
|
|
CopyMem (Descriptor, &(UsbIf->IfSetting->Desc), sizeof (EFI_USB_INTERFACE_DESCRIPTOR));
|
|
|
|
gBS->RestoreTPL (OldTpl);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Retrieve the endpoint descriptor from this interface setting.
|
|
|
|
@param This The USB IO instance.
|
|
@param Index The index (start from zero) of the endpoint to
|
|
retrieve.
|
|
@param Descriptor The variable to receive the descriptor.
|
|
|
|
@retval EFI_SUCCESS The endpoint descriptor is returned.
|
|
@retval EFI_INVALID_PARAMETER Some parameter is invalid.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbIoGetEndpointDescriptor (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
IN UINT8 Index,
|
|
OUT EFI_USB_ENDPOINT_DESCRIPTOR *Descriptor
|
|
)
|
|
{
|
|
USB_INTERFACE *UsbIf;
|
|
EFI_TPL OldTpl;
|
|
|
|
OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
|
|
|
|
UsbIf = USB_INTERFACE_FROM_USBIO (This);
|
|
|
|
if ((Descriptor == NULL) || (Index > 15)) {
|
|
gBS->RestoreTPL (OldTpl);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Index >= UsbIf->IfSetting->Desc.NumEndpoints) {
|
|
gBS->RestoreTPL (OldTpl);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
CopyMem (
|
|
Descriptor,
|
|
&(UsbIf->IfSetting->Endpoints[Index]->Desc),
|
|
sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)
|
|
);
|
|
|
|
gBS->RestoreTPL (OldTpl);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Retrieve the supported language ID table from the device.
|
|
|
|
@param This The USB IO instance.
|
|
@param LangIDTable The table to return the language IDs.
|
|
@param TableSize The size, in bytes, of the table LangIDTable.
|
|
|
|
@retval EFI_SUCCESS The language ID is return.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbIoGetSupportedLanguages (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
OUT UINT16 **LangIDTable,
|
|
OUT UINT16 *TableSize
|
|
)
|
|
{
|
|
USB_DEVICE *Dev;
|
|
USB_INTERFACE *UsbIf;
|
|
EFI_TPL OldTpl;
|
|
|
|
OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
|
|
|
|
UsbIf = USB_INTERFACE_FROM_USBIO (This);
|
|
Dev = UsbIf->Device;
|
|
|
|
*LangIDTable = Dev->LangId;
|
|
*TableSize = (UINT16) (Dev->TotalLangId * sizeof (UINT16));
|
|
|
|
gBS->RestoreTPL (OldTpl);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Retrieve an indexed string in the language of LangID.
|
|
|
|
@param This The USB IO instance.
|
|
@param LangID The language ID of the string to retrieve.
|
|
@param StringIndex The index of the string.
|
|
@param String The variable to receive the string.
|
|
|
|
@retval EFI_SUCCESS The string is returned.
|
|
@retval EFI_NOT_FOUND No such string existed.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbIoGetStringDescriptor (
|
|
IN EFI_USB_IO_PROTOCOL *This,
|
|
IN UINT16 LangID,
|
|
IN UINT8 StringIndex,
|
|
OUT CHAR16 **String
|
|
)
|
|
{
|
|
USB_DEVICE *Dev;
|
|
USB_INTERFACE *UsbIf;
|
|
EFI_USB_STRING_DESCRIPTOR *StrDesc;
|
|
EFI_TPL OldTpl;
|
|
UINT8 *Buf;
|
|
UINT8 Index;
|
|
EFI_STATUS Status;
|
|
|
|
if ((StringIndex == 0) || (LangID == 0)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
|
|
|
|
UsbIf = USB_INTERFACE_FROM_USBIO (This);
|
|
Dev = UsbIf->Device;
|
|
|
|
//
|
|
// Check whether language ID is supported
|
|
//
|
|
Status = EFI_NOT_FOUND;
|
|
|
|
for (Index = 0; Index < Dev->TotalLangId; Index++) {
|
|
ASSERT (Index < USB_MAX_LANG_ID);
|
|
if (Dev->LangId[Index] == LangID) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Index == Dev->TotalLangId) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Retrieve the string descriptor then allocate a buffer
|
|
// to hold the string itself.
|
|
//
|
|
StrDesc = UsbGetOneString (Dev, StringIndex, LangID);
|
|
|
|
if (StrDesc == NULL) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
if (StrDesc->Length <= 2) {
|
|
goto FREE_STR;
|
|
}
|
|
|
|
Buf = AllocateZeroPool (StrDesc->Length);
|
|
|
|
if (Buf == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto FREE_STR;
|
|
}
|
|
|
|
CopyMem (Buf, StrDesc->String, StrDesc->Length - 2);
|
|
*String = (CHAR16 *) Buf;
|
|
Status = EFI_SUCCESS;
|
|
|
|
FREE_STR:
|
|
gBS->FreePool (StrDesc);
|
|
|
|
ON_EXIT:
|
|
gBS->RestoreTPL (OldTpl);
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Reset the device, then if that succeeds, reconfigure the
|
|
device with its address and current active configuration.
|
|
|
|
@param This The USB IO instance.
|
|
|
|
@retval EFI_SUCCESS The device is reset and configured.
|
|
@retval Others Failed to reset the device.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbIoPortReset (
|
|
IN EFI_USB_IO_PROTOCOL *This
|
|
)
|
|
{
|
|
USB_INTERFACE *UsbIf;
|
|
USB_INTERFACE *HubIf;
|
|
USB_DEVICE *Dev;
|
|
EFI_TPL OldTpl;
|
|
EFI_STATUS Status;
|
|
UINT8 DevAddress;
|
|
|
|
OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
|
|
|
|
UsbIf = USB_INTERFACE_FROM_USBIO (This);
|
|
Dev = UsbIf->Device;
|
|
|
|
if (UsbIf->IsHub) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
HubIf = Dev->ParentIf;
|
|
Status = HubIf->HubApi->ResetPort (HubIf, Dev->ParentPort);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG (( EFI_D_ERROR, "UsbIoPortReset: failed to reset hub port %d@hub %d, %r \n",
|
|
Dev->ParentPort, Dev->ParentAddr, Status));
|
|
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
HubIf->HubApi->ClearPortChange (HubIf, Dev->ParentPort);
|
|
|
|
//
|
|
// Reset the device to its current address. The device now has an address
|
|
// of ZERO after port reset, so need to set Dev->Address to the device again for
|
|
// host to communicate with it.
|
|
//
|
|
DevAddress = Dev->Address;
|
|
Dev->Address = 0;
|
|
Status = UsbSetAddress (Dev, DevAddress);
|
|
Dev->Address = DevAddress;
|
|
|
|
gBS->Stall (USB_SET_DEVICE_ADDRESS_STALL);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// It may fail due to device disconnection or other reasons.
|
|
//
|
|
DEBUG (( EFI_D_ERROR, "UsbIoPortReset: failed to set address for device %d - %r\n",
|
|
Dev->Address, Status));
|
|
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
DEBUG (( EFI_D_INFO, "UsbIoPortReset: device is now ADDRESSED at %d\n", Dev->Address));
|
|
|
|
//
|
|
// Reset the current active configure, after this device
|
|
// is in CONFIGURED state.
|
|
//
|
|
if (Dev->ActiveConfig != NULL) {
|
|
Status = UsbSetConfig (Dev, Dev->ActiveConfig->Desc.ConfigurationValue);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG (( EFI_D_ERROR, "UsbIoPortReset: failed to set configure for device %d - %r\n",
|
|
Dev->Address, Status));
|
|
}
|
|
}
|
|
|
|
ON_EXIT:
|
|
gBS->RestoreTPL (OldTpl);
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Install Usb Bus Protocol on host controller, and start the Usb bus.
|
|
|
|
@param This The USB bus driver binding instance.
|
|
@param Controller The controller to check.
|
|
@param RemainingDevicePath The remaining device patch.
|
|
|
|
@retval EFI_SUCCESS The controller is controlled by the usb bus.
|
|
@retval EFI_ALREADY_STARTED The controller is already controlled by the usb bus.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbBusBuildProtocol (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
{
|
|
USB_BUS *UsbBus;
|
|
USB_DEVICE *RootHub;
|
|
USB_INTERFACE *RootIf;
|
|
EFI_STATUS Status;
|
|
EFI_STATUS Status2;
|
|
|
|
UsbBus = AllocateZeroPool (sizeof (USB_BUS));
|
|
|
|
if (UsbBus == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
UsbBus->Signature = USB_BUS_SIGNATURE;
|
|
UsbBus->HostHandle = Controller;
|
|
UsbBus->MaxDevices = USB_MAX_DEVICES;
|
|
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiDevicePathProtocolGuid,
|
|
(VOID **) &UsbBus->DevicePath,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_ERROR, "UsbBusStart: Failed to open device path %r\n", Status));
|
|
|
|
FreePool (UsbBus);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Get USB_HC2/USB_HC host controller protocol (EHCI/UHCI).
|
|
// This is for backward compatibility with EFI 1.x. In UEFI
|
|
// 2.x, USB_HC2 replaces USB_HC. We will open both USB_HC2
|
|
// and USB_HC because EHCI driver will install both protocols
|
|
// (for the same reason). If we don't consume both of them,
|
|
// the unconsumed one may be opened by others.
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiUsb2HcProtocolGuid,
|
|
(VOID **) &(UsbBus->Usb2Hc),
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
|
|
Status2 = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiUsbHcProtocolGuid,
|
|
(VOID **) &(UsbBus->UsbHc),
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
|
|
if (EFI_ERROR (Status) && EFI_ERROR (Status2)) {
|
|
DEBUG ((EFI_D_ERROR, "UsbBusStart: Failed to open USB_HC/USB2_HC %r\n", Status));
|
|
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto CLOSE_HC;
|
|
}
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// The EFI_USB2_HC_PROTOCOL is produced for XHCI support.
|
|
// Then its max supported devices are 256. Otherwise it's 128.
|
|
//
|
|
ASSERT (UsbBus->Usb2Hc != NULL);
|
|
if (UsbBus->Usb2Hc->MajorRevision == 0x3) {
|
|
UsbBus->MaxDevices = 256;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Install an EFI_USB_BUS_PROTOCOL to host controller to identify it.
|
|
//
|
|
Status = gBS->InstallProtocolInterface (
|
|
&Controller,
|
|
&gEfiCallerIdGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
&UsbBus->BusId
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_ERROR, "UsbBusStart: Failed to install bus protocol %r\n", Status));
|
|
goto CLOSE_HC;
|
|
}
|
|
|
|
//
|
|
// Initial the wanted child device path list, and add first RemainingDevicePath
|
|
//
|
|
InitializeListHead (&UsbBus->WantedUsbIoDPList);
|
|
Status = UsbBusAddWantedUsbIoDP (&UsbBus->BusId, RemainingDevicePath);
|
|
ASSERT (!EFI_ERROR (Status));
|
|
//
|
|
// Create a fake usb device for root hub
|
|
//
|
|
RootHub = AllocateZeroPool (sizeof (USB_DEVICE));
|
|
|
|
if (RootHub == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto UNINSTALL_USBBUS;
|
|
}
|
|
|
|
RootIf = AllocateZeroPool (sizeof (USB_INTERFACE));
|
|
|
|
if (RootIf == NULL) {
|
|
FreePool (RootHub);
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto FREE_ROOTHUB;
|
|
}
|
|
|
|
RootHub->Bus = UsbBus;
|
|
RootHub->NumOfInterface = 1;
|
|
RootHub->Interfaces[0] = RootIf;
|
|
RootHub->Tier = 0;
|
|
RootIf->Signature = USB_INTERFACE_SIGNATURE;
|
|
RootIf->Device = RootHub;
|
|
RootIf->DevicePath = UsbBus->DevicePath;
|
|
|
|
//
|
|
// Report Status Code here since we will enumerate the USB devices
|
|
//
|
|
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
|
|
EFI_PROGRESS_CODE,
|
|
(EFI_IO_BUS_USB | EFI_IOB_PC_DETECT),
|
|
UsbBus->DevicePath
|
|
);
|
|
|
|
Status = mUsbRootHubApi.Init (RootIf);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_ERROR, "UsbBusStart: Failed to init root hub %r\n", Status));
|
|
goto FREE_ROOTHUB;
|
|
}
|
|
|
|
UsbBus->Devices[0] = RootHub;
|
|
|
|
DEBUG ((EFI_D_INFO, "UsbBusStart: usb bus started on %p, root hub %p\n", Controller, RootIf));
|
|
return EFI_SUCCESS;
|
|
|
|
FREE_ROOTHUB:
|
|
if (RootIf != NULL) {
|
|
FreePool (RootIf);
|
|
}
|
|
if (RootHub != NULL) {
|
|
FreePool (RootHub);
|
|
}
|
|
|
|
UNINSTALL_USBBUS:
|
|
gBS->UninstallProtocolInterface (Controller, &gEfiCallerIdGuid, &UsbBus->BusId);
|
|
|
|
CLOSE_HC:
|
|
if (UsbBus->Usb2Hc != NULL) {
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiUsb2HcProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
}
|
|
if (UsbBus->UsbHc != NULL) {
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiUsbHcProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
}
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiDevicePathProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
FreePool (UsbBus);
|
|
|
|
DEBUG ((EFI_D_ERROR, "UsbBusStart: Failed to start bus driver %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
The USB bus driver entry pointer.
|
|
|
|
@param ImageHandle The driver image handle.
|
|
@param SystemTable The system table.
|
|
|
|
@return EFI_SUCCESS The component name protocol is installed.
|
|
@return Others Failed to init the usb driver.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbBusDriverEntryPoint (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
return EfiLibInstallDriverBindingComponentName2 (
|
|
ImageHandle,
|
|
SystemTable,
|
|
&mUsbBusDriverBinding,
|
|
ImageHandle,
|
|
&mUsbBusComponentName,
|
|
&mUsbBusComponentName2
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
Check whether USB bus driver support this device.
|
|
|
|
@param This The USB bus driver binding protocol.
|
|
@param Controller The controller handle to check.
|
|
@param RemainingDevicePath The remaining device path.
|
|
|
|
@retval EFI_SUCCESS The bus supports this controller.
|
|
@retval EFI_UNSUPPORTED This device isn't supported.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbBusControllerDriverSupported (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_DEV_PATH_PTR DevicePathNode;
|
|
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
|
|
EFI_USB2_HC_PROTOCOL *Usb2Hc;
|
|
EFI_USB_HC_PROTOCOL *UsbHc;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Check whether device path is valid
|
|
//
|
|
if (RemainingDevicePath != NULL) {
|
|
//
|
|
// Check if RemainingDevicePath is the End of Device Path Node,
|
|
// if yes, go on checking other conditions
|
|
//
|
|
if (!IsDevicePathEnd (RemainingDevicePath)) {
|
|
//
|
|
// If RemainingDevicePath isn't the End of Device Path Node,
|
|
// check its validation
|
|
//
|
|
DevicePathNode.DevPath = RemainingDevicePath;
|
|
|
|
if ((DevicePathNode.DevPath->Type != MESSAGING_DEVICE_PATH) ||
|
|
(DevicePathNode.DevPath->SubType != MSG_USB_DP &&
|
|
DevicePathNode.DevPath->SubType != MSG_USB_CLASS_DP
|
|
&& DevicePathNode.DevPath->SubType != MSG_USB_WWID_DP
|
|
)) {
|
|
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check whether USB_HC2 protocol is installed
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiUsb2HcProtocolGuid,
|
|
(VOID **) &Usb2Hc,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (Status == EFI_ALREADY_STARTED) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// If failed to open USB_HC2, fall back to USB_HC
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiUsbHcProtocolGuid,
|
|
(VOID **) &UsbHc,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (Status == EFI_ALREADY_STARTED) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Close the USB_HC used to perform the supported test
|
|
//
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiUsbHcProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Close the USB_HC2 used to perform the supported test
|
|
//
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiUsb2HcProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
}
|
|
|
|
//
|
|
// Open the EFI Device Path protocol needed to perform the supported test
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiDevicePathProtocolGuid,
|
|
(VOID **) &ParentDevicePath,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (Status == EFI_ALREADY_STARTED) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Close protocol, don't use device path protocol in the Support() function
|
|
//
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiDevicePathProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Start to process the controller.
|
|
|
|
@param This The USB bus driver binding instance.
|
|
@param Controller The controller to check.
|
|
@param RemainingDevicePath The remaining device patch.
|
|
|
|
@retval EFI_SUCCESS The controller is controlled by the usb bus.
|
|
@retval EFI_ALREADY_STARTED The controller is already controlled by the usb
|
|
bus.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbBusControllerDriverStart (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_USB_BUS_PROTOCOL *UsbBusId;
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
|
|
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiDevicePathProtocolGuid,
|
|
(VOID **) &ParentDevicePath,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Report Status Code here since we will initialize the host controller
|
|
//
|
|
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
|
|
EFI_PROGRESS_CODE,
|
|
(EFI_IO_BUS_USB | EFI_IOB_PC_INIT),
|
|
ParentDevicePath
|
|
);
|
|
|
|
//
|
|
// Locate the USB bus protocol, if it is found, USB bus
|
|
// is already started on this controller.
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiCallerIdGuid,
|
|
(VOID **) &UsbBusId,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// If first start, build the bus execute environment and install bus protocol
|
|
//
|
|
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_IO_BUS_USB | EFI_P_PC_ENABLE));
|
|
Status = UsbBusBuildProtocol (This, Controller, RemainingDevicePath);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
//
|
|
// Try get the Usb Bus protocol interface again
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiCallerIdGuid,
|
|
(VOID **) &UsbBusId,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
ASSERT (!EFI_ERROR (Status));
|
|
} else {
|
|
//
|
|
// USB Bus driver need to control the recursive connect policy of the bus, only those wanted
|
|
// usb child device will be recursively connected.
|
|
// The RemainingDevicePath indicate the child usb device which user want to fully recursively connecte this time.
|
|
// All wanted usb child devices will be remembered by the usb bus driver itself.
|
|
// If RemainingDevicePath == NULL, all the usb child devices in the usb bus are wanted devices.
|
|
//
|
|
// Save the passed in RemainingDevicePath this time
|
|
//
|
|
if (RemainingDevicePath != NULL) {
|
|
if (IsDevicePathEnd (RemainingDevicePath)) {
|
|
//
|
|
// If RemainingDevicePath is the End of Device Path Node,
|
|
// skip enumerate any device and return EFI_SUCESSS
|
|
//
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
Status = UsbBusAddWantedUsbIoDP (UsbBusId, RemainingDevicePath);
|
|
ASSERT (!EFI_ERROR (Status));
|
|
//
|
|
// Ensure all wanted child usb devices are fully recursively connected
|
|
//
|
|
Status = UsbBusRecursivelyConnectWantedUsbIo (UsbBusId);
|
|
ASSERT (!EFI_ERROR (Status));
|
|
}
|
|
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Stop handle the controller by this USB bus driver.
|
|
|
|
@param This The USB bus driver binding protocol.
|
|
@param Controller The controller to release.
|
|
@param NumberOfChildren The child of USB bus that opened controller
|
|
BY_CHILD.
|
|
@param ChildHandleBuffer The array of child handle.
|
|
|
|
@retval EFI_SUCCESS The controller or children are stopped.
|
|
@retval EFI_DEVICE_ERROR Failed to stop the driver.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UsbBusControllerDriverStop (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN UINTN NumberOfChildren,
|
|
IN EFI_HANDLE *ChildHandleBuffer
|
|
)
|
|
{
|
|
USB_BUS *Bus;
|
|
USB_DEVICE *RootHub;
|
|
USB_DEVICE *UsbDev;
|
|
USB_INTERFACE *RootIf;
|
|
USB_INTERFACE *UsbIf;
|
|
EFI_USB_BUS_PROTOCOL *BusId;
|
|
EFI_USB_IO_PROTOCOL *UsbIo;
|
|
EFI_TPL OldTpl;
|
|
UINTN Index;
|
|
EFI_STATUS Status;
|
|
EFI_STATUS ReturnStatus;
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
if (NumberOfChildren > 0) {
|
|
//
|
|
// BugBug: Raise TPL to callback level instead of USB_BUS_TPL to avoid TPL conflict
|
|
//
|
|
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
|
|
|
|
ReturnStatus = EFI_SUCCESS;
|
|
for (Index = 0; Index < NumberOfChildren; Index++) {
|
|
Status = gBS->OpenProtocol (
|
|
ChildHandleBuffer[Index],
|
|
&gEfiUsbIoProtocolGuid,
|
|
(VOID **) &UsbIo,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// It is possible that the child has already been released:
|
|
// 1. For combo device, free one device will release others.
|
|
// 2. If a hub is released, all devices on its down facing
|
|
// ports are released also.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
UsbIf = USB_INTERFACE_FROM_USBIO (UsbIo);
|
|
UsbDev = UsbIf->Device;
|
|
|
|
ReturnStatus = UsbRemoveDevice (UsbDev);
|
|
}
|
|
|
|
gBS->RestoreTPL (OldTpl);
|
|
return ReturnStatus;
|
|
}
|
|
|
|
DEBUG (( EFI_D_INFO, "UsbBusStop: usb bus stopped on %p\n", Controller));
|
|
|
|
//
|
|
// Locate USB_BUS for the current host controller
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiCallerIdGuid,
|
|
(VOID **) &BusId,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Bus = USB_BUS_FROM_THIS (BusId);
|
|
|
|
//
|
|
// Stop the root hub, then free all the devices
|
|
//
|
|
// BugBug: Raise TPL to callback level instead of USB_BUS_TPL to avoid TPL conflict
|
|
//
|
|
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
|
|
|
|
RootHub = Bus->Devices[0];
|
|
RootIf = RootHub->Interfaces[0];
|
|
|
|
ASSERT (Bus->MaxDevices <= 256);
|
|
ReturnStatus = EFI_SUCCESS;
|
|
for (Index = 1; Index < Bus->MaxDevices; Index++) {
|
|
if (Bus->Devices[Index] != NULL) {
|
|
Status = UsbRemoveDevice (Bus->Devices[Index]);
|
|
if (EFI_ERROR (Status)) {
|
|
ReturnStatus = Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
gBS->RestoreTPL (OldTpl);
|
|
|
|
if (!EFI_ERROR (ReturnStatus)) {
|
|
mUsbRootHubApi.Release (RootIf);
|
|
gBS->FreePool (RootIf);
|
|
gBS->FreePool (RootHub);
|
|
|
|
Status = UsbBusFreeUsbDPList (&Bus->WantedUsbIoDPList);
|
|
ASSERT (!EFI_ERROR (Status));
|
|
|
|
//
|
|
// Uninstall the bus identifier and close USB_HC/USB2_HC protocols
|
|
//
|
|
gBS->UninstallProtocolInterface (Controller, &gEfiCallerIdGuid, &Bus->BusId);
|
|
|
|
if (Bus->Usb2Hc != NULL) {
|
|
Status = gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiUsb2HcProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
}
|
|
|
|
if (Bus->UsbHc != NULL) {
|
|
Status = gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiUsbHcProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
}
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiDevicePathProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
gBS->FreePool (Bus);
|
|
}
|
|
}
|
|
return Status;
|
|
}
|