From e237e7ae9fc23f32a25040d49cc9a16f2a7f3b4c Mon Sep 17 00:00:00 2001 From: vanjeff Date: Wed, 11 Jul 2007 08:47:37 +0000 Subject: Import Usb/UsbBusDxe and Usb/UsbMassStorageDxe into MdeModulePkg. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3193 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Bus/Usb/UsbBusDxe/ComponentName.c | 164 +++ MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf | 99 ++ MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.msa | 85 ++ MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c | 991 +++++++++++++++++ MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h | 146 +++ MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c | 991 +++++++++++++++++ MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.h | 153 +++ MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c | 1334 +++++++++++++++++++++++ MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h | 140 +++ MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c | 769 +++++++++++++ MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.h | 220 ++++ MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.c | 1384 ++++++++++++++++++++++++ MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.h | 210 ++++ 13 files changed, 6686 insertions(+) create mode 100644 MdeModulePkg/Bus/Usb/UsbBusDxe/ComponentName.c create mode 100644 MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf create mode 100644 MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.msa create mode 100644 MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c create mode 100644 MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h create mode 100644 MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c create mode 100644 MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.h create mode 100644 MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c create mode 100644 MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h create mode 100644 MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c create mode 100644 MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.h create mode 100644 MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.c create mode 100644 MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.h (limited to 'MdeModulePkg/Bus/Usb/UsbBusDxe') diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/ComponentName.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/ComponentName.c new file mode 100644 index 0000000000..da9737202f --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/ComponentName.c @@ -0,0 +1,164 @@ +/** @file + +Copyright (c) 2004 - 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. + +Module Name: + + ComponentName.c + +Abstract: + + +**/ + +// +// The package level header files this module uses +// +#include + +// +// The Library classes this module consumes +// +#include + + +// +// EFI Component Name Functions +// +EFI_STATUS +EFIAPI +UsbBusComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +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 +// +EFI_COMPONENT_NAME_PROTOCOL mUsbBusComponentName = { + UsbBusComponentNameGetDriverName, + UsbBusComponentNameGetControllerName, + "eng" +}; + +STATIC EFI_UNICODE_STRING_TABLE mUsbBusDriverNameTable[] = { + { "eng", L"Usb Bus Driver" }, + { NULL , NULL } +}; + +EFI_STATUS +EFIAPI +UsbBusComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME2_PROTOCOL instance. + Language - A pointer to a three character ISO 639-2 language identifier. + This is the language of the driver name that 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. + DriverName - 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. + + Returns: + EFI_SUCCESS - The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - DriverName is NULL. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return LookupUnicodeString ( + Language, + mUsbBusComponentName.SupportedLanguages, + mUsbBusDriverNameTable, + DriverName + ); +} + +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 + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME2_PROTOCOL instance. + ControllerHandle - The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + ChildHandle - 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. + Language - A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that 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. + ControllerName - 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. + + Returns: + 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. + EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - ControllerName is NULL. + EFI_UNSUPPORTED - The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return EFI_UNSUPPORTED; +} diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf new file mode 100644 index 0000000000..423d9fb307 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf @@ -0,0 +1,99 @@ +#/** @file +# Component name for module UsbBus +# +# Copyright (c) 2006, Intel Corporation. All right reserved. +# +# 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 Section - statements that will be processed to create a Makefile. +# +################################################################################ +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UsbBusDxe + FILE_GUID = 240612B7-A063-11d4-9A3A-0090273FC14D + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = UsbBusDriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +################################################################################ +# +# Sources Section - list of files that are required for the build to succeed. +# +################################################################################ + +[Sources.common] + UsbDesc.c + UsbEnumer.c + UsbEnumer.h + usbbus.c + UsbHub.c + ComponentName.c + UsbUtility.h + UsbHub.h + UsbUtility.c + UsbDesc.h + usbbus.h + +################################################################################ +# +# Package Dependency Section - list of Package files that are required for +# this module. +# +################################################################################ + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +################################################################################ +# +# Library Class Section - list of Library Classes that are required for +# this module. +# +################################################################################ + +[LibraryClasses] + MemoryAllocationLib + DevicePathLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + DebugLib + + +################################################################################ +# +# Protocol C Name Section - list of Protocol and Protocol Notify C Names +# that this module uses or produces. +# +################################################################################ + +[Protocols] + gEfiUsbIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiUsb2HcProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiUsbHcProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.msa b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.msa new file mode 100644 index 0000000000..deaecc66b0 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.msa @@ -0,0 +1,85 @@ + + + UsbBusDxe + DXE_DRIVER + 240612B7-A063-11d4-9A3A-0090273FC14D + 1.0 + Component name for module UsbBus + FIX ME! + Copyright (c) 2006, Intel Corporation. All right reserved. + 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. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + UsbBusDxe + + + + DebugLib + + + BaseMemoryLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + UefiLib + + + DevicePathLib + + + MemoryAllocationLib + + + + usbbus.h + UsbDesc.h + UsbUtility.c + UsbHub.h + UsbUtility.h + ComponentName.c + UsbHub.c + usbbus.c + UsbEnumer.h + UsbEnumer.c + UsbDesc.c + + + + + + + + gEfiUsbHcProtocolGuid + + + gEfiUsb2HcProtocolGuid + + + gEfiDevicePathProtocolGuid + + + gEfiUsbIoProtocolGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + UsbBusDriverEntryPoint + + + \ No newline at end of file diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c new file mode 100644 index 0000000000..ffed1493c6 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c @@ -0,0 +1,991 @@ +/** @file + +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. + + Module Name: + + UsbDesc.c + + Abstract: + + Manage Usb Descriptor List + + Revision History + + +**/ + +#include "UsbBus.h" + + +/** + Free the interface setting descriptor + + @param Setting The descriptor to free + + @return None + +**/ +STATIC +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) { + gBS->FreePool (Ep); + } + } + + gBS->FreePool (Setting->Endpoints); + } + + gBS->FreePool (Setting); +} + + + +/** + Free a configuration descriptor with its interface + descriptors. It may be initialized partially + + @param Config The configuration descriptor to free + + @return None + +**/ +STATIC +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]); + } + } + + gBS->FreePool (Interface); + } + + gBS->FreePool (Config->Interfaces); + } + + gBS->FreePool (Config); + +} + + + +/** + Free a device descriptor with its configurations + + @param DevDesc The device descriptor + + @return None + +**/ +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]); + } + } + + gBS->FreePool (DevDesc->Configs); + } + + gBS->FreePool (DevDesc); +} + + +/** + Create a descriptor + + @param DescBuf The buffer of raw descriptor + @param Len The lenght of the raw descriptor buffer + @param Type The type of descriptor to create + @param Consumed Number of bytes consumed + + @return Created descriptor or NULL + +**/ +STATIC +VOID * +UsbCreateDesc ( + IN UINT8 *DescBuf, + IN INTN Len, + IN UINT8 Type, + OUT INTN *Consumed + ) +{ + USB_DESC_HEAD *Head; + INTN DescLen; + INTN CtrlLen; + INTN 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; + Head = (USB_DESC_HEAD*)(DescBuf + Offset); + } + + if ((Len <= Offset) || (Len < Offset + DescLen) || + (Head->Type != Type) || (Head->Len != DescLen)) { + USB_ERROR (("UsbCreateDesc: met mal-format descriptor\n")); + return NULL; + } + + Desc = AllocateZeroPool (CtrlLen); + + if (Desc == NULL) { + return NULL; + } + + CopyMem (Desc, Head, DescLen); + *Consumed = Offset + Head->Len; + + return Desc; +} + + +/** + Parse an interface desciptor and its endpoints + + @param DescBuf The buffer of raw descriptor + @param Len The lenght of the raw descriptor buffer + @param Consumed The number of raw descriptor consumed + + @return The create interface setting or NULL if failed + +**/ +STATIC +USB_INTERFACE_SETTING * +UsbParseInterfaceDesc ( + IN UINT8 *DescBuf, + IN INTN Len, + OUT INTN *Consumed + ) +{ + USB_INTERFACE_SETTING *Setting; + USB_ENDPOINT_DESC *Ep; + UINTN Index; + UINTN NumEp; + INTN Used; + INTN Offset; + + *Consumed = 0; + Setting = UsbCreateDesc (DescBuf, Len, USB_DESC_TYPE_INTERFACE, &Used); + + if (Setting == NULL) { + USB_ERROR (("UsbParseInterfaceDesc: failed to create interface descriptor\n")); + return NULL; + } + + Offset = Used; + + // + // Create an arry to hold the interface's endpoints + // + NumEp = Setting->Desc.NumEndpoints; + + USB_DEBUG (("UsbParseInterfaceDesc: interface %d(setting %d) has %d endpoints\n", + Setting->Desc.InterfaceNumber, Setting->Desc.AlternateSetting, 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; Index++) { + Ep = UsbCreateDesc (DescBuf + Offset, Len - Offset, USB_DESC_TYPE_ENDPOINT, &Used); + + if (Ep == NULL) { + USB_ERROR (("UsbParseInterfaceDesc: failed to create endpoint(index %d)\n", 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 lenght of the raw descriptor buffer + + @return The created configuration descriptor + +**/ +STATIC +USB_CONFIG_DESC * +UsbParseConfigDesc ( + IN UINT8 *DescBuf, + IN INTN Len + ) +{ + USB_CONFIG_DESC *Config; + USB_INTERFACE_SETTING *Setting; + USB_INTERFACE_DESC *Interface; + UINTN Index; + UINTN NumIf; + INTN 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; + } + + USB_DEBUG (("UsbParseConfigDesc: config %d has %d interfaces\n", + Config->Desc.ConfigurationValue, 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; + + while (Len > 0) { + Setting = UsbParseInterfaceDesc (DescBuf, Len, &Consumed); + + if ((Setting == NULL)) { + USB_ERROR (("UsbParseConfigDesc: failed to parse interface setting\n")); + goto ON_ERROR; + + } else if (Setting->Desc.InterfaceNumber >= NumIf) { + USB_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, + 50 * USB_STALL_1_MS, + &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 + +**/ +STATIC +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. + // + UsbDev->MaxPacket0 = 8; + + for (Index = 0; Index < 3; Index++) { + Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_DEVICE, 0, 0, &DevDesc, 8); + + if (!EFI_ERROR (Status)) { + UsbDev->MaxPacket0 = DevDesc.MaxPacketSize0; + return EFI_SUCCESS; + } + + gBS->Stall (100 * USB_STALL_1_MS); + } + + 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 + +**/ +STATIC +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)) { + gBS->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 + +**/ +STATIC +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 = (Max < USB_MAX_LANG_ID ? 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 togetther 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 + +**/ +STATIC +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)) { + USB_ERROR (("UsbGetOneConfig: failed to get descript length(%d) %r\n", + Status, Desc.TotalLength)); + + return NULL; + } + + USB_DEBUG (("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)) { + USB_ERROR (("UsbGetOneConfig: failed to get full descript %r\n", Status)); + + gBS->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)) { + USB_ERROR (("UsbBuildDescTable: failed to get device descriptor - %r\n", Status)); + return Status; + } + + DevDesc = UsbDev->DevDesc; + NumConfig = DevDesc->Desc.NumConfigurations; + DevDesc->Configs = AllocateZeroPool (NumConfig * sizeof (USB_CONFIG_DESC *)); + + if (DevDesc->Configs == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + USB_DEBUG (("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) { + USB_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); + + gBS->FreePool (Config); + + if (ConfigDesc == NULL) { + USB_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)) { + USB_DEBUG (("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, + 10 * USB_STALL_1_MS, + NULL, + 0, + &UsbResult + ); + + return Status; +} diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h new file mode 100644 index 0000000000..bdecfcf973 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h @@ -0,0 +1,146 @@ +/** @file + +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. + + Module Name: + + UsbDesc.h + + Abstract: + + Manage Usb Descriptor List + + Revision History + + +**/ + +#ifndef _USB_DESCRIPTOR_H_ +#define _USB_DESCRIPTOR_H_ + +enum { + USB_MAX_INTERFACE_SETTING = 8, +}; + +// +// 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; + UINT8 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; + +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_STATUS +UsbGetMaxPacketSize0 ( + IN USB_DEVICE *UsbDev + ); + +VOID +UsbFreeDevDesc ( + IN USB_DEVICE_DESC *DevDesc + ); + +EFI_USB_STRING_DESCRIPTOR* +UsbGetOneString ( + IN USB_DEVICE *UsbDev, + IN UINT8 StringIndex, + IN UINT16 LangId + ); + +EFI_STATUS +UsbBuildDescTable ( + IN USB_DEVICE *UsbDev + ); + +EFI_STATUS +UsbSetAddress ( + IN USB_DEVICE *UsbDev, + IN UINT8 Address + ); + +EFI_STATUS +UsbSetConfig ( + IN USB_DEVICE *UsbDev, + IN UINT8 ConfigIndex + ); + +EFI_STATUS +UsbIoClearFeature ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN UINTN Target, + IN UINT16 Feature, + IN UINT16 Index + ); +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c new file mode 100644 index 0000000000..fc0007dd11 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c @@ -0,0 +1,991 @@ +/** @file + +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. + + Module Name: + + UsbEnumer.c + + Abstract: + + Usb bus enumeration support + + Revision History + + +**/ + +#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; + UINTN Index; + + for (Index = 0; Index < UsbIf->IfSetting->Desc.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 + + @return None + +**/ +STATIC +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) { + gBS->FreePool (UsbIf->DevicePath); + } + + gBS->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. + +**/ +STATIC +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; + UsbIf->IfSetting = IfDesc->Settings[IfDesc->ActiveIndex]; + UsbIf->UsbIo = mUsbIoProtocol; + + // + // 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) { + USB_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)) { + USB_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 + ); + + USB_ERROR (("UsbCreateInterface: failed to open host for child - %r\n", Status)); + goto ON_ERROR; + } + + return UsbIf; + +ON_ERROR: + if (UsbIf->DevicePath) { + gBS->FreePool (UsbIf->DevicePath); + } + + gBS->FreePool (UsbIf); + return NULL; +} + + +/** + Free the resource used by this USB device + + @param Device The USB device to free + + @return None + +**/ +STATIC +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 + +**/ +STATIC +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; + return Device; +} + + +/** + Connect the USB interface with its driver. EFI USB bus will + create a USB interface for each seperate 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 + +**/ +STATIC +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)) { + USB_DEBUG (("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. + // + OldTpl = UsbGetCurrentTpl (); + USB_DEBUG (("UsbConnectDriver: TPL before connect is %d\n", OldTpl)); + + gBS->RestoreTPL (TPL_CALLBACK); + + Status = gBS->ConnectController (UsbIf->Handle, NULL, NULL, TRUE); + UsbIf->IsManaged = (BOOLEAN)!EFI_ERROR (Status); + + USB_DEBUG (("UsbConnectDriver: TPL after connect is %d\n", 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; + UINT8 Index; + + // + // Locate the active alternate setting + // + Setting = NULL; + + for (Index = 0; Index < IfDesc->NumOfSetting; Index++) { + Setting = IfDesc->Settings[Index]; + + if (Setting->Desc.AlternateSetting == Alternate) { + break; + } + } + + if (Index == IfDesc->NumOfSetting) { + return EFI_NOT_FOUND; + } + + IfDesc->ActiveIndex = Index; + + USB_DEBUG (("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; + + USB_DEBUG (("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) { + return EFI_OUT_OF_RESOURCES; + } + + 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)) { + USB_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 + + @return None + +**/ +STATIC +VOID +UsbDisconnectDriver ( + IN USB_INTERFACE *UsbIf + ) +{ + EFI_TPL OldTpl; + + // + // Release the hub if it's a hub controller, otherwise + // disconnect the driver if it is managed by other drivers. + // + if (UsbIf->IsHub) { + 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 (); + USB_DEBUG (("UsbDisconnectDriver: old TPL is %d\n", OldTpl)); + + gBS->RestoreTPL (TPL_CALLBACK); + + gBS->DisconnectController (UsbIf->Handle, NULL, NULL); + UsbIf->IsManaged = FALSE; + + USB_DEBUG (("UsbDisconnectDriver: TPL after disconnect is %d\n", UsbGetCurrentTpl())); + ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK); + + gBS->RaiseTPL (OldTpl); + } +} + + + +/** + Remove the current device configuration + + @param Device The USB device to remove configuration from + + @return None + +**/ +VOID +UsbRemoveConfig ( + IN USB_DEVICE *Device + ) +{ + USB_INTERFACE *UsbIf; + UINTN Index; + + // + // Remove each interface of the device + // + for (Index = 0; Index < Device->NumOfInterface; Index++) { + UsbIf = Device->Interfaces[Index]; + + if (UsbIf == NULL) { + continue; + } + + UsbDisconnectDriver (UsbIf); + UsbFreeInterface (UsbIf); + Device->Interfaces[Index] = NULL; + } + + Device->ActiveConfig = NULL; + Device->NumOfInterface = 0; +} + + + +/** + 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; + UINT8 Index; + + Bus = Device->Bus; + + // + // Remove all the devices on its downstream ports. Search from devices[1]. + // Devices[0] is the root hub. + // + for (Index = 1; Index < USB_MAX_DEVICES; Index++) { + Child = Bus->Devices[Index]; + + if ((Child == NULL) || (Child->ParentAddr != Device->Address)) { + continue; + } + + Status = UsbRemoveDevice (Child); + + if (EFI_ERROR (Status)) { + USB_ERROR (("UsbRemoveDevice: failed to remove child, ignore error\n")); + Bus->Devices[Index] = NULL; + } + } + + UsbRemoveConfig (Device); + + USB_DEBUG (("UsbRemoveDevice: device %d removed\n", Device->Address)); + + Bus->Devices[Device->Address] = NULL; + UsbFreeDevice (Device); + + return EFI_SUCCESS; +} + + +/** + 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 + +**/ +STATIC +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 < USB_MAX_DEVICES; 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 + +**/ +STATIC +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; + UINT8 Address; + UINT8 Config; + EFI_STATUS Status; + + Address = USB_MAX_DEVICES; + Parent = HubIf->Device; + Bus = Parent->Bus; + HubApi = HubIf->HubApi; + + + // + // Wait at least 100 ms for the power on port to stable + // + gBS->Stall (100 * USB_STALL_1_MS); + + // + // 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)) { + USB_ERROR (("UsbEnumerateNewDev: failed to reset port %d - %r\n", Port, Status)); + + return Status; + } + + USB_DEBUG (("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)) { + USB_ERROR (("UsbEnumerateNewDev: failed to get speed of port %d\n", Port)); + goto ON_ERROR; + } + + if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_LOW_SPEED)) { + Child->Speed = EFI_USB_SPEED_LOW; + + } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_HIGH_SPEED)) { + Child->Speed = EFI_USB_SPEED_HIGH; + + } else { + Child->Speed = EFI_USB_SPEED_FULL; + } + + USB_DEBUG (("UsbEnumerateNewDev: device is of %d speed\n", Child->Speed)); + + if (Child->Speed != EFI_USB_SPEED_HIGH) { + // + // If the child isn't a high speed device, it is necessary to + // set the transaction translator. This is quite simple: + // 1. if parent is of high speed, then parent is our translator + // 2. otherwise use parent's translator. + // + if (Parent->Speed == EFI_USB_SPEED_HIGH) { + Child->Translator.TranslatorHubAddress = Parent->Address; + Child->Translator.TranslatorPortNumber = Port; + + } else { + Child->Translator = Parent->Translator; + } + + USB_DEBUG (("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 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)) { + USB_ERROR (("UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status)); + goto ON_ERROR; + } + + USB_DEBUG (("UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0)); + + // + // 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. + // + for (Address = 1; Address < USB_MAX_DEVICES; Address++) { + if (Bus->Devices[Address] == NULL) { + break; + } + } + + if (Address == USB_MAX_DEVICES) { + USB_ERROR (("UsbEnumerateNewDev: address pool is full for port %d\n", Port)); + + Status = EFI_ACCESS_DENIED; + goto ON_ERROR; + } + + Bus->Devices[Address] = Child; + Status = UsbSetAddress (Child, Address); + Child->Address = Address; + + if (EFI_ERROR (Status)) { + USB_ERROR (("UsbEnumerateNewDev: failed to set device address - %r\n", Status)); + goto ON_ERROR; + } + + // + // Wait 20ms for set address to complete + // + gBS->Stall (20 * USB_STALL_1_MS); + + USB_DEBUG (("UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address)); + + // + // Host learns about the device¡¯s abilities by requesting device's + // entire descriptions. + // + Status = UsbBuildDescTable (Child); + + if (EFI_ERROR (Status)) { + USB_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)) { + USB_ERROR (("UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status)); + goto ON_ERROR; + } + + USB_DEBUG (("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)) { + USB_ERROR (("UsbEnumerateNewDev: failed to create interfaces - %r\n", Status)); + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + if (Address != USB_MAX_DEVICES) { + Bus->Devices[Address] = NULL; + } + + if (Child != NULL) { + UsbFreeDevice (Child); + } + + 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 + +**/ +STATIC +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)) { + USB_ERROR (("UsbEnumeratePort: failed to get state of port %d\n", Port)); + return Status; + } + + if (PortState.PortChangeStatus == 0) { + return EFI_SUCCESS; + } + + USB_DEBUG (("UsbEnumeratePort: port %d state - %x, change - %x\n", + Port, PortState.PortStatus, PortState.PortChangeStatus)); + + // + // 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. + // + Status = EFI_SUCCESS; + + if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_OVERCURRENT)) { + // + // If overcurrent condition is cleared, enable the port again + // + if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_OVERCURRENT)) { + HubApi->SetPortFeature (HubIf, Port, USB_HUB_PORT_POWER); + } + + } else if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) { + // + // Device connected or disconnected. Either way, if there is + // already a device present in the bus, need to remove it. + // + Child = UsbFindChild (HubIf, Port); + + if (Child != NULL) { + USB_DEBUG (("UsbEnumeratePort: device at port %d removed from system\n", Port)); + UsbRemoveDevice (Child); + } + + if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) { + // + // Now, new device connected, enumerate and configure the device + // + USB_DEBUG (("UsbEnumeratePort: new device connected at port %d\n", Port)); + Status = UsbEnumerateNewDev (HubIf, Port); + + } else { + USB_DEBUG (("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 + + @return None + +**/ +VOID +UsbHubEnumeration ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + USB_INTERFACE *HubIf; + UINT8 Byte; + UINT8 Bit; + UINT8 Index; + + ASSERT (Context); + + HubIf = (USB_INTERFACE *) Context; + + 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 + + @return None + +**/ +VOID +UsbRootHubEnumeration ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + USB_INTERFACE *RootHub; + UINT8 Index; + + RootHub = (USB_INTERFACE *) Context; + + for (Index = 0; Index < RootHub->NumOfPort; Index++) { + UsbEnumeratePort (RootHub, Index); + } +} diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.h new file mode 100644 index 0000000000..e14b2d753c --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.h @@ -0,0 +1,153 @@ +/** @file + +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. + + Module Name: + + UsbEnumer.h + + Abstract: + + USB bus enumeration interface + + Revision History + + +**/ + +#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 + ); + +typedef 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; +} USB_HUB_API; + +USB_ENDPOINT_DESC* +UsbGetEndpointDesc ( + IN USB_INTERFACE *UsbIf, + IN UINT8 EpAddr + ); + + +EFI_STATUS +UsbSelectSetting ( + IN USB_INTERFACE_DESC *IfDesc, + IN UINT8 Alternate + ); + +EFI_STATUS +UsbSelectConfig ( + IN USB_DEVICE *Device, + IN UINT8 ConfigIndex + ); + +VOID +UsbRemoveConfig ( + IN USB_DEVICE *Device + ); + +EFI_STATUS +UsbRemoveDevice ( + IN USB_DEVICE *Device + ); + +VOID +UsbHubEnumeration ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +VOID +UsbRootHubEnumeration ( + IN EFI_EVENT Event, + IN VOID *Context + ); +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c new file mode 100644 index 0000000000..25332f432f --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c @@ -0,0 +1,1334 @@ +/** @file + +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. + + Module Name: + + UsbHub.c + + Abstract: + + Unified interface for RootHub and Hub + + Revision History + + +**/ + +#include "UsbBus.h" + +// +// 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 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 + +**/ +STATIC +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. + +**/ +STATIC +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, + 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 = (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, + Port + 1, + NULL, + 0 + ); + + 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 + +**/ +STATIC +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 + +**/ +STATIC +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 retrive the port state + +**/ +STATIC +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, + 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 + +**/ +STATIC +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, + 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 + +**/ +STATIC +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 + +**/ +STATIC +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, + 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 + +**/ +STATIC +EFI_STATUS +UsbHubReadDesc ( + IN USB_DEVICE *HubDev, + OUT EFI_USB_HUB_DESCRIPTOR *HubDesc + ) +{ + EFI_STATUS Status; + + // + // 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 + +**/ +STATIC +EFI_STATUS +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)) { + USB_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)) { + USB_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; +} + +// +// 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. +// +#define USB_HUB_MAP_SIZE 5 + +USB_CHANGE_FEATURE_MAP mHubFeatureMap[USB_HUB_MAP_SIZE] = { + {USB_PORT_STAT_C_CONNECTION, USB_HUB_C_PORT_CONNECT}, + {USB_PORT_STAT_C_ENABLE, USB_HUB_C_PORT_ENABLE}, + {USB_PORT_STAT_C_SUSPEND, USB_HUB_C_PORT_SUSPEND}, + {USB_PORT_STAT_C_OVERCURRENT, USB_HUB_C_PORT_OVER_CURRENT}, + {USB_PORT_STAT_C_RESET, USB_HUB_C_PORT_RESET}, +}; + +#define USB_ROOT_HUB_MAP_SIZE 5 + +USB_CHANGE_FEATURE_MAP mRootHubFeatureMap[USB_ROOT_HUB_MAP_SIZE] = { + {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}, +}; + + + +/** + 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 + +**/ +STATIC +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; + + // + // Locate the interrupt endpoint for port change map + // + HubIf->IsHub = FALSE; + Setting = HubIf->IfSetting; + HubDev = HubIf->Device; + EpDesc = NULL; + + for (Index = 0; Index < Setting->Desc.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 == Setting->Desc.NumEndpoints) { + USB_ERROR (("UsbHubInit: no interrupt endpoint found for hub %d\n", HubDev->Address)); + return EFI_DEVICE_ERROR; + } + + Status = UsbHubReadDesc (HubDev, &HubDesc); + + if (EFI_ERROR (Status)) { + USB_ERROR (("UsbHubInit: failed to read HUB descriptor %r\n", Status)); + return Status; + } + + HubIf->NumOfPort = HubDesc.NumPorts; + + USB_DEBUG (("UsbHubInit: hub %d has %d ports\n", HubDev->Address,HubIf->NumOfPort)); + + // + // 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)) { + USB_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)) { + USB_ERROR (("UsbHubInit: failed to queue interrupt transfer for hub %d - %r\n", + HubDev->Address, Status)); + + gBS->CloseEvent (HubIf->HubNotify); + HubIf->HubNotify = NULL; + + return Status; + } + + // + // 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; + + // + // 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, USB_HUB_PORT_POWER); + } + + gBS->Stall (HubDesc.PwrOn2PwrGood * 2 * USB_STALL_1_MS); + UsbHubAckHubStatus (HubIf->Device); + + USB_DEBUG (("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 + +**/ +STATIC +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 + + @return None + +**/ +STATIC +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 < USB_HUB_MAP_SIZE; Index++) { + Map = &mHubFeatureMap[Index]; + + if (USB_BIT_IS_SET (PortState.PortChangeStatus, Map->ChangedBit)) { + UsbHubCtrlClearPortFeature (HubIf->Device, Port, 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 + +**/ +STATIC +EFI_STATUS +UsbHubSetPortFeature ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port, + IN EFI_USB_PORT_FEATURE Feature + ) +{ + EFI_STATUS Status; + + Status = UsbHubCtrlSetPortFeature (HubIf->Device, Port, 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 + +**/ +STATIC +EFI_STATUS +UsbHubClearPortFeature ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port, + IN EFI_USB_PORT_FEATURE Feature + ) +{ + EFI_STATUS Status; + + Status = UsbHubCtrlClearPortFeature (HubIf->Device, Port, Feature); + + return Status; +} + + +/** + Interface funtion 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 + +**/ +STATIC +EFI_STATUS +UsbHubResetPort ( + IN USB_INTERFACE *HubIf, + IN UINT8 Port + ) +{ + EFI_USB_PORT_STATUS PortState; + UINTN Index; + EFI_STATUS Status; + + Status = UsbHubSetPortFeature (HubIf, Port, USB_HUB_PORT_RESET); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Drive the reset signal for at least 10ms. Check USB 2.0 Spec + // section 7.1.7.5 for timing requirements. + // + gBS->Stall (20 * USB_STALL_1_MS); + + // + // USB hub will clear RESET bit if reset is actually finished. + // + ZeroMem (&PortState, sizeof (EFI_USB_PORT_STATUS)); + + for (Index = 0; Index < 20; Index++) { + Status = UsbHubGetPortStatus (HubIf, Port, &PortState); + + if (!EFI_ERROR (Status) && + !USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_RESET)) { + + return EFI_SUCCESS; + } + + gBS->Stall (5 * USB_STALL_1_MS); + } + + 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 + +**/ +STATIC +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; + + USB_DEBUG (("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 initialied for root hub + @retval Others Failed to initialize the hub + +**/ +STATIC +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; + } + + USB_DEBUG (("UsbRootHubInit: root hub %x - 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; + } + + 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 + +**/ +STATIC +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 + + @retval EFI_SUCCESS The port state is returned + @retval Others Failed to retrieve the port state + +**/ +STATIC +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 < USB_ROOT_HUB_MAP_SIZE; Index++) { + Map = &mRootHubFeatureMap[Index]; + + if (USB_BIT_IS_SET (PortState.PortChangeStatus, Map->ChangedBit)) { + UsbHcClearRootHubPortFeature (HubIf->Device->Bus, Port, 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 + +**/ +STATIC +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 + +**/ +STATIC +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 funtion 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 + +**/ +STATIC +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 = UsbHcSetRootHubPortFeature (Bus, Port, EfiUsbPortReset); + + if (EFI_ERROR (Status)) { + USB_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 (50 * USB_STALL_1_MS); + + Status = UsbHcClearRootHubPortFeature (Bus, Port, EfiUsbPortReset); + + if (EFI_ERROR (Status)) { + USB_ERROR (("UsbRootHubResetPort: failed to clear reset on port %d\n", Port)); + return Status; + } + + gBS->Stall (USB_STALL_1_MS); + + // + // 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_HUB_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 (10 * USB_STALL_1_MS); + } + + if (Index == USB_HUB_LOOP) { + USB_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) { + USB_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)) { + USB_ERROR (("UsbRootHubResetPort: failed to enable port %d for UHCI\n", Port)); + return Status; + } + + gBS->Stall (20 * USB_STALL_1_MS); + } + } + + 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. + +**/ +STATIC +EFI_STATUS +UsbRootHubRelease ( + IN USB_INTERFACE *HubIf + ) +{ + USB_DEBUG (("UsbRootHubRelease: root hub released for hub %x\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/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h new file mode 100644 index 0000000000..0238ea0184 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h @@ -0,0 +1,140 @@ +/** @file + +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. + + Module Name: + + UsbHub.h + + Abstract: + + The definition for USB hub + + Revision History + + +**/ + +#ifndef _USB_HUB_H_ +#define _USB_HUB_H_ + +#define USB_ENDPOINT_ADDR(EpAddr) ((EpAddr) & 0x7F) +#define USB_ENDPOINT_TYPE(Desc) ((Desc)->Attributes & USB_ENDPOINT_TYPE_MASK) + +enum { + USB_DESC_TYPE_HUB = 0x29, + + // + // Hub class control transfer target + // + USB_HUB_TARGET_HUB = 0, + USB_HUB_TARGET_PORT = 3, + + // + // HUB class specific contrl transfer request type + // + USB_HUB_REQ_GET_STATUS = 0, + USB_HUB_REQ_CLEAR_FEATURE = 1, + USB_HUB_REQ_SET_FEATURE = 3, + USB_HUB_REQ_GET_DESC = 6, + USB_HUB_REQ_SET_DESC = 7, + USB_HUB_REQ_CLEAR_TT = 8, + USB_HUB_REQ_RESET_TT = 9, + USB_HUB_REQ_GET_TT_STATE = 10, + USB_HUB_REQ_STOP_TT = 11, + + + // + // USB hub class feature selector + // + USB_HUB_C_HUB_LOCAL_POWER = 0, + USB_HUB_C_HUB_OVER_CURRENT = 1, + USB_HUB_PORT_CONNECTION = 0, + USB_HUB_PORT_ENABLE = 1, + USB_HUB_PORT_SUSPEND = 2, + USB_HUB_PORT_OVER_CURRENT = 3, + USB_HUB_PORT_RESET = 4, + USB_HUB_PORT_POWER = 8, + USB_HUB_PORT_LOW_SPEED = 9, + USB_HUB_C_PORT_CONNECT = 16, + USB_HUB_C_PORT_ENABLE = 17, + USB_HUB_C_PORT_SUSPEND = 18, + USB_HUB_C_PORT_OVER_CURRENT = 19, + USB_HUB_C_PORT_RESET = 20, + USB_HUB_PORT_TEST = 21, + USB_HUB_PORT_INDICATOR = 22, + + // + // USB hub power control method. In gang power control + // + USB_HUB_GANG_POWER_CTRL = 0, + USB_HUB_PORT_POWER_CTRL = 0x01, + + // + // USB hub status bits + // + USB_HUB_STAT_LOCAL_POWER = 0x01, + USB_HUB_STAT_OVER_CURRENT = 0x02, + USB_HUB_STAT_C_LOCAL_POWER = 0x01, + USB_HUB_STAT_C_OVER_CURRENT = 0x02, + + USB_HUB_CLASS_CODE = 0x09, + USB_HUB_SUBCLASS_CODE = 0x00, + + + USB_HUB_LOOP = 50, +}; + +#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; +#pragma pack() + + +typedef struct { + UINT16 ChangedBit; + EFI_USB_PORT_FEATURE Feature; +} USB_CHANGE_FEATURE_MAP; + + +EFI_STATUS +UsbHubCtrlClearTTBuffer ( + IN USB_DEVICE *UsbDev, + IN UINT8 Port, + IN UINT16 DevAddr, + IN UINT16 EpNum, + IN UINT16 EpType + ); + + +BOOLEAN +UsbIsHubInterface ( + IN USB_INTERFACE *UsbIf + ); + +EFI_STATUS +UsbHubAckHubStatus ( + IN USB_DEVICE *UsbDev + ); + +extern USB_HUB_API mUsbHubApi; +extern USB_HUB_API mUsbRootHubApi; +#endif + diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c new file mode 100644 index 0000000000..216f948ff8 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c @@ -0,0 +1,769 @@ +/** @file + +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. + + Module Name: + + UsbUtility.c + + Abstract: + + Wrapper function for usb host controller interface + + Revision History + + +**/ + + +#include "UsbBus.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 + ) +{ + 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 + + @return GC_TODO: add return values + +**/ +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 = (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, + &Usb2Hc, + mUsbBusDriverBinding.DriverBindingHandle, + Child, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + } else { + Status = gBS->OpenProtocol ( + Bus->HostHandle, + &gEfiUsbHcProtocolGuid, + &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 + + @return None + +**/ +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. + + VOID + + @return Current TPL + +**/ +EFI_TPL +UsbGetCurrentTpl ( + VOID + ) +{ + EFI_TPL Tpl; + + Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + gBS->RestoreTPL (Tpl); + + return Tpl; +} + + +#ifdef EFI_DEBUG +VOID +UsbDebug ( + IN CHAR8 *Format, + ... + ) +/*++ + +Routine Description: + + USB's debug output function. + +Arguments: + + Format - The format parameters to the print + ... - The variable length parameters after format + +Returns: + + None + +--*/ +{ + VA_LIST Marker; + + VA_START (Marker, Format); + DebugVPrint (DEBUG_INFO, Format, Marker); + VA_END (Marker); +} + + + +/** + USB's error output function. + + @param Format The format parameters to the print + @param ... The variable length parameters after format + + @return None + +**/ +VOID +UsbError ( + IN CHAR8 *Format, + ... + ) +{ + VA_LIST Marker; + + VA_START (Marker, Format); + DebugVPrint (DEBUG_ERROR, Format, Marker); + VA_END (Marker); +} + +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.h new file mode 100644 index 0000000000..2dafd1c0c1 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.h @@ -0,0 +1,220 @@ +/** @file + +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. + + Module Name: + + UsbUtility.h + + Abstract: + + Manage Usb Port/Hc/Etc + + Revision History + + +**/ + +#ifndef _USB_UTILITY_H +#define _USB_UTILITY_H + +EFI_STATUS +UsbHcGetCapability ( + IN USB_BUS *UsbBus, + OUT UINT8 *MaxSpeed, + OUT UINT8 *NumOfPort, + OUT UINT8 *Is64BitCapable + ); + +EFI_STATUS +UsbHcReset ( + IN USB_BUS *UsbBus, + IN UINT16 Attributes + ); + + +EFI_STATUS +UsbHcGetState ( + IN USB_BUS *UsbBus, + OUT EFI_USB_HC_STATE *State + ); + + +EFI_STATUS +UsbHcSetState ( + IN USB_BUS *UsbBus, + IN EFI_USB_HC_STATE State + ); + + +EFI_STATUS +UsbHcGetRootHubPortStatus ( + IN USB_BUS *UsbBus, + IN UINT8 PortIndex, + OUT EFI_USB_PORT_STATUS *PortStatus + ); + + +EFI_STATUS +UsbHcSetRootHubPortFeature ( + IN USB_BUS *UsbBus, + IN UINT8 PortIndex, + IN EFI_USB_PORT_FEATURE Feature + ); + + +EFI_STATUS +UsbHcClearRootHubPortFeature ( + IN USB_BUS *UsbBus, + IN UINT8 PortIndex, + IN EFI_USB_PORT_FEATURE Feature + ); + + +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 +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 +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 +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 +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 + ); + + +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 + ); + + +EFI_STATUS +UsbOpenHostProtoByChild ( + IN USB_BUS *Bus, + IN EFI_HANDLE Child + ); + + +VOID +UsbCloseHostProtoByChild ( + IN USB_BUS *Bus, + IN EFI_HANDLE Child + ); + + +EFI_TPL +UsbGetCurrentTpl ( + VOID + ); + +// +// USB debug support routines +// +#ifdef EFI_DEBUG + #define USB_DEBUG(arg) UsbDebug arg + #define USB_ERROR(arg) UsbError arg +#else + #define USB_DEBUG(arg) + #define USB_ERROR(arg) +#endif + +VOID +UsbDebug ( + IN CHAR8 *Format, + ... + ); + + +VOID +UsbError ( + IN CHAR8 *Format, + ... + ); +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.c new file mode 100644 index 0000000000..c5a0604fca --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.c @@ -0,0 +1,1384 @@ +/** @file + +Copyright (c) 2004 - 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. + + Module Name: + + UsbBus.c + + Abstract: + + Usb Bus Driver Binding and Bus IO Protocol + + Revision History + + +**/ + +#include "UsbBus.h" + +// +// USB_BUS_PROTOCOL is only used to locate USB_BUS +// +EFI_GUID mUsbBusProtocolGuid = EFI_USB_BUS_PROTOCOL_GUID; + + +/** + 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 + +**/ +STATIC +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 + // + 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 irrellvant. + // + 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; + } + + USB_DEBUG (("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)) { + 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 + +**/ +STATIC +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 + // + 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 + +**/ +STATIC +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 + +**/ +STATIC +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 + +**/ +STATIC +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 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 + +**/ +STATIC +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 + +**/ +STATIC +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. + +**/ +STATIC +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 + +**/ +STATIC +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 + +**/ +STATIC +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 >= UsbIf->IfSetting->Desc.NumEndpoints)) { + gBS->RestoreTPL (OldTpl); + return EFI_INVALID_PARAMETER; + } + + 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 number of supported languanges + + @retval EFI_SUCCESS The language ID is return + +**/ +STATIC +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 = Dev->TotalLangId; + + 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 + +**/ +STATIC +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++) { + 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; + UINT8 Address; + EFI_TPL OldTpl; + EFI_STATUS Status; + + OldTpl = gBS->RaiseTPL (USB_BUS_TPL); + + UsbIf = USB_INTERFACE_FROM_USBIO (This); + Dev = UsbIf->Device; + + HubIf = Dev->ParentIf; + Status = HubIf->HubApi->ResetPort (HubIf, Dev->ParentPort); + + if (EFI_ERROR (Status)) { + USB_ERROR (("UsbIoPortReset: failed to reset hub port %d@hub %d, %r \n", + Dev->ParentPort, Dev->ParentAddr, Status)); + + goto ON_EXIT; + } + + // + // Reset the device to its current address. The device now has a + // address of ZERO, so need to set Dev->Address to zero first for + // host to communicate with the device + // + Address = Dev->Address; + Dev->Address = 0; + Status = UsbSetAddress (Dev, Address); + + if (EFI_ERROR (Status)) { + USB_ERROR (("UsbIoPortReset: failed to set address for device %d - %r\n", + Address, Status)); + + goto ON_EXIT; + } + + Dev->Address = 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)) { + USB_ERROR (("UsbIoPortReset: failed to set configure for device %d - %r\n", + Address, Status)); + } + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + +EFI_USB_IO_PROTOCOL mUsbIoProtocol = { + UsbIoControlTransfer, + UsbIoBulkTransfer, + UsbIoAsyncInterruptTransfer, + UsbIoSyncInterruptTransfer, + UsbIoIsochronousTransfer, + UsbIoAsyncIsochronousTransfer, + UsbIoGetDeviceDescriptor, + UsbIoGetActiveConfigDescriptor, + UsbIoGetInterfaceDescriptor, + UsbIoGetEndpointDescriptor, + UsbIoGetStringDescriptor, + UsbIoGetSupportedLanguages, + UsbIoPortReset +}; + +//@MT: EFI_DRIVER_ENTRY_POINT (UsbBusDriverEntryPoint) + +EFI_STATUS +EFIAPI +UsbBusDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +/*++ + +Routine Description: + + The USB bus driver entry pointer + +Arguments: + + ImageHandle - The driver image handle + SystemTable - The system table + +Returns: + + EFI_SUCCESS - The component name protocol is installed + Others - Failed to init the usb driver + +--*/ +{ + return EfiLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &mUsbBusDriverBinding, + ImageHandle, + &mUsbBusComponentName, + NULL, + NULL + ); +} + + +/** + Check whether USB bus driver support this device + + @param This The USB bus driver binding protocol + @param Controller The controller handle to test againist + @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) { + DevicePathNode.DevPath = RemainingDevicePath; + + if ((DevicePathNode.DevPath->Type != MESSAGING_DEVICE_PATH) || + (DevicePathNode.DevPath->SubType != MSG_USB_DP) || + (DevicePathNodeLength (DevicePathNode.DevPath) != sizeof (USB_DEVICE_PATH))) { + + return EFI_UNSUPPORTED; + } + } + + 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)) { + return Status; + } + + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // 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)) { + gBS->CloseProtocol ( + Controller, + &gEfiUsb2HcProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return EFI_SUCCESS; + } + + // + // 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)) { + gBS->CloseProtocol ( + Controller, + &gEfiUsbHcProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + 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 + ) +{ + USB_BUS *UsbBus; + USB_DEVICE *RootHub; + USB_INTERFACE *RootIf; + EFI_USB_BUS_PROTOCOL *UsbBusId; + EFI_STATUS Status; + EFI_STATUS Status2; + + // + // Locate the USB bus protocol, if it is found, USB bus + // is already started on this controller. + // + Status = gBS->OpenProtocol ( + Controller, + &mUsbBusProtocolGuid, + (VOID **) &UsbBusId, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + UsbBus = AllocateZeroPool (sizeof (USB_BUS)); + + if (UsbBus == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UsbBus->Signature = USB_BUS_SIGNATURE; + UsbBus->HostHandle = Controller; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &UsbBus->DevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + USB_ERROR (("UsbBusStart: Failed to open device path %r\n", Status)); + + gBS->FreePool (UsbBus); + return Status; + } + + // + // Get USB_HC2/USB_HC host controller protocol (EHCI/UHCI). + // This is for backward compatbility 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)) { + USB_ERROR (("UsbBusStart: Failed to open USB_HC/USB2_HC %r\n", Status)); + + Status = EFI_DEVICE_ERROR; + goto CLOSE_HC; + } + + // + // Create a fake usb device for root hub + // + RootHub = AllocateZeroPool (sizeof (USB_DEVICE)); + + if (RootHub == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto CLOSE_HC; + } + + RootIf = AllocateZeroPool (sizeof (USB_INTERFACE)); + + if (RootIf == NULL) { + gBS->FreePool (RootHub); + Status = EFI_OUT_OF_RESOURCES; + goto CLOSE_HC; + } + + RootHub->Bus = UsbBus; + RootHub->NumOfInterface = 1; + RootHub->Interfaces[0] = RootIf; + RootIf->Signature = USB_INTERFACE_SIGNATURE; + RootIf->Device = RootHub; + RootIf->DevicePath = UsbBus->DevicePath; + + Status = mUsbRootHubApi.Init (RootIf); + + if (EFI_ERROR (Status)) { + USB_ERROR (("UsbBusStart: Failed to init root hub %r\n", Status)); + goto FREE_ROOTHUB; + } + + UsbBus->Devices[0] = RootHub; + + // + // Install an EFI_USB_BUS_PROTOCOL to host controler to identify it. + // + Status = gBS->InstallProtocolInterface ( + &Controller, + &mUsbBusProtocolGuid, + EFI_NATIVE_INTERFACE, + &UsbBus->BusId + ); + + if (EFI_ERROR (Status)) { + USB_ERROR (("UsbBusStart: Failed to install bus protocol %r\n", Status)); + + mUsbRootHubApi.Release (RootIf); + goto FREE_ROOTHUB; + } + + UsbHcReset (UsbBus, EFI_USB_HC_RESET_GLOBAL); + UsbHcSetState (UsbBus, EfiUsbHcStateOperational); + + USB_DEBUG (("UsbBusStart: usb bus started on %x, root hub %x\n", Controller, RootIf)); + return EFI_SUCCESS; + +FREE_ROOTHUB: + gBS->FreePool (RootIf); + gBS->FreePool (RootHub); + +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 + ); + + gBS->FreePool (UsbBus); + + USB_ERROR (("UsbBusStart: Failed to start bus driver %r\n", Status)); + return Status; +} + + +/** + 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; + + Status = EFI_SUCCESS; + + if (NumberOfChildren > 0) { + OldTpl = gBS->RaiseTPL (USB_BUS_TPL); + + 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; + + UsbRemoveDevice (UsbDev); + } + + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + } + + USB_DEBUG (("UsbBusStop: usb bus stopped on %x\n", Controller)); + + // + // Locate USB_BUS for the current host controller + // + Status = gBS->OpenProtocol ( + Controller, + &mUsbBusProtocolGuid, + (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 + // + OldTpl = gBS->RaiseTPL (USB_BUS_TPL); + UsbHcSetState (Bus, EfiUsbHcStateHalt); + + RootHub = Bus->Devices[0]; + RootIf = RootHub->Interfaces[0]; + + mUsbRootHubApi.Release (RootIf); + + for (Index = 1; Index < USB_MAX_DEVICES; Index++) { + if (Bus->Devices[Index] != NULL) { + UsbRemoveDevice (Bus->Devices[Index]); + } + } + + gBS->RestoreTPL (OldTpl); + + gBS->FreePool (RootIf); + gBS->FreePool (RootHub); + + // + // Uninstall the bus identifier and close USB_HC/USB2_HC protocols + // + gBS->UninstallProtocolInterface (Controller, &mUsbBusProtocolGuid, &Bus->BusId); + + if (Bus->Usb2Hc != NULL) { + gBS->CloseProtocol ( + Controller, + &gEfiUsb2HcProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + if (Bus->UsbHc != NULL) { + gBS->CloseProtocol ( + Controller, + &gEfiUsbHcProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + gBS->FreePool (Bus); + + return Status; +} + +EFI_DRIVER_BINDING_PROTOCOL mUsbBusDriverBinding = { + UsbBusControllerDriverSupported, + UsbBusControllerDriverStart, + UsbBusControllerDriverStop, + 0xa, + NULL, + NULL +}; diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.h new file mode 100644 index 0000000000..2ecc2c8e50 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.h @@ -0,0 +1,210 @@ +/** @file +Copyright (c) 2004 - 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. + + Module Name: + + UsbBus.h + + Abstract: + + Usb Bus Driver Binding and Bus IO Protocol + + Revision History + + +**/ + +#ifndef _EFI_USB_BUS_H_ +#define _EFI_USB_BUS_H_ + +// +// The package level header files this module uses +// +#include +// +// The protocols, PPI and GUID defintions for this module +// +#include +#include +#include +#include +// +// The Library classes this module consumes +// +#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" + +enum { + // + // Time definition + // + USB_STALL_1_MS = 1000, + TICKS_PER_MS = 10000U, + USB_ROOTHUB_POLL_INTERVAL = 1000 * TICKS_PER_MS, + USB_HUB_POLL_INTERVAL = 64, + + // + // Maximum definition + // + USB_MAX_LANG_ID = 16, + USB_MAX_INTERFACE = 16, + USB_MAX_DEVICES = 128, + + // + // Bus raises TPL to TPL_NOTIFY to serialize all its operations + // to protect shared data structures. + // + USB_BUS_TPL = TPL_NOTIFY, + + USB_INTERFACE_SIGNATURE = EFI_SIGNATURE_32 ('U', 'S', 'B', 'I'), + USB_BUS_SIGNATURE = EFI_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 EFI_USB_BUS_PROTOCOL_GUID \ + {0x2B2F68CC, 0x0CD2, 0x44cf, 0x8E, 0x8B, 0xBB, 0xA2, 0x0B, 0x1B, 0x5B, 0x75} + +#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 +// +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. +// +typedef struct _USB_DEVICE { + USB_BUS *Bus; + + // + // Configuration information + // + UINT8 Speed; + UINT8 Address; + UINT8 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 +} USB_DEVICE; + +// +// Stands for different functions of USB device +// +typedef 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; +} USB_INTERFACE; + +// +// Stands for the current USB Bus +// +typedef 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; + + // + // 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[USB_MAX_DEVICES]; +} USB_BUS; + +extern EFI_USB_IO_PROTOCOL mUsbIoProtocol; +extern EFI_DRIVER_BINDING_PROTOCOL mUsbBusDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL mUsbBusComponentName; + +#endif -- cgit v1.2.3