From 7f05fa00f73038b425002566d3afe6c3ade2ccdb Mon Sep 17 00:00:00 2001 From: Guo Mang Date: Thu, 22 Dec 2016 15:55:38 +0800 Subject: MdeModulePkg: Move to new location Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang --- .../MdeModulePkg/Bus/Usb/UsbBusDxe/ComponentName.c | 309 ++++ Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.c | 1532 ++++++++++++++++++++ Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h | 770 ++++++++++ Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf | 79 + Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.uni | Bin 0 -> 1934 bytes .../Bus/Usb/UsbBusDxe/UsbBusDxeExtra.uni | Bin 0 -> 1336 bytes Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c | 978 +++++++++++++ Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h | 233 +++ Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c | 1065 ++++++++++++++ Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.h | 203 +++ Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c | 1411 ++++++++++++++++++ Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h | 199 +++ Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c | 1377 ++++++++++++++++++ Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.h | 398 +++++ 14 files changed, 8554 insertions(+) create mode 100644 Core/MdeModulePkg/Bus/Usb/UsbBusDxe/ComponentName.c create mode 100644 Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.c create mode 100644 Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h create mode 100644 Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf create mode 100644 Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.uni create mode 100644 Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxeExtra.uni create mode 100644 Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c create mode 100644 Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h create mode 100644 Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c create mode 100644 Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.h create mode 100644 Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c create mode 100644 Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h create mode 100644 Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c create mode 100644 Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.h (limited to 'Core/MdeModulePkg/Bus/Usb/UsbBusDxe') diff --git a/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/ComponentName.c b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/ComponentName.c new file mode 100644 index 0000000000..dc00208632 --- /dev/null +++ b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/ComponentName.c @@ -0,0 +1,309 @@ +/** @file + + UEFI Component Name(2) protocol implementation for Usb Bus driver. + +Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.
+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 + + +#include + + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbBusComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbBusComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL mUsbBusComponentName = { + UsbBusComponentNameGetDriverName, + UsbBusComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL mUsbBusComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) UsbBusComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) UsbBusComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUsbBusDriverNameTable[] = { + { "eng;en", L"Usb Bus Driver" }, + { NULL , NULL } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbBusComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mUsbBusDriverNameTable, + DriverName, + (BOOLEAN)(This == &mUsbBusComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbBusComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.c b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.c new file mode 100644 index 0000000000..ef002f53bb --- /dev/null +++ b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.c @@ -0,0 +1,1532 @@ +/** @file + + Usb Bus Driver Binding and Bus IO Protocol. + +Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved.
+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; + + if (UsbStatus == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (USB_BUS_TPL); + + UsbIf = USB_INTERFACE_FROM_USBIO (This); + Dev = UsbIf->Device; + + Status = UsbHcControlTransfer ( + Dev->Bus, + Dev->Address, + Dev->Speed, + Dev->MaxPacket0, + Request, + Direction, + Data, + &DataLength, + (UINTN) Timeout, + &Dev->Translator, + UsbStatus + ); + + 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; + } + } + + UsbHcReset (UsbBus, EFI_USB_HC_RESET_GLOBAL); + UsbHcSetState (UsbBus, EfiUsbHcStateOperational); + + // + // 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; +} diff --git a/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h new file mode 100644 index 0000000000..9ede83ab7e --- /dev/null +++ b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h @@ -0,0 +1,770 @@ +/** @file + + Usb Bus Driver Binding and Bus IO Protocol. + +Copyright (c) 2004 - 2012, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef _EFI_USB_BUS_H_ +#define _EFI_USB_BUS_H_ + + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +typedef struct _USB_DEVICE USB_DEVICE; +typedef struct _USB_INTERFACE USB_INTERFACE; +typedef struct _USB_BUS USB_BUS; +typedef struct _USB_HUB_API USB_HUB_API; + + +#include "UsbUtility.h" +#include "UsbDesc.h" +#include "UsbHub.h" +#include "UsbEnumer.h" + +// +// USB bus timeout experience values +// + +#define USB_MAX_LANG_ID 16 +#define USB_MAX_INTERFACE 16 +#define USB_MAX_DEVICES 128 + +#define USB_BUS_1_MILLISECOND 1000 + +// +// Roothub and hub's polling interval, set by experience, +// The unit of roothub is 100us, means 100ms as interval, and +// the unit of hub is 1ms, means 64ms as interval. +// +#define USB_ROOTHUB_POLL_INTERVAL (100 * 10000U) +#define USB_HUB_POLL_INTERVAL 64 + +// +// Wait for port stable to work, refers to specification +// [USB20-9.1.2] +// +#define USB_WAIT_PORT_STABLE_STALL (100 * USB_BUS_1_MILLISECOND) + +// +// Wait for port statue reg change, set by experience +// +#define USB_WAIT_PORT_STS_CHANGE_STALL (100) + +// +// Wait for set device address, refers to specification +// [USB20-9.2.6.3, it says 2ms] +// +#define USB_SET_DEVICE_ADDRESS_STALL (2 * USB_BUS_1_MILLISECOND) + +// +// Wait for retry max packet size, set by experience +// +#define USB_RETRY_MAX_PACK_SIZE_STALL (100 * USB_BUS_1_MILLISECOND) + +// +// Wait for hub port power-on, refers to specification +// [USB20-11.23.2] +// +#define USB_SET_PORT_POWER_STALL (2 * USB_BUS_1_MILLISECOND) + +// +// Wait for port reset, refers to specification +// [USB20-7.1.7.5, it says 10ms for hub and 50ms for +// root hub] +// +// According to USB2.0, Chapter 11.5.1.5 Resetting, +// the worst case for TDRST is 20ms +// +#define USB_SET_PORT_RESET_STALL (20 * USB_BUS_1_MILLISECOND) +#define USB_SET_ROOT_PORT_RESET_STALL (50 * USB_BUS_1_MILLISECOND) + +// +// Wait for port recovery to accept SetAddress, refers to specification +// [USB20-7.1.7.5, it says 10 ms for TRSTRCY] +// +#define USB_SET_PORT_RECOVERY_STALL (10 * USB_BUS_1_MILLISECOND) + +// +// Wait for clear roothub port reset, set by experience +// +#define USB_CLR_ROOT_PORT_RESET_STALL (20 * USB_BUS_1_MILLISECOND) + +// +// Wait for set roothub port enable, set by experience +// +#define USB_SET_ROOT_PORT_ENABLE_STALL (20 * USB_BUS_1_MILLISECOND) + +// +// Send general device request timeout. +// +// The USB Specification 2.0, section 11.24.1 recommends a value of +// 50 milliseconds. We use a value of 500 milliseconds to work +// around slower hubs and devices. +// +#define USB_GENERAL_DEVICE_REQUEST_TIMEOUT 500 + +// +// Send clear feature request timeout, set by experience +// +#define USB_CLEAR_FEATURE_REQUEST_TIMEOUT 10 + +// +// Bus raises TPL to TPL_NOTIFY to serialize all its operations +// to protect shared data structures. +// +#define USB_BUS_TPL TPL_NOTIFY + +#define USB_INTERFACE_SIGNATURE SIGNATURE_32 ('U', 'S', 'B', 'I') +#define USB_BUS_SIGNATURE SIGNATURE_32 ('U', 'S', 'B', 'B') + +#define USB_BIT(a) ((UINTN)(1 << (a))) +#define USB_BIT_IS_SET(Data, Bit) ((BOOLEAN)(((Data) & (Bit)) == (Bit))) + +#define USB_INTERFACE_FROM_USBIO(a) \ + CR(a, USB_INTERFACE, UsbIo, USB_INTERFACE_SIGNATURE) + +#define USB_BUS_FROM_THIS(a) \ + CR(a, USB_BUS, BusId, USB_BUS_SIGNATURE) + +// +// Used to locate USB_BUS +// UsbBusProtocol is the private protocol. +// gEfiCallerIdGuid will be used as its protocol guid. +// +typedef struct _EFI_USB_BUS_PROTOCOL { + UINT64 Reserved; +} EFI_USB_BUS_PROTOCOL; + + +// +// Stands for the real USB device. Each device may +// has several seperately working interfaces. +// +struct _USB_DEVICE { + USB_BUS *Bus; + + // + // Configuration information + // + UINT8 Speed; + UINT8 Address; + UINT32 MaxPacket0; + + // + // The device's descriptors and its configuration + // + USB_DEVICE_DESC *DevDesc; + USB_CONFIG_DESC *ActiveConfig; + + UINT16 LangId [USB_MAX_LANG_ID]; + UINT16 TotalLangId; + + UINT8 NumOfInterface; + USB_INTERFACE *Interfaces [USB_MAX_INTERFACE]; + + // + // Parent child relationship + // + EFI_USB2_HC_TRANSACTION_TRANSLATOR Translator; + + UINT8 ParentAddr; + USB_INTERFACE *ParentIf; + UINT8 ParentPort; // Start at 0 + UINT8 Tier; + BOOLEAN DisconnectFail; +}; + +// +// Stands for different functions of USB device +// +struct _USB_INTERFACE { + UINTN Signature; + USB_DEVICE *Device; + USB_INTERFACE_DESC *IfDesc; + USB_INTERFACE_SETTING *IfSetting; + + // + // Handles and protocols + // + EFI_HANDLE Handle; + EFI_USB_IO_PROTOCOL UsbIo; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + BOOLEAN IsManaged; + + // + // Hub device special data + // + BOOLEAN IsHub; + USB_HUB_API *HubApi; + UINT8 NumOfPort; + EFI_EVENT HubNotify; + + // + // Data used only by normal hub devices + // + USB_ENDPOINT_DESC *HubEp; + UINT8 *ChangeMap; + + // + // Data used only by root hub to hand over device to + // companion UHCI driver if low/full speed devices are + // connected to EHCI. + // + UINT8 MaxSpeed; +}; + +// +// Stands for the current USB Bus +// +struct _USB_BUS { + UINTN Signature; + EFI_USB_BUS_PROTOCOL BusId; + + // + // Managed USB host controller + // + EFI_HANDLE HostHandle; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_USB2_HC_PROTOCOL *Usb2Hc; + EFI_USB_HC_PROTOCOL *UsbHc; + + // + // Recorded the max supported usb devices. + // XHCI can support up to 255 devices. + // EHCI/UHCI/OHCI supports up to 127 devices. + // + UINT32 MaxDevices; + // + // An array of device that is on the bus. Devices[0] is + // for root hub. Device with address i is at Devices[i]. + // + USB_DEVICE *Devices[256]; + + // + // USB Bus driver need to control the recursive connect policy of the bus, only those wanted + // usb child device will be recursively connected. + // + // WantedUsbIoDPList tracks the Usb child devices which user want to recursivly fully connecte, + // every wanted child device is stored in a item of the WantedUsbIoDPList, whose structrure is + // DEVICE_PATH_LIST_ITEM + // + LIST_ENTRY WantedUsbIoDPList; + +}; + +// +// USB Hub Api +// +struct _USB_HUB_API{ + USB_HUB_INIT Init; + USB_HUB_GET_PORT_STATUS GetPortStatus; + USB_HUB_CLEAR_PORT_CHANGE ClearPortChange; + USB_HUB_SET_PORT_FEATURE SetPortFeature; + USB_HUB_CLEAR_PORT_FEATURE ClearPortFeature; + USB_HUB_RESET_PORT ResetPort; + USB_HUB_RELEASE Release; +}; + +#define USB_US_LAND_ID 0x0409 + +#define DEVICE_PATH_LIST_ITEM_SIGNATURE SIGNATURE_32('d','p','l','i') +typedef struct _DEVICE_PATH_LIST_ITEM{ + UINTN Signature; + LIST_ENTRY Link; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; +} DEVICE_PATH_LIST_ITEM; + +typedef struct { + USB_CLASS_DEVICE_PATH UsbClass; + EFI_DEVICE_PATH_PROTOCOL End; +} USB_CLASS_FORMAT_DEVICE_PATH; + +/** + Free a DEVICE_PATH_LIST_ITEM list. + + @param UsbIoDPList a DEVICE_PATH_LIST_ITEM list pointer. + + @retval EFI_INVALID_PARAMETER If parameters are invalid, return this value. + @retval EFI_SUCCESS If free operation is successful, return this value. + +**/ +EFI_STATUS +EFIAPI +UsbBusFreeUsbDPList ( + IN LIST_ENTRY *UsbIoDPList + ); + +/** + Store a wanted usb child device info (its Usb part of device path) which is indicated by + RemainingDevicePath in a Usb bus which is indicated by UsbBusId. + + @param UsbBusId Point to EFI_USB_BUS_PROTOCOL interface. + @param RemainingDevicePath The remaining device patch. + + @retval EFI_SUCCESS Add operation is successful. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +UsbBusAddWantedUsbIoDP ( + IN EFI_USB_BUS_PROTOCOL *UsbBusId, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Check whether a usb child device is the wanted device in a bus. + + @param Bus The Usb bus's private data pointer. + @param UsbIf The usb child device inferface. + + @retval True If a usb child device is the wanted device in a bus. + @retval False If a usb child device is *NOT* the wanted device in a bus. + +**/ +BOOLEAN +EFIAPI +UsbBusIsWantedUsbIO ( + IN USB_BUS *Bus, + IN USB_INTERFACE *UsbIf + ); + +/** + Recursively connnect every wanted usb child device to ensure they all fully connected. + Check all the child Usb IO handles in this bus, recursively connecte if it is wanted usb child device. + + @param UsbBusId Point to EFI_USB_BUS_PROTOCOL interface. + + @retval EFI_SUCCESS Connect is done successfully. + @retval EFI_INVALID_PARAMETER The parameter is invalid. + +**/ +EFI_STATUS +EFIAPI +UsbBusRecursivelyConnectWantedUsbIo ( + IN EFI_USB_BUS_PROTOCOL *UsbBusId + ); + +/** + 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 succeded. + @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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +extern EFI_USB_IO_PROTOCOL mUsbIoProtocol; +extern EFI_DRIVER_BINDING_PROTOCOL mUsbBusDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL mUsbBusComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL mUsbBusComponentName2; + +#endif diff --git a/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf new file mode 100644 index 0000000000..b7b30cc937 --- /dev/null +++ b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf @@ -0,0 +1,79 @@ +## @file +# The Usb Bus Dxe driver is used to enumerate and manage all attached usb devices. +# +# Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+# +# 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. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UsbBusDxe + MODULE_UNI_FILE = UsbBusDxe.uni + FILE_GUID = 240612B7-A063-11d4-9A3A-0090273FC14D + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = UsbBusDriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC ARM AARCH64 +# +# DRIVER_BINDING = mUsbBusDriverBinding +# COMPONENT_NAME = mUsbBusComponentName +# COMPONENT_NAME2 = mUsbBusComponentName2 +# + +[Sources] + UsbDesc.c + UsbEnumer.c + UsbEnumer.h + UsbBus.c + UsbHub.c + ComponentName.c + UsbUtility.h + UsbHub.h + UsbUtility.c + UsbDesc.h + UsbBus.h + +[Packages] + MdePkg/MdePkg.dec + + +[LibraryClasses] + MemoryAllocationLib + DevicePathLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + DebugLib + ReportStatusCodeLib + + +[Protocols] + gEfiUsbIoProtocolGuid ## BY_START + ## TO_START + ## BY_START + gEfiDevicePathProtocolGuid + gEfiUsb2HcProtocolGuid ## TO_START + gEfiUsbHcProtocolGuid ## TO_START + +# [Event] +# +# EVENT_TYPE_PERIODIC_TIMER ## CONSUMES +# + +[UserExtensions.TianoCore."ExtraFiles"] + UsbBusDxeExtra.uni diff --git a/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.uni b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.uni new file mode 100644 index 0000000000..70f9e62436 Binary files /dev/null and b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.uni differ diff --git a/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxeExtra.uni b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxeExtra.uni new file mode 100644 index 0000000000..305ab09c3e Binary files /dev/null and b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxeExtra.uni differ diff --git a/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c new file mode 100644 index 0000000000..9687eb0bca --- /dev/null +++ b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c @@ -0,0 +1,978 @@ +/** @file + + Manage Usb Descriptor List + +Copyright (c) 2007 - 2013, Intel Corporation. All rights reserved.
+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" + + +/** + Free the interface setting descriptor. + + @param Setting The descriptor to free. + +**/ +VOID +UsbFreeInterfaceDesc ( + IN USB_INTERFACE_SETTING *Setting + ) +{ + USB_ENDPOINT_DESC *Ep; + UINTN Index; + + if (Setting->Endpoints != NULL) { + // + // Each interface setting may have several endpoints, free them first. + // + for (Index = 0; Index < Setting->Desc.NumEndpoints; Index++) { + Ep = Setting->Endpoints[Index]; + + if (Ep != NULL) { + FreePool (Ep); + } + } + + // + // Only call FreePool() if NumEndpoints > 0. + // + if (Setting->Desc.NumEndpoints > 0) { + FreePool (Setting->Endpoints); + } + } + + FreePool (Setting); +} + + +/** + Free a configuration descriptor with its interface + descriptors. It may be initialized partially. + + @param Config The configuration descriptor to free. + +**/ +VOID +UsbFreeConfigDesc ( + IN USB_CONFIG_DESC *Config + ) +{ + USB_INTERFACE_DESC *Interface; + UINTN Index; + UINTN SetIndex; + + if (Config->Interfaces != NULL) { + // + // A configuration may have several interfaces, free the interface + // + for (Index = 0; Index < Config->Desc.NumInterfaces; Index++) { + Interface = Config->Interfaces[Index]; + + if (Interface == NULL) { + continue; + } + + // + // Each interface may have several settings, free the settings + // + for (SetIndex = 0; SetIndex < Interface->NumOfSetting; SetIndex++) { + if (Interface->Settings[SetIndex] != NULL) { + UsbFreeInterfaceDesc (Interface->Settings[SetIndex]); + } + } + + FreePool (Interface); + } + + FreePool (Config->Interfaces); + } + + FreePool (Config); + +} + + +/** + Free a device descriptor with its configurations. + + @param DevDesc The device descriptor. + +**/ +VOID +UsbFreeDevDesc ( + IN USB_DEVICE_DESC *DevDesc + ) +{ + UINTN Index; + + if (DevDesc->Configs != NULL) { + for (Index = 0; Index < DevDesc->Desc.NumConfigurations; Index++) { + if (DevDesc->Configs[Index] != NULL) { + UsbFreeConfigDesc (DevDesc->Configs[Index]); + } + } + + FreePool (DevDesc->Configs); + } + + FreePool (DevDesc); +} + + +/** + Create a descriptor. + + @param DescBuf The buffer of raw descriptor. + @param Len The length of the raw descriptor buffer. + @param Type The type of descriptor to create. + @param Consumed Number of bytes consumed. + + @return Created descriptor or NULL. + +**/ +VOID * +UsbCreateDesc ( + IN UINT8 *DescBuf, + IN UINTN Len, + IN UINT8 Type, + OUT UINTN *Consumed + ) +{ + USB_DESC_HEAD *Head; + UINTN DescLen; + UINTN CtrlLen; + UINTN Offset; + VOID *Desc; + + DescLen = 0; + CtrlLen = 0; + *Consumed = 0; + + switch (Type) { + case USB_DESC_TYPE_DEVICE: + DescLen = sizeof (EFI_USB_DEVICE_DESCRIPTOR); + CtrlLen = sizeof (USB_DEVICE_DESC); + break; + + case USB_DESC_TYPE_CONFIG: + DescLen = sizeof (EFI_USB_CONFIG_DESCRIPTOR); + CtrlLen = sizeof (USB_CONFIG_DESC); + break; + + case USB_DESC_TYPE_INTERFACE: + DescLen = sizeof (EFI_USB_INTERFACE_DESCRIPTOR); + CtrlLen = sizeof (USB_INTERFACE_SETTING); + break; + + case USB_DESC_TYPE_ENDPOINT: + DescLen = sizeof (EFI_USB_ENDPOINT_DESCRIPTOR); + CtrlLen = sizeof (USB_ENDPOINT_DESC); + break; + } + + // + // All the descriptor has a common LTV (Length, Type, Value) + // format. Skip the descriptor that isn't of this Type + // + Offset = 0; + Head = (USB_DESC_HEAD*)DescBuf; + + while ((Offset < Len) && (Head->Type != Type)) { + Offset += Head->Len; + if (Len <= Offset) { + DEBUG (( EFI_D_ERROR, "UsbCreateDesc: met mal-format descriptor, Beyond boundary!\n")); + return NULL; + } + Head = (USB_DESC_HEAD*)(DescBuf + Offset); + if (Head->Len == 0) { + DEBUG (( EFI_D_ERROR, "UsbCreateDesc: met mal-format descriptor, Head->Len = 0!\n")); + return NULL; + } + } + + if ((Len <= Offset) || (Len < Offset + DescLen) || + (Head->Type != Type) || (Head->Len != DescLen)) { + DEBUG (( EFI_D_ERROR, "UsbCreateDesc: met mal-format descriptor\n")); + return NULL; + } + + Desc = AllocateZeroPool ((UINTN) CtrlLen); + if (Desc == NULL) { + return NULL; + } + + CopyMem (Desc, Head, (UINTN) DescLen); + + *Consumed = Offset + Head->Len; + + return Desc; +} + + +/** + Parse an interface descriptor and its endpoints. + + @param DescBuf The buffer of raw descriptor. + @param Len The length of the raw descriptor buffer. + @param Consumed The number of raw descriptor consumed. + + @return The create interface setting or NULL if failed. + +**/ +USB_INTERFACE_SETTING * +UsbParseInterfaceDesc ( + IN UINT8 *DescBuf, + IN UINTN Len, + OUT UINTN *Consumed + ) +{ + USB_INTERFACE_SETTING *Setting; + USB_ENDPOINT_DESC *Ep; + UINTN Index; + UINTN NumEp; + UINTN Used; + UINTN Offset; + + *Consumed = 0; + Setting = UsbCreateDesc (DescBuf, Len, USB_DESC_TYPE_INTERFACE, &Used); + + if (Setting == NULL) { + DEBUG (( EFI_D_ERROR, "UsbParseInterfaceDesc: failed to create interface descriptor\n")); + return NULL; + } + + Offset = Used; + + // + // Create an array to hold the interface's endpoints + // + NumEp = Setting->Desc.NumEndpoints; + + DEBUG (( EFI_D_INFO, "UsbParseInterfaceDesc: interface %d(setting %d) has %d endpoints\n", + Setting->Desc.InterfaceNumber, Setting->Desc.AlternateSetting, (UINT32)NumEp)); + + if (NumEp == 0) { + goto ON_EXIT; + } + + Setting->Endpoints = AllocateZeroPool (sizeof (USB_ENDPOINT_DESC *) * NumEp); + + if (Setting->Endpoints == NULL) { + goto ON_ERROR; + } + + // + // Create the endpoints for this interface + // + for (Index = 0; (Index < NumEp) && (Offset < Len); Index++) { + Ep = UsbCreateDesc (DescBuf + Offset, Len - Offset, USB_DESC_TYPE_ENDPOINT, &Used); + + if (Ep == NULL) { + DEBUG (( EFI_D_ERROR, "UsbParseInterfaceDesc: failed to create endpoint(index %d)\n", (UINT32)Index)); + goto ON_ERROR; + } + + Setting->Endpoints[Index] = Ep; + Offset += Used; + } + + +ON_EXIT: + *Consumed = Offset; + return Setting; + +ON_ERROR: + UsbFreeInterfaceDesc (Setting); + return NULL; +} + + +/** + Parse the configuration descriptor and its interfaces. + + @param DescBuf The buffer of raw descriptor. + @param Len The length of the raw descriptor buffer. + + @return The created configuration descriptor. + +**/ +USB_CONFIG_DESC * +UsbParseConfigDesc ( + IN UINT8 *DescBuf, + IN UINTN Len + ) +{ + USB_CONFIG_DESC *Config; + USB_INTERFACE_SETTING *Setting; + USB_INTERFACE_DESC *Interface; + UINTN Index; + UINTN NumIf; + UINTN Consumed; + + ASSERT (DescBuf != NULL); + + Config = UsbCreateDesc (DescBuf, Len, USB_DESC_TYPE_CONFIG, &Consumed); + + if (Config == NULL) { + return NULL; + } + + // + // Initialize an array of setting for the configuration's interfaces. + // + NumIf = Config->Desc.NumInterfaces; + Config->Interfaces = AllocateZeroPool (sizeof (USB_INTERFACE_DESC *) * NumIf); + + if (Config->Interfaces == NULL) { + goto ON_ERROR; + } + + DEBUG (( EFI_D_INFO, "UsbParseConfigDesc: config %d has %d interfaces\n", + Config->Desc.ConfigurationValue, (UINT32)NumIf)); + + for (Index = 0; Index < NumIf; Index++) { + Interface = AllocateZeroPool (sizeof (USB_INTERFACE_DESC)); + + if (Interface == NULL) { + goto ON_ERROR; + } + + Config->Interfaces[Index] = Interface; + } + + // + // If a configuration has several interfaces, these interfaces are + // numbered from zero to n. If a interface has several settings, + // these settings are also number from zero to m. The interface + // setting must be organized as |interface 0, setting 0|interface 0 + // setting 1|interface 1, setting 0|interface 2, setting 0|. Check + // USB2.0 spec, page 267. + // + DescBuf += Consumed; + Len -= Consumed; + + // + // Make allowances for devices that return extra data at the + // end of their config descriptors + // + while (Len >= sizeof (EFI_USB_INTERFACE_DESCRIPTOR)) { + Setting = UsbParseInterfaceDesc (DescBuf, Len, &Consumed); + + if (Setting == NULL) { + DEBUG (( EFI_D_ERROR, "UsbParseConfigDesc: warning: failed to get interface setting, stop parsing now.\n")); + break; + + } else if (Setting->Desc.InterfaceNumber >= NumIf) { + DEBUG (( EFI_D_ERROR, "UsbParseConfigDesc: mal-formated interface descriptor\n")); + + UsbFreeInterfaceDesc (Setting); + goto ON_ERROR; + } + + // + // Insert the descriptor to the corresponding set. + // + Interface = Config->Interfaces[Setting->Desc.InterfaceNumber]; + + if (Interface->NumOfSetting >= USB_MAX_INTERFACE_SETTING) { + goto ON_ERROR; + } + + Interface->Settings[Interface->NumOfSetting] = Setting; + Interface->NumOfSetting++; + + DescBuf += Consumed; + Len -= Consumed; + } + + return Config; + +ON_ERROR: + UsbFreeConfigDesc (Config); + return NULL; +} + + +/** + USB standard control transfer support routine. This + function is used by USB device. It is possible that + the device's interfaces are still waiting to be + enumerated. + + @param UsbDev The usb device. + @param Direction The direction of data transfer. + @param Type Standard / class specific / vendor specific. + @param Target The receiving target. + @param Request Which request. + @param Value The wValue parameter of the request. + @param Index The wIndex parameter of the request. + @param Buf The buffer to receive data into / transmit from. + @param Length The length of the buffer. + + @retval EFI_SUCCESS The control request is executed. + @retval EFI_DEVICE_ERROR Failed to execute the control transfer. + +**/ +EFI_STATUS +UsbCtrlRequest ( + IN USB_DEVICE *UsbDev, + IN EFI_USB_DATA_DIRECTION Direction, + IN UINTN Type, + IN UINTN Target, + IN UINTN Request, + IN UINT16 Value, + IN UINT16 Index, + IN OUT VOID *Buf, + IN UINTN Length + ) +{ + EFI_USB_DEVICE_REQUEST DevReq; + EFI_STATUS Status; + UINT32 Result; + UINTN Len; + + ASSERT ((UsbDev != NULL) && (UsbDev->Bus != NULL)); + + DevReq.RequestType = USB_REQUEST_TYPE (Direction, Type, Target); + DevReq.Request = (UINT8) Request; + DevReq.Value = Value; + DevReq.Index = Index; + DevReq.Length = (UINT16) Length; + + Len = Length; + Status = UsbHcControlTransfer ( + UsbDev->Bus, + UsbDev->Address, + UsbDev->Speed, + UsbDev->MaxPacket0, + &DevReq, + Direction, + Buf, + &Len, + USB_GENERAL_DEVICE_REQUEST_TIMEOUT, + &UsbDev->Translator, + &Result + ); + + return Status; +} + + +/** + Get the standard descriptors. + + @param UsbDev The USB device to read descriptor from. + @param DescType The type of descriptor to read. + @param DescIndex The index of descriptor to read. + @param LangId Language ID, only used to get string, otherwise set + it to 0. + @param Buf The buffer to hold the descriptor read. + @param Length The length of the buffer. + + @retval EFI_SUCCESS The descriptor is read OK. + @retval Others Failed to retrieve the descriptor. + +**/ +EFI_STATUS +UsbCtrlGetDesc ( + IN USB_DEVICE *UsbDev, + IN UINTN DescType, + IN UINTN DescIndex, + IN UINT16 LangId, + OUT VOID *Buf, + IN UINTN Length + ) +{ + EFI_STATUS Status; + + Status = UsbCtrlRequest ( + UsbDev, + EfiUsbDataIn, + USB_REQ_TYPE_STANDARD, + USB_TARGET_DEVICE, + USB_REQ_GET_DESCRIPTOR, + (UINT16) ((DescType << 8) | DescIndex), + LangId, + Buf, + Length + ); + + return Status; +} + + +/** + Return the max packet size for endpoint zero. This function + is the first function called to get descriptors during bus + enumeration. + + @param UsbDev The usb device. + + @retval EFI_SUCCESS The max packet size of endpoint zero is retrieved. + @retval EFI_DEVICE_ERROR Failed to retrieve it. + +**/ +EFI_STATUS +UsbGetMaxPacketSize0 ( + IN USB_DEVICE *UsbDev + ) +{ + EFI_USB_DEVICE_DESCRIPTOR DevDesc; + EFI_STATUS Status; + UINTN Index; + + + // + // Get the first 8 bytes of the device descriptor which contains + // max packet size for endpoint 0, which is at least 8. + // + for (Index = 0; Index < 3; Index++) { + Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_DEVICE, 0, 0, &DevDesc, 8); + + if (!EFI_ERROR (Status)) { + if ((DevDesc.BcdUSB == 0x0300) && (DevDesc.MaxPacketSize0 == 9)) { + UsbDev->MaxPacket0 = 1 << 9; + return EFI_SUCCESS; + } + UsbDev->MaxPacket0 = DevDesc.MaxPacketSize0; + return EFI_SUCCESS; + } + + gBS->Stall (USB_RETRY_MAX_PACK_SIZE_STALL); + } + + return EFI_DEVICE_ERROR; +} + + +/** + Get the device descriptor for the device. + + @param UsbDev The Usb device to retrieve descriptor from. + + @retval EFI_SUCCESS The device descriptor is returned. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +EFI_STATUS +UsbGetDevDesc ( + IN USB_DEVICE *UsbDev + ) +{ + USB_DEVICE_DESC *DevDesc; + EFI_STATUS Status; + + DevDesc = AllocateZeroPool (sizeof (USB_DEVICE_DESC)); + + if (DevDesc == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = UsbCtrlGetDesc ( + UsbDev, + USB_DESC_TYPE_DEVICE, + 0, + 0, + DevDesc, + sizeof (EFI_USB_DEVICE_DESCRIPTOR) + ); + + if (EFI_ERROR (Status)) { + gBS->FreePool (DevDesc); + } else { + UsbDev->DevDesc = DevDesc; + } + + return Status; +} + + +/** + Retrieve the indexed string for the language. It requires two + steps to get a string, first to get the string's length. Then + the string itself. + + @param UsbDev The usb device. + @param Index The index the string to retrieve. + @param LangId Language ID. + + @return The created string descriptor or NULL. + +**/ +EFI_USB_STRING_DESCRIPTOR * +UsbGetOneString ( + IN USB_DEVICE *UsbDev, + IN UINT8 Index, + IN UINT16 LangId + ) +{ + EFI_USB_STRING_DESCRIPTOR Desc; + EFI_STATUS Status; + UINT8 *Buf; + + // + // First get two bytes which contains the string length. + // + Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_STRING, Index, LangId, &Desc, 2); + + if (EFI_ERROR (Status)) { + return NULL; + } + + Buf = AllocateZeroPool (Desc.Length); + + if (Buf == NULL) { + return NULL; + } + + Status = UsbCtrlGetDesc ( + UsbDev, + USB_DESC_TYPE_STRING, + Index, + LangId, + Buf, + Desc.Length + ); + + if (EFI_ERROR (Status)) { + FreePool (Buf); + return NULL; + } + + return (EFI_USB_STRING_DESCRIPTOR *) Buf; +} + + +/** + Build the language ID table for string descriptors. + + @param UsbDev The Usb device. + + @retval EFI_UNSUPPORTED This device doesn't support string table. + +**/ +EFI_STATUS +UsbBuildLangTable ( + IN USB_DEVICE *UsbDev + ) +{ + EFI_USB_STRING_DESCRIPTOR *Desc; + EFI_STATUS Status; + UINTN Index; + UINTN Max; + UINT16 *Point; + + // + // The string of language ID zero returns the supported languages + // + Desc = UsbGetOneString (UsbDev, 0, 0); + + if (Desc == NULL) { + return EFI_UNSUPPORTED; + } + + if (Desc->Length < 4) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + + Status = EFI_SUCCESS; + + Max = (Desc->Length - 2) / 2; + Max = MIN(Max, USB_MAX_LANG_ID); + + Point = Desc->String; + for (Index = 0; Index < Max; Index++) { + UsbDev->LangId[Index] = *Point; + Point++; + } + + UsbDev->TotalLangId = (UINT16)Max; + +ON_EXIT: + gBS->FreePool (Desc); + return Status; +} + + +/** + Retrieve the indexed configure for the device. USB device + returns the configuration together with the interfaces for + this configuration. Configuration descriptor is also of + variable length. + + @param UsbDev The Usb interface. + @param Index The index of the configuration. + + @return The created configuration descriptor. + +**/ +EFI_USB_CONFIG_DESCRIPTOR * +UsbGetOneConfig ( + IN USB_DEVICE *UsbDev, + IN UINT8 Index + ) +{ + EFI_USB_CONFIG_DESCRIPTOR Desc; + EFI_STATUS Status; + VOID *Buf; + + // + // First get four bytes which contains the total length + // for this configuration. + // + Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_CONFIG, Index, 0, &Desc, 8); + + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "UsbGetOneConfig: failed to get descript length(%d) %r\n", + Desc.TotalLength, Status)); + + return NULL; + } + + DEBUG (( EFI_D_INFO, "UsbGetOneConfig: total length is %d\n", Desc.TotalLength)); + + Buf = AllocateZeroPool (Desc.TotalLength); + + if (Buf == NULL) { + return NULL; + } + + Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_CONFIG, Index, 0, Buf, Desc.TotalLength); + + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "UsbGetOneConfig: failed to get full descript %r\n", Status)); + + FreePool (Buf); + return NULL; + } + + return Buf; +} + + +/** + Build the whole array of descriptors. This function must + be called after UsbGetMaxPacketSize0 returns the max packet + size correctly for endpoint 0. + + @param UsbDev The Usb device. + + @retval EFI_SUCCESS The descriptor table is build. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the descriptor. + +**/ +EFI_STATUS +UsbBuildDescTable ( + IN USB_DEVICE *UsbDev + ) +{ + EFI_USB_CONFIG_DESCRIPTOR *Config; + USB_DEVICE_DESC *DevDesc; + USB_CONFIG_DESC *ConfigDesc; + UINT8 NumConfig; + EFI_STATUS Status; + UINT8 Index; + + // + // Get the device descriptor, then allocate the configure + // descriptor pointer array to hold configurations. + // + Status = UsbGetDevDesc (UsbDev); + + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "UsbBuildDescTable: failed to get device descriptor - %r\n", Status)); + return Status; + } + + DevDesc = UsbDev->DevDesc; + NumConfig = DevDesc->Desc.NumConfigurations; + if (NumConfig == 0) { + return EFI_DEVICE_ERROR; + } + + DevDesc->Configs = AllocateZeroPool (NumConfig * sizeof (USB_CONFIG_DESC *)); + if (DevDesc->Configs == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DEBUG (( EFI_D_INFO, "UsbBuildDescTable: device has %d configures\n", NumConfig)); + + // + // Read each configurations, then parse them + // + for (Index = 0; Index < NumConfig; Index++) { + Config = UsbGetOneConfig (UsbDev, Index); + + if (Config == NULL) { + DEBUG (( EFI_D_ERROR, "UsbBuildDescTable: failed to get configure (index %d)\n", Index)); + + // + // If we can get the default descriptor, it is likely that the + // device is still operational. + // + if (Index == 0) { + return EFI_DEVICE_ERROR; + } + + break; + } + + ConfigDesc = UsbParseConfigDesc ((UINT8 *) Config, Config->TotalLength); + + FreePool (Config); + + if (ConfigDesc == NULL) { + DEBUG (( EFI_D_ERROR, "UsbBuildDescTable: failed to parse configure (index %d)\n", Index)); + + // + // If we can get the default descriptor, it is likely that the + // device is still operational. + // + if (Index == 0) { + return EFI_DEVICE_ERROR; + } + + break; + } + + DevDesc->Configs[Index] = ConfigDesc; + } + + // + // Don't return error even this function failed because + // it is possible for the device to not support strings. + // + Status = UsbBuildLangTable (UsbDev); + + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_INFO, "UsbBuildDescTable: get language ID table %r\n", Status)); + } + + return EFI_SUCCESS; +} + + +/** + Set the device's address. + + @param UsbDev The device to set address to. + @param Address The address to set. + + @retval EFI_SUCCESS The device is set to the address. + @retval Others Failed to set the device address. + +**/ +EFI_STATUS +UsbSetAddress ( + IN USB_DEVICE *UsbDev, + IN UINT8 Address + ) +{ + EFI_STATUS Status; + + Status = UsbCtrlRequest ( + UsbDev, + EfiUsbNoData, + USB_REQ_TYPE_STANDARD, + USB_TARGET_DEVICE, + USB_REQ_SET_ADDRESS, + Address, + 0, + NULL, + 0 + ); + + return Status; +} + + +/** + Set the device's configuration. This function changes + the device's internal state. UsbSelectConfig changes + the Usb bus's internal state. + + @param UsbDev The USB device to set configure to. + @param ConfigIndex The configure index to set. + + @retval EFI_SUCCESS The device is configured now. + @retval Others Failed to set the device configure. + +**/ +EFI_STATUS +UsbSetConfig ( + IN USB_DEVICE *UsbDev, + IN UINT8 ConfigIndex + ) +{ + EFI_STATUS Status; + + Status = UsbCtrlRequest ( + UsbDev, + EfiUsbNoData, + USB_REQ_TYPE_STANDARD, + USB_TARGET_DEVICE, + USB_REQ_SET_CONFIG, + ConfigIndex, + 0, + NULL, + 0 + ); + + return Status; +} + + +/** + Usb UsbIo interface to clear the feature. This is should + only be used by HUB which is considered a device driver + on top of the UsbIo interface. + + @param UsbIo The UsbIo interface. + @param Target The target of the transfer: endpoint/device. + @param Feature The feature to clear. + @param Index The wIndex parameter. + + @retval EFI_SUCCESS The device feature is cleared. + @retval Others Failed to clear the feature. + +**/ +EFI_STATUS +UsbIoClearFeature ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN UINTN Target, + IN UINT16 Feature, + IN UINT16 Index + ) +{ + EFI_USB_DEVICE_REQUEST DevReq; + UINT32 UsbResult; + EFI_STATUS Status; + + DevReq.RequestType = USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD, Target); + DevReq.Request = USB_REQ_CLEAR_FEATURE; + DevReq.Value = Feature; + DevReq.Index = Index; + DevReq.Length = 0; + + Status = UsbIo->UsbControlTransfer ( + UsbIo, + &DevReq, + EfiUsbNoData, + USB_CLEAR_FEATURE_REQUEST_TIMEOUT, + NULL, + 0, + &UsbResult + ); + + return Status; +} diff --git a/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h new file mode 100644 index 0000000000..482a71f338 --- /dev/null +++ b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h @@ -0,0 +1,233 @@ +/** @file + + Manage Usb Descriptor List + +Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef _USB_DESCRIPTOR_H_ +#define _USB_DESCRIPTOR_H_ + +#define USB_MAX_INTERFACE_SETTING 256 + +// +// The RequestType in EFI_USB_DEVICE_REQUEST is composed of +// three fields: One bit direction, 2 bit type, and 5 bit +// target. +// +#define USB_REQUEST_TYPE(Dir, Type, Target) \ + ((UINT8)((((Dir) == EfiUsbDataIn ? 0x01 : 0) << 7) | (Type) | (Target))) + +// +// A common header for usb standard descriptor. +// Each stand descriptor has a length and type. +// +#pragma pack(1) +typedef struct { + UINT8 Len; + UINT8 Type; +} USB_DESC_HEAD; +#pragma pack() + + +// +// Each USB device has a device descriptor. Each device may +// have several configures. Each configure contains several +// interfaces. Each interface may have several settings. Each +// setting has several endpoints. +// +// EFI_USB_..._DESCRIPTOR must be the first member of the +// structure. +// +typedef struct { + EFI_USB_ENDPOINT_DESCRIPTOR Desc; + UINT8 Toggle; +} USB_ENDPOINT_DESC; + +typedef struct { + EFI_USB_INTERFACE_DESCRIPTOR Desc; + USB_ENDPOINT_DESC **Endpoints; +} USB_INTERFACE_SETTING; + +// +// An interface may have several settings. Use a +// fixed max number of settings to simplify code. +// It should sufice in most environments. +// +typedef struct { + USB_INTERFACE_SETTING* Settings[USB_MAX_INTERFACE_SETTING]; + UINTN NumOfSetting; + UINTN ActiveIndex; // Index of active setting +} USB_INTERFACE_DESC; + +typedef struct { + EFI_USB_CONFIG_DESCRIPTOR Desc; + USB_INTERFACE_DESC **Interfaces; +} USB_CONFIG_DESC; + +typedef struct { + EFI_USB_DEVICE_DESCRIPTOR Desc; + USB_CONFIG_DESC **Configs; +} USB_DEVICE_DESC; + +/** + USB standard control transfer support routine. This + function is used by USB device. It is possible that + the device's interfaces are still waiting to be + enumerated. + + @param UsbDev The usb device. + @param Direction The direction of data transfer. + @param Type Standard / class specific / vendor specific. + @param Target The receiving target. + @param Request Which request. + @param Value The wValue parameter of the request. + @param Index The wIndex parameter of the request. + @param Buf The buffer to receive data into / transmit from. + @param Length The length of the buffer. + + @retval EFI_SUCCESS The control request is executed. + @retval EFI_DEVICE_ERROR Failed to execute the control transfer. + +**/ +EFI_STATUS +UsbCtrlRequest ( + IN USB_DEVICE *UsbDev, + IN EFI_USB_DATA_DIRECTION Direction, + IN UINTN Type, + IN UINTN Target, + IN UINTN Request, + IN UINT16 Value, + IN UINT16 Index, + IN OUT VOID *Buf, + IN UINTN Length + ); + +/** + Return the max packet size for endpoint zero. This function + is the first function called to get descriptors during bus + enumeration. + + @param UsbDev The usb device. + + @retval EFI_SUCCESS The max packet size of endpoint zero is retrieved. + @retval EFI_DEVICE_ERROR Failed to retrieve it. + +**/ +EFI_STATUS +UsbGetMaxPacketSize0 ( + IN USB_DEVICE *UsbDev + ); + +/** + Free a device descriptor with its configurations. + + @param DevDesc The device descriptor. + + @return None. + +**/ +VOID +UsbFreeDevDesc ( + IN USB_DEVICE_DESC *DevDesc + ); + +/** + Retrieve the indexed string for the language. It requires two + steps to get a string, first to get the string's length. Then + the string itself. + + @param UsbDev The usb device. + @param StringIndex The index of the string to retrieve. + @param LangId Language ID. + + @return The created string descriptor or NULL. + +**/ +EFI_USB_STRING_DESCRIPTOR* +UsbGetOneString ( + IN USB_DEVICE *UsbDev, + IN UINT8 StringIndex, + IN UINT16 LangId + ); + +/** + Build the whole array of descriptors. This function must + be called after UsbGetMaxPacketSize0 returns the max packet + size correctly for endpoint 0. + + @param UsbDev The Usb device. + + @retval EFI_SUCCESS The descriptor table is build. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the descriptor. + +**/ +EFI_STATUS +UsbBuildDescTable ( + IN USB_DEVICE *UsbDev + ); + +/** + Set the device's address. + + @param UsbDev The device to set address to. + @param Address The address to set. + + @retval EFI_SUCCESS The device is set to the address. + @retval Others Failed to set the device address. + +**/ +EFI_STATUS +UsbSetAddress ( + IN USB_DEVICE *UsbDev, + IN UINT8 Address + ); + +/** + Set the device's configuration. This function changes + the device's internal state. UsbSelectConfig changes + the Usb bus's internal state. + + @param UsbDev The USB device to set configure to. + @param ConfigIndex The configure index to set. + + @retval EFI_SUCCESS The device is configured now. + @retval Others Failed to set the device configure. + +**/ +EFI_STATUS +UsbSetConfig ( + IN USB_DEVICE *UsbDev, + IN UINT8 ConfigIndex + ); + +/** + Usb UsbIo interface to clear the feature. This is should + only be used by HUB which is considered a device driver + on top of the UsbIo interface. + + @param UsbIo The UsbIo interface. + @param Target The target of the transfer: endpoint/device. + @param Feature The feature to clear. + @param Index The wIndex parameter. + + @retval EFI_SUCCESS The device feature is cleared. + @retval Others Failed to clear the feature. + +**/ +EFI_STATUS +UsbIoClearFeature ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN UINTN Target, + IN UINT16 Feature, + IN UINT16 Index + ); +#endif diff --git a/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c new file mode 100644 index 0000000000..79453fed26 --- /dev/null +++ b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c @@ -0,0 +1,1065 @@ +/** @file + + Usb bus enumeration support. + +Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.
+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" + +/** + Return the endpoint descriptor in this interface. + + @param UsbIf The interface to search in. + @param EpAddr The address of the endpoint to return. + + @return The endpoint descriptor or NULL. + +**/ +USB_ENDPOINT_DESC * +UsbGetEndpointDesc ( + IN USB_INTERFACE *UsbIf, + IN UINT8 EpAddr + ) +{ + USB_ENDPOINT_DESC *EpDesc; + UINT8 Index; + UINT8 NumEndpoints; + + NumEndpoints = UsbIf->IfSetting->Desc.NumEndpoints; + + for (Index = 0; Index < NumEndpoints; Index++) { + EpDesc = UsbIf->IfSetting->Endpoints[Index]; + + if (EpDesc->Desc.EndpointAddress == EpAddr) { + return EpDesc; + } + } + + return NULL; +} + + +/** + Free the resource used by USB interface. + + @param UsbIf The USB interface to free. + +**/ +VOID +UsbFreeInterface ( + IN USB_INTERFACE *UsbIf + ) +{ + UsbCloseHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle); + + gBS->UninstallMultipleProtocolInterfaces ( + UsbIf->Handle, + &gEfiDevicePathProtocolGuid, + UsbIf->DevicePath, + &gEfiUsbIoProtocolGuid, + &UsbIf->UsbIo, + NULL + ); + + if (UsbIf->DevicePath != NULL) { + FreePool (UsbIf->DevicePath); + } + + FreePool (UsbIf); +} + + +/** + Create an interface for the descriptor IfDesc. Each + device's configuration can have several interfaces. + + @param Device The device has the interface descriptor. + @param IfDesc The interface descriptor. + + @return The created USB interface for the descriptor, or NULL. + +**/ +USB_INTERFACE * +UsbCreateInterface ( + IN USB_DEVICE *Device, + IN USB_INTERFACE_DESC *IfDesc + ) +{ + USB_DEVICE_PATH UsbNode; + USB_INTERFACE *UsbIf; + USB_INTERFACE *HubIf; + EFI_STATUS Status; + + UsbIf = AllocateZeroPool (sizeof (USB_INTERFACE)); + + if (UsbIf == NULL) { + return NULL; + } + + UsbIf->Signature = USB_INTERFACE_SIGNATURE; + UsbIf->Device = Device; + UsbIf->IfDesc = IfDesc; + ASSERT (IfDesc->ActiveIndex < USB_MAX_INTERFACE_SETTING); + UsbIf->IfSetting = IfDesc->Settings[IfDesc->ActiveIndex]; + + CopyMem ( + &(UsbIf->UsbIo), + &mUsbIoProtocol, + sizeof (EFI_USB_IO_PROTOCOL) + ); + + // + // Install protocols for USBIO and device path + // + UsbNode.Header.Type = MESSAGING_DEVICE_PATH; + UsbNode.Header.SubType = MSG_USB_DP; + UsbNode.ParentPortNumber = Device->ParentPort; + UsbNode.InterfaceNumber = UsbIf->IfSetting->Desc.InterfaceNumber; + + SetDevicePathNodeLength (&UsbNode.Header, sizeof (UsbNode)); + + HubIf = Device->ParentIf; + ASSERT (HubIf != NULL); + + UsbIf->DevicePath = AppendDevicePathNode (HubIf->DevicePath, &UsbNode.Header); + + if (UsbIf->DevicePath == NULL) { + DEBUG ((EFI_D_ERROR, "UsbCreateInterface: failed to create device path\n")); + + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &UsbIf->Handle, + &gEfiDevicePathProtocolGuid, + UsbIf->DevicePath, + &gEfiUsbIoProtocolGuid, + &UsbIf->UsbIo, + NULL + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UsbCreateInterface: failed to install UsbIo - %r\n", Status)); + goto ON_ERROR; + } + + // + // Open USB Host Controller Protocol by Child + // + Status = UsbOpenHostProtoByChild (Device->Bus, UsbIf->Handle); + + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + &UsbIf->Handle, + &gEfiDevicePathProtocolGuid, + UsbIf->DevicePath, + &gEfiUsbIoProtocolGuid, + &UsbIf->UsbIo, + NULL + ); + + DEBUG ((EFI_D_ERROR, "UsbCreateInterface: failed to open host for child - %r\n", Status)); + goto ON_ERROR; + } + + return UsbIf; + +ON_ERROR: + if (UsbIf->DevicePath != NULL) { + FreePool (UsbIf->DevicePath); + } + + FreePool (UsbIf); + return NULL; +} + + +/** + Free the resource used by this USB device. + + @param Device The USB device to free. + +**/ +VOID +UsbFreeDevice ( + IN USB_DEVICE *Device + ) +{ + if (Device->DevDesc != NULL) { + UsbFreeDevDesc (Device->DevDesc); + } + + gBS->FreePool (Device); +} + + +/** + Create a device which is on the parent's ParentPort port. + + @param ParentIf The parent HUB interface. + @param ParentPort The port on the HUB this device is connected to. + + @return Created USB device, Or NULL. + +**/ +USB_DEVICE * +UsbCreateDevice ( + IN USB_INTERFACE *ParentIf, + IN UINT8 ParentPort + ) +{ + USB_DEVICE *Device; + + ASSERT (ParentIf != NULL); + + Device = AllocateZeroPool (sizeof (USB_DEVICE)); + + if (Device == NULL) { + return NULL; + } + + Device->Bus = ParentIf->Device->Bus; + Device->MaxPacket0 = 8; + Device->ParentAddr = ParentIf->Device->Address; + Device->ParentIf = ParentIf; + Device->ParentPort = ParentPort; + Device->Tier = (UINT8)(ParentIf->Device->Tier + 1); + return Device; +} + + +/** + Connect the USB interface with its driver. EFI USB bus will + create a USB interface for each separate interface descriptor. + + @param UsbIf The interface to connect driver to. + + @return EFI_SUCCESS Interface is managed by some driver. + @return Others Failed to locate a driver for this interface. + +**/ +EFI_STATUS +UsbConnectDriver ( + IN USB_INTERFACE *UsbIf + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + + Status = EFI_SUCCESS; + + // + // Hub is maintained by the USB bus driver. Otherwise try to + // connect drivers with this interface + // + if (UsbIsHubInterface (UsbIf)) { + DEBUG ((EFI_D_INFO, "UsbConnectDriver: found a hub device\n")); + Status = mUsbHubApi.Init (UsbIf); + + } else { + // + // This function is called in both UsbIoControlTransfer and + // the timer callback in hub enumeration. So, at least it is + // called at TPL_CALLBACK. Some driver sitting on USB has + // twisted TPL used. It should be no problem for us to connect + // or disconnect at CALLBACK. + // + + // + // Only recursively wanted usb child device + // + if (UsbBusIsWantedUsbIO (UsbIf->Device->Bus, UsbIf)) { + OldTpl = UsbGetCurrentTpl (); + DEBUG ((EFI_D_INFO, "UsbConnectDriver: TPL before connect is %d, %p\n", (UINT32)OldTpl, UsbIf->Handle)); + + gBS->RestoreTPL (TPL_CALLBACK); + + Status = gBS->ConnectController (UsbIf->Handle, NULL, NULL, TRUE); + UsbIf->IsManaged = (BOOLEAN)!EFI_ERROR (Status); + + DEBUG ((EFI_D_INFO, "UsbConnectDriver: TPL after connect is %d\n", (UINT32)UsbGetCurrentTpl())); + ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK); + + gBS->RaiseTPL (OldTpl); + } + } + + return Status; +} + + +/** + Select an alternate setting for the interface. + Each interface can have several mutually exclusive + settings. Only one setting is active. It will + also reset its endpoints' toggle to zero. + + @param IfDesc The interface descriptor to set. + @param Alternate The alternate setting number to locate. + + @retval EFI_NOT_FOUND There is no setting with this alternate index. + @retval EFI_SUCCESS The interface is set to Alternate setting. + +**/ +EFI_STATUS +UsbSelectSetting ( + IN USB_INTERFACE_DESC *IfDesc, + IN UINT8 Alternate + ) +{ + USB_INTERFACE_SETTING *Setting; + UINTN Index; + + // + // Locate the active alternate setting + // + Setting = NULL; + + for (Index = 0; Index < IfDesc->NumOfSetting; Index++) { + ASSERT (Index < USB_MAX_INTERFACE_SETTING); + Setting = IfDesc->Settings[Index]; + + if (Setting->Desc.AlternateSetting == Alternate) { + break; + } + } + + if (Index == IfDesc->NumOfSetting) { + return EFI_NOT_FOUND; + } + + IfDesc->ActiveIndex = Index; + + ASSERT (Setting != NULL); + DEBUG ((EFI_D_INFO, "UsbSelectSetting: setting %d selected for interface %d\n", + Alternate, Setting->Desc.InterfaceNumber)); + + // + // Reset the endpoint toggle to zero + // + for (Index = 0; Index < Setting->Desc.NumEndpoints; Index++) { + Setting->Endpoints[Index]->Toggle = 0; + } + + return EFI_SUCCESS; +} + + +/** + Select a new configuration for the device. Each + device may support several configurations. + + @param Device The device to select configuration. + @param ConfigValue The index of the configuration ( != 0). + + @retval EFI_NOT_FOUND There is no configuration with the index. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_SUCCESS The configuration is selected. + +**/ +EFI_STATUS +UsbSelectConfig ( + IN USB_DEVICE *Device, + IN UINT8 ConfigValue + ) +{ + USB_DEVICE_DESC *DevDesc; + USB_CONFIG_DESC *ConfigDesc; + USB_INTERFACE_DESC *IfDesc; + USB_INTERFACE *UsbIf; + EFI_STATUS Status; + UINT8 Index; + + // + // Locate the active config, then set the device's pointer + // + DevDesc = Device->DevDesc; + ConfigDesc = NULL; + + for (Index = 0; Index < DevDesc->Desc.NumConfigurations; Index++) { + ConfigDesc = DevDesc->Configs[Index]; + + if (ConfigDesc->Desc.ConfigurationValue == ConfigValue) { + break; + } + } + + if (Index == DevDesc->Desc.NumConfigurations) { + return EFI_NOT_FOUND; + } + + Device->ActiveConfig = ConfigDesc; + + DEBUG ((EFI_D_INFO, "UsbSelectConfig: config %d selected for device %d\n", + ConfigValue, Device->Address)); + + // + // Create interfaces for each USB interface descriptor. + // + for (Index = 0; Index < ConfigDesc->Desc.NumInterfaces; Index++) { + // + // First select the default interface setting, and reset + // the endpoint toggles to zero for its endpoints. + // + IfDesc = ConfigDesc->Interfaces[Index]; + UsbSelectSetting (IfDesc, IfDesc->Settings[0]->Desc.AlternateSetting); + + // + // Create a USB_INTERFACE and install USB_IO and other protocols + // + UsbIf = UsbCreateInterface (Device, ConfigDesc->Interfaces[Index]); + + if (UsbIf == NULL) { + Device->NumOfInterface = Index; + return EFI_OUT_OF_RESOURCES; + } + + ASSERT (Index < USB_MAX_INTERFACE); + Device->Interfaces[Index] = UsbIf; + + // + // Connect the device to drivers, if it failed, ignore + // the error. Don't let the unsupported interfaces to block + // the supported interfaces. + // + Status = UsbConnectDriver (UsbIf); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UsbSelectConfig: failed to connect driver %r, ignored\n", Status)); + } + } + + Device->NumOfInterface = Index; + + return EFI_SUCCESS; +} + + +/** + Disconnect the USB interface with its driver. + + @param UsbIf The interface to disconnect driver from. + +**/ +EFI_STATUS +UsbDisconnectDriver ( + IN USB_INTERFACE *UsbIf + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + + // + // Release the hub if it's a hub controller, otherwise + // disconnect the driver if it is managed by other drivers. + // + Status = EFI_SUCCESS; + if (UsbIf->IsHub) { + Status = UsbIf->HubApi->Release (UsbIf); + + } else if (UsbIf->IsManaged) { + // + // This function is called in both UsbIoControlTransfer and + // the timer callback in hub enumeration. So, at least it is + // called at TPL_CALLBACK. Some driver sitting on USB has + // twisted TPL used. It should be no problem for us to connect + // or disconnect at CALLBACK. + // + OldTpl = UsbGetCurrentTpl (); + DEBUG ((EFI_D_INFO, "UsbDisconnectDriver: old TPL is %d, %p\n", (UINT32)OldTpl, UsbIf->Handle)); + + gBS->RestoreTPL (TPL_CALLBACK); + + Status = gBS->DisconnectController (UsbIf->Handle, NULL, NULL); + if (!EFI_ERROR (Status)) { + UsbIf->IsManaged = FALSE; + } + + DEBUG (( EFI_D_INFO, "UsbDisconnectDriver: TPL after disconnect is %d, %d\n", (UINT32)UsbGetCurrentTpl(), Status)); + ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK); + + gBS->RaiseTPL (OldTpl); + } + + return Status; +} + + +/** + Remove the current device configuration. + + @param Device The USB device to remove configuration from. + +**/ +EFI_STATUS +UsbRemoveConfig ( + IN USB_DEVICE *Device + ) +{ + USB_INTERFACE *UsbIf; + UINTN Index; + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + + // + // Remove each interface of the device + // + ReturnStatus = EFI_SUCCESS; + for (Index = 0; Index < Device->NumOfInterface; Index++) { + ASSERT (Index < USB_MAX_INTERFACE); + UsbIf = Device->Interfaces[Index]; + + if (UsbIf == NULL) { + continue; + } + + Status = UsbDisconnectDriver (UsbIf); + if (!EFI_ERROR (Status)) { + UsbFreeInterface (UsbIf); + Device->Interfaces[Index] = NULL; + } else { + ReturnStatus = Status; + } + } + + Device->ActiveConfig = NULL; + return ReturnStatus; +} + + +/** + Remove the device and all its children from the bus. + + @param Device The device to remove. + + @retval EFI_SUCCESS The device is removed. + +**/ +EFI_STATUS +UsbRemoveDevice ( + IN USB_DEVICE *Device + ) +{ + USB_BUS *Bus; + USB_DEVICE *Child; + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + UINTN Index; + + Bus = Device->Bus; + + // + // Remove all the devices on its downstream ports. Search from devices[1]. + // Devices[0] is the root hub. + // + ReturnStatus = EFI_SUCCESS; + for (Index = 1; Index < Bus->MaxDevices; Index++) { + Child = Bus->Devices[Index]; + + if ((Child == NULL) || (Child->ParentAddr != Device->Address)) { + continue; + } + + Status = UsbRemoveDevice (Child); + + if (!EFI_ERROR (Status)) { + Bus->Devices[Index] = NULL; + } else { + Bus->Devices[Index]->DisconnectFail = TRUE; + ReturnStatus = Status; + DEBUG ((EFI_D_INFO, "UsbRemoveDevice: failed to remove child %p at parent %p\n", Child, Device)); + } + } + + if (EFI_ERROR (ReturnStatus)) { + return ReturnStatus; + } + + Status = UsbRemoveConfig (Device); + + if (!EFI_ERROR (Status)) { + DEBUG (( EFI_D_INFO, "UsbRemoveDevice: device %d removed\n", Device->Address)); + + ASSERT (Device->Address < Bus->MaxDevices); + Bus->Devices[Device->Address] = NULL; + UsbFreeDevice (Device); + } else { + Bus->Devices[Device->Address]->DisconnectFail = TRUE; + } + return Status; +} + + +/** + Find the child device on the hub's port. + + @param HubIf The hub interface. + @param Port The port of the hub this child is connected to. + + @return The device on the hub's port, or NULL if there is none. + +**/ +USB_DEVICE * +UsbFindChild ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port + ) +{ + USB_DEVICE *Device; + USB_BUS *Bus; + UINTN Index; + + Bus = HubIf->Device->Bus; + + // + // Start checking from device 1, device 0 is the root hub + // + for (Index = 1; Index < Bus->MaxDevices; Index++) { + Device = Bus->Devices[Index]; + + if ((Device != NULL) && (Device->ParentAddr == HubIf->Device->Address) && + (Device->ParentPort == Port)) { + + return Device; + } + } + + return NULL; +} + + +/** + Enumerate and configure the new device on the port of this HUB interface. + + @param HubIf The HUB that has the device connected. + @param Port The port index of the hub (started with zero). + + @retval EFI_SUCCESS The device is enumerated (added or removed). + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device. + @retval Others Failed to enumerate the device. + +**/ +EFI_STATUS +UsbEnumerateNewDev ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port + ) +{ + USB_BUS *Bus; + USB_HUB_API *HubApi; + USB_DEVICE *Child; + USB_DEVICE *Parent; + EFI_USB_PORT_STATUS PortState; + UINTN Address; + UINT8 Config; + EFI_STATUS Status; + + Parent = HubIf->Device; + Bus = Parent->Bus; + HubApi = HubIf->HubApi; + Address = Bus->MaxDevices; + + gBS->Stall (USB_WAIT_PORT_STABLE_STALL); + + // + // Hub resets the device for at least 10 milliseconds. + // Host learns device speed. If device is of low/full speed + // and the hub is a EHCI root hub, ResetPort will release + // the device to its companion UHCI and return an error. + // + Status = HubApi->ResetPort (HubIf, Port); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to reset port %d - %r\n", Port, Status)); + + return Status; + } + + DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: hub port %d is reset\n", Port)); + + Child = UsbCreateDevice (HubIf, Port); + + if (Child == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // OK, now identify the device speed. After reset, hub + // fully knows the actual device speed. + // + Status = HubApi->GetPortStatus (HubIf, Port, &PortState); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get speed of port %d\n", Port)); + goto ON_ERROR; + } + + if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) { + DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: No device present at port %d\n", Port)); + goto ON_ERROR; + } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_SUPER_SPEED)){ + Child->Speed = EFI_USB_SPEED_SUPER; + Child->MaxPacket0 = 512; + } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_HIGH_SPEED)) { + Child->Speed = EFI_USB_SPEED_HIGH; + Child->MaxPacket0 = 64; + } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_LOW_SPEED)) { + Child->Speed = EFI_USB_SPEED_LOW; + Child->MaxPacket0 = 8; + } else { + Child->Speed = EFI_USB_SPEED_FULL; + Child->MaxPacket0 = 8; + } + + DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device is of %d speed\n", Child->Speed)); + + if (((Child->Speed == EFI_USB_SPEED_LOW) || (Child->Speed == EFI_USB_SPEED_FULL)) && + (Parent->Speed == EFI_USB_SPEED_HIGH)) { + // + // If the child is a low or full speed device, it is necessary to + // set the transaction translator. Port TT is 1-based. + // This is quite simple: + // 1. if parent is of high speed, then parent is our translator + // 2. otherwise use parent's translator. + // + Child->Translator.TranslatorHubAddress = Parent->Address; + Child->Translator.TranslatorPortNumber = (UINT8) (Port + 1); + } else { + Child->Translator = Parent->Translator; + } + DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device uses translator (%d, %d)\n", + Child->Translator.TranslatorHubAddress, + Child->Translator.TranslatorPortNumber)); + + // + // After port is reset, hub establishes a signal path between + // the device and host (DEFALUT state). Device's registers are + // reset, use default address 0 (host enumerates one device at + // a time) , and ready to respond to control transfer at EP 0. + // + + // + // Host assigns an address to the device. Device completes the + // status stage with default address, then switches to new address. + // ADDRESS state. Address zero is reserved for root hub. + // + ASSERT (Bus->MaxDevices <= 256); + for (Address = 1; Address < Bus->MaxDevices; Address++) { + if (Bus->Devices[Address] == NULL) { + break; + } + } + + if (Address >= Bus->MaxDevices) { + DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: address pool is full for port %d\n", Port)); + + Status = EFI_ACCESS_DENIED; + goto ON_ERROR; + } + + Status = UsbSetAddress (Child, (UINT8)Address); + Child->Address = (UINT8)Address; + Bus->Devices[Address] = Child; + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set device address - %r\n", Status)); + goto ON_ERROR; + } + + gBS->Stall (USB_SET_DEVICE_ADDRESS_STALL); + + DEBUG ((EFI_D_INFO, "UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address)); + + // + // Host sends a Get_Descriptor request to learn the max packet + // size of default pipe (only part of the device's descriptor). + // + Status = UsbGetMaxPacketSize0 (Child); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status)); + goto ON_ERROR; + } + + DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0)); + + // + // Host learns about the device's abilities by requesting device's + // entire descriptions. + // + Status = UsbBuildDescTable (Child); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to build descriptor table - %r\n", Status)); + goto ON_ERROR; + } + + // + // Select a default configuration: UEFI must set the configuration + // before the driver can connect to the device. + // + Config = Child->DevDesc->Configs[0]->Desc.ConfigurationValue; + Status = UsbSetConfig (Child, Config); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status)); + goto ON_ERROR; + } + + DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device %d is now in CONFIGED state\n", Address)); + + // + // Host assigns and loads a device driver. + // + Status = UsbSelectConfig (Child, Config); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to create interfaces - %r\n", Status)); + goto ON_ERROR; + } + + // + // Report Status Code to indicate USB device has been detected by hotplug + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_IO_BUS_USB | EFI_IOB_PC_HOTPLUG), + Bus->DevicePath + ); + return EFI_SUCCESS; + +ON_ERROR: + // + // If reach here, it means the enumeration process on a given port is interrupted due to error. + // The s/w resources, including the assigned address(Address) and the allocated usb device data + // structure(Bus->Devices[Address]), will NOT be freed here. These resources will be freed when + // the device is unplugged from the port or DriverBindingStop() is invoked. + // + // This way is used to co-work with the lower layer EDKII UHCI/EHCI/XHCI host controller driver. + // It's mainly because to keep UEFI spec unchanged EDKII XHCI driver have to maintain a state machine + // to keep track of the mapping between actual address and request address. If the request address + // (Address) is freed here, the Address value will be used by next enumerated device. Then EDKII XHCI + // host controller driver will have wrong information, which will cause further transaction error. + // + // EDKII UHCI/EHCI doesn't get impacted as it's make sense to reserve s/w resource till it gets unplugged. + // + return Status; +} + + +/** + Process the events on the port. + + @param HubIf The HUB that has the device connected. + @param Port The port index of the hub (started with zero). + + @retval EFI_SUCCESS The device is enumerated (added or removed). + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device. + @retval Others Failed to enumerate the device. + +**/ +EFI_STATUS +UsbEnumeratePort ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port + ) +{ + USB_HUB_API *HubApi; + USB_DEVICE *Child; + EFI_USB_PORT_STATUS PortState; + EFI_STATUS Status; + + Child = NULL; + HubApi = HubIf->HubApi; + + // + // Host learns of the new device by polling the hub for port changes. + // + Status = HubApi->GetPortStatus (HubIf, Port, &PortState); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UsbEnumeratePort: failed to get state of port %d\n", Port)); + return Status; + } + + // + // Only handle connection/enable/overcurrent/reset change. + // Usb super speed hub may report other changes, such as warm reset change. Ignore them. + // + if ((PortState.PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) { + return EFI_SUCCESS; + } + + DEBUG (( EFI_D_INFO, "UsbEnumeratePort: port %d state - %02x, change - %02x on %p\n", + Port, PortState.PortStatus, PortState.PortChangeStatus, HubIf)); + + // + // This driver only process two kinds of events now: over current and + // connect/disconnect. Other three events are: ENABLE, SUSPEND, RESET. + // ENABLE/RESET is used to reset port. SUSPEND isn't supported. + // + + if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_OVERCURRENT)) { + + if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_OVERCURRENT)) { + // + // Case1: + // Both OverCurrent and OverCurrentChange set, means over current occurs, + // which probably is caused by short circuit. It has to wait system hardware + // to perform recovery. + // + DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: Critical Over Current\n", Port)); + return EFI_DEVICE_ERROR; + + } + // + // Case2: + // Only OverCurrentChange set, means system has been recoveried from + // over current. As a result, all ports are nearly power-off, so + // it's necessary to detach and enumerate all ports again. + // + DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 2.0 device Recovery Over Current\n", Port)); + } + + if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_ENABLE)) { + // + // Case3: + // 1.1 roothub port reg doesn't reflect over-current state, while its counterpart + // on 2.0 roothub does. When over-current has influence on 1.1 device, the port + // would be disabled, so it's also necessary to detach and enumerate again. + // + DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 1.1 device Recovery Over Current\n", Port)); + } + + if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) { + // + // Case4: + // Device connected or disconnected normally. + // + DEBUG ((EFI_D_INFO, "UsbEnumeratePort: Device Connect/Disconnect Normally\n", Port)); + } + + // + // Following as the above cases, it's safety to remove and create again. + // + Child = UsbFindChild (HubIf, Port); + + if (Child != NULL) { + DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device at port %d removed from root hub %p\n", Port, HubIf)); + UsbRemoveDevice (Child); + } + + if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) { + // + // Now, new device connected, enumerate and configure the device + // + DEBUG (( EFI_D_INFO, "UsbEnumeratePort: new device connected at port %d\n", Port)); + Status = UsbEnumerateNewDev (HubIf, Port); + + } else { + DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device disconnected event on port %d\n", Port)); + } + + HubApi->ClearPortChange (HubIf, Port); + return Status; +} + + +/** + Enumerate all the changed hub ports. + + @param Event The event that is triggered. + @param Context The context to the event. + +**/ +VOID +EFIAPI +UsbHubEnumeration ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + USB_INTERFACE *HubIf; + UINT8 Byte; + UINT8 Bit; + UINT8 Index; + USB_DEVICE *Child; + + ASSERT (Context != NULL); + + HubIf = (USB_INTERFACE *) Context; + + for (Index = 0; Index < HubIf->NumOfPort; Index++) { + Child = UsbFindChild (HubIf, Index); + if ((Child != NULL) && (Child->DisconnectFail == TRUE)) { + DEBUG (( EFI_D_INFO, "UsbEnumeratePort: The device disconnect fails at port %d from hub %p, try again\n", Index, HubIf)); + UsbRemoveDevice (Child); + } + } + + if (HubIf->ChangeMap == NULL) { + return ; + } + + // + // HUB starts its port index with 1. + // + Byte = 0; + Bit = 1; + + for (Index = 0; Index < HubIf->NumOfPort; Index++) { + if (USB_BIT_IS_SET (HubIf->ChangeMap[Byte], USB_BIT (Bit))) { + UsbEnumeratePort (HubIf, Index); + } + + USB_NEXT_BIT (Byte, Bit); + } + + UsbHubAckHubStatus (HubIf->Device); + + gBS->FreePool (HubIf->ChangeMap); + HubIf->ChangeMap = NULL; + return ; +} + + +/** + Enumerate all the changed hub ports. + + @param Event The event that is triggered. + @param Context The context to the event. + +**/ +VOID +EFIAPI +UsbRootHubEnumeration ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + USB_INTERFACE *RootHub; + UINT8 Index; + USB_DEVICE *Child; + + RootHub = (USB_INTERFACE *) Context; + + for (Index = 0; Index < RootHub->NumOfPort; Index++) { + Child = UsbFindChild (RootHub, Index); + if ((Child != NULL) && (Child->DisconnectFail == TRUE)) { + DEBUG (( EFI_D_INFO, "UsbEnumeratePort: The device disconnect fails at port %d from root hub %p, try again\n", Index, RootHub)); + UsbRemoveDevice (Child); + } + + UsbEnumeratePort (RootHub, Index); + } +} diff --git a/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.h b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.h new file mode 100644 index 0000000000..ef7d440917 --- /dev/null +++ b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.h @@ -0,0 +1,203 @@ +/** @file + + USB bus enumeration interface. + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef _USB_ENUMERATION_H_ +#define _USB_ENUMERATION_H_ + +// +// Advance the byte and bit to the next bit, adjust byte accordingly. +// +#define USB_NEXT_BIT(Byte, Bit) \ + do { \ + (Bit)++; \ + if ((Bit) > 7) { \ + (Byte)++; \ + (Bit) = 0; \ + } \ + } while (0) + + +// +// Common interface used by usb bus enumeration process. +// This interface is defined to mask the difference between +// the root hub and normal hub. So, bus enumeration code +// can be shared by both root hub and normal hub +// +typedef +EFI_STATUS +(*USB_HUB_INIT) ( + IN USB_INTERFACE *UsbIf + ); + +// +// Get the port status. This function is required to +// ACK the port change bits although it will return +// the port changes in PortState. Bus enumeration code +// doesn't need to ACK the port change bits. +// +typedef +EFI_STATUS +(*USB_HUB_GET_PORT_STATUS) ( + IN USB_INTERFACE *UsbIf, + IN UINT8 Port, + OUT EFI_USB_PORT_STATUS *PortState + ); + +typedef +VOID +(*USB_HUB_CLEAR_PORT_CHANGE) ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port + ); + +typedef +EFI_STATUS +(*USB_HUB_SET_PORT_FEATURE) ( + IN USB_INTERFACE *UsbIf, + IN UINT8 Port, + IN EFI_USB_PORT_FEATURE Feature + ); + +typedef +EFI_STATUS +(*USB_HUB_CLEAR_PORT_FEATURE) ( + IN USB_INTERFACE *UsbIf, + IN UINT8 Port, + IN EFI_USB_PORT_FEATURE Feature + ); + +typedef +EFI_STATUS +(*USB_HUB_RESET_PORT) ( + IN USB_INTERFACE *UsbIf, + IN UINT8 Port + ); + +typedef +EFI_STATUS +(*USB_HUB_RELEASE) ( + IN USB_INTERFACE *UsbIf + ); + +/** + Return the endpoint descriptor in this interface. + + @param UsbIf The interface to search in. + @param EpAddr The address of the endpoint to return. + + @return The endpoint descriptor or NULL. + +**/ +USB_ENDPOINT_DESC* +UsbGetEndpointDesc ( + IN USB_INTERFACE *UsbIf, + IN UINT8 EpAddr + ); + +/** + Select an alternate setting for the interface. + Each interface can have several mutually exclusive + settings. Only one setting is active. It will + also reset its endpoints' toggle to zero. + + @param IfDesc The interface descriptor to set. + @param Alternate The alternate setting number to locate. + + @retval EFI_NOT_FOUND There is no setting with this alternate index. + @retval EFI_SUCCESS The interface is set to Alternate setting. + +**/ +EFI_STATUS +UsbSelectSetting ( + IN USB_INTERFACE_DESC *IfDesc, + IN UINT8 Alternate + ); + +/** + Select a new configuration for the device. Each + device may support several configurations. + + @param Device The device to select configuration. + @param ConfigIndex The index of the configuration ( != 0). + + @retval EFI_NOT_FOUND There is no configuration with the index. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_SUCCESS The configuration is selected. + +**/ +EFI_STATUS +UsbSelectConfig ( + IN USB_DEVICE *Device, + IN UINT8 ConfigIndex + ); + +/** + Remove the current device configuration. + + @param Device The USB device to remove configuration from. + + @return None. + +**/ +EFI_STATUS +UsbRemoveConfig ( + IN USB_DEVICE *Device + ); + +/** + Remove the device and all its children from the bus. + + @param Device The device to remove. + + @retval EFI_SUCCESS The device is removed. + +**/ +EFI_STATUS +UsbRemoveDevice ( + IN USB_DEVICE *Device + ); + +/** + Enumerate all the changed hub ports. + + @param Event The event that is triggered. + @param Context The context to the event. + + @return None. + +**/ +VOID +EFIAPI +UsbHubEnumeration ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Enumerate all the changed hub ports. + + @param Event The event that is triggered. + @param Context The context to the event. + + @return None. + +**/ +VOID +EFIAPI +UsbRootHubEnumeration ( + IN EFI_EVENT Event, + IN VOID *Context + ); +#endif diff --git a/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c new file mode 100644 index 0000000000..e3752d1f83 --- /dev/null +++ b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c @@ -0,0 +1,1411 @@ +/** @file + + Unified interface for RootHub and Hub. + +Copyright (c) 2007 - 2012, Intel Corporation. All rights reserved.
+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" + +// +// Array that maps the change bit to feature value which is +// used to clear these change bit. USB HUB API will clear +// these change bit automatically. For non-root hub, these +// bits determine whether hub will report the port in changed +// bit maps. +// +USB_CHANGE_FEATURE_MAP mHubFeatureMap[] = { + {USB_PORT_STAT_C_CONNECTION, EfiUsbPortConnectChange}, + {USB_PORT_STAT_C_ENABLE, EfiUsbPortEnableChange}, + {USB_PORT_STAT_C_SUSPEND, EfiUsbPortSuspendChange}, + {USB_PORT_STAT_C_OVERCURRENT, EfiUsbPortOverCurrentChange}, + {USB_PORT_STAT_C_RESET, EfiUsbPortResetChange} +}; + +USB_CHANGE_FEATURE_MAP mRootHubFeatureMap[] = { + {USB_PORT_STAT_C_CONNECTION, EfiUsbPortConnectChange}, + {USB_PORT_STAT_C_ENABLE, EfiUsbPortEnableChange}, + {USB_PORT_STAT_C_SUSPEND, EfiUsbPortSuspendChange}, + {USB_PORT_STAT_C_OVERCURRENT, EfiUsbPortOverCurrentChange}, + {USB_PORT_STAT_C_RESET, EfiUsbPortResetChange}, +}; + +// +// USB hub class specific requests. Although USB hub +// is related to an interface, these requests are sent +// to the control endpoint of the device. +// +/** + USB hub control transfer to set the hub depth. + + @param HubDev The device of the hub. + @param Depth The depth to set. + + @retval EFI_SUCCESS Depth of the hub is set. + @retval Others Failed to set the depth. + +**/ +EFI_STATUS +UsbHubCtrlSetHubDepth ( + IN USB_DEVICE *HubDev, + IN UINT16 Depth + ) +{ + EFI_STATUS Status; + + Status = UsbCtrlRequest ( + HubDev, + EfiUsbNoData, + USB_REQ_TYPE_CLASS, + USB_HUB_TARGET_HUB, + USB_HUB_REQ_SET_DEPTH, + Depth, + 0, + NULL, + 0 + ); + + return Status; +} + +/** + USB hub control transfer to clear the hub feature. + + @param HubDev The device of the hub. + @param Feature The feature to clear. + + @retval EFI_SUCCESS Feature of the hub is cleared. + @retval Others Failed to clear the feature. + +**/ +EFI_STATUS +UsbHubCtrlClearHubFeature ( + IN USB_DEVICE *HubDev, + IN UINT16 Feature + ) +{ + EFI_STATUS Status; + + Status = UsbCtrlRequest ( + HubDev, + EfiUsbNoData, + USB_REQ_TYPE_CLASS, + USB_HUB_TARGET_HUB, + USB_HUB_REQ_CLEAR_FEATURE, + Feature, + 0, + NULL, + 0 + ); + + return Status; +} + + +/** + Clear the feature of the device's port. + + @param HubDev The hub device. + @param Port The port to clear feature. + @param Feature The feature to clear. + + @retval EFI_SUCCESS The feature of the port is cleared. + @retval Others Failed to clear the feature. + +**/ +EFI_STATUS +UsbHubCtrlClearPortFeature ( + IN USB_DEVICE *HubDev, + IN UINT8 Port, + IN UINT16 Feature + ) +{ + EFI_STATUS Status; + + // + // In USB bus, all the port index starts from 0. But HUB + // indexes its port from 1. So, port number is added one. + // + Status = UsbCtrlRequest ( + HubDev, + EfiUsbNoData, + USB_REQ_TYPE_CLASS, + USB_HUB_TARGET_PORT, + USB_HUB_REQ_CLEAR_FEATURE, + Feature, + (UINT16) (Port + 1), + NULL, + 0 + ); + + return Status; +} + + +/** + Clear the transaction translate buffer if full/low + speed control/bulk transfer failed and the transfer + uses this hub as translator.Remember to clear the TT + buffer of transaction translator, not that of the + parent. + + @param HubDev The hub device. + @param Port The port of the hub. + @param DevAddr Address of the failed transaction. + @param EpNum The endpoint number of the failed transaction. + @param EpType The type of failed transaction. + + @retval EFI_SUCCESS The TT buffer is cleared. + @retval Others Failed to clear the TT buffer. + +**/ +EFI_STATUS +UsbHubCtrlClearTTBuffer ( + IN USB_DEVICE *HubDev, + IN UINT8 Port, + IN UINT16 DevAddr, + IN UINT16 EpNum, + IN UINT16 EpType + ) +{ + EFI_STATUS Status; + UINT16 Value; + + // + // Check USB2.0 spec page 424 for wValue's encoding + // + Value = (UINT16) ((EpNum & 0x0F) | (DevAddr << 4) | + ((EpType & 0x03) << 11) | ((EpNum & 0x80) << 15)); + + Status = UsbCtrlRequest ( + HubDev, + EfiUsbNoData, + USB_REQ_TYPE_CLASS, + USB_HUB_TARGET_PORT, + USB_HUB_REQ_CLEAR_TT, + Value, + (UINT16) (Port + 1), + NULL, + 0 + ); + + return Status; +} + +/** + Usb hub control transfer to get the super speed hub descriptor. + + @param HubDev The hub device. + @param Buf The buffer to hold the descriptor. + + @retval EFI_SUCCESS The hub descriptor is retrieved. + @retval Others Failed to retrieve the hub descriptor. + +**/ +EFI_STATUS +UsbHubCtrlGetSuperSpeedHubDesc ( + IN USB_DEVICE *HubDev, + OUT VOID *Buf + ) +{ + EFI_STATUS Status; + + Status = EFI_INVALID_PARAMETER; + + Status = UsbCtrlRequest ( + HubDev, + EfiUsbDataIn, + USB_REQ_TYPE_CLASS, + USB_HUB_TARGET_HUB, + USB_HUB_REQ_GET_DESC, + (UINT16) (USB_DESC_TYPE_HUB_SUPER_SPEED << 8), + 0, + Buf, + 32 + ); + + return Status; +} + +/** + Usb hub control transfer to get the hub descriptor. + + @param HubDev The hub device. + @param Buf The buffer to hold the descriptor. + @param Len The length to retrieve. + + @retval EFI_SUCCESS The hub descriptor is retrieved. + @retval Others Failed to retrieve the hub descriptor. + +**/ +EFI_STATUS +UsbHubCtrlGetHubDesc ( + IN USB_DEVICE *HubDev, + OUT VOID *Buf, + IN UINTN Len + ) +{ + EFI_STATUS Status; + + Status = UsbCtrlRequest ( + HubDev, + EfiUsbDataIn, + USB_REQ_TYPE_CLASS, + USB_HUB_TARGET_HUB, + USB_HUB_REQ_GET_DESC, + (UINT16) (USB_DESC_TYPE_HUB << 8), + 0, + Buf, + Len + ); + + return Status; +} + + +/** + Usb hub control transfer to get the hub status. + + @param HubDev The hub device. + @param State The variable to return the status. + + @retval EFI_SUCCESS The hub status is returned in State. + @retval Others Failed to get the hub status. + +**/ +EFI_STATUS +UsbHubCtrlGetHubStatus ( + IN USB_DEVICE *HubDev, + OUT UINT32 *State + ) +{ + EFI_STATUS Status; + + Status = UsbCtrlRequest ( + HubDev, + EfiUsbDataIn, + USB_REQ_TYPE_CLASS, + USB_HUB_TARGET_HUB, + USB_HUB_REQ_GET_STATUS, + 0, + 0, + State, + 4 + ); + + return Status; +} + + +/** + Usb hub control transfer to get the port status. + + @param HubDev The hub device. + @param Port The port of the hub. + @param State Variable to return the hub port state. + + @retval EFI_SUCCESS The port state is returned in State. + @retval Others Failed to retrieve the port state. + +**/ +EFI_STATUS +UsbHubCtrlGetPortStatus ( + IN USB_DEVICE *HubDev, + IN UINT8 Port, + OUT VOID *State + ) +{ + EFI_STATUS Status; + + // + // In USB bus, all the port index starts from 0. But HUB + // indexes its port from 1. So, port number is added one. + // No need to convert the hub bit to UEFI definition, they + // are the same + // + Status = UsbCtrlRequest ( + HubDev, + EfiUsbDataIn, + USB_REQ_TYPE_CLASS, + USB_HUB_TARGET_PORT, + USB_HUB_REQ_GET_STATUS, + 0, + (UINT16) (Port + 1), + State, + 4 + ); + + return Status; +} + + +/** + Usb hub control transfer to reset the TT (Transaction Transaltor). + + @param HubDev The hub device. + @param Port The port of the hub. + + @retval EFI_SUCCESS The TT of the hub is reset. + @retval Others Failed to reset the port. + +**/ +EFI_STATUS +UsbHubCtrlResetTT ( + IN USB_DEVICE *HubDev, + IN UINT8 Port + ) +{ + EFI_STATUS Status; + + Status = UsbCtrlRequest ( + HubDev, + EfiUsbNoData, + USB_REQ_TYPE_CLASS, + USB_HUB_TARGET_HUB, + USB_HUB_REQ_RESET_TT, + 0, + (UINT16) (Port + 1), + NULL, + 0 + ); + + return Status; +} + + +/** + Usb hub control transfer to set the hub feature. + + @param HubDev The hub device. + @param Feature The feature to set. + + @retval EFI_SUCESS The feature is set for the hub. + @retval Others Failed to set the feature. + +**/ +EFI_STATUS +UsbHubCtrlSetHubFeature ( + IN USB_DEVICE *HubDev, + IN UINT8 Feature + ) +{ + EFI_STATUS Status; + + Status = UsbCtrlRequest ( + HubDev, + EfiUsbNoData, + USB_REQ_TYPE_CLASS, + USB_HUB_TARGET_HUB, + USB_HUB_REQ_SET_FEATURE, + Feature, + 0, + NULL, + 0 + ); + + return Status; +} + + +/** + Usb hub control transfer to set the port feature. + + @param HubDev The Usb hub device. + @param Port The Usb port to set feature for. + @param Feature The feature to set. + + @retval EFI_SUCCESS The feature is set for the port. + @retval Others Failed to set the feature. + +**/ +EFI_STATUS +UsbHubCtrlSetPortFeature ( + IN USB_DEVICE *HubDev, + IN UINT8 Port, + IN UINT8 Feature + ) +{ + EFI_STATUS Status; + + // + // In USB bus, all the port index starts from 0. But HUB + // indexes its port from 1. So, port number is added one. + // + Status = UsbCtrlRequest ( + HubDev, + EfiUsbNoData, + USB_REQ_TYPE_CLASS, + USB_HUB_TARGET_PORT, + USB_HUB_REQ_SET_FEATURE, + Feature, + (UINT16) (Port + 1), + NULL, + 0 + ); + + return Status; +} + + +/** + Read the whole usb hub descriptor. It is necessary + to do it in two steps because hub descriptor is of + variable length. + + @param HubDev The hub device. + @param HubDesc The variable to return the descriptor. + + @retval EFI_SUCCESS The hub descriptor is read. + @retval Others Failed to read the hub descriptor. + +**/ +EFI_STATUS +UsbHubReadDesc ( + IN USB_DEVICE *HubDev, + OUT EFI_USB_HUB_DESCRIPTOR *HubDesc + ) +{ + EFI_STATUS Status; + + if (HubDev->Speed == EFI_USB_SPEED_SUPER) { + // + // Get the super speed hub descriptor + // + Status = UsbHubCtrlGetSuperSpeedHubDesc (HubDev, HubDesc); + } else { + + // + // First get the hub descriptor length + // + Status = UsbHubCtrlGetHubDesc (HubDev, HubDesc, 2); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the whole hub descriptor + // + Status = UsbHubCtrlGetHubDesc (HubDev, HubDesc, HubDesc->Length); + } + + return Status; +} + + + +/** + Ack the hub change bits. If these bits are not ACKed, Hub will + always return changed bit map from its interrupt endpoint. + + @param HubDev The hub device. + + @retval EFI_SUCCESS The hub change status is ACKed. + @retval Others Failed to ACK the hub status. + +**/ +EFI_STATUS +UsbHubAckHubStatus ( + IN USB_DEVICE *HubDev + ) +{ + EFI_USB_PORT_STATUS HubState; + EFI_STATUS Status; + + Status = UsbHubCtrlGetHubStatus (HubDev, (UINT32 *) &HubState); + + if (EFI_ERROR (Status)) { + return Status; + } + + if (USB_BIT_IS_SET (HubState.PortChangeStatus, USB_HUB_STAT_C_LOCAL_POWER)) { + UsbHubCtrlClearHubFeature (HubDev, USB_HUB_C_HUB_LOCAL_POWER); + } + + if (USB_BIT_IS_SET (HubState.PortChangeStatus, USB_HUB_STAT_C_OVER_CURRENT)) { + UsbHubCtrlClearHubFeature (HubDev, USB_HUB_C_HUB_OVER_CURRENT); + } + + return EFI_SUCCESS; +} + + +/** + Test whether the interface is a hub interface. + + @param UsbIf The interface to test. + + @retval TRUE The interface is a hub interface. + @retval FALSE The interface isn't a hub interface. + +**/ +BOOLEAN +UsbIsHubInterface ( + IN USB_INTERFACE *UsbIf + ) +{ + EFI_USB_INTERFACE_DESCRIPTOR *Setting; + + // + // If the hub is a high-speed hub with multiple TT, + // the hub will has a default setting of single TT. + // + Setting = &UsbIf->IfSetting->Desc; + + if ((Setting->InterfaceClass == USB_HUB_CLASS_CODE) && + (Setting->InterfaceSubClass == USB_HUB_SUBCLASS_CODE)) { + + return TRUE; + } + + return FALSE; +} + + +/** + The callback function to the USB hub status change + interrupt endpoint. It is called periodically by + the underlying host controller. + + @param Data The data read. + @param DataLength The length of the data read. + @param Context The context. + @param Result The result of the last interrupt transfer. + + @retval EFI_SUCCESS The process is OK. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + +**/ +EFI_STATUS +EFIAPI +UsbOnHubInterrupt ( + IN VOID *Data, + IN UINTN DataLength, + IN VOID *Context, + IN UINT32 Result + ) +{ + USB_INTERFACE *HubIf; + EFI_USB_IO_PROTOCOL *UsbIo; + EFI_USB_ENDPOINT_DESCRIPTOR *EpDesc; + EFI_STATUS Status; + + HubIf = (USB_INTERFACE *) Context; + UsbIo = &(HubIf->UsbIo); + EpDesc = &(HubIf->HubEp->Desc); + + if (Result != EFI_USB_NOERROR) { + // + // If endpoint is stalled, clear the stall. Use UsbIo to access + // the control transfer so internal status are maintained. + // + if (USB_BIT_IS_SET (Result, EFI_USB_ERR_STALL)) { + UsbIoClearFeature ( + UsbIo, + USB_TARGET_ENDPOINT, + USB_FEATURE_ENDPOINT_HALT, + EpDesc->EndpointAddress + ); + } + + // + // Delete and submit a new async interrupt + // + Status = UsbIo->UsbAsyncInterruptTransfer ( + UsbIo, + EpDesc->EndpointAddress, + FALSE, + 0, + 0, + NULL, + NULL + ); + + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "UsbOnHubInterrupt: failed to remove async transfer - %r\n", Status)); + return Status; + } + + Status = UsbIo->UsbAsyncInterruptTransfer ( + UsbIo, + EpDesc->EndpointAddress, + TRUE, + USB_HUB_POLL_INTERVAL, + HubIf->NumOfPort / 8 + 1, + UsbOnHubInterrupt, + HubIf + ); + + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "UsbOnHubInterrupt: failed to submit new async transfer - %r\n", Status)); + } + + return Status; + } + + if ((DataLength == 0) || (Data == NULL)) { + return EFI_SUCCESS; + } + + // + // OK, actually something is changed, save the change map + // then signal the HUB to do enumeration. This is a good + // practise since UsbOnHubInterrupt is called in the context + // of host contrller's AsyncInterrupt monitor. + // + HubIf->ChangeMap = AllocateZeroPool (DataLength); + + if (HubIf->ChangeMap == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (HubIf->ChangeMap, Data, DataLength); + gBS->SignalEvent (HubIf->HubNotify); + + return EFI_SUCCESS; +} + + + + +/** + Initialize the device for a non-root hub. + + @param HubIf The USB hub interface. + + @retval EFI_SUCCESS The hub is initialized. + @retval EFI_DEVICE_ERROR Failed to initialize the hub. + +**/ +EFI_STATUS +UsbHubInit ( + IN USB_INTERFACE *HubIf + ) +{ + EFI_USB_HUB_DESCRIPTOR HubDesc; + USB_ENDPOINT_DESC *EpDesc; + USB_INTERFACE_SETTING *Setting; + EFI_USB_IO_PROTOCOL *UsbIo; + USB_DEVICE *HubDev; + EFI_STATUS Status; + UINT8 Index; + UINT8 NumEndpoints; + UINT16 Depth; + + // + // Locate the interrupt endpoint for port change map + // + HubIf->IsHub = FALSE; + Setting = HubIf->IfSetting; + HubDev = HubIf->Device; + EpDesc = NULL; + NumEndpoints = Setting->Desc.NumEndpoints; + + for (Index = 0; Index < NumEndpoints; Index++) { + ASSERT ((Setting->Endpoints != NULL) && (Setting->Endpoints[Index] != NULL)); + + EpDesc = Setting->Endpoints[Index]; + + if (USB_BIT_IS_SET (EpDesc->Desc.EndpointAddress, USB_ENDPOINT_DIR_IN) && + (USB_ENDPOINT_TYPE (&EpDesc->Desc) == USB_ENDPOINT_INTERRUPT)) { + break; + } + } + + if (Index == NumEndpoints) { + DEBUG (( EFI_D_ERROR, "UsbHubInit: no interrupt endpoint found for hub %d\n", HubDev->Address)); + return EFI_DEVICE_ERROR; + } + + Status = UsbHubReadDesc (HubDev, &HubDesc); + + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "UsbHubInit: failed to read HUB descriptor %r\n", Status)); + return Status; + } + + HubIf->NumOfPort = HubDesc.NumPorts; + + DEBUG (( EFI_D_INFO, "UsbHubInit: hub %d has %d ports\n", HubDev->Address,HubIf->NumOfPort)); + + // + // OK, set IsHub to TRUE. Now usb bus can handle this device + // as a working HUB. If failed eariler, bus driver will not + // recognize it as a hub. Other parts of the bus should be able + // to work. + // + HubIf->IsHub = TRUE; + HubIf->HubApi = &mUsbHubApi; + HubIf->HubEp = EpDesc; + + if (HubIf->Device->Speed == EFI_USB_SPEED_SUPER) { + Depth = (UINT16)(HubIf->Device->Tier - 1); + DEBUG ((EFI_D_INFO, "UsbHubInit: Set Hub Depth as 0x%x\n", Depth)); + UsbHubCtrlSetHubDepth (HubIf->Device, Depth); + + for (Index = 0; Index < HubDesc.NumPorts; Index++) { + UsbHubCtrlSetPortFeature (HubIf->Device, Index, USB_HUB_PORT_REMOTE_WAKE_MASK); + } + } else { + // + // Feed power to all the hub ports. It should be ok + // for both gang/individual powered hubs. + // + for (Index = 0; Index < HubDesc.NumPorts; Index++) { + UsbHubCtrlSetPortFeature (HubIf->Device, Index, (EFI_USB_PORT_FEATURE) USB_HUB_PORT_POWER); + } + + // + // Update for the usb hub has no power on delay requirement + // + if (HubDesc.PwrOn2PwrGood > 0) { + gBS->Stall (HubDesc.PwrOn2PwrGood * USB_SET_PORT_POWER_STALL); + } + UsbHubAckHubStatus (HubIf->Device); + } + + // + // Create an event to enumerate the hub's port. On + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + UsbHubEnumeration, + HubIf, + &HubIf->HubNotify + ); + + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "UsbHubInit: failed to create signal for hub %d - %r\n", + HubDev->Address, Status)); + + return Status; + } + + // + // Create AsyncInterrupt to query hub port change endpoint + // periodically. If the hub ports are changed, hub will return + // changed port map from the interrupt endpoint. The port map + // must be able to hold (HubIf->NumOfPort + 1) bits (one bit for + // host change status). + // + UsbIo = &HubIf->UsbIo; + Status = UsbIo->UsbAsyncInterruptTransfer ( + UsbIo, + EpDesc->Desc.EndpointAddress, + TRUE, + USB_HUB_POLL_INTERVAL, + HubIf->NumOfPort / 8 + 1, + UsbOnHubInterrupt, + HubIf + ); + + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "UsbHubInit: failed to queue interrupt transfer for hub %d - %r\n", + HubDev->Address, Status)); + + gBS->CloseEvent (HubIf->HubNotify); + HubIf->HubNotify = NULL; + + return Status; + } + + DEBUG (( EFI_D_INFO, "UsbHubInit: hub %d initialized\n", HubDev->Address)); + return Status; +} + + + +/** + Get the port status. This function is required to + ACK the port change bits although it will return + the port changes in PortState. Bus enumeration code + doesn't need to ACK the port change bits. + + @param HubIf The hub interface. + @param Port The port of the hub to get state. + @param PortState Variable to return the port state. + + @retval EFI_SUCCESS The port status is successfully returned. + @retval Others Failed to return the status. + +**/ +EFI_STATUS +UsbHubGetPortStatus ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port, + OUT EFI_USB_PORT_STATUS *PortState + ) +{ + EFI_STATUS Status; + + Status = UsbHubCtrlGetPortStatus (HubIf->Device, Port, PortState); + + return Status; +} + + + +/** + Clear the port change status. + + @param HubIf The hub interface. + @param Port The hub port. + +**/ +VOID +UsbHubClearPortChange ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port + ) +{ + EFI_USB_PORT_STATUS PortState; + USB_CHANGE_FEATURE_MAP *Map; + UINTN Index; + EFI_STATUS Status; + + Status = UsbHubGetPortStatus (HubIf, Port, &PortState); + + if (EFI_ERROR (Status)) { + return; + } + + // + // OK, get the usb port status, now ACK the change bits. + // Don't return error when failed to clear the change bits. + // It may lead to extra port state report. USB bus should + // be able to handle this. + // + for (Index = 0; Index < sizeof (mHubFeatureMap) / sizeof (mHubFeatureMap[0]); Index++) { + Map = &mHubFeatureMap[Index]; + + if (USB_BIT_IS_SET (PortState.PortChangeStatus, Map->ChangedBit)) { + UsbHubCtrlClearPortFeature (HubIf->Device, Port, (UINT16) Map->Feature); + } + } +} + + + +/** + Function to set the port feature for non-root hub. + + @param HubIf The hub interface. + @param Port The port of the hub. + @param Feature The feature of the port to set. + + @retval EFI_SUCCESS The hub port feature is set. + @retval Others Failed to set the port feature. + +**/ +EFI_STATUS +UsbHubSetPortFeature ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port, + IN EFI_USB_PORT_FEATURE Feature + ) +{ + EFI_STATUS Status; + + Status = UsbHubCtrlSetPortFeature (HubIf->Device, Port, (UINT8) Feature); + + return Status; +} + + +/** + Interface function to clear the port feature for non-root hub. + + @param HubIf The hub interface. + @param Port The port of the hub to clear feature for. + @param Feature The feature to clear. + + @retval EFI_SUCCESS The port feature is cleared. + @retval Others Failed to clear the port feature. + +**/ +EFI_STATUS +UsbHubClearPortFeature ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port, + IN EFI_USB_PORT_FEATURE Feature + ) +{ + EFI_STATUS Status; + + Status = UsbHubCtrlClearPortFeature (HubIf->Device, Port, (UINT8) Feature); + + return Status; +} + + +/** + Interface function to reset the port. + + @param HubIf The hub interface. + @param Port The port to reset. + + @retval EFI_SUCCESS The hub port is reset. + @retval EFI_TIMEOUT Failed to reset the port in time. + @retval Others Failed to reset the port. + +**/ +EFI_STATUS +UsbHubResetPort ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port + ) +{ + EFI_USB_PORT_STATUS PortState; + UINTN Index; + EFI_STATUS Status; + + Status = UsbHubGetPortStatus (HubIf, Port, &PortState); + + if (EFI_ERROR (Status)) { + return Status; + } else if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_RESET)) { + DEBUG (( EFI_D_INFO, "UsbHubResetPort: skip reset on hub %p port %d\n", HubIf, Port)); + return EFI_SUCCESS; + } + + Status = UsbHubSetPortFeature (HubIf, Port, (EFI_USB_PORT_FEATURE) USB_HUB_PORT_RESET); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Drive the reset signal for worst 20ms. Check USB 2.0 Spec + // section 7.1.7.5 for timing requirements. + // + gBS->Stall (USB_SET_PORT_RESET_STALL); + + // + // Check USB_PORT_STAT_C_RESET bit to see if the resetting state is done. + // + ZeroMem (&PortState, sizeof (EFI_USB_PORT_STATUS)); + + for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) { + Status = UsbHubGetPortStatus (HubIf, Port, &PortState); + + if (EFI_ERROR (Status)) { + return Status; + } + + if (!EFI_ERROR (Status) && + USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_RESET)) { + gBS->Stall (USB_SET_PORT_RECOVERY_STALL); + return EFI_SUCCESS; + } + + gBS->Stall (USB_WAIT_PORT_STS_CHANGE_STALL); + } + + return EFI_TIMEOUT; +} + + +/** + Release the hub's control of the interface. + + @param HubIf The hub interface. + + @retval EFI_SUCCESS The interface is release of hub control. + +**/ +EFI_STATUS +UsbHubRelease ( + IN USB_INTERFACE *HubIf + ) +{ + EFI_USB_IO_PROTOCOL *UsbIo; + EFI_STATUS Status; + + UsbIo = &HubIf->UsbIo; + Status = UsbIo->UsbAsyncInterruptTransfer ( + UsbIo, + HubIf->HubEp->Desc.EndpointAddress, + FALSE, + USB_HUB_POLL_INTERVAL, + 0, + NULL, + 0 + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseEvent (HubIf->HubNotify); + + HubIf->IsHub = FALSE; + HubIf->HubApi = NULL; + HubIf->HubEp = NULL; + HubIf->HubNotify = NULL; + + DEBUG (( EFI_D_INFO, "UsbHubRelease: hub device %d released\n", HubIf->Device->Address)); + return EFI_SUCCESS; +} + + + +/** + Initialize the interface for root hub. + + @param HubIf The root hub interface. + + @retval EFI_SUCCESS The interface is initialized for root hub. + @retval Others Failed to initialize the hub. + +**/ +EFI_STATUS +UsbRootHubInit ( + IN USB_INTERFACE *HubIf + ) +{ + EFI_STATUS Status; + UINT8 MaxSpeed; + UINT8 NumOfPort; + UINT8 Support64; + + Status = UsbHcGetCapability (HubIf->Device->Bus, &MaxSpeed, &NumOfPort, &Support64); + + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG (( EFI_D_INFO, "UsbRootHubInit: root hub %p - max speed %d, %d ports\n", + HubIf, MaxSpeed, NumOfPort)); + + HubIf->IsHub = TRUE; + HubIf->HubApi = &mUsbRootHubApi; + HubIf->HubEp = NULL; + HubIf->MaxSpeed = MaxSpeed; + HubIf->NumOfPort = NumOfPort; + HubIf->HubNotify = NULL; + + // + // Create a timer to poll root hub ports periodically + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + UsbRootHubEnumeration, + HubIf, + &HubIf->HubNotify + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // It should signal the event immediately here, or device detection + // by bus enumeration might be delayed by the timer interval. + // + gBS->SignalEvent (HubIf->HubNotify); + + Status = gBS->SetTimer ( + HubIf->HubNotify, + TimerPeriodic, + USB_ROOTHUB_POLL_INTERVAL + ); + + if (EFI_ERROR (Status)) { + gBS->CloseEvent (HubIf->HubNotify); + } + + return Status; +} + + +/** + Get the port status. This function is required to + ACK the port change bits although it will return + the port changes in PortState. Bus enumeration code + doesn't need to ACK the port change bits. + + @param HubIf The root hub interface. + @param Port The root hub port to get the state. + @param PortState Variable to return the port state. + + @retval EFI_SUCCESS The port state is returned. + @retval Others Failed to retrieve the port state. + +**/ +EFI_STATUS +UsbRootHubGetPortStatus ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port, + OUT EFI_USB_PORT_STATUS *PortState + ) +{ + USB_BUS *Bus; + EFI_STATUS Status; + + Bus = HubIf->Device->Bus; + Status = UsbHcGetRootHubPortStatus (Bus, Port, PortState); + + return Status; +} + + +/** + Clear the port change status. + + @param HubIf The root hub interface. + @param Port The root hub port. + +**/ +VOID +UsbRootHubClearPortChange ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port + ) +{ + EFI_USB_PORT_STATUS PortState; + USB_CHANGE_FEATURE_MAP *Map; + UINTN Index; + EFI_STATUS Status; + + Status = UsbRootHubGetPortStatus (HubIf, Port, &PortState); + + if (EFI_ERROR (Status)) { + return; + } + + // + // OK, get the usb port status, now ACK the change bits. + // Don't return error when failed to clear the change bits. + // It may lead to extra port state report. USB bus should + // be able to handle this. + // + for (Index = 0; Index < sizeof (mRootHubFeatureMap) / sizeof (mRootHubFeatureMap[0]); Index++) { + Map = &mRootHubFeatureMap[Index]; + + if (USB_BIT_IS_SET (PortState.PortChangeStatus, Map->ChangedBit)) { + UsbHcClearRootHubPortFeature (HubIf->Device->Bus, Port, (EFI_USB_PORT_FEATURE) Map->Feature); + } + } +} + + +/** + Set the root hub port feature. + + @param HubIf The Usb hub interface. + @param Port The hub port. + @param Feature The feature to set. + + @retval EFI_SUCCESS The root hub port is set with the feature. + @retval Others Failed to set the feature. + +**/ +EFI_STATUS +UsbRootHubSetPortFeature ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port, + IN EFI_USB_PORT_FEATURE Feature + ) +{ + EFI_STATUS Status; + + Status = UsbHcSetRootHubPortFeature (HubIf->Device->Bus, Port, Feature); + + return Status; +} + + +/** + Clear the root hub port feature. + + @param HubIf The root hub interface. + @param Port The root hub port. + @param Feature The feature to clear. + + @retval EFI_SUCCESS The root hub port is cleared of the feature. + @retval Others Failed to clear the feature. + +**/ +EFI_STATUS +UsbRootHubClearPortFeature ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port, + IN EFI_USB_PORT_FEATURE Feature + ) +{ + EFI_STATUS Status; + + Status = UsbHcClearRootHubPortFeature (HubIf->Device->Bus, Port, Feature); + + return Status; +} + + +/** + Interface function to reset the root hub port. + + @param RootIf The root hub interface. + @param Port The port to reset. + + @retval EFI_SUCCESS The hub port is reset. + @retval EFI_TIMEOUT Failed to reset the port in time. + @retval EFI_NOT_FOUND The low/full speed device connected to high speed. + root hub is released to the companion UHCI. + @retval Others Failed to reset the port. + +**/ +EFI_STATUS +UsbRootHubResetPort ( + IN USB_INTERFACE *RootIf, + IN UINT8 Port + ) +{ + USB_BUS *Bus; + EFI_STATUS Status; + EFI_USB_PORT_STATUS PortState; + UINTN Index; + + // + // Notice: although EHCI requires that ENABLED bit be cleared + // when reset the port, we don't need to care that here. It + // should be handled in the EHCI driver. + // + Bus = RootIf->Device->Bus; + + Status = UsbHcGetRootHubPortStatus (Bus, Port, &PortState); + + if (EFI_ERROR (Status)) { + return Status; + } else if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_RESET)) { + DEBUG (( EFI_D_INFO, "UsbRootHubResetPort: skip reset on root port %d\n", Port)); + return EFI_SUCCESS; + } + + Status = UsbHcSetRootHubPortFeature (Bus, Port, EfiUsbPortReset); + + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "UsbRootHubResetPort: failed to start reset on port %d\n", Port)); + return Status; + } + + // + // Drive the reset signal for at least 50ms. Check USB 2.0 Spec + // section 7.1.7.5 for timing requirements. + // + gBS->Stall (USB_SET_ROOT_PORT_RESET_STALL); + + Status = UsbHcClearRootHubPortFeature (Bus, Port, EfiUsbPortReset); + + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "UsbRootHubResetPort: failed to clear reset on port %d\n", Port)); + return Status; + } + + gBS->Stall (USB_CLR_ROOT_PORT_RESET_STALL); + + // + // USB host controller won't clear the RESET bit until + // reset is actually finished. + // + ZeroMem (&PortState, sizeof (EFI_USB_PORT_STATUS)); + + for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) { + Status = UsbHcGetRootHubPortStatus (Bus, Port, &PortState); + + if (EFI_ERROR (Status)) { + return Status; + } + + if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_RESET)) { + break; + } + + gBS->Stall (USB_WAIT_PORT_STS_CHANGE_STALL); + } + + if (Index == USB_WAIT_PORT_STS_CHANGE_LOOP) { + DEBUG ((EFI_D_ERROR, "UsbRootHubResetPort: reset not finished in time on port %d\n", Port)); + return EFI_TIMEOUT; + } + + if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_ENABLE)) { + // + // OK, the port is reset. If root hub is of high speed and + // the device is of low/full speed, release the ownership to + // companion UHCI. If root hub is of full speed, it won't + // automatically enable the port, we need to enable it manually. + // + if (RootIf->MaxSpeed == EFI_USB_SPEED_HIGH) { + DEBUG (( EFI_D_ERROR, "UsbRootHubResetPort: release low/full speed device (%d) to UHCI\n", Port)); + + UsbRootHubSetPortFeature (RootIf, Port, EfiUsbPortOwner); + return EFI_NOT_FOUND; + + } else { + + Status = UsbRootHubSetPortFeature (RootIf, Port, EfiUsbPortEnable); + + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "UsbRootHubResetPort: failed to enable port %d for UHCI\n", Port)); + return Status; + } + + gBS->Stall (USB_SET_ROOT_PORT_ENABLE_STALL); + } + } + + return EFI_SUCCESS; +} + + +/** + Release the root hub's control of the interface. + + @param HubIf The root hub interface. + + @retval EFI_SUCCESS The root hub's control of the interface is + released. + +**/ +EFI_STATUS +UsbRootHubRelease ( + IN USB_INTERFACE *HubIf + ) +{ + DEBUG (( EFI_D_INFO, "UsbRootHubRelease: root hub released for hub %p\n", HubIf)); + + gBS->SetTimer (HubIf->HubNotify, TimerCancel, USB_ROOTHUB_POLL_INTERVAL); + gBS->CloseEvent (HubIf->HubNotify); + + return EFI_SUCCESS; +} + +USB_HUB_API mUsbHubApi = { + UsbHubInit, + UsbHubGetPortStatus, + UsbHubClearPortChange, + UsbHubSetPortFeature, + UsbHubClearPortFeature, + UsbHubResetPort, + UsbHubRelease +}; + +USB_HUB_API mUsbRootHubApi = { + UsbRootHubInit, + UsbRootHubGetPortStatus, + UsbRootHubClearPortChange, + UsbRootHubSetPortFeature, + UsbRootHubClearPortFeature, + UsbRootHubResetPort, + UsbRootHubRelease +}; diff --git a/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h new file mode 100644 index 0000000000..4e5fcd85e0 --- /dev/null +++ b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h @@ -0,0 +1,199 @@ +/** @file + + The definition for USB hub. + +Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef _USB_HUB_H_ +#define _USB_HUB_H_ + +#include + +#define USB_ENDPOINT_ADDR(EpAddr) ((EpAddr) & 0x7F) +#define USB_ENDPOINT_TYPE(Desc) ((Desc)->Attributes & USB_ENDPOINT_TYPE_MASK) + + +#define USB_DESC_TYPE_HUB 0x29 + +#define USB_DESC_TYPE_HUB_SUPER_SPEED 0x2a + +// +// Hub class control transfer target +// +#define USB_HUB_TARGET_HUB 0 +#define USB_HUB_TARGET_PORT 3 +// +// HUB class specific contrl transfer request type +// +#define USB_HUB_REQ_GET_STATUS 0 +#define USB_HUB_REQ_CLEAR_FEATURE 1 +#define USB_HUB_REQ_SET_FEATURE 3 +#define USB_HUB_REQ_GET_DESC 6 +#define USB_HUB_REQ_SET_DESC 7 +#define USB_HUB_REQ_CLEAR_TT 8 +#define USB_HUB_REQ_RESET_TT 9 +#define USB_HUB_REQ_GET_TT_STATE 10 +#define USB_HUB_REQ_STOP_TT 11 + +#define USB_HUB_REQ_SET_DEPTH 12 + +// +// USB hub class feature selector +// +#define USB_HUB_C_HUB_LOCAL_POWER 0 +#define USB_HUB_C_HUB_OVER_CURRENT 1 +#define USB_HUB_PORT_CONNECTION 0 +#define USB_HUB_PORT_ENABLE 1 +#define USB_HUB_PORT_SUSPEND 2 +#define USB_HUB_PORT_OVER_CURRENT 3 +#define USB_HUB_PORT_RESET 4 + +#define USB_HUB_PORT_LINK_STATE 5 + +#define USB_HUB_PORT_POWER 8 +#define USB_HUB_PORT_LOW_SPEED 9 +#define USB_HUB_C_PORT_CONNECT 16 +#define USB_HUB_C_PORT_ENABLE 17 +#define USB_HUB_C_PORT_SUSPEND 18 +#define USB_HUB_C_PORT_OVER_CURRENT 19 +#define USB_HUB_C_PORT_RESET 20 +#define USB_HUB_PORT_TEST 21 +#define USB_HUB_PORT_INDICATOR 22 + +#define USB_HUB_C_PORT_LINK_STATE 25 +#define USB_HUB_PORT_REMOTE_WAKE_MASK 27 +#define USB_HUB_BH_PORT_RESET 28 +#define USB_HUB_C_BH_PORT_RESET 29 + +// +// Constant value for Port Status & Port Change Status of SuperSpeed port +// +#define USB_SS_PORT_STAT_C_BH_RESET 0x0020 +#define USB_SS_PORT_STAT_C_PORT_LINK_STATE 0x0040 +// +// USB hub power control method. In gang power control +// +#define USB_HUB_GANG_POWER_CTRL 0 +#define USB_HUB_PORT_POWER_CTRL 0x01 +// +// USB hub status bits +// +#define USB_HUB_STAT_LOCAL_POWER 0x01 +#define USB_HUB_STAT_OVER_CURRENT 0x02 +#define USB_HUB_STAT_C_LOCAL_POWER 0x01 +#define USB_HUB_STAT_C_OVER_CURRENT 0x02 + +#define USB_HUB_CLASS_CODE 0x09 +#define USB_HUB_SUBCLASS_CODE 0x00 + +// +// Host software return timeout if port status doesn't change +// after 500ms(LOOP * STALL = 5000 * 0.1ms), set by experience +// +#define USB_WAIT_PORT_STS_CHANGE_LOOP 5000 + +#pragma pack(1) +// +// Hub descriptor, the last two fields are of variable lenght. +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 NumPorts; + UINT16 HubCharacter; + UINT8 PwrOn2PwrGood; + UINT8 HubContrCurrent; + UINT8 Filler[16]; +} EFI_USB_HUB_DESCRIPTOR; + +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 NumPorts; + UINT16 HubCharacter; + UINT8 PwrOn2PwrGood; + UINT8 HubContrCurrent; + UINT8 HubHdrDecLat; + UINT8 HubDelay; + UINT8 DeviceRemovable; +} EFI_USB_SUPER_SPEED_HUB_DESCRIPTOR; + +#pragma pack() + + +typedef struct { + UINT16 ChangedBit; + EFI_USB_PORT_FEATURE Feature; +} USB_CHANGE_FEATURE_MAP; + + +/** + Clear the transaction translate buffer if full/low + speed control/bulk transfer failed and the transfer + uses this hub as translator.Remember to clear the TT + buffer of transaction translator, not that of the + parent. + + @param UsbDev The Usb device. + @param Port The port of the hub. + @param DevAddr Address of the failed transaction. + @param EpNum The endpoint number of the failed transaction. + @param EpType The type of failed transaction. + + @retval EFI_SUCCESS The TT buffer is cleared. + @retval Others Failed to clear the TT buffer. + +**/ +EFI_STATUS +UsbHubCtrlClearTTBuffer ( + IN USB_DEVICE *UsbDev, + IN UINT8 Port, + IN UINT16 DevAddr, + IN UINT16 EpNum, + IN UINT16 EpType + ); + + +/** + Test whether the interface is a hub interface. + + @param UsbIf The interface to test. + + @retval TRUE The interface is a hub interface. + @retval FALSE The interface isn't a hub interface. + +**/ +BOOLEAN +UsbIsHubInterface ( + IN USB_INTERFACE *UsbIf + ); + + +/** + Ack the hub change bits. If these bits are not ACKed, Hub will + always return changed bit map from its interrupt endpoint. + + @param UsbDev The Usb device. + + @retval EFI_SUCCESS The hub change status is ACKed. + @retval Others Failed to ACK the hub status. + +**/ +EFI_STATUS +UsbHubAckHubStatus ( + IN USB_DEVICE *UsbDev + ); + +extern USB_HUB_API mUsbHubApi; +extern USB_HUB_API mUsbRootHubApi; +#endif + diff --git a/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c new file mode 100644 index 0000000000..e67a88541b --- /dev/null +++ b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c @@ -0,0 +1,1377 @@ +/** @file + + Wrapper function for usb host controller interface. + +Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+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" + +// +// if RemainingDevicePath== NULL, then all Usb child devices in this bus are wanted. +// Use a shor form Usb class Device Path, which could match any usb device, in WantedUsbIoDPList to indicate all Usb devices +// are wanted Usb devices +// +USB_CLASS_FORMAT_DEVICE_PATH mAllUsbClassDevicePath = { + { + { + MESSAGING_DEVICE_PATH, + MSG_USB_CLASS_DP, + { + (UINT8) (sizeof (USB_CLASS_DEVICE_PATH)), + (UINT8) ((sizeof (USB_CLASS_DEVICE_PATH)) >> 8) + } + }, + 0xffff, // VendorId + 0xffff, // ProductId + 0xff, // DeviceClass + 0xff, // DeviceSubClass + 0xff // DeviceProtocol + }, + + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + END_DEVICE_PATH_LENGTH, + 0 + } + } +}; + + +/** + Get the capability of the host controller. + + @param UsbBus The usb driver. + @param MaxSpeed The maximum speed this host controller supports. + @param NumOfPort The number of the root hub port. + @param Is64BitCapable Whether this controller support 64 bit addressing. + + @retval EFI_SUCCESS The host controller capability is returned. + @retval Others Failed to retrieve the host controller capability. + +**/ +EFI_STATUS +UsbHcGetCapability ( + IN USB_BUS *UsbBus, + OUT UINT8 *MaxSpeed, + OUT UINT8 *NumOfPort, + OUT UINT8 *Is64BitCapable + ) +{ + EFI_STATUS Status; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->GetCapability ( + UsbBus->Usb2Hc, + MaxSpeed, + NumOfPort, + Is64BitCapable + ); + + } else { + Status = UsbBus->UsbHc->GetRootHubPortNumber (UsbBus->UsbHc, NumOfPort); + + *MaxSpeed = EFI_USB_SPEED_FULL; + *Is64BitCapable = (UINT8) FALSE; + } + + return Status; +} + + +/** + Reset the host controller. + + @param UsbBus The usb bus driver. + @param Attributes The reset type, only global reset is used by this driver. + + @retval EFI_SUCCESS The reset operation succeeded. + @retval EFI_INVALID_PARAMETER Attributes is not valid. + @retval EFI_UNSUPPOURTED The type of reset specified by Attributes is + not currently supported by the host controller. + @retval EFI_DEVICE_ERROR Host controller isn't halted to reset. +**/ +EFI_STATUS +UsbHcReset ( + IN USB_BUS *UsbBus, + IN UINT16 Attributes + ) +{ + EFI_STATUS Status; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->Reset (UsbBus->Usb2Hc, Attributes); + } else { + Status = UsbBus->UsbHc->Reset (UsbBus->UsbHc, Attributes); + } + + return Status; +} + + +/** + Get the current operation state of the host controller. + + @param UsbBus The USB bus driver. + @param State The host controller operation state. + + @retval EFI_SUCCESS The operation state is returned in State. + @retval Others Failed to get the host controller state. + +**/ +EFI_STATUS +UsbHcGetState ( + IN USB_BUS *UsbBus, + OUT EFI_USB_HC_STATE *State + ) +{ + EFI_STATUS Status; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->GetState (UsbBus->Usb2Hc, State); + } else { + Status = UsbBus->UsbHc->GetState (UsbBus->UsbHc, State); + } + + return Status; +} + + +/** + Set the host controller operation state. + + @param UsbBus The USB bus driver. + @param State The state to set. + + @retval EFI_SUCCESS The host controller is now working at State. + @retval Others Failed to set operation state. + +**/ +EFI_STATUS +UsbHcSetState ( + IN USB_BUS *UsbBus, + IN EFI_USB_HC_STATE State + ) +{ + EFI_STATUS Status; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->SetState (UsbBus->Usb2Hc, State); + } else { + Status = UsbBus->UsbHc->SetState (UsbBus->UsbHc, State); + } + + return Status; +} + + +/** + Get the root hub port state. + + @param UsbBus The USB bus driver. + @param PortIndex The index of port. + @param PortStatus The variable to save port state. + + @retval EFI_SUCCESS The root port state is returned in. + @retval Others Failed to get the root hub port state. + +**/ +EFI_STATUS +UsbHcGetRootHubPortStatus ( + IN USB_BUS *UsbBus, + IN UINT8 PortIndex, + OUT EFI_USB_PORT_STATUS *PortStatus + ) +{ + EFI_STATUS Status; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->GetRootHubPortStatus (UsbBus->Usb2Hc, PortIndex, PortStatus); + } else { + Status = UsbBus->UsbHc->GetRootHubPortStatus (UsbBus->UsbHc, PortIndex, PortStatus); + } + + return Status; +} + + +/** + Set the root hub port feature. + + @param UsbBus The USB bus driver. + @param PortIndex The port index. + @param Feature The port feature to set. + + @retval EFI_SUCCESS The port feature is set. + @retval Others Failed to set port feature. + +**/ +EFI_STATUS +UsbHcSetRootHubPortFeature ( + IN USB_BUS *UsbBus, + IN UINT8 PortIndex, + IN EFI_USB_PORT_FEATURE Feature + ) +{ + EFI_STATUS Status; + + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->SetRootHubPortFeature (UsbBus->Usb2Hc, PortIndex, Feature); + } else { + Status = UsbBus->UsbHc->SetRootHubPortFeature (UsbBus->UsbHc, PortIndex, Feature); + } + + return Status; +} + + +/** + Clear the root hub port feature. + + @param UsbBus The USB bus driver. + @param PortIndex The port index. + @param Feature The port feature to clear. + + @retval EFI_SUCCESS The port feature is clear. + @retval Others Failed to clear port feature. + +**/ +EFI_STATUS +UsbHcClearRootHubPortFeature ( + IN USB_BUS *UsbBus, + IN UINT8 PortIndex, + IN EFI_USB_PORT_FEATURE Feature + ) +{ + EFI_STATUS Status; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->ClearRootHubPortFeature (UsbBus->Usb2Hc, PortIndex, Feature); + } else { + Status = UsbBus->UsbHc->ClearRootHubPortFeature (UsbBus->UsbHc, PortIndex, Feature); + } + + return Status; +} + + +/** + Execute a control transfer to the device. + + @param UsbBus The USB bus driver. + @param DevAddr The device address. + @param DevSpeed The device speed. + @param MaxPacket Maximum packet size of endpoint 0. + @param Request The control transfer request. + @param Direction The direction of data stage. + @param Data The buffer holding data. + @param DataLength The length of the data. + @param TimeOut Timeout (in ms) to wait until timeout. + @param Translator The transaction translator for low/full speed device. + @param UsbResult The result of transfer. + + @retval EFI_SUCCESS The control transfer finished without error. + @retval Others The control transfer failed, reason returned in UsbReslt. + +**/ +EFI_STATUS +UsbHcControlTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION Direction, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *UsbResult + ) +{ + EFI_STATUS Status; + BOOLEAN IsSlowDevice; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->ControlTransfer ( + UsbBus->Usb2Hc, + DevAddr, + DevSpeed, + MaxPacket, + Request, + Direction, + Data, + DataLength, + TimeOut, + Translator, + UsbResult + ); + + } else { + IsSlowDevice = (BOOLEAN)(EFI_USB_SPEED_LOW == DevSpeed); + Status = UsbBus->UsbHc->ControlTransfer ( + UsbBus->UsbHc, + DevAddr, + IsSlowDevice, + (UINT8) MaxPacket, + Request, + Direction, + Data, + DataLength, + TimeOut, + UsbResult + ); + } + + return Status; +} + + +/** + Execute a bulk transfer to the device's endpoint. + + @param UsbBus The USB bus driver. + @param DevAddr The target device address. + @param EpAddr The target endpoint address, with direction encoded in + bit 7. + @param DevSpeed The device's speed. + @param MaxPacket The endpoint's max packet size. + @param BufferNum The number of data buffer. + @param Data Array of pointers to data buffer. + @param DataLength The length of data buffer. + @param DataToggle On input, the initial data toggle to use, also return + the next toggle on output. + @param TimeOut The time to wait until timeout. + @param Translator The transaction translator for low/full speed device. + @param UsbResult The result of USB execution. + + @retval EFI_SUCCESS The bulk transfer is finished without error. + @retval Others Failed to execute bulk transfer, result in UsbResult. + +**/ +EFI_STATUS +UsbHcBulkTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN UINT8 BufferNum, + IN OUT VOID *Data[EFI_USB_MAX_BULK_BUFFER_NUM], + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *UsbResult + ) +{ + EFI_STATUS Status; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->BulkTransfer ( + UsbBus->Usb2Hc, + DevAddr, + EpAddr, + DevSpeed, + MaxPacket, + BufferNum, + Data, + DataLength, + DataToggle, + TimeOut, + Translator, + UsbResult + ); + } else { + Status = UsbBus->UsbHc->BulkTransfer ( + UsbBus->UsbHc, + DevAddr, + EpAddr, + (UINT8) MaxPacket, + *Data, + DataLength, + DataToggle, + TimeOut, + UsbResult + ); + } + + return Status; +} + + +/** + Queue or cancel an asynchronous interrupt transfer. + + @param UsbBus The USB bus driver. + @param DevAddr The target device address. + @param EpAddr The target endpoint address, with direction encoded in + bit 7. + @param DevSpeed The device's speed. + @param MaxPacket The endpoint's max packet size. + @param IsNewTransfer Whether this is a new request. If not, cancel the old + request. + @param DataToggle Data toggle to use on input, next toggle on output. + @param PollingInterval The interval to poll the interrupt transfer (in ms). + @param DataLength The length of periodical data receive. + @param Translator The transaction translator for low/full speed device. + @param Callback Function to call when data is received. + @param Context The context to the callback. + + @retval EFI_SUCCESS The asynchronous transfer is queued. + @retval Others Failed to queue the transfer. + +**/ +EFI_STATUS +UsbHcAsyncInterruptTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN BOOLEAN IsNewTransfer, + IN OUT UINT8 *DataToggle, + IN UINTN PollingInterval, + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, + IN VOID *Context OPTIONAL + ) +{ + EFI_STATUS Status; + BOOLEAN IsSlowDevice; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->AsyncInterruptTransfer ( + UsbBus->Usb2Hc, + DevAddr, + EpAddr, + DevSpeed, + MaxPacket, + IsNewTransfer, + DataToggle, + PollingInterval, + DataLength, + Translator, + Callback, + Context + ); + } else { + IsSlowDevice = (BOOLEAN)(EFI_USB_SPEED_LOW == DevSpeed); + + Status = UsbBus->UsbHc->AsyncInterruptTransfer ( + UsbBus->UsbHc, + DevAddr, + EpAddr, + IsSlowDevice, + (UINT8) MaxPacket, + IsNewTransfer, + DataToggle, + PollingInterval, + DataLength, + Callback, + Context + ); + } + + return Status; +} + + +/** + Execute a synchronous interrupt transfer to the target endpoint. + + @param UsbBus The USB bus driver. + @param DevAddr The target device address. + @param EpAddr The target endpoint address, with direction encoded in + bit 7. + @param DevSpeed The device's speed. + @param MaxPacket The endpoint's max packet size. + @param Data Pointer to data buffer. + @param DataLength The length of data buffer. + @param DataToggle On input, the initial data toggle to use, also return + the next toggle on output. + @param TimeOut The time to wait until timeout. + @param Translator The transaction translator for low/full speed device. + @param UsbResult The result of USB execution. + + @retval EFI_SUCCESS The synchronous interrupt transfer is OK. + @retval Others Failed to execute the synchronous interrupt transfer. + +**/ +EFI_STATUS +UsbHcSyncInterruptTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *UsbResult + ) +{ + EFI_STATUS Status; + BOOLEAN IsSlowDevice; + + if (UsbBus->Usb2Hc != NULL) { + Status = UsbBus->Usb2Hc->SyncInterruptTransfer ( + UsbBus->Usb2Hc, + DevAddr, + EpAddr, + DevSpeed, + MaxPacket, + Data, + DataLength, + DataToggle, + TimeOut, + Translator, + UsbResult + ); + } else { + IsSlowDevice = (BOOLEAN) ((EFI_USB_SPEED_LOW == DevSpeed) ? TRUE : FALSE); + Status = UsbBus->UsbHc->SyncInterruptTransfer ( + UsbBus->UsbHc, + DevAddr, + EpAddr, + IsSlowDevice, + (UINT8) MaxPacket, + Data, + DataLength, + DataToggle, + TimeOut, + UsbResult + ); + } + + return Status; +} + + +/** + Execute a synchronous Isochronous USB transfer. + + @param UsbBus The USB bus driver. + @param DevAddr The target device address. + @param EpAddr The target endpoint address, with direction encoded in + bit 7. + @param DevSpeed The device's speed. + @param MaxPacket The endpoint's max packet size. + @param BufferNum The number of data buffer. + @param Data Array of pointers to data buffer. + @param DataLength The length of data buffer. + @param Translator The transaction translator for low/full speed device. + @param UsbResult The result of USB execution. + + @retval EFI_UNSUPPORTED The isochronous transfer isn't supported now. + +**/ +EFI_STATUS +UsbHcIsochronousTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN UINT8 BufferNum, + IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM], + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *UsbResult + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Queue an asynchronous isochronous transfer. + + @param UsbBus The USB bus driver. + @param DevAddr The target device address. + @param EpAddr The target endpoint address, with direction encoded in + bit 7. + @param DevSpeed The device's speed. + @param MaxPacket The endpoint's max packet size. + @param BufferNum The number of data buffer. + @param Data Array of pointers to data buffer. + @param DataLength The length of data buffer. + @param Translator The transaction translator for low/full speed device. + @param Callback The function to call when data is transferred. + @param Context The context to the callback function. + + @retval EFI_UNSUPPORTED The asynchronous isochronous transfer isn't supported. + +**/ +EFI_STATUS +UsbHcAsyncIsochronousTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN UINT8 BufferNum, + IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM], + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, + IN VOID *Context + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Open the USB host controller protocol BY_CHILD. + + @param Bus The USB bus driver. + @param Child The child handle. + + @return The open protocol return. + +**/ +EFI_STATUS +UsbOpenHostProtoByChild ( + IN USB_BUS *Bus, + IN EFI_HANDLE Child + ) +{ + EFI_USB_HC_PROTOCOL *UsbHc; + EFI_USB2_HC_PROTOCOL *Usb2Hc; + EFI_STATUS Status; + + if (Bus->Usb2Hc != NULL) { + Status = gBS->OpenProtocol ( + Bus->HostHandle, + &gEfiUsb2HcProtocolGuid, + (VOID **) &Usb2Hc, + mUsbBusDriverBinding.DriverBindingHandle, + Child, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + } else { + Status = gBS->OpenProtocol ( + Bus->HostHandle, + &gEfiUsbHcProtocolGuid, + (VOID **) &UsbHc, + mUsbBusDriverBinding.DriverBindingHandle, + Child, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } + + return Status; +} + + +/** + Close the USB host controller protocol BY_CHILD. + + @param Bus The USB bus driver. + @param Child The child handle. + +**/ +VOID +UsbCloseHostProtoByChild ( + IN USB_BUS *Bus, + IN EFI_HANDLE Child + ) +{ + if (Bus->Usb2Hc != NULL) { + gBS->CloseProtocol ( + Bus->HostHandle, + &gEfiUsb2HcProtocolGuid, + mUsbBusDriverBinding.DriverBindingHandle, + Child + ); + + } else { + gBS->CloseProtocol ( + Bus->HostHandle, + &gEfiUsbHcProtocolGuid, + mUsbBusDriverBinding.DriverBindingHandle, + Child + ); + } +} + + +/** + return the current TPL, copied from the EDKII glue lib. + + @param VOID. + + @return Current TPL. + +**/ +EFI_TPL +UsbGetCurrentTpl ( + VOID + ) +{ + EFI_TPL Tpl; + + Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + gBS->RestoreTPL (Tpl); + + return Tpl; +} + +/** + Create a new device path which only contain the first Usb part of the DevicePath. + + @param DevicePath A full device path which contain the usb nodes. + + @return A new device path which only contain the Usb part of the DevicePath. + +**/ +EFI_DEVICE_PATH_PROTOCOL * +EFIAPI +GetUsbDPFromFullDP ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_DEVICE_PATH_PROTOCOL *UsbDevicePathPtr; + EFI_DEVICE_PATH_PROTOCOL *UsbDevicePathBeginPtr; + EFI_DEVICE_PATH_PROTOCOL *UsbDevicePathEndPtr; + UINTN Size; + + // + // Get the Usb part first Begin node in full device path + // + UsbDevicePathBeginPtr = DevicePath; + while ( (!IsDevicePathEnd (UsbDevicePathBeginPtr))&& + ((UsbDevicePathBeginPtr->Type != MESSAGING_DEVICE_PATH) || + (UsbDevicePathBeginPtr->SubType != MSG_USB_DP && + UsbDevicePathBeginPtr->SubType != MSG_USB_CLASS_DP + && UsbDevicePathBeginPtr->SubType != MSG_USB_WWID_DP + ))) { + + UsbDevicePathBeginPtr = NextDevicePathNode(UsbDevicePathBeginPtr); + } + + // + // Get the Usb part first End node in full device path + // + UsbDevicePathEndPtr = UsbDevicePathBeginPtr; + while ((!IsDevicePathEnd (UsbDevicePathEndPtr))&& + (UsbDevicePathEndPtr->Type == MESSAGING_DEVICE_PATH) && + (UsbDevicePathEndPtr->SubType == MSG_USB_DP || + UsbDevicePathEndPtr->SubType == MSG_USB_CLASS_DP + || UsbDevicePathEndPtr->SubType == MSG_USB_WWID_DP + )) { + + UsbDevicePathEndPtr = NextDevicePathNode(UsbDevicePathEndPtr); + } + + Size = GetDevicePathSize (UsbDevicePathBeginPtr); + Size -= GetDevicePathSize (UsbDevicePathEndPtr); + if (Size ==0){ + // + // The passed in DevicePath does not contain the usb nodes + // + return NULL; + } + + // + // Create a new device path which only contain the above Usb part + // + UsbDevicePathPtr = AllocateZeroPool (Size + sizeof (EFI_DEVICE_PATH_PROTOCOL)); + ASSERT (UsbDevicePathPtr != NULL); + CopyMem (UsbDevicePathPtr, UsbDevicePathBeginPtr, Size); + // + // Append end device path node + // + UsbDevicePathEndPtr = (EFI_DEVICE_PATH_PROTOCOL *) ((UINTN) UsbDevicePathPtr + Size); + SetDevicePathEndNode (UsbDevicePathEndPtr); + return UsbDevicePathPtr; +} + +/** + Check whether a usb device path is in a DEVICE_PATH_LIST_ITEM list. + + @param UsbDP a usb device path of DEVICE_PATH_LIST_ITEM. + @param UsbIoDPList a DEVICE_PATH_LIST_ITEM list. + + @retval TRUE there is a DEVICE_PATH_LIST_ITEM in UsbIoDPList which contains the passed in UsbDP. + @retval FALSE there is no DEVICE_PATH_LIST_ITEM in UsbIoDPList which contains the passed in UsbDP. + +**/ +BOOLEAN +EFIAPI +SearchUsbDPInList ( + IN EFI_DEVICE_PATH_PROTOCOL *UsbDP, + IN LIST_ENTRY *UsbIoDPList + ) +{ + LIST_ENTRY *ListIndex; + DEVICE_PATH_LIST_ITEM *ListItem; + BOOLEAN Found; + UINTN UsbDpDevicePathSize; + + // + // Check that UsbDP and UsbIoDPList are valid + // + if ((UsbIoDPList == NULL) || (UsbDP == NULL)) { + return FALSE; + } + + Found = FALSE; + ListIndex = UsbIoDPList->ForwardLink; + while (ListIndex != UsbIoDPList){ + ListItem = CR(ListIndex, DEVICE_PATH_LIST_ITEM, Link, DEVICE_PATH_LIST_ITEM_SIGNATURE); + // + // Compare DEVICE_PATH_LIST_ITEM.DevicePath[] + // + ASSERT (ListItem->DevicePath != NULL); + + UsbDpDevicePathSize = GetDevicePathSize (UsbDP); + if (UsbDpDevicePathSize == GetDevicePathSize (ListItem->DevicePath)) { + if ((CompareMem (UsbDP, ListItem->DevicePath, UsbDpDevicePathSize)) == 0) { + Found = TRUE; + break; + } + } + ListIndex = ListIndex->ForwardLink; + } + + return Found; +} + +/** + Add a usb device path into the DEVICE_PATH_LIST_ITEM list. + + @param UsbDP a usb device path of DEVICE_PATH_LIST_ITEM. + @param UsbIoDPList a DEVICE_PATH_LIST_ITEM list. + + @retval EFI_INVALID_PARAMETER If parameters are invalid, return this value. + @retval EFI_SUCCESS If Add operation is successful, return this value. + +**/ +EFI_STATUS +EFIAPI +AddUsbDPToList ( + IN EFI_DEVICE_PATH_PROTOCOL *UsbDP, + IN LIST_ENTRY *UsbIoDPList + ) +{ + DEVICE_PATH_LIST_ITEM *ListItem; + + // + // Check that UsbDP and UsbIoDPList are valid + // + if ((UsbIoDPList == NULL) || (UsbDP == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (SearchUsbDPInList (UsbDP, UsbIoDPList)){ + return EFI_SUCCESS; + } + + // + // Prepare the usbio device path DEVICE_PATH_LIST_ITEM structure. + // + ListItem = AllocateZeroPool (sizeof (DEVICE_PATH_LIST_ITEM)); + ASSERT (ListItem != NULL); + ListItem->Signature = DEVICE_PATH_LIST_ITEM_SIGNATURE; + ListItem->DevicePath = DuplicateDevicePath (UsbDP); + + InsertTailList (UsbIoDPList, &ListItem->Link); + + return EFI_SUCCESS; +} + +/** + Check whether usb device, whose interface is UsbIf, matches the usb class which indicated by + UsbClassDevicePathPtr whose is a short form usb class device path. + + @param UsbClassDevicePathPtr a short form usb class device path. + @param UsbIf a usb device interface. + + @retval TRUE the usb device match the usb class. + @retval FALSE the usb device does not match the usb class. + +**/ +BOOLEAN +EFIAPI +MatchUsbClass ( + IN USB_CLASS_DEVICE_PATH *UsbClassDevicePathPtr, + IN USB_INTERFACE *UsbIf + ) +{ + USB_INTERFACE_DESC *IfDesc; + EFI_USB_INTERFACE_DESCRIPTOR *ActIfDesc; + EFI_USB_DEVICE_DESCRIPTOR *DevDesc; + + + if ((UsbClassDevicePathPtr->Header.Type != MESSAGING_DEVICE_PATH) || + (UsbClassDevicePathPtr->Header.SubType != MSG_USB_CLASS_DP)){ + ASSERT (0); + return FALSE; + } + + IfDesc = UsbIf->IfDesc; + ASSERT (IfDesc->ActiveIndex < USB_MAX_INTERFACE_SETTING); + ActIfDesc = &(IfDesc->Settings[IfDesc->ActiveIndex]->Desc); + DevDesc = &(UsbIf->Device->DevDesc->Desc); + + // + // If connect class policy, determine whether to create device handle by the five fields + // in class device path node. + // + // In addtion, hub interface is always matched for this policy. + // + if ((ActIfDesc->InterfaceClass == USB_HUB_CLASS_CODE) && + (ActIfDesc->InterfaceSubClass == USB_HUB_SUBCLASS_CODE)) { + return TRUE; + } + + // + // If vendor id or product id is 0xffff, they will be ignored. + // + if ((UsbClassDevicePathPtr->VendorId == 0xffff || UsbClassDevicePathPtr->VendorId == DevDesc->IdVendor) && + (UsbClassDevicePathPtr->ProductId == 0xffff || UsbClassDevicePathPtr->ProductId == DevDesc->IdProduct)) { + + // + // If Class in Device Descriptor is set to 0, the counterparts in interface should be checked. + // + if (DevDesc->DeviceClass == 0) { + if ((UsbClassDevicePathPtr->DeviceClass == ActIfDesc->InterfaceClass || + UsbClassDevicePathPtr->DeviceClass == 0xff) && + (UsbClassDevicePathPtr->DeviceSubClass == ActIfDesc->InterfaceSubClass || + UsbClassDevicePathPtr->DeviceSubClass == 0xff) && + (UsbClassDevicePathPtr->DeviceProtocol == ActIfDesc->InterfaceProtocol || + UsbClassDevicePathPtr->DeviceProtocol == 0xff)) { + return TRUE; + } + + } else if ((UsbClassDevicePathPtr->DeviceClass == DevDesc->DeviceClass || + UsbClassDevicePathPtr->DeviceClass == 0xff) && + (UsbClassDevicePathPtr->DeviceSubClass == DevDesc->DeviceSubClass || + UsbClassDevicePathPtr->DeviceSubClass == 0xff) && + (UsbClassDevicePathPtr->DeviceProtocol == DevDesc->DeviceProtocol || + UsbClassDevicePathPtr->DeviceProtocol == 0xff)) { + + return TRUE; + } + } + + return FALSE; +} + +/** + Check whether usb device, whose interface is UsbIf, matches the usb WWID requirement which indicated by + UsbWWIDDevicePathPtr whose is a short form usb WWID device path. + + @param UsbWWIDDevicePathPtr a short form usb WWID device path. + @param UsbIf a usb device interface. + + @retval TRUE the usb device match the usb WWID requirement. + @retval FALSE the usb device does not match the usb WWID requirement. + +**/ +BOOLEAN +MatchUsbWwid ( + IN USB_WWID_DEVICE_PATH *UsbWWIDDevicePathPtr, + IN USB_INTERFACE *UsbIf + ) +{ + USB_INTERFACE_DESC *IfDesc; + EFI_USB_INTERFACE_DESCRIPTOR *ActIfDesc; + EFI_USB_DEVICE_DESCRIPTOR *DevDesc; + EFI_USB_STRING_DESCRIPTOR *StrDesc; + UINT16 Index; + CHAR16 *CompareStr; + UINTN CompareLen; + UINTN Length; + + if ((UsbWWIDDevicePathPtr->Header.Type != MESSAGING_DEVICE_PATH) || + (UsbWWIDDevicePathPtr->Header.SubType != MSG_USB_WWID_DP )){ + ASSERT (0); + return FALSE; + } + + IfDesc = UsbIf->IfDesc; + ASSERT (IfDesc->ActiveIndex < USB_MAX_INTERFACE_SETTING); + ActIfDesc = &(IfDesc->Settings[IfDesc->ActiveIndex]->Desc); + DevDesc = &(UsbIf->Device->DevDesc->Desc); + + // + // In addition, Hub interface is always matched for this policy. + // + if ((ActIfDesc->InterfaceClass == USB_HUB_CLASS_CODE) && + (ActIfDesc->InterfaceSubClass == USB_HUB_SUBCLASS_CODE)) { + return TRUE; + } + + // + // Check Vendor Id, Product Id and Interface Number. + // + if ((DevDesc->IdVendor != UsbWWIDDevicePathPtr->VendorId) || + (DevDesc->IdProduct != UsbWWIDDevicePathPtr->ProductId) || + (ActIfDesc->InterfaceNumber != UsbWWIDDevicePathPtr->InterfaceNumber)) { + return FALSE; + } + + // + // Check SerialNumber. + // + if (DevDesc->StrSerialNumber == 0) { + return FALSE; + } + + // + // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters. + // + CompareStr = (CHAR16 *) (UINTN) (UsbWWIDDevicePathPtr + 1); + CompareLen = (DevicePathNodeLength (UsbWWIDDevicePathPtr) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16); + if (CompareStr[CompareLen - 1] == L'\0') { + CompareLen--; + } + + // + // Compare serial number in each supported language. + // + for (Index = 0; Index < UsbIf->Device->TotalLangId; Index++) { + StrDesc = UsbGetOneString (UsbIf->Device, DevDesc->StrSerialNumber, UsbIf->Device->LangId[Index]); + if (StrDesc == NULL) { + continue; + } + + Length = (StrDesc->Length - 2) / sizeof (CHAR16); + if ((Length >= CompareLen) && + (CompareMem (StrDesc->String + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) { + return TRUE; + } + } + + return FALSE; +} + +/** + Free a DEVICE_PATH_LIST_ITEM list. + + @param UsbIoDPList a DEVICE_PATH_LIST_ITEM list pointer. + + @retval EFI_INVALID_PARAMETER If parameters are invalid, return this value. + @retval EFI_SUCCESS If free operation is successful, return this value. + +**/ +EFI_STATUS +EFIAPI +UsbBusFreeUsbDPList ( + IN LIST_ENTRY *UsbIoDPList + ) +{ + LIST_ENTRY *ListIndex; + DEVICE_PATH_LIST_ITEM *ListItem; + + // + // Check that ControllerHandle is a valid handle + // + if (UsbIoDPList == NULL) { + return EFI_INVALID_PARAMETER; + } + + ListIndex = UsbIoDPList->ForwardLink; + while (ListIndex != UsbIoDPList){ + ListItem = CR(ListIndex, DEVICE_PATH_LIST_ITEM, Link, DEVICE_PATH_LIST_ITEM_SIGNATURE); + // + // Free DEVICE_PATH_LIST_ITEM.DevicePath[] + // + if (ListItem->DevicePath != NULL){ + FreePool(ListItem->DevicePath); + } + // + // Free DEVICE_PATH_LIST_ITEM itself + // + ListIndex = ListIndex->ForwardLink; + RemoveEntryList (&ListItem->Link); + FreePool (ListItem); + } + + InitializeListHead (UsbIoDPList); + return EFI_SUCCESS; +} + +/** + Store a wanted usb child device info (its Usb part of device path) which is indicated by + RemainingDevicePath in a Usb bus which is indicated by UsbBusId. + + @param UsbBusId Point to EFI_USB_BUS_PROTOCOL interface. + @param RemainingDevicePath The remaining device patch. + + @retval EFI_SUCCESS Add operation is successful. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +UsbBusAddWantedUsbIoDP ( + IN EFI_USB_BUS_PROTOCOL *UsbBusId, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + USB_BUS *Bus; + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePathPtr; + + // + // Check whether remaining device path is valid + // + if (RemainingDevicePath != NULL && !IsDevicePathEnd (RemainingDevicePath)) { + if ((RemainingDevicePath->Type != MESSAGING_DEVICE_PATH) || + (RemainingDevicePath->SubType != MSG_USB_DP && + RemainingDevicePath->SubType != MSG_USB_CLASS_DP + && RemainingDevicePath->SubType != MSG_USB_WWID_DP + )) { + return EFI_INVALID_PARAMETER; + } + } + + if (UsbBusId == NULL){ + return EFI_INVALID_PARAMETER; + } + + Bus = USB_BUS_FROM_THIS (UsbBusId); + + if (RemainingDevicePath == NULL) { + // + // RemainingDevicePath == NULL means all Usb devices in this bus are wanted. + // Here use a Usb class Device Path in WantedUsbIoDPList to indicate all Usb devices + // are wanted Usb devices + // + Status = UsbBusFreeUsbDPList (&Bus->WantedUsbIoDPList); + ASSERT (!EFI_ERROR (Status)); + DevicePathPtr = DuplicateDevicePath ((EFI_DEVICE_PATH_PROTOCOL *) &mAllUsbClassDevicePath); + } else if (!IsDevicePathEnd (RemainingDevicePath)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // Create new Usb device path according to the usb part in remaining device path + // + DevicePathPtr = GetUsbDPFromFullDP (RemainingDevicePath); + } else { + // + // If RemainingDevicePath is the End of Device Path Node, + // skip enumerate any device and return EFI_SUCESSS + // + return EFI_SUCCESS; + } + + ASSERT (DevicePathPtr != NULL); + Status = AddUsbDPToList (DevicePathPtr, &Bus->WantedUsbIoDPList); + ASSERT (!EFI_ERROR (Status)); + FreePool (DevicePathPtr); + return EFI_SUCCESS; +} + +/** + Check whether a usb child device is the wanted device in a bus. + + @param Bus The Usb bus's private data pointer. + @param UsbIf The usb child device inferface. + + @retval True If a usb child device is the wanted device in a bus. + @retval False If a usb child device is *NOT* the wanted device in a bus. + +**/ +BOOLEAN +EFIAPI +UsbBusIsWantedUsbIO ( + IN USB_BUS *Bus, + IN USB_INTERFACE *UsbIf + ) +{ + EFI_DEVICE_PATH_PROTOCOL *DevicePathPtr; + LIST_ENTRY *WantedUsbIoDPListPtr; + LIST_ENTRY *WantedListIndex; + DEVICE_PATH_LIST_ITEM *WantedListItem; + BOOLEAN DoConvert; + UINTN FirstDevicePathSize; + + // + // Check whether passed in parameters are valid + // + if ((UsbIf == NULL) || (Bus == NULL)) { + return FALSE; + } + // + // Check whether UsbIf is Hub + // + if (UsbIf->IsHub) { + return TRUE; + } + + // + // Check whether all Usb devices in this bus are wanted + // + if (SearchUsbDPInList ((EFI_DEVICE_PATH_PROTOCOL *)&mAllUsbClassDevicePath, &Bus->WantedUsbIoDPList)){ + return TRUE; + } + + // + // Check whether the Usb device match any item in WantedUsbIoDPList + // + WantedUsbIoDPListPtr = &Bus->WantedUsbIoDPList; + // + // Create new Usb device path according to the usb part in UsbIo full device path + // + DevicePathPtr = GetUsbDPFromFullDP (UsbIf->DevicePath); + ASSERT (DevicePathPtr != NULL); + + DoConvert = FALSE; + WantedListIndex = WantedUsbIoDPListPtr->ForwardLink; + while (WantedListIndex != WantedUsbIoDPListPtr){ + WantedListItem = CR(WantedListIndex, DEVICE_PATH_LIST_ITEM, Link, DEVICE_PATH_LIST_ITEM_SIGNATURE); + ASSERT (WantedListItem->DevicePath->Type == MESSAGING_DEVICE_PATH); + switch (WantedListItem->DevicePath->SubType) { + case MSG_USB_DP: + FirstDevicePathSize = GetDevicePathSize (WantedListItem->DevicePath); + if (FirstDevicePathSize == GetDevicePathSize (DevicePathPtr)) { + if (CompareMem ( + WantedListItem->DevicePath, + DevicePathPtr, + GetDevicePathSize (DevicePathPtr)) == 0 + ) { + DoConvert = TRUE; + } + } + break; + case MSG_USB_CLASS_DP: + if (MatchUsbClass((USB_CLASS_DEVICE_PATH *)WantedListItem->DevicePath, UsbIf)) { + DoConvert = TRUE; + } + break; + case MSG_USB_WWID_DP: + if (MatchUsbWwid((USB_WWID_DEVICE_PATH *)WantedListItem->DevicePath, UsbIf)) { + DoConvert = TRUE; + } + break; + default: + ASSERT (0); + break; + } + + if (DoConvert) { + break; + } + + WantedListIndex = WantedListIndex->ForwardLink; + } + gBS->FreePool (DevicePathPtr); + + // + // Check whether the new Usb device path is wanted + // + if (DoConvert){ + return TRUE; + } else { + return FALSE; + } +} + +/** + Recursively connnect every wanted usb child device to ensure they all fully connected. + Check all the child Usb IO handles in this bus, recursively connecte if it is wanted usb child device. + + @param UsbBusId Point to EFI_USB_BUS_PROTOCOL interface. + + @retval EFI_SUCCESS Connect is done successfully. + @retval EFI_INVALID_PARAMETER The parameter is invalid. + +**/ +EFI_STATUS +EFIAPI +UsbBusRecursivelyConnectWantedUsbIo ( + IN EFI_USB_BUS_PROTOCOL *UsbBusId + ) +{ + USB_BUS *Bus; + EFI_STATUS Status; + UINTN Index; + EFI_USB_IO_PROTOCOL *UsbIo; + USB_INTERFACE *UsbIf; + UINTN UsbIoHandleCount; + EFI_HANDLE *UsbIoBuffer; + EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath; + + if (UsbBusId == NULL){ + return EFI_INVALID_PARAMETER; + } + + Bus = USB_BUS_FROM_THIS (UsbBusId); + + // + // Get all Usb IO handles in system + // + UsbIoHandleCount = 0; + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiUsbIoProtocolGuid, NULL, &UsbIoHandleCount, &UsbIoBuffer); + if (Status == EFI_NOT_FOUND || UsbIoHandleCount == 0) { + return EFI_SUCCESS; + } + ASSERT (!EFI_ERROR (Status)); + + for (Index = 0; Index < UsbIoHandleCount; Index++) { + // + // Check whether the USB IO handle is a child of this bus + // Note: The usb child handle maybe invalid because of hot plugged out during the loop + // + UsbIoDevicePath = NULL; + Status = gBS->HandleProtocol (UsbIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID *) &UsbIoDevicePath); + if (EFI_ERROR (Status) || UsbIoDevicePath == NULL) { + continue; + } + if (CompareMem ( + UsbIoDevicePath, + Bus->DevicePath, + (GetDevicePathSize (Bus->DevicePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL)) + ) != 0) { + continue; + } + + // + // Get the child Usb IO interface + // + Status = gBS->HandleProtocol( + UsbIoBuffer[Index], + &gEfiUsbIoProtocolGuid, + (VOID **) &UsbIo + ); + if (EFI_ERROR (Status)) { + continue; + } + UsbIf = USB_INTERFACE_FROM_USBIO (UsbIo); + + if (UsbBusIsWantedUsbIO (Bus, UsbIf)) { + if (!UsbIf->IsManaged) { + // + // Recursively connect the wanted Usb Io handle + // + DEBUG ((EFI_D_INFO, "UsbConnectDriver: TPL before connect is %d\n", (UINT32)UsbGetCurrentTpl ())); + Status = gBS->ConnectController (UsbIf->Handle, NULL, NULL, TRUE); + UsbIf->IsManaged = (BOOLEAN)!EFI_ERROR (Status); + DEBUG ((EFI_D_INFO, "UsbConnectDriver: TPL after connect is %d\n", (UINT32)UsbGetCurrentTpl())); + } + } + } + + return EFI_SUCCESS; +} + diff --git a/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.h b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.h new file mode 100644 index 0000000000..26709caa36 --- /dev/null +++ b/Core/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.h @@ -0,0 +1,398 @@ +/** @file + + Manage Usb Port/Hc/Etc. + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef _EFI_USB_UTILITY_H_ +#define _EFI_USB_UTILITY_H_ + +/** + Get the capability of the host controller. + + @param UsbBus The usb driver. + @param MaxSpeed The maximum speed this host controller supports. + @param NumOfPort The number of the root hub port. + @param Is64BitCapable Whether this controller support 64 bit addressing. + + @retval EFI_SUCCESS The host controller capability is returned. + @retval Others Failed to retrieve the host controller capability. + +**/ +EFI_STATUS +UsbHcGetCapability ( + IN USB_BUS *UsbBus, + OUT UINT8 *MaxSpeed, + OUT UINT8 *NumOfPort, + OUT UINT8 *Is64BitCapable + ); + +/** + Reset the host controller. + + @param UsbBus The usb bus driver. + @param Attributes The reset type, only global reset is used by this driver. + + @retval EFI_SUCCESS The reset operation succeeded. + @retval EFI_INVALID_PARAMETER Attributes is not valid. + @retval EFI_UNSUPPOURTED The type of reset specified by Attributes is + not currently supported by the host controller. + @retval EFI_DEVICE_ERROR Host controller isn't halted to reset. +**/ +EFI_STATUS +UsbHcReset ( + IN USB_BUS *UsbBus, + IN UINT16 Attributes + ); + +/** + Get the current operation state of the host controller. + + @param UsbBus The USB bus driver. + @param State The host controller operation state. + + @retval EFI_SUCCESS The operation state is returned in State. + @retval Others Failed to get the host controller state. + +**/ +EFI_STATUS +UsbHcGetState ( + IN USB_BUS *UsbBus, + OUT EFI_USB_HC_STATE *State + ); + +/** + Set the host controller operation state. + + @param UsbBus The USB bus driver. + @param State The state to set. + + @retval EFI_SUCCESS The host controller is now working at State. + @retval Others Failed to set operation state. + +**/ +EFI_STATUS +UsbHcSetState ( + IN USB_BUS *UsbBus, + IN EFI_USB_HC_STATE State + ); + +/** + Get the root hub port state. + + @param UsbBus The USB bus driver. + @param PortIndex The index of port. + @param PortStatus The variable to save port state. + + @retval EFI_SUCCESS The root port state is returned in. + @retval Others Failed to get the root hub port state. + +**/ +EFI_STATUS +UsbHcGetRootHubPortStatus ( + IN USB_BUS *UsbBus, + IN UINT8 PortIndex, + OUT EFI_USB_PORT_STATUS *PortStatus + ); + +/** + Set the root hub port feature. + + @param UsbBus The USB bus driver. + @param PortIndex The port index. + @param Feature The port feature to set. + + @retval EFI_SUCCESS The port feature is set. + @retval Others Failed to set port feature. + +**/ +EFI_STATUS +UsbHcSetRootHubPortFeature ( + IN USB_BUS *UsbBus, + IN UINT8 PortIndex, + IN EFI_USB_PORT_FEATURE Feature + ); + +/** + Clear the root hub port feature. + + @param UsbBus The USB bus driver. + @param PortIndex The port index. + @param Feature The port feature to clear. + + @retval EFI_SUCCESS The port feature is clear. + @retval Others Failed to clear port feature. + +**/ +EFI_STATUS +UsbHcClearRootHubPortFeature ( + IN USB_BUS *UsbBus, + IN UINT8 PortIndex, + IN EFI_USB_PORT_FEATURE Feature + ); + +/** + Execute a control transfer to the device. + + @param UsbBus The USB bus driver. + @param DevAddr The device address. + @param DevSpeed The device speed. + @param MaxPacket Maximum packet size of endpoint 0. + @param Request The control transfer request. + @param Direction The direction of data stage. + @param Data The buffer holding data. + @param DataLength The length of the data. + @param TimeOut Timeout (in ms) to wait until timeout. + @param Translator The transaction translator for low/full speed device. + @param UsbResult The result of transfer. + + @retval EFI_SUCCESS The control transfer finished without error. + @retval Others The control transfer failed, reason returned in UsbReslt. + +**/ +EFI_STATUS +UsbHcControlTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION Direction, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *UsbResult + ); + +/** + Execute a bulk transfer to the device's endpoint. + + @param UsbBus The USB bus driver. + @param DevAddr The target device address. + @param EpAddr The target endpoint address, with direction encoded in + bit 7. + @param DevSpeed The device's speed. + @param MaxPacket The endpoint's max packet size. + @param BufferNum The number of data buffer. + @param Data Array of pointers to data buffer. + @param DataLength The length of data buffer. + @param DataToggle On input, the initial data toggle to use, also return + the next toggle on output. + @param TimeOut The time to wait until timeout. + @param Translator The transaction translator for low/full speed device. + @param UsbResult The result of USB execution. + + @retval EFI_SUCCESS The bulk transfer is finished without error. + @retval Others Failed to execute bulk transfer, result in UsbResult. + +**/ +EFI_STATUS +UsbHcBulkTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN UINT8 BufferNum, + IN OUT VOID *Data[EFI_USB_MAX_BULK_BUFFER_NUM], + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *UsbResult + ); + +/** + Queue or cancel an asynchronous interrupt transfer. + + @param UsbBus The USB bus driver. + @param DevAddr The target device address. + @param EpAddr The target endpoint address, with direction encoded in + bit 7. + @param DevSpeed The device's speed. + @param MaxPacket The endpoint's max packet size. + @param IsNewTransfer Whether this is a new request. If not, cancel the old + request. + @param DataToggle Data toggle to use on input, next toggle on output. + @param PollingInterval The interval to poll the interrupt transfer (in ms). + @param DataLength The length of periodical data receive. + @param Translator The transaction translator for low/full speed device. + @param Callback Function to call when data is received. + @param Context The context to the callback. + + @retval EFI_SUCCESS The asynchronous transfer is queued. + @retval Others Failed to queue the transfer. + +**/ +EFI_STATUS +UsbHcAsyncInterruptTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN BOOLEAN IsNewTransfer, + IN OUT UINT8 *DataToggle, + IN UINTN PollingInterval, + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, + IN VOID *Context OPTIONAL + ); + +/** + Execute a synchronous interrupt transfer to the target endpoint. + + @param UsbBus The USB bus driver. + @param DevAddr The target device address. + @param EpAddr The target endpoint address, with direction encoded in + bit 7. + @param DevSpeed The device's speed. + @param MaxPacket The endpoint's max packet size. + @param Data Pointer to data buffer. + @param DataLength The length of data buffer. + @param DataToggle On input, the initial data toggle to use, also return + the next toggle on output. + @param TimeOut The time to wait until timeout. + @param Translator The transaction translator for low/full speed device. + @param UsbResult The result of USB execution. + + @retval EFI_SUCCESS The synchronous interrupt transfer is OK. + @retval Others Failed to execute the synchronous interrupt transfer. + +**/ +EFI_STATUS +UsbHcSyncInterruptTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *UsbResult + ); + +/** + Execute a synchronous Isochronous USB transfer. + + @param UsbBus The USB bus driver. + @param DevAddr The target device address. + @param EpAddr The target endpoint address, with direction encoded in + bit 7. + @param DevSpeed The device's speed. + @param MaxPacket The endpoint's max packet size. + @param BufferNum The number of data buffer. + @param Data Array of pointers to data buffer. + @param DataLength The length of data buffer. + @param Translator The transaction translator for low/full speed device. + @param UsbResult The result of USB execution. + + @retval EFI_UNSUPPORTED The isochronous transfer isn't supported now. + +**/ +EFI_STATUS +UsbHcIsochronousTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN UINT8 BufferNum, + IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM], + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *UsbResult + ); + +/** + Queue an asynchronous isochronous transfer. + + @param UsbBus The USB bus driver. + @param DevAddr The target device address. + @param EpAddr The target endpoint address, with direction encoded in + bit 7. + @param DevSpeed The device's speed. + @param MaxPacket The endpoint's max packet size. + @param BufferNum The number of data buffer. + @param Data Array of pointers to data buffer. + @param DataLength The length of data buffer. + @param Translator The transaction translator for low/full speed device. + @param Callback The function to call when data is transferred. + @param Context The context to the callback function. + + @retval EFI_UNSUPPORTED The asynchronous isochronous transfer isn't supported. + +**/ +EFI_STATUS +UsbHcAsyncIsochronousTransfer ( + IN USB_BUS *UsbBus, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN UINT8 BufferNum, + IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM], + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, + IN VOID *Context + ); + +/** + Open the USB host controller protocol BY_CHILD. + + @param Bus The USB bus driver. + @param Child The child handle. + + @return The open protocol return. + +**/ +EFI_STATUS +UsbOpenHostProtoByChild ( + IN USB_BUS *Bus, + IN EFI_HANDLE Child + ); + +/** + Close the USB host controller protocol BY_CHILD. + + @param Bus The USB bus driver. + @param Child The child handle. + + @return None. + +**/ +VOID +UsbCloseHostProtoByChild ( + IN USB_BUS *Bus, + IN EFI_HANDLE Child + ); + +/** + return the current TPL, copied from the EDKII glue lib. + + @param VOID. + + @return Current TPL. + +**/ +EFI_TPL +UsbGetCurrentTpl ( + VOID + ); + +#endif -- cgit v1.2.3