From: vanjeff Date: Wed, 11 Jul 2007 08:47:37 +0000 (+0000) Subject: Import Usb/UsbBusDxe and Usb/UsbMassStorageDxe into MdeModulePkg. X-Git-Tag: edk2-stable201903~22762 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=e237e7ae9fc23f32a25040d49cc9a16f2a7f3b4c 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 --- 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 diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/ComponentName.c b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/ComponentName.c new file mode 100644 index 0000000000..bd0f939225 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/ComponentName.c @@ -0,0 +1,153 @@ + /*++ + +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 +UsbMassStorageGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +UsbMassStorageGetControllerName ( + 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 gUsbMassStorageComponentName = { + UsbMassStorageGetDriverName, + UsbMassStorageGetControllerName, + "eng" +}; + +STATIC EFI_UNICODE_STRING_TABLE +mUsbMassStorageDriverNameTable[] = { + {"eng", L"Usb Mass Storage Driver"}, + {NULL, NULL} +}; + +EFI_STATUS +EFIAPI +UsbMassStorageGetDriverName ( + 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_NAME_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, + gUsbMassStorageComponentName.SupportedLanguages, + mUsbMassStorageDriverNameTable, + DriverName + ); +} + +EFI_STATUS +EFIAPI +UsbMassStorageGetControllerName ( + 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_NAME_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_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return EFI_UNSUPPORTED; +} diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMass.h b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMass.h new file mode 100644 index 0000000000..c48dee5a7b --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMass.h @@ -0,0 +1,149 @@ +/** @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: + + UsbMass.h + +Abstract: + + Defination for the USB mass storage class driver. The USB mass storage + class is specified in two layers: the bottom layer is the transportation + protocol. The top layer is the command set. The transportation layer + provides the transportation of the command, data and result. The command + set defines what the command, data and result. The Bulk-Only-Transport and + Control/Bulk/Interrupt transport are two transportation protocol. USB mass + storage class adopts various industrial standard as its command set. + +Revision History + + +**/ + +#ifndef _EFI_USBMASS_H_ +#define _EFI_USBMASS_H_ + +// +// The package level header files this module uses +// +#include +// +// The protocols, PPI and GUID defintions for this module +// +#include +#include +// +// The Library classes this module consumes +// +#include +#include +#include +#include +#include +#include + +#define USB_IS_IN_ENDPOINT(EndPointAddr) (((EndPointAddr) & 0x80) == 0x80) +#define USB_IS_OUT_ENDPOINT(EndPointAddr) (((EndPointAddr) & 0x80) == 0) +#define USB_IS_BULK_ENDPOINT(Attribute) (((Attribute) & 0x03) == 0x02) +#define USB_IS_INTERRUPT_ENDPOINT(Attribute) (((Attribute) & 0x03) == 0x03) +#define USB_IS_ERROR(Result, Error) (((Result) & (Error)) != 0) + +enum { + // + // Usb mass storage class code + // + USB_MASS_STORE_CLASS = 0x08, + + // + // Usb mass storage subclass code, specify the command set used. + // + USB_MASS_STORE_RBC = 0x01, // Reduced Block Commands + USB_MASS_STORE_8020I = 0x02, // SFF-8020i, typically a CD/DVD device + USB_MASS_STORE_QIC = 0x03, // Typically a tape device + USB_MASS_STORE_UFI = 0x04, // Typically a floopy disk driver device + USB_MASS_STORE_8070I = 0x05, // SFF-8070i, typically a floppy disk driver device. + USB_MASS_STORE_SCSI = 0x06, // SCSI transparent command set + + // + // Usb mass storage protocol code, specify the transport protocol + // + USB_MASS_STORE_CBI0 = 0x00, // CBI protocol with command completion interrupt + USB_MASS_STORE_CBI1 = 0x01, // CBI protocol without command completion interrupt + USB_MASS_STORE_BOT = 0x50, // Bulk-Only Transport + + USB_MASS_STALL_1_MS = 1000, + USB_MASS_STALL_1_S = 1000 * USB_MASS_STALL_1_MS, + + USB_MASS_CMD_SUCCESS = 0, + USB_MASS_CMD_FAIL, + USB_MASS_CMD_PERSISTENT, +}; + +typedef +EFI_STATUS +(*USB_MASS_INIT_TRANSPORT) ( + IN EFI_USB_IO_PROTOCOL *Usb, + IN EFI_HANDLE Controller, + OUT VOID **Context OPTIONAL + ); + +typedef +EFI_STATUS +(*USB_MASS_EXEC_COMMAND) ( + IN VOID *Context, + IN VOID *Cmd, + IN UINT8 CmdLen, + IN EFI_USB_DATA_DIRECTION DataDir, + IN VOID *Data, + IN UINT32 DataLen, + IN UINT32 Timeout, + OUT UINT32 *CmdStatus + ); + +typedef +EFI_STATUS +(*USB_MASS_RESET) ( + IN VOID *Context, + IN BOOLEAN ExtendedVerification + ); + +typedef +EFI_STATUS +(*USB_MASS_FINI) ( + IN VOID *Context + ); + +// +// This structure contains information necessary to select the +// proper transport protocol. The mass storage class defines +// two transport protocols. One is the CBI, and the other is BOT. +// CBI is being obseleted. The design is made modular by this +// structure so that the CBI protocol can be easily removed when +// it is no longer necessary. +// +typedef struct { + UINT8 Protocol; + USB_MASS_INIT_TRANSPORT Init; // Initialize the mass storage transport protocol + USB_MASS_EXEC_COMMAND ExecCommand; // Transport command to the device then get result + USB_MASS_RESET Reset; // Reset the device + USB_MASS_FINI Fini; // Clean up the resources. +} USB_MASS_TRANSPORT; + + +EFI_STATUS +UsbClearEndpointStall ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN UINT8 EndpointAddress + ); + +extern UINTN mUsbMscInfo; +extern UINTN mUsbMscError; +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c new file mode 100644 index 0000000000..980f8b2895 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c @@ -0,0 +1,886 @@ +/** @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: + + UsbMassBoot.c + +Abstract: + + This file implement the command set of "USB Mass Storage Specification + for Bootability". + +Revision History + + +**/ + +#include "UsbMassImpl.h" + + +/** + Read an UINT32 from the buffer to avoid byte alignment problems, then + convert that to the little endia. The USB mass storage bootability spec + use big endia + + @param Buf The buffer contains the first byte of the UINT32 + in big endia. + + @return The UINT32 value read from the buffer in little endia. + +**/ +STATIC +UINT32 +UsbBootGetUint32 ( + IN UINT8 *Buf + ) +{ + UINT32 Value; + + CopyMem (&Value, Buf, sizeof (UINT32)); + return USB_BOOT_SWAP32 (Value); +} + + +/** + Put an UINT32 in little endia to the buffer. The data is converted to + big endia before writing. + + @param Buf The buffer to write data to + @param Data32 The data to write. + + @return None + +**/ +STATIC +VOID +UsbBootPutUint32 ( + IN UINT8 *Buf, + IN UINT32 Data32 + ) +{ + Data32 = USB_BOOT_SWAP32 (Data32); + CopyMem (Buf, &Data32, sizeof (UINT32)); +} + + +/** + Put an UINT16 in little endia to the buffer. The data is converted to + big endia before writing. + + @param Buf The buffer to write data to + @param Data16 The data to write + + @return None + +**/ +STATIC +VOID +UsbBootPutUint16 ( + IN UINT8 *Buf, + IN UINT16 Data16 + ) +{ + Data16 = USB_BOOT_SWAP16 (Data16); + CopyMem (Buf, &Data16, sizeof (UINT16)); +} + + +/** + Request sense information via sending Request Sense + Packet Command. + + @param UsbMass The device to be requested sense data + + @retval EFI_DEVICE_ERROR Hardware error + @retval EFI_SUCCESS Success + +**/ +EFI_STATUS +UsbBootRequestSense ( + IN USB_MASS_DEVICE *UsbMass + ) +{ + USB_BOOT_REQUEST_SENSE_CMD SenseCmd; + USB_BOOT_REQUEST_SENSE_DATA SenseData; + EFI_BLOCK_IO_MEDIA *Media; + USB_MASS_TRANSPORT *Transport; + EFI_STATUS Status; + UINT32 CmdResult; + + Transport = UsbMass->Transport; + + // + // Request the sense data from the device if command failed + // + ZeroMem (&SenseCmd, sizeof (USB_BOOT_REQUEST_SENSE_CMD)); + ZeroMem (&SenseData, sizeof (USB_BOOT_REQUEST_SENSE_DATA)); + + SenseCmd.OpCode = USB_BOOT_REQUEST_SENSE_OPCODE; + SenseCmd.Lun = USB_BOOT_LUN (UsbMass->Lun); + SenseCmd.AllocLen = sizeof (USB_BOOT_REQUEST_SENSE_DATA); + + Status = Transport->ExecCommand ( + UsbMass->Context, + &SenseCmd, + sizeof (USB_BOOT_REQUEST_SENSE_CMD), + EfiUsbDataIn, + &SenseData, + sizeof (USB_BOOT_REQUEST_SENSE_DATA), + USB_BOOT_GENERAL_CMD_TIMEOUT, + &CmdResult + ); + if (EFI_ERROR (Status) || CmdResult != USB_MASS_CMD_SUCCESS) { + DEBUG ((mUsbMscError, "UsbBootRequestSense: (%r) CmdResult=0x%x\n", Status, CmdResult)); + return EFI_DEVICE_ERROR; + } + + // + // Interpret the sense data and update the media status if necessary. + // + Media = &UsbMass->BlockIoMedia; + + switch (USB_BOOT_SENSE_KEY (SenseData.SenseKey)) { + + case USB_BOOT_SENSE_NO_SENSE: + case USB_BOOT_SENSE_RECOVERED: + // + // Suppose hardware can handle this case, and recover later by itself + // + Status = EFI_NOT_READY; + break; + + case USB_BOOT_SENSE_NOT_READY: + switch (SenseData.ASC) { + case USB_BOOT_ASC_NO_MEDIA: + Status = EFI_NO_MEDIA; + Media->MediaPresent = FALSE; + break; + + case USB_BOOT_ASC_MEDIA_UPSIDE_DOWN: + Status = EFI_DEVICE_ERROR; + Media->MediaPresent = FALSE; + break; + + case USB_BOOT_ASC_NOT_READY: + if (SenseData.ASCQ == USB_BOOT_ASCQ_IN_PROGRESS || + SenseData.ASCQ == USB_BOOT_ASCQ_DEVICE_BUSY) { + // + // Regular timeout, and need retry once more + // + DEBUG ((mUsbMscInfo, "UsbBootRequestSense: Not ready and need retry once more\n")); + Status = EFI_NOT_READY; + } + } + break; + + case USB_BOOT_SENSE_ILLEGAL_REQUEST: + Status = EFI_INVALID_PARAMETER; + break; + + case USB_BOOT_SENSE_UNIT_ATTENTION: + Status = EFI_DEVICE_ERROR; + if (SenseData.ASC == USB_BOOT_ASC_MEDIA_CHANGE) { + Status = EFI_MEDIA_CHANGED; + UsbMass->BlockIoMedia.MediaId++; + } + break; + + case USB_BOOT_SNESE_DATA_PROTECT: + Status = EFI_WRITE_PROTECTED; + UsbMass->BlockIoMedia.ReadOnly = TRUE; + break; + + default: + Status = EFI_DEVICE_ERROR; + break; + } + + DEBUG ((mUsbMscInfo, "UsbBootRequestSense: (%r) with sense key %x/%x/%x\n", + Status, + USB_BOOT_SENSE_KEY (SenseData.SenseKey), + SenseData.ASC, + SenseData.ASCQ + )); + + return Status; +} + + +/** + Execute the USB mass storage bootability commands. If execution + failed, retrieve the error by REQUEST_SENSE then update the device's + status, such as ReadyOnly. + + @param UsbMass The device to issue commands to + @param Cmd The command to execute + @param CmdLen The length of the command + @param DataDir The direction of data transfer + @param Data The buffer to hold the data + @param DataLen The length of expected data + @param Timeout The timeout used to transfer + + @retval EFI_SUCCESS The command is excuted OK + @retval EFI_DEVICE_ERROR Failed to request sense + @retval EFI_INVALID_PARAMETER The command has some invalid parameters + @retval EFI_WRITE_PROTECTED The device is write protected + @retval EFI_MEDIA_CHANGED The device media has been changed + +**/ +STATIC +EFI_STATUS +UsbBootExecCmd ( + IN USB_MASS_DEVICE *UsbMass, + IN VOID *Cmd, + IN UINT8 CmdLen, + IN EFI_USB_DATA_DIRECTION DataDir, + IN VOID *Data, + IN UINT32 DataLen, + IN UINT32 Timeout + ) +{ + USB_MASS_TRANSPORT *Transport; + EFI_STATUS Status; + UINT32 CmdResult; + + Transport = UsbMass->Transport; + Status = Transport->ExecCommand ( + UsbMass->Context, + Cmd, + CmdLen, + DataDir, + Data, + DataLen, + Timeout, + &CmdResult + ); + // + // ExecCommand return success and get the right CmdResult means + // the commnad transfer is OK. + // + if ((CmdResult == USB_MASS_CMD_SUCCESS) && !EFI_ERROR(Status)) { + return EFI_SUCCESS; + } + + return UsbBootRequestSense (UsbMass); +} + + +/** + Execute the USB mass storage bootability commands. If execution + failed, retrieve the error by REQUEST_SENSE then update the device's + status, such as ReadyOnly. + + @param UsbMass The device to issue commands to + @param Cmd The command to execute + @param CmdLen The length of the command + @param DataDir The direction of data transfer + @param Data The buffer to hold the data + @param DataLen The length of expected data + + @retval EFI_SUCCESS The command is excuted OK + @retval EFI_DEVICE_ERROR Failed to request sense + @retval EFI_INVALID_PARAMETER The command has some invalid parameters + @retval EFI_WRITE_PROTECTED The device is write protected + @retval EFI_MEDIA_CHANGED The device media has been changed + +**/ +STATIC +EFI_STATUS +UsbBootExecCmdWithRetry ( + IN USB_MASS_DEVICE *UsbMass, + IN VOID *Cmd, + IN UINT8 CmdLen, + IN EFI_USB_DATA_DIRECTION DataDir, + IN VOID *Data, + IN UINT32 DataLen, + IN UINT32 Timeout + ) +{ + EFI_STATUS Status; + INT16 Index; + + // + // If the device isn't ready, wait some time. If the device is ready, + // retry the command again. + // + Status = EFI_SUCCESS; + + for (Index = 0; Index < USB_BOOT_COMMAND_RETRY; Index++) { + // + // Execute the command with an increasingly larger timeout value. + // + Status = UsbBootExecCmd ( + UsbMass, + Cmd, + CmdLen, + DataDir, + Data, + DataLen, + Timeout * (Index + 1) + ); + if (Status == EFI_SUCCESS || + Status == EFI_MEDIA_CHANGED) { + break; + } + // + // Need retry once more, so reset index + // + if (Status == EFI_NOT_READY) { + Index = 0; + } + } + + return Status; +} + + + +/** + Use the TEST UNIT READY command to check whether it is ready. + If it is ready, update the parameters. + + @param UsbMass The device to test + + @retval EFI_SUCCESS The device is ready and parameters are updated. + @retval Others Device not ready. + +**/ +EFI_STATUS +UsbBootIsUnitReady ( + IN USB_MASS_DEVICE *UsbMass + ) +{ + USB_BOOT_TEST_UNIT_READY_CMD TestCmd; + + ZeroMem (&TestCmd, sizeof (USB_BOOT_TEST_UNIT_READY_CMD)); + + TestCmd.OpCode = USB_BOOT_TEST_UNIT_READY_OPCODE; + TestCmd.Lun = USB_BOOT_LUN (UsbMass->Lun); + + return UsbBootExecCmdWithRetry ( + UsbMass, + &TestCmd, + sizeof (USB_BOOT_TEST_UNIT_READY_CMD), + EfiUsbNoData, + NULL, + 0, + USB_BOOT_GENERAL_CMD_TIMEOUT + ); +} + + +/** + Inquiry Command requests that information regrarding parameters of + the Device be sent to the Host. + + @param UsbMass The device to inquiry. + + @retval EFI_SUCCESS The device is ready and parameters are updated. + @retval Others Device not ready. + +**/ +EFI_STATUS +UsbBootInquiry ( + IN USB_MASS_DEVICE *UsbMass + ) +{ + USB_BOOT_INQUIRY_CMD InquiryCmd; + USB_BOOT_INQUIRY_DATA InquiryData; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + + Media = &(UsbMass->BlockIoMedia); + + // + // Use the Inquiry command to get the RemovableMedia setting. + // + ZeroMem (&InquiryCmd, sizeof (USB_BOOT_INQUIRY_CMD)); + ZeroMem (&InquiryData, sizeof (USB_BOOT_INQUIRY_DATA)); + + InquiryCmd.OpCode = USB_BOOT_INQUIRY_OPCODE; + InquiryCmd.Lun = USB_BOOT_LUN (UsbMass->Lun); + InquiryCmd.AllocLen = sizeof (InquiryData); + + Status = UsbBootExecCmdWithRetry ( + UsbMass, + &InquiryCmd, + sizeof (USB_BOOT_INQUIRY_CMD), + EfiUsbDataIn, + &InquiryData, + sizeof (USB_BOOT_INQUIRY_DATA), + USB_BOOT_INQUIRY_CMD_TIMEOUT + ); + if (EFI_ERROR (Status)) { + return Status; + } + + UsbMass->Pdt = USB_BOOT_PDT (InquiryData.Pdt); + Media->RemovableMedia = USB_BOOT_REMOVABLE (InquiryData.Removable); + // + // Default value 512 Bytes, in case no media present at first time + // + Media->BlockSize = 0x0200; + + return Status; +} + + +/** + Get the capacity of the USB mass storage media, including + the presentation, block size, and last block number. This + function is used to get the disk parameters at the start if + it is a non-removable media or to detect the media if it is + removable. + + @param UsbMass The device to retireve disk gemotric. + + @retval EFI_SUCCESS The disk gemotric is successfully retrieved. + @retval EFI_DEVICE_ERROR Something is inconsistent with the disk gemotric. + +**/ +EFI_STATUS +UsbBootReadCapacity ( + IN USB_MASS_DEVICE *UsbMass + ) +{ + USB_BOOT_READ_CAPACITY_CMD CapacityCmd; + USB_BOOT_READ_CAPACITY_DATA CapacityData; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + + Media = &UsbMass->BlockIoMedia; + + // + // Use the READ CAPACITY command to get the block length and last blockno + // + ZeroMem (&CapacityCmd, sizeof (USB_BOOT_READ_CAPACITY_CMD)); + ZeroMem (&CapacityData, sizeof (USB_BOOT_READ_CAPACITY_DATA)); + + CapacityCmd.OpCode = USB_BOOT_READ_CAPACITY_OPCODE; + CapacityCmd.Lun = USB_BOOT_LUN (UsbMass->Lun); + + Status = UsbBootExecCmdWithRetry ( + UsbMass, + &CapacityCmd, + sizeof (USB_BOOT_READ_CAPACITY_CMD), + EfiUsbDataIn, + &CapacityData, + sizeof (USB_BOOT_READ_CAPACITY_DATA), + USB_BOOT_GENERAL_CMD_TIMEOUT + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Media->MediaPresent = TRUE; + Media->LastBlock = UsbBootGetUint32 (CapacityData.LastLba); + Media->BlockSize = UsbBootGetUint32 (CapacityData.BlockLen); + + DEBUG ((mUsbMscInfo, "UsbBootReadCapacity Success LBA=%d BlockSize=%d\n", + Media->LastBlock, Media->BlockSize)); + + return EFI_SUCCESS; +} + + +/** + Retrieves mode sense information via sending Mode Sense + Packet Command. + + @param UsbMass The USB_FLOPPY_DEV instance. + + @retval EFI_DEVICE_ERROR Hardware error + @retval EFI_SUCCESS Success + +**/ +EFI_STATUS +UsbBootModeSense ( + IN USB_MASS_DEVICE *UsbMass + ) +{ + EFI_STATUS Status; + USB_BOOT_MODE_SENSE_CMD ModeSenseCmd; + USB_BOOT_MODE_PARA_HEADER ModeParaHeader; + UINT8 CommandSet; + + ZeroMem (&ModeSenseCmd, sizeof (USB_BOOT_MODE_SENSE_CMD)); + ZeroMem (&ModeParaHeader, sizeof (USB_BOOT_MODE_PARA_HEADER)); + + // + // overuse Context Pointer, the first field of Bot or Cbi is EFI_USB_INTERFACE_DESCRIPTOR + // + CommandSet = ((EFI_USB_INTERFACE_DESCRIPTOR *) (UsbMass->Context))->InterfaceSubClass; + + if (CommandSet == USB_MASS_STORE_SCSI) { + // + // Not UFI Command Set, no ModeSense Command + // + return EFI_SUCCESS; + } + + ModeSenseCmd.OpCode = USB_BOOT_MODE_SENSE10_OPCODE; + ModeSenseCmd.PageCode = 0x3f; + ModeSenseCmd.ParaListLenLsb = (UINT8) sizeof (USB_BOOT_MODE_PARA_HEADER); + + Status = UsbBootExecCmdWithRetry ( + UsbMass, + &ModeSenseCmd, + sizeof (USB_BOOT_MODE_SENSE_CMD), + EfiUsbDataIn, + &ModeParaHeader, + sizeof (USB_BOOT_MODE_PARA_HEADER), + USB_BOOT_GENERAL_CMD_TIMEOUT + ); + // + // Did nothing with the Header here + // But probably should + // + + return Status; +} + + +/** + Get the parameters for the USB mass storage media, including + the RemovableMedia, block size, and last block number. This + function is used both to initialize the media during the + DriverBindingStart and to re-initialize it when the media is + changed. Althought the RemoveableMedia is unlikely to change, + I include it here. + + @param UsbMass The device to retireve disk gemotric. + + @retval EFI_SUCCESS The disk gemotric is successfully retrieved. + @retval EFI_DEVICE_ERROR Something is inconsistent with the disk gemotric. + +**/ +EFI_STATUS +UsbBootGetParams ( + IN USB_MASS_DEVICE *UsbMass + ) +{ + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + + Status = UsbBootInquiry (UsbMass); + if (EFI_ERROR (Status)) { + DEBUG ((mUsbMscError, "UsbBootGetParams: UsbBootInquiry (%r)\n", Status)); + return Status; + } + + Media = &(UsbMass->BlockIoMedia); + // + // Don't use the Removable bit in inquirydata to test whether the media + // is removable because many flash disks wrongly set this bit. + // + if ((UsbMass->Pdt == USB_PDT_CDROM) || (UsbMass->Pdt == USB_PDT_OPTICAL)) { + // + // CD-Rom or Optical device + // + UsbMass->OpticalStorage = TRUE; + // + // Default value 2048 Bytes, in case no media present at first time + // + Media->BlockSize = 0x0800; + } else { + // + // Non CD-Rom device need ModeSenseCmd between InquiryCmd and ReadCapacityCmd + // + Status = UsbBootModeSense (UsbMass); + if (EFI_ERROR (Status)) { + DEBUG ((mUsbMscError, "UsbBootGetParams: UsbBootModeSense (%r)\n", Status)); + return Status; + } + } + + return UsbBootReadCapacity (UsbMass); +} + + +/** + Detect whether the removable media is present and whether it has changed. + The Non-removable media doesn't need it. + + @param UsbMass The device to retireve disk gemotric. + + @retval EFI_SUCCESS The disk gemotric is successfully retrieved. + @retval EFI_DEVICE_ERROR Something is inconsistent with the disk gemotric. + +**/ +EFI_STATUS +UsbBootDetectMedia ( + IN USB_MASS_DEVICE *UsbMass + ) +{ + EFI_BLOCK_IO_MEDIA OldMedia; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + + Media = &UsbMass->BlockIoMedia; + OldMedia = UsbMass->BlockIoMedia; + + // + // First test whether the device is ready and get status + // If media changed or ready, need read the device's capacity + // + Status = UsbBootIsUnitReady (UsbMass); + if ((Status == EFI_SUCCESS && Media->MediaPresent) || + (Status == EFI_MEDIA_CHANGED)) { + if ((UsbMass->Pdt != USB_PDT_CDROM) && + (UsbMass->Pdt != USB_PDT_OPTICAL)) { + // + // Non CD-Rom device need ModeSenseCmd between InquiryCmd and ReadCapacityCmd + // + UsbBootModeSense (UsbMass); + } + DEBUG ((mUsbMscInfo, "UsbBootDetectMedia: Need Read Capacity\n")); + Status = UsbBootReadCapacity (UsbMass); + } + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Detect whether it is necessary to reinstall the BlockIO + // + if ((Media->MediaId != OldMedia.MediaId) || + (Media->MediaPresent != OldMedia.MediaPresent) || + (Media->ReadOnly != OldMedia.ReadOnly) || + (Media->BlockSize != OldMedia.BlockSize) || + (Media->LastBlock != OldMedia.LastBlock)) { + DEBUG ((mUsbMscInfo, "UsbBootDetectMedia: Need reinstall BlockIoProtocol\n")); + Media->MediaId++; + gBS->ReinstallProtocolInterface ( + UsbMass->Controller, + &gEfiBlockIoProtocolGuid, + &UsbMass->BlockIo, + &UsbMass->BlockIo + ); + // + // Check whether media present or media changed or write protected + // + if (Media->MediaPresent == FALSE) { + Status = EFI_NO_MEDIA; + } + if (Media->MediaId != OldMedia.MediaId) { + Status = EFI_MEDIA_CHANGED; + } + if (Media->ReadOnly != OldMedia.ReadOnly) { + Status = EFI_WRITE_PROTECTED; + } + } + + return Status; +} + + +/** + Read some blocks from the device. + + @param UsbMass The USB mass storage device to read from + @param Lba The start block number + @param TotalBlock Total block number to read + @param Buffer The buffer to read to + + @retval EFI_SUCCESS Data are read into the buffer + @retval Others Failed to read all the data + +**/ +EFI_STATUS +UsbBootReadBlocks ( + IN USB_MASS_DEVICE *UsbMass, + IN UINT32 Lba, + IN UINTN TotalBlock, + OUT UINT8 *Buffer + ) +{ + USB_BOOT_READ10_CMD ReadCmd; + EFI_STATUS Status; + UINT16 Count; + UINT32 BlockSize; + UINT32 ByteSize; + UINT32 Timeout; + + BlockSize = UsbMass->BlockIoMedia.BlockSize; + Status = EFI_SUCCESS; + + while (TotalBlock > 0) { + // + // Split the total blocks into smaller pieces to ease the pressure + // on the device. We must split the total block because the READ10 + // command only has 16 bit transfer length (in the unit of block). + // + Count = (UINT16)((TotalBlock < USB_BOOT_IO_BLOCKS) ? TotalBlock : USB_BOOT_IO_BLOCKS); + ByteSize = (UINT32)Count * BlockSize; + + // + // Optical device need longer timeout than other device + // + if (UsbMass->OpticalStorage == TRUE) { + Timeout = (UINT32)Count * USB_BOOT_OPTICAL_BLOCK_TIMEOUT; + } else { + Timeout = (UINT32)Count * USB_BOOT_GENERAL_BLOCK_TIMEOUT; + } + + // + // Fill in the command then execute + // + ZeroMem (&ReadCmd, sizeof (USB_BOOT_READ10_CMD)); + + ReadCmd.OpCode = USB_BOOT_READ10_OPCODE; + ReadCmd.Lun = USB_BOOT_LUN (UsbMass->Lun); + UsbBootPutUint32 (ReadCmd.Lba, Lba); + UsbBootPutUint16 (ReadCmd.TransferLen, Count); + + Status = UsbBootExecCmdWithRetry ( + UsbMass, + &ReadCmd, + sizeof (USB_BOOT_READ10_CMD), + EfiUsbDataIn, + Buffer, + ByteSize, + Timeout + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Lba += Count; + Buffer += Count * BlockSize; + TotalBlock -= Count; + } + + return Status; +} + + +/** + Write some blocks to the device. + + @param UsbMass The USB mass storage device to write to + @param Lba The start block number + @param TotalBlock Total block number to write + @param Buffer The buffer to write to + + @retval EFI_SUCCESS Data are written into the buffer + @retval Others Failed to write all the data + +**/ +EFI_STATUS +UsbBootWriteBlocks ( + IN USB_MASS_DEVICE *UsbMass, + IN UINT32 Lba, + IN UINTN TotalBlock, + OUT UINT8 *Buffer + ) +{ + USB_BOOT_WRITE10_CMD WriteCmd; + EFI_STATUS Status; + UINT16 Count; + UINT32 BlockSize; + UINT32 ByteSize; + UINT32 Timeout; + + BlockSize = UsbMass->BlockIoMedia.BlockSize; + Status = EFI_SUCCESS; + + while (TotalBlock > 0) { + // + // Split the total blocks into smaller pieces to ease the pressure + // on the device. We must split the total block because the WRITE10 + // command only has 16 bit transfer length (in the unit of block). + // + Count = (UINT16)((TotalBlock < USB_BOOT_IO_BLOCKS) ? TotalBlock : USB_BOOT_IO_BLOCKS); + ByteSize = (UINT32)Count * BlockSize; + + // + // Optical device need longer timeout than other device + // + if (UsbMass->OpticalStorage == TRUE) { + Timeout = (UINT32)Count * USB_BOOT_OPTICAL_BLOCK_TIMEOUT; + } else { + Timeout = (UINT32)Count * USB_BOOT_GENERAL_BLOCK_TIMEOUT; + } + + // + // Fill in the write10 command block + // + ZeroMem (&WriteCmd, sizeof (USB_BOOT_WRITE10_CMD)); + + WriteCmd.OpCode = USB_BOOT_WRITE10_OPCODE; + WriteCmd.Lun = USB_BOOT_LUN (UsbMass->Lun); + UsbBootPutUint32 (WriteCmd.Lba, Lba); + UsbBootPutUint16 (WriteCmd.TransferLen, Count); + + Status = UsbBootExecCmdWithRetry ( + UsbMass, + &WriteCmd, + sizeof (USB_BOOT_WRITE10_CMD), + EfiUsbDataOut, + Buffer, + ByteSize, + Timeout + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Lba += Count; + Buffer += Count * BlockSize; + TotalBlock -= Count; + } + + return Status; +} + + +/** + Use the USB clear feature control transfer to clear the endpoint + stall condition. + + @param UsbIo The USB IO protocol to use + @param EndpointAddr The endpoint to clear stall for + + @retval EFI_SUCCESS The endpoint stall condtion is clear + @retval Others Failed to clear the endpoint stall condtion + +**/ +EFI_STATUS +UsbClearEndpointStall ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN UINT8 EndpointAddr + ) +{ + EFI_USB_DEVICE_REQUEST Request; + EFI_STATUS Status; + UINT32 CmdResult; + UINT32 Timeout; + + Request.RequestType = 0x02; + Request.Request = USB_REQ_CLEAR_FEATURE; + Request.Value = USB_FEATURE_ENDPOINT_HALT; + Request.Index = EndpointAddr; + Request.Length = 0; + Timeout = USB_BOOT_GENERAL_CMD_TIMEOUT / USB_MASS_STALL_1_MS; + + Status = UsbIo->UsbControlTransfer ( + UsbIo, + &Request, + EfiUsbNoData, + Timeout, + NULL, + 0, + &CmdResult + ); + + return Status; +} diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.h b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.h new file mode 100644 index 0000000000..cee70be412 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.h @@ -0,0 +1,268 @@ +/** @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: + + UsbMassBoot.h + +Abstract: + + The definition of command and data of the USB mass storage for + bootability command set. + +Revision History + + +**/ + +#ifndef _EFI_USB_MASS_BOOT_H_ +#define _EFI_USB_MASS_BOOT_H_ + +enum { + // + // The opcodes of various usb boot commands: + // INQUIRY/REQUEST_SENSE are "No Timeout Commands" as specified + // by MMC command set. Others are "Group 1 Timeout Commands". That + // is they should be retried if driver is ready. + // We can't use the Peripheral Device Type in Inquiry data to + // determine the timeout used. For example, both floppy and flash + // are likely set their PDT to 0, or Direct Access Device. + // + USB_BOOT_INQUIRY_OPCODE = 0x12, + USB_BOOT_REQUEST_SENSE_OPCODE = 0x03, + + USB_BOOT_MODE_SENSE10_OPCODE = 0x5a, + USB_BOOT_READ_CAPACITY_OPCODE = 0x25, + USB_BOOT_TEST_UNIT_READY_OPCODE = 0x00, + USB_BOOT_READ10_OPCODE = 0x28, + USB_BOOT_WRITE10_OPCODE = 0x2a, + + // + // The Sense Key part of the sense data. Sense data has three levels: + // Sense key, Additional Sense Code and Additional Sense Code Qualifier + // + USB_BOOT_SENSE_NO_SENSE = 0x00, // No sense key + USB_BOOT_SENSE_RECOVERED = 0x01, // Last command succeed with recovery actions + USB_BOOT_SENSE_NOT_READY = 0x02, // Device not ready + USB_BOOT_SNESE_MEDIUM_ERROR = 0X03, // Failed probably because flaw in the media + USB_BOOT_SENSE_HARDWARE_ERROR = 0X04, // Non-recoverable hardware failure + USB_BOOT_SENSE_ILLEGAL_REQUEST = 0X05, // Illegal parameters in the request + USB_BOOT_SENSE_UNIT_ATTENTION = 0X06, // Removable medium may have been changed + USB_BOOT_SNESE_DATA_PROTECT = 0X07, // Write protected + USB_BOOT_SENSE_BLANK_CHECK = 0X08, // Blank/non-blank medium while reading/writing + USB_BOOT_SENSE_VENDOR = 0X09, // Vendor specific sense key + USB_BOOT_SENSE_ABORTED = 0X0B, // Command aborted by the device + USB_BOOT_SENSE_VOLUME_OVERFLOW = 0x0D, // Partition overflow + USB_BOOT_SENSE_MISCOMPARE = 0x0E, // Source data mis-match while verfying. + + USB_BOOT_ASC_NOT_READY = 0x04, + USB_BOOT_ASC_MEDIA_UPSIDE_DOWN = 0x06, + USB_BOOT_ASC_NO_MEDIA = 0x3A, + USB_BOOT_ASC_MEDIA_CHANGE = 0x28, + + USB_BOOT_ASCQ_IN_PROGRESS = 0x01, + USB_BOOT_ASCQ_DEVICE_BUSY = 0xFF, + + // + // Other parameters + // + USB_BOOT_IO_BLOCKS = 64, + + // + // Boot Retry times + // + USB_BOOT_COMMAND_RETRY = 5, + USB_BOOT_WAIT_RETRY = 5, + + // + // Boot Stall time + // + USB_BOOT_UNIT_READY_STALL = 50 * USB_MASS_STALL_1_MS, + + // + // Boot Transfer timeout + // + USB_BOOT_GENERAL_BLOCK_TIMEOUT = 200 * USB_MASS_STALL_1_MS, + USB_BOOT_OPTICAL_BLOCK_TIMEOUT = 1 * USB_MASS_STALL_1_S, + USB_BOOT_GENERAL_CMD_TIMEOUT = 1 * USB_MASS_STALL_1_S, + USB_BOOT_INQUIRY_CMD_TIMEOUT = 3 * USB_MASS_STALL_1_S, + + // + // Supported PDT codes, or Peripheral Device Type + // + USB_PDT_DIRECT_ACCESS = 0x00, // Direct access device + USB_PDT_CDROM = 0x05, // CDROM + USB_PDT_OPTICAL = 0x07, // Non-CD optical disks + USB_PDT_SIMPLE_DIRECT = 0x0E, // Simplified direct access device +}; + +// +// The required commands are INQUIRY, READ CAPACITY, TEST UNIT READY, +// READ10, WRITE10, and REQUEST SENSE. The BLOCK_IO protocol uses LBA +// so it isn't necessary to issue MODE SENSE / READ FORMAT CAPACITY +// command to retrieve the disk gemotrics. +// +#pragma pack(1) +typedef struct { + UINT8 OpCode; + UINT8 Lun; // Lun (high 3 bits) + UINT8 Reserved0[2]; + UINT8 AllocLen; + UINT8 Reserved1; + UINT8 Pad[6]; +} USB_BOOT_INQUIRY_CMD; + +typedef struct { + UINT8 Pdt; // Peripheral Device Type (low 5 bits) + UINT8 Removable; // Removable Media (highest bit) + UINT8 Reserved0[2]; + UINT8 AddLen; // Additional length + UINT8 Reserved1[3]; + UINT8 VendorID[8]; + UINT8 ProductID[16]; + UINT8 ProductRevision[4]; +} USB_BOOT_INQUIRY_DATA; + +typedef struct { + UINT8 OpCode; + UINT8 Lun; + UINT8 Reserved0[8]; + UINT8 Pad[2]; +} USB_BOOT_READ_CAPACITY_CMD; + +typedef struct { + UINT8 LastLba[4]; + UINT8 BlockLen[4]; +} USB_BOOT_READ_CAPACITY_DATA; + +typedef struct { + UINT8 OpCode; + UINT8 Lun; + UINT8 Reserved[4]; + UINT8 Pad[6]; +} USB_BOOT_TEST_UNIT_READY_CMD; + +typedef struct { + UINT8 OpCode; + UINT8 Lun; + UINT8 PageCode; + UINT8 Reserved0[4]; + UINT8 ParaListLenMsb; + UINT8 ParaListLenLsb; + UINT8 Reserved1; + UINT8 Pad[2]; +} USB_BOOT_MODE_SENSE_CMD; + +typedef struct { + UINT8 ModeDataLenMsb; + UINT8 ModeDataLenLsb; + UINT8 Reserved0[4]; + UINT8 BlkDesLenMsb; + UINT8 BlkDesLenLsb; +} USB_BOOT_MODE_PARA_HEADER; + +typedef struct { + UINT8 OpCode; + UINT8 Lun; // Lun (High 3 bits) + UINT8 Lba[4]; // Logical block address + UINT8 Reserved0; + UINT8 TransferLen[2]; // Transfer length + UINT8 Reserverd1; + UINT8 Pad[2]; +} USB_BOOT_READ10_CMD; + +typedef struct { + UINT8 OpCode; + UINT8 Lun; + UINT8 Lba[4]; + UINT8 Reserved0; + UINT8 TransferLen[2]; + UINT8 Reserverd1; + UINT8 Pad[2]; +} USB_BOOT_WRITE10_CMD; + +typedef struct { + UINT8 OpCode; + UINT8 Lun; // Lun (High 3 bits) + UINT8 Reserved0[2]; + UINT8 AllocLen; // Allocation length + UINT8 Reserved1; + UINT8 Pad[6]; +} USB_BOOT_REQUEST_SENSE_CMD; + +typedef struct { + UINT8 ErrorCode; + UINT8 Reserved0; + UINT8 SenseKey; // Sense key (low 4 bits) + UINT8 Infor[4]; + UINT8 AddLen; // Additional Sense length, 10 + UINT8 Reserved1[4]; + UINT8 ASC; // Additional Sense Code + UINT8 ASCQ; // Additional Sense Code Qualifier + UINT8 Reserverd2[4]; +} USB_BOOT_REQUEST_SENSE_DATA; +#pragma pack() + +// +// Convert a LUN number to that in the command +// +#define USB_BOOT_LUN(Lun) ((Lun) << 5) + +// +// Get the removable, PDT, and sense key bits from the command data +// +#define USB_BOOT_REMOVABLE(RmbByte) (((RmbByte) & 0x80) != 0) +#define USB_BOOT_PDT(Pdt) ((Pdt) & 0x1f) +#define USB_BOOT_SENSE_KEY(Key) ((Key) & 0x0f) + +// +// Swap the byte sequence of a UINT32. Intel CPU uses little endian +// in UEFI environment, but USB boot uses big endian. +// +#define USB_BOOT_SWAP32(Data32) \ + ((((Data32) & 0x000000ff) << 24) | (((Data32) & 0xff000000) >> 24) | \ + (((Data32) & 0x0000ff00) << 8) | (((Data32) & 0x00ff0000) >> 8)) + +#define USB_BOOT_SWAP16(Data16) \ + ((((Data16) & 0x00ff) << 8) | (((Data16) & 0xff00) >> 8)) + +EFI_STATUS +UsbBootGetParams ( + IN USB_MASS_DEVICE *UsbMass + ); + +EFI_STATUS +UsbBootIsUnitReady ( + IN USB_MASS_DEVICE *UsbMass + ); + +EFI_STATUS +UsbBootDetectMedia ( + IN USB_MASS_DEVICE *UsbMass + ); + +EFI_STATUS +UsbBootReadBlocks ( + IN USB_MASS_DEVICE *UsbMass, + IN UINT32 Lba, + IN UINTN TotalBlock, + OUT UINT8 *Buffer + ); + +EFI_STATUS +UsbBootWriteBlocks ( + IN USB_MASS_DEVICE *UsbMass, + IN UINT32 Lba, + IN UINTN TotalBlock, + OUT UINT8 *Buffer + ); +#endif + diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.c b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.c new file mode 100644 index 0000000000..0d6cca167b --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.c @@ -0,0 +1,561 @@ +/** @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: + + UsbMassBot.c + +Abstract: + + Implementation of the USB mass storage Bulk-Only Transport protocol. + +Revision History + + +**/ + +#include "UsbMass.h" +#include "UsbMassBot.h" + +UINTN mUsbBotInfo = DEBUG_INFO; +UINTN mUsbBotError = DEBUG_ERROR; + +STATIC +EFI_STATUS +UsbBotResetDevice ( + IN VOID *Context, + IN BOOLEAN ExtendedVerification + ); + + +/** + Initialize the USB mass storage class BOT transport protocol. + It will save its context which is a USB_BOT_PROTOCOL structure + in the Context if Context isn't NULL. + + @param UsbIo The USB IO protocol to use + @param Controller The controller to init + @param Context The variable to save the context to + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory + @retval EFI_UNSUPPORTED The transport protocol doesn't support the device. + @retval EFI_SUCCESS The device is supported and protocol initialized. + +**/ +STATIC +EFI_STATUS +UsbBotInit ( + IN EFI_USB_IO_PROTOCOL * UsbIo, + IN EFI_HANDLE Controller, + OUT VOID **Context OPTIONAL + ) +{ + USB_BOT_PROTOCOL *UsbBot; + EFI_USB_INTERFACE_DESCRIPTOR *Interface; + EFI_USB_ENDPOINT_DESCRIPTOR EndPoint; + EFI_STATUS Status; + UINT8 Index; + + // + // Allocate the BOT context, append two endpoint descriptors to it + // + UsbBot = AllocateZeroPool ( + sizeof (USB_BOT_PROTOCOL) + 2 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR) + ); + if (UsbBot == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UsbBot->UsbIo = UsbIo; + + // + // Get the interface descriptor and validate that it + // is a USB MSC BOT interface. + // + Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbBot->Interface); + + if (EFI_ERROR (Status)) { + DEBUG ((mUsbBotError, "UsbBotInit: Get invalid BOT interface (%r)\n", Status)); + goto ON_ERROR; + } + + Interface = &UsbBot->Interface; + + if (Interface->InterfaceProtocol != USB_MASS_STORE_BOT) { + Status = EFI_UNSUPPORTED; + goto ON_ERROR; + } + + // + // Locate and save the first bulk-in and bulk-out endpoint + // + for (Index = 0; Index < Interface->NumEndpoints; Index++) { + Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint); + + if (EFI_ERROR (Status) || !USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) { + continue; + } + + if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) && + (UsbBot->BulkInEndpoint == NULL)) { + + UsbBot->BulkInEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1); + *UsbBot->BulkInEndpoint = EndPoint; + } + + if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) && + (UsbBot->BulkOutEndpoint == NULL)) { + + UsbBot->BulkOutEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1) + 1; + *UsbBot->BulkOutEndpoint = EndPoint; + } + } + + if ((UsbBot->BulkInEndpoint == NULL) || (UsbBot->BulkOutEndpoint == NULL)) { + DEBUG ((mUsbBotError, "UsbBotInit: In/Out Endpoint invalid\n")); + Status = EFI_UNSUPPORTED; + goto ON_ERROR; + } + + // + // The USB BOT protocol uses dCBWTag to match the CBW and CSW. + // + UsbBot->CbwTag = 0x01; + + if (Context != NULL) { + *Context = UsbBot; + } else { + gBS->FreePool (UsbBot); + } + + return EFI_SUCCESS; + +ON_ERROR: + gBS->FreePool (UsbBot); + return Status; +} + + +/** + Send the command to the device using Bulk-Out endpoint + + @param UsbBot The USB BOT device + @param Cmd The command to transfer to device + @param CmdLen the length of the command + @param DataDir The direction of the data + @param TransLen The expected length of the data + + @retval EFI_NOT_READY The device return NAK to the transfer + @retval EFI_SUCCESS The command is sent to the device. + @retval Others Failed to send the command to device + +**/ +STATIC +EFI_STATUS +UsbBotSendCommand ( + IN USB_BOT_PROTOCOL *UsbBot, + IN UINT8 *Cmd, + IN UINT8 CmdLen, + IN EFI_USB_DATA_DIRECTION DataDir, + IN UINT32 TransLen + ) +{ + USB_BOT_CBW Cbw; + EFI_STATUS Status; + UINT32 Result; + UINTN DataLen; + UINTN Timeout; + + ASSERT ((CmdLen > 0) && (CmdLen <= USB_BOT_MAX_CMDLEN)); + + // + // Fill in the CSW. Only the first LUN is supported now. + // + Cbw.Signature = USB_BOT_CBW_SIGNATURE; + Cbw.Tag = UsbBot->CbwTag; + Cbw.DataLen = TransLen; + Cbw.Flag = ((DataDir == EfiUsbDataIn) ? 0x80 : 0); + Cbw.Lun = 0; + Cbw.CmdLen = CmdLen; + + ZeroMem (Cbw.CmdBlock, USB_BOT_MAX_CMDLEN); + CopyMem (Cbw.CmdBlock, Cmd, CmdLen); + + Result = 0; + DataLen = sizeof (USB_BOT_CBW); + Timeout = USB_BOT_CBW_TIMEOUT / USB_MASS_STALL_1_MS; + + // + // Use the UsbIo to send the command to the device. The default + // time out is enough. + // + Status = UsbBot->UsbIo->UsbBulkTransfer ( + UsbBot->UsbIo, + UsbBot->BulkOutEndpoint->EndpointAddress, + &Cbw, + &DataLen, + Timeout, + &Result + ); + // + // Respond to Bulk-Out endpoint stall with a Reset Recovery, + // see the spec section 5.3.1 + // + if (EFI_ERROR (Status)) { + if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL) && DataDir == EfiUsbDataOut) { + UsbBotResetDevice (UsbBot, FALSE); + } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) { + Status = EFI_NOT_READY; + } + } + + return Status; +} + + +/** + Transfer the data between the device and host. BOT transfer + is composed of three phase, command, data, and status. + + @param UsbBot The USB BOT device + @param DataDir The direction of the data + @param Data The buffer to hold data + @param TransLen The expected length of the data + @param Timeout The time to wait the command to complete + + @retval EFI_SUCCESS The data is transferred + @retval Others Failed to transfer data + +**/ +STATIC +EFI_STATUS +UsbBotDataTransfer ( + IN USB_BOT_PROTOCOL *UsbBot, + IN EFI_USB_DATA_DIRECTION DataDir, + IN OUT UINT8 *Data, + IN OUT UINTN *TransLen, + IN UINT32 Timeout + ) +{ + EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint; + EFI_STATUS Status; + UINT32 Result; + + // + // It's OK if no data to transfer + // + if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) { + return EFI_SUCCESS; + } + + // + // Select the endpoint then issue the transfer + // + if (DataDir == EfiUsbDataIn) { + Endpoint = UsbBot->BulkInEndpoint; + } else { + Endpoint = UsbBot->BulkOutEndpoint; + } + + Result = 0; + Timeout = Timeout / USB_MASS_STALL_1_MS; + + Status = UsbBot->UsbIo->UsbBulkTransfer ( + UsbBot->UsbIo, + Endpoint->EndpointAddress, + Data, + TransLen, + Timeout, + &Result + ); + if (EFI_ERROR (Status)) { + DEBUG ((mUsbBotError, "UsbBotDataTransfer: (%r)\n", Status)); + if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) { + DEBUG ((mUsbBotError, "UsbBotDataTransfer: DataIn Stall\n")); + UsbClearEndpointStall (UsbBot->UsbIo, Endpoint->EndpointAddress); + } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) { + Status = EFI_NOT_READY; + } + } + + return Status; +} + + +/** + Get the command execution status from device. BOT transfer is + composed of three phase, command, data, and status. + This function return the transfer status of the BOT's CSW status, + and return the high level command execution result in Result. So + even it returns EFI_SUCCESS, the command may still have failed. + + @param UsbBot The USB BOT device + @param TransLen The expected length of the data + @param Timeout The time to wait the command to complete + @param CmdStatus The result of the command execution. + + @retval EFI_DEVICE_ERROR Failed to retrieve the command execute result + @retval EFI_SUCCESS Command execute result is retrieved and in the + Result. + +**/ +STATIC +EFI_STATUS +UsbBotGetStatus ( + IN USB_BOT_PROTOCOL *UsbBot, + IN UINT32 TransLen, + OUT UINT8 *CmdStatus + ) +{ + USB_BOT_CSW Csw; + UINTN Len; + UINT8 Endpoint; + EFI_STATUS Status; + UINT32 Result; + EFI_USB_IO_PROTOCOL *UsbIo; + UINT32 Index; + UINTN Timeout; + + *CmdStatus = USB_BOT_COMMAND_ERROR; + Status = EFI_DEVICE_ERROR; + Endpoint = UsbBot->BulkInEndpoint->EndpointAddress; + UsbIo = UsbBot->UsbIo; + Timeout = USB_BOT_CSW_TIMEOUT / USB_MASS_STALL_1_MS; + + for (Index = 0; Index < USB_BOT_GET_STATUS_RETRY; Index++) { + // + // Attemp to the read CSW from bulk in endpoint + // + ZeroMem (&Csw, sizeof (USB_BOT_CSW)); + Result = 0; + Len = sizeof (USB_BOT_CSW); + Status = UsbIo->UsbBulkTransfer ( + UsbIo, + Endpoint, + &Csw, + &Len, + Timeout, + &Result + ); + if (EFI_ERROR(Status)) { + DEBUG ((mUsbBotError, "UsbBotGetStatus (%r)\n", Status)); + if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) { + DEBUG ((mUsbBotError, "UsbBotGetStatus: DataIn Stall\n")); + UsbClearEndpointStall (UsbIo, Endpoint); + } + continue; + } + + if (Csw.Signature != USB_BOT_CSW_SIGNATURE) { + // + // Invalid Csw need perform reset recovery + // + DEBUG ((mUsbBotError, "UsbBotGetStatus: Device return a invalid signature\n")); + Status = UsbBotResetDevice (UsbBot, FALSE); + } else if (Csw.CmdStatus == USB_BOT_COMMAND_ERROR) { + // + // Respond phase error need perform reset recovery + // + DEBUG ((mUsbBotError, "UsbBotGetStatus: Device return a phase error\n")); + Status = UsbBotResetDevice (UsbBot, FALSE); + } else { + + *CmdStatus = Csw.CmdStatus; + break; + } + } + // + //The tag is increased even there is an error. + // + UsbBot->CbwTag++; + + return Status; +} + + +/** + Call the Usb mass storage class transport protocol to issue + the command/data/status circle to execute the commands + + @param Context The context of the BOT protocol, that is, + USB_BOT_PROTOCOL + @param Cmd The high level command + @param CmdLen The command length + @param DataDir The direction of the data transfer + @param Data The buffer to hold data + @param DataLen The length of the data + @param Timeout The time to wait command + @param CmdStatus The result of high level command execution + + @retval EFI_DEVICE_ERROR Failed to excute command + @retval EFI_SUCCESS The command is executed OK, and result in CmdStatus + +**/ +STATIC +EFI_STATUS +UsbBotExecCommand ( + IN VOID *Context, + IN VOID *Cmd, + IN UINT8 CmdLen, + IN EFI_USB_DATA_DIRECTION DataDir, + IN VOID *Data, + IN UINT32 DataLen, + IN UINT32 Timeout, + OUT UINT32 *CmdStatus + ) +{ + USB_BOT_PROTOCOL *UsbBot; + EFI_STATUS Status; + UINTN TransLen; + UINT8 Result; + + *CmdStatus = USB_MASS_CMD_FAIL; + UsbBot = (USB_BOT_PROTOCOL *) Context; + + // + // Send the command to the device. Return immediately if device + // rejects the command. + // + Status = UsbBotSendCommand (UsbBot, Cmd, CmdLen, DataDir, DataLen); + if (EFI_ERROR (Status)) { + DEBUG ((mUsbBotError, "UsbBotExecCommand: UsbBotSendCommand (%r)\n", Status)); + return Status; + } + + // + // Transfer the data. Don't return immediately even data transfer + // failed. The host should attempt to receive the CSW no matter + // whether it succeeds or failed. + // + TransLen = (UINTN) DataLen; + UsbBotDataTransfer (UsbBot, DataDir, Data, &TransLen, Timeout); + + // + // Get the status, if that succeeds, interpret the result + // + Status = UsbBotGetStatus (UsbBot, DataLen, &Result); + if (EFI_ERROR (Status)) { + DEBUG ((mUsbBotError, "UsbBotExecCommand: UsbBotGetStatus (%r)\n", Status)); + return Status; + } + + if (Result == 0) { + *CmdStatus = USB_MASS_CMD_SUCCESS; + } + + return EFI_SUCCESS; +} + + +/** + Reset the mass storage device by BOT protocol + + @param Context The context of the BOT protocol, that is, + USB_BOT_PROTOCOL + + @retval EFI_SUCCESS The device is reset + @retval Others Failed to reset the device. + +**/ +STATIC +EFI_STATUS +UsbBotResetDevice ( + IN VOID *Context, + IN BOOLEAN ExtendedVerification + ) +{ + USB_BOT_PROTOCOL *UsbBot; + EFI_USB_DEVICE_REQUEST Request; + EFI_STATUS Status; + UINT32 Result; + UINT32 Timeout; + + UsbBot = (USB_BOT_PROTOCOL *) Context; + + if (ExtendedVerification) { + // + // If we need to do strictly reset, reset its parent hub port + // + Status = UsbBot->UsbIo->UsbPortReset (UsbBot->UsbIo); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Issue a class specific "Bulk-Only Mass Storage Reset reqest. + // See the spec section 3.1 + // + Request.RequestType = 0x21; + Request.Request = USB_BOT_RESET_REQUEST; + Request.Value = 0; + Request.Index = UsbBot->Interface.InterfaceNumber; + Request.Length = 0; + Timeout = USB_BOT_RESET_TIMEOUT / USB_MASS_STALL_1_MS; + + Status = UsbBot->UsbIo->UsbControlTransfer ( + UsbBot->UsbIo, + &Request, + EfiUsbNoData, + Timeout, + NULL, + 0, + &Result + ); + + if (EFI_ERROR (Status)) { + DEBUG ((mUsbBotError, "UsbBotResetDevice: (%r)\n", Status)); + return Status; + } + + // + // The device shall NAK the host's request until the reset is + // complete. We can use this to sync the device and host. For + // now just stall 100ms to wait the device. + // + gBS->Stall (USB_BOT_RESET_STALL); + + // + // Clear the Bulk-In and Bulk-Out stall condition. + // + UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkInEndpoint->EndpointAddress); + UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkOutEndpoint->EndpointAddress); + return Status; +} + + +/** + Clean up the resource used by this BOT protocol + + @param Context The context of the BOT protocol, that is, + USB_BOT_PROTOCOL + + @retval EFI_SUCCESS The resource is cleaned up. + +**/ +STATIC +EFI_STATUS +UsbBotFini ( + IN VOID *Context + ) +{ + gBS->FreePool (Context); + return EFI_SUCCESS; +} + +USB_MASS_TRANSPORT +mUsbBotTransport = { + USB_MASS_STORE_BOT, + UsbBotInit, + UsbBotExecCommand, + UsbBotResetDevice, + UsbBotFini +}; diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.h b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.h new file mode 100644 index 0000000000..e11e9c5108 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.h @@ -0,0 +1,102 @@ +/** @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: + + UsbMassBot.h + +Abstract: + + Defination for the USB mass storage Bulk-Only Transport protocol. + This implementation is based on the "Universal Serial Bus Mass + Storage Class Bulk-Only Transport" Revision 1.0, September 31, 1999. + +Revision History + + +**/ + +#ifndef _EFI_USBMASS_BOT_H_ +#define _EFI_USBMASS_BOT_H_ + +enum { + // + // Usb Bulk-Only class specfic request + // + USB_BOT_RESET_REQUEST = 0xFF, // Bulk-Only Mass Storage Reset + USB_BOT_GETLUN_REQUEST = 0xFE, // Get Max Lun + USB_BOT_CBW_SIGNATURE = 0x43425355, // dCBWSignature, tag the packet as CBW + USB_BOT_CSW_SIGNATURE = 0x53425355, // dCSWSignature, tag the packet as CSW + USB_BOT_MAX_LUN = 0x0F, // Lun number is from 0 to 15 + USB_BOT_MAX_CMDLEN = 16, // Maxium number of command from command set + + // + // Usb BOT command block status values + // + USB_BOT_COMMAND_OK = 0x00, // Command passed, good status + USB_BOT_COMMAND_FAILED = 0x01, // Command failed + USB_BOT_COMMAND_ERROR = 0x02, // Phase error, need to reset the device + + // + // Usb Bot retry times + // + USB_BOT_GET_STATUS_RETRY = 3, + + // + // Usb Bot stall time + // + USB_BOT_RESET_STALL = 100 * USB_MASS_STALL_1_MS, + + // + // Usb Bot transfer timeout + // + USB_BOT_CBW_TIMEOUT = 1 * USB_MASS_STALL_1_S, + USB_BOT_CSW_TIMEOUT = 1 * USB_MASS_STALL_1_S, + USB_BOT_RESET_TIMEOUT = 3 * USB_MASS_STALL_1_S, +}; + +// +// The CBW (Command Block Wrapper) and CSW (Command Status Wrapper) +// structures used by the Usb BOT protocol. +// +#pragma pack(1) +typedef struct { + UINT32 Signature; + UINT32 Tag; + UINT32 DataLen; // Length of data between CBW and CSW + UINT8 Flag; // Bit 7, 0 ~ Data-Out, 1 ~ Data-In + UINT8 Lun; // Lun number. Bits 0~3 are used + UINT8 CmdLen; // Length of the command. Bits 0~4 are used + UINT8 CmdBlock[USB_BOT_MAX_CMDLEN]; +} USB_BOT_CBW; + +typedef struct { + UINT32 Signature; + UINT32 Tag; + UINT32 DataResidue; + UINT8 CmdStatus; +} USB_BOT_CSW; +#pragma pack() + +// +// Put Interface at the first field is to make it easy to get by Context, which +// could be BOT/CBI Protocol instance +// +typedef struct { + EFI_USB_INTERFACE_DESCRIPTOR Interface; + EFI_USB_ENDPOINT_DESCRIPTOR *BulkInEndpoint; + EFI_USB_ENDPOINT_DESCRIPTOR *BulkOutEndpoint; + UINT32 CbwTag; + EFI_USB_IO_PROTOCOL *UsbIo; +} USB_BOT_PROTOCOL; + +extern USB_MASS_TRANSPORT mUsbBotTransport; +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.c b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.c new file mode 100644 index 0000000000..1028e201c2 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.c @@ -0,0 +1,619 @@ +/** @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: + + UsbMassCbi.c + +Abstract: + + Implementation of the USB mass storage Control/Bulk/Interrupt transpor. + Notice: it is being obseleted by the standard body in favor of the BOT + (Bulk-Only Transport). + +Revision History + + +**/ + +#include "UsbMass.h" +#include "UsbMassCbi.h" + +UINTN mUsbCbiInfo = DEBUG_INFO; +UINTN mUsbCbiError = DEBUG_ERROR; + +STATIC +EFI_STATUS +UsbCbiResetDevice ( + IN VOID *Context, + IN BOOLEAN ExtendedVerification + ); + + +/** + Initialize the USB mass storage class CBI transport protocol. + If Context isn't NULL, it will save its context in it. + + @param UsbIo The USB IO to use + @param Controller The device controller + @param Context The variable to save context in + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory + @retval EFI_UNSUPPORTED The device isn't supported + @retval EFI_SUCCESS The CBI protocol is initialized. + +**/ +STATIC +EFI_STATUS +UsbCbiInit ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN EFI_HANDLE Controller, + OUT VOID **Context OPTIONAL + ) +{ + USB_CBI_PROTOCOL *UsbCbi; + EFI_USB_INTERFACE_DESCRIPTOR *Interface; + EFI_USB_ENDPOINT_DESCRIPTOR EndPoint; + EFI_STATUS Status; + UINT8 Index; + + // + // Allocate the CBI context + // + UsbCbi = AllocateZeroPool ( + sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR) + ); + + if (UsbCbi == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UsbCbi->UsbIo = UsbIo; + + // + // Get the interface descriptor and validate that it is a USB mass + // storage class CBI interface. + // + Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbCbi->Interface); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Interface = &UsbCbi->Interface; + if ((Interface->InterfaceProtocol != USB_MASS_STORE_CBI0) + && (Interface->InterfaceProtocol != USB_MASS_STORE_CBI1)) { + Status = EFI_UNSUPPORTED; + goto ON_ERROR; + } + + // + // Locate and save the bulk-in, bulk-out, and interrupt endpoint + // + for (Index = 0; Index < Interface->NumEndpoints; Index++) { + Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint); + if (EFI_ERROR (Status)) { + continue; + } + + if (USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) { + // + // Use the first Bulk-In and Bulk-Out endpoints + // + if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) && + (UsbCbi->BulkInEndpoint == NULL)) { + + UsbCbi->BulkInEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1); + *UsbCbi->BulkInEndpoint = EndPoint; + } + + if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) && + (UsbCbi->BulkOutEndpoint == NULL)) { + + UsbCbi->BulkOutEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1) + 1; + *UsbCbi->BulkOutEndpoint = EndPoint; + } + + } else if (USB_IS_INTERRUPT_ENDPOINT (EndPoint.Attributes)) { + // + // Use the first interrupt endpoint if it is CBI0 + // + if ((Interface->InterfaceProtocol == USB_MASS_STORE_CBI0) && + (UsbCbi->InterruptEndpoint == NULL)) { + + UsbCbi->InterruptEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1) + 2; + *UsbCbi->InterruptEndpoint = EndPoint; + } + } + } + + if ((UsbCbi->BulkInEndpoint == NULL) + || (UsbCbi->BulkOutEndpoint == NULL) + || ((Interface->InterfaceProtocol == USB_MASS_STORE_CBI0) + && (UsbCbi->InterruptEndpoint == NULL))) { + Status = EFI_UNSUPPORTED; + goto ON_ERROR; + } + + if (Context != NULL) { + *Context = UsbCbi; + } else { + gBS->FreePool (UsbCbi); + } + return EFI_SUCCESS; + +ON_ERROR: + gBS->FreePool (UsbCbi); + return Status; +} + + + +/** + Send the command to the device using class specific control transfer. + + @param UsbCbi The USB CBI protocol + @param Cmd The high level command to transfer to device + @param CmdLen The length of the command + @param Timeout The time to wait the command to finish + + @retval EFI_SUCCESS The command is transferred to device + @retval Others The command failed to transfer to device + +**/ +STATIC +EFI_STATUS +UsbCbiSendCommand ( + IN USB_CBI_PROTOCOL *UsbCbi, + IN UINT8 *Cmd, + IN UINT8 CmdLen, + IN UINT32 Timeout + ) +{ + EFI_USB_DEVICE_REQUEST Request; + EFI_STATUS Status; + UINT32 TransStatus; + UINTN DataLen; + INTN Retry; + + // + // Fill in the device request, CBI use the "Accept Device-Specific + // Cmd" (ADSC) class specific request to send commands + // + Request.RequestType = 0x21; + Request.Request = 0; + Request.Value = 0; + Request.Index = UsbCbi->Interface.InterfaceNumber; + Request.Length = CmdLen; + + Status = EFI_SUCCESS; + Timeout = Timeout / USB_MASS_STALL_1_MS; + + for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) { + // + // Use the UsbIo to send the command to the device + // + TransStatus = 0; + DataLen = CmdLen; + + Status = UsbCbi->UsbIo->UsbControlTransfer ( + UsbCbi->UsbIo, + &Request, + EfiUsbDataOut, + Timeout, + Cmd, + DataLen, + &TransStatus + ); + // + // The device can fail the command by STALL the control endpoint. + // It can delay the command by NAK the data or status stage, this + // is a "class-specific exemption to the USB specification". Retry + // if the command is NAKed. + // + if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) { + continue; + } + + break; + } + + return Status; +} + + +/** + Transfer data between the device and host. The CBI contains three phase, + command, data, and status. This is data phase. + + @param UsbCbi The USB CBI device + @param DataDir The direction of the data transfer + @param Data The buffer to hold the data + @param TransLen The expected transfer length + @param Timeout The time to wait the command to execute + + @retval EFI_SUCCESS The data transfer succeeded + @retval Others Failed to transfer all the data + +**/ +STATIC +EFI_STATUS +UsbCbiDataTransfer ( + IN USB_CBI_PROTOCOL *UsbCbi, + IN EFI_USB_DATA_DIRECTION DataDir, + IN OUT UINT8 *Data, + IN OUT UINTN *TransLen, + IN UINT32 Timeout + ) +{ + EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint; + EFI_STATUS Status; + UINT32 TransStatus; + UINTN Remain; + UINTN Increment; + UINT8 *Next; + UINTN Retry; + + // + // It's OK if no data to transfer + // + if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) { + return EFI_SUCCESS; + } + + // + // Select the endpoint then issue the transfer + // + if (DataDir == EfiUsbDataIn) { + Endpoint = UsbCbi->BulkInEndpoint; + } else { + Endpoint = UsbCbi->BulkOutEndpoint; + } + + Next = Data; + Remain = *TransLen; + Retry = 0; + Status = EFI_SUCCESS; + Timeout = Timeout / USB_MASS_STALL_1_MS; + + // + // Transfer the data, if the device returns NAK, retry it. + // + while (Remain > 0) { + TransStatus = 0; + + if (Remain > (UINTN) USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize) { + Increment = USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize; + } else { + Increment = Remain; + } + + Status = UsbCbi->UsbIo->UsbBulkTransfer ( + UsbCbi->UsbIo, + Endpoint->EndpointAddress, + Next, + &Increment, + Timeout, + &TransStatus + ); + if (EFI_ERROR (Status)) { + if (TransStatus == EFI_USB_ERR_NAK) { + // + // The device can NAK the host if either the data/buffer isn't + // aviable or the command is in-progress. The data can be partly + // transferred. The transfer is aborted if several succssive data + // transfer commands are NAKed. + // + if (Increment == 0) { + if (++Retry > USB_CBI_MAX_RETRY) { + goto ON_EXIT; + } + + } else { + Next += Increment; + Remain -= Increment; + Retry = 0; + } + + continue; + } + + // + // The device can fail the command by STALL the bulk endpoint. + // Clear the stall if that is the case. + // + if (TransStatus == EFI_USB_ERR_STALL) { + UsbClearEndpointStall (UsbCbi->UsbIo, Endpoint->EndpointAddress); + } + + goto ON_EXIT; + } + + Next += Increment; + Remain -= Increment; + } + +ON_EXIT: + *TransLen -= Remain; + return Status; +} + + +/** + Get the result of high level command execution from interrupt + endpoint. This function returns the USB transfer status, and + put the high level command execution result in Result. + + @param UsbCbi The USB CBI protocol + @param Timeout The time to wait the command to execute + @param Result GC_TODO: add argument description + + @retval EFI_SUCCESS The high level command execution result is + retrieved in Result. + @retval Others Failed to retrieve the result. + +**/ +STATIC +EFI_STATUS +UsbCbiGetStatus ( + IN USB_CBI_PROTOCOL *UsbCbi, + IN UINT32 Timeout, + OUT USB_CBI_STATUS *Result + ) +{ + UINTN Len; + UINT8 Endpoint; + EFI_STATUS Status; + UINT32 TransStatus; + INTN Retry; + + Endpoint = UsbCbi->InterruptEndpoint->EndpointAddress; + Status = EFI_SUCCESS; + Timeout = Timeout / USB_MASS_STALL_1_MS; + + // + // Attemp to the read the result from interrupt endpoint + // + for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) { + TransStatus = 0; + Len = sizeof (USB_CBI_STATUS); + + Status = UsbCbi->UsbIo->UsbSyncInterruptTransfer ( + UsbCbi->UsbIo, + Endpoint, + Result, + &Len, + Timeout, + &TransStatus + ); + // + // The CBI can NAK the interrupt endpoint if the command is in-progress. + // + if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) { + continue; + } + + break; + } + + return Status; +} + + +/** + Execute USB mass storage command through the CBI0/CBI1 transport protocol + + @param Context The USB CBI device + @param Cmd The command to transfer to device + @param CmdLen The length of the command + @param DataDir The direction of data transfer + @param Data The buffer to hold the data + @param DataLen The length of the buffer + @param Timeout The time to wait + @param CmdStatus The result of the command execution + + @retval EFI_SUCCESS The command is executed OK and result in CmdStatus. + @retval EFI_DEVICE_ERROR Failed to execute the command + +**/ +STATIC +EFI_STATUS +UsbCbiExecCommand ( + IN VOID *Context, + IN VOID *Cmd, + IN UINT8 CmdLen, + IN EFI_USB_DATA_DIRECTION DataDir, + IN VOID *Data, + IN UINT32 DataLen, + IN UINT32 Timeout, + OUT UINT32 *CmdStatus + ) +{ + USB_CBI_PROTOCOL *UsbCbi; + USB_CBI_STATUS Result; + EFI_STATUS Status; + UINTN TransLen; + + *CmdStatus = USB_MASS_CMD_SUCCESS; + UsbCbi = (USB_CBI_PROTOCOL *) Context; + + // + // Send the command to the device. Return immediately if device + // rejects the command. + // + Status = UsbCbiSendCommand (UsbCbi, Cmd, CmdLen, Timeout); + if (EFI_ERROR (Status)) { + DEBUG ((mUsbCbiError, "UsbCbiExecCommand: UsbCbiSendCommand (%r)\n",Status)); + return Status; + } + + // + // Transfer the data, return this status if no interrupt endpoint + // is used to report the transfer status. + // + TransLen = (UINTN) DataLen; + + Status = UsbCbiDataTransfer (UsbCbi, DataDir, Data, &TransLen, Timeout); + if (UsbCbi->InterruptEndpoint == NULL) { + DEBUG ((mUsbCbiError, "UsbCbiExecCommand: UsbCbiDataTransfer (%r)\n",Status)); + return Status; + } + + // + // Get the status, if that succeeds, interpret the result + // + Status = UsbCbiGetStatus (UsbCbi, Timeout, &Result); + if (EFI_ERROR (Status)) { + DEBUG ((mUsbCbiError, "UsbCbiExecCommand: UsbCbiGetStatus (%r)\n",Status)); + return EFI_DEVICE_ERROR; + } + + if (UsbCbi->Interface.InterfaceSubClass == USB_MASS_STORE_UFI) { + // + // For UFI device, ASC and ASCQ are returned. + // + if (Result.Type != 0) { + *CmdStatus = USB_MASS_CMD_FAIL; + } + + } else { + // + // Check page 27, CBI spec 1.1 for vaious reture status. + // + switch (Result.Value & 0x03) { + case 0x00: + // + // Pass + // + *CmdStatus = USB_MASS_CMD_SUCCESS; + break; + + case 0x02: + // + // Phase Error, response with reset. Fall through to Fail. + // + UsbCbiResetDevice (UsbCbi, FALSE); + + case 0x01: + // + // Fail + // + *CmdStatus = USB_MASS_CMD_FAIL; + break; + + case 0x03: + // + // Persistent Fail, need to send REQUEST SENSE. + // + *CmdStatus = USB_MASS_CMD_PERSISTENT; + break; + } + } + + return EFI_SUCCESS; +} + + +/** + Call the Usb mass storage class transport protocol to + reset the device. The reset is defined as a Non-Data + command. Don't use UsbCbiExecCommand to send the command + to device because that may introduce recursive loop. + + @param Context The USB CBI device protocol + + @retval EFI_SUCCESS the device is reset + @retval Others Failed to reset the device + +**/ +STATIC +EFI_STATUS +UsbCbiResetDevice ( + IN VOID *Context, + IN BOOLEAN ExtendedVerification + ) +{ + UINT8 ResetCmd[USB_CBI_RESET_CMD_LEN]; + USB_CBI_PROTOCOL *UsbCbi; + USB_CBI_STATUS Result; + EFI_STATUS Status; + UINT32 Timeout; + + UsbCbi = (USB_CBI_PROTOCOL *) Context; + + // + // Fill in the reset command. + // + SetMem (ResetCmd, USB_CBI_RESET_CMD_LEN, 0xFF); + + ResetCmd[0] = 0x1D; + ResetCmd[1] = 0x04; + Timeout = USB_CBI_RESET_TIMEOUT / USB_MASS_STALL_1_MS; + + // + // Send the command to the device. Don't use UsbCbiExecCommand here. + // + Status = UsbCbiSendCommand (UsbCbi, ResetCmd, USB_CBI_RESET_CMD_LEN, Timeout); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Just retrieve the status and ignore that. Then stall + // 50ms to wait it complete + // + UsbCbiGetStatus (UsbCbi, Timeout, &Result); + gBS->Stall (50 * 1000); + + // + // Clear the Bulk-In and Bulk-Out stall condition and + // init data toggle. + // + UsbClearEndpointStall (UsbCbi->UsbIo, UsbCbi->BulkInEndpoint->EndpointAddress); + UsbClearEndpointStall (UsbCbi->UsbIo, UsbCbi->BulkOutEndpoint->EndpointAddress); + return Status; +} + + +/** + Clean up the CBI protocol's resource + + @param Context The CBI protocol + + @retval EFI_SUCCESS The resource is cleaned up. + +**/ +STATIC +EFI_STATUS +UsbCbiFini ( + IN VOID *Context + ) +{ + gBS->FreePool (Context); + return EFI_SUCCESS; +} + +USB_MASS_TRANSPORT +mUsbCbi0Transport = { + USB_MASS_STORE_CBI0, + UsbCbiInit, + UsbCbiExecCommand, + UsbCbiResetDevice, + UsbCbiFini +}; + +USB_MASS_TRANSPORT +mUsbCbi1Transport = { + USB_MASS_STORE_CBI1, + UsbCbiInit, + UsbCbiExecCommand, + UsbCbiResetDevice, + UsbCbiFini +}; diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.h b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.h new file mode 100644 index 0000000000..d4a9f700ad --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.h @@ -0,0 +1,64 @@ +/** @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: + + UsbMassCbi.h + +Abstract: + + Defination for the USB mass storage Control/Bulk/Interrupt transpor. + +Revision History + + +**/ + +#ifndef _EFI_USBMASS_CBI_H_ +#define _EFI_USBMASS_CBI_H_ + +enum { + USB_CBI_MAX_PACKET_NUM = 16, + USB_CBI_RESET_CMD_LEN = 12, + + // + // Usb CBI retry times + // + USB_CBI_MAX_RETRY = 3, + + // + // Usb Cbi transfer timeout + // + USB_CBI_RESET_TIMEOUT = 1 * USB_MASS_STALL_1_S, +}; + +// +// Put Interface at the first field is to make it easy to get by Context, which +// could be BOT/CBI Protocol instance +// +typedef struct { + EFI_USB_INTERFACE_DESCRIPTOR Interface; + EFI_USB_ENDPOINT_DESCRIPTOR *BulkInEndpoint; + EFI_USB_ENDPOINT_DESCRIPTOR *BulkOutEndpoint; + EFI_USB_ENDPOINT_DESCRIPTOR *InterruptEndpoint; + EFI_USB_IO_PROTOCOL *UsbIo; +} USB_CBI_PROTOCOL; + +#pragma pack(1) +typedef struct { + UINT8 Type; + UINT8 Value; +} USB_CBI_STATUS; +#pragma pack() + +extern USB_MASS_TRANSPORT mUsbCbi0Transport; +extern USB_MASS_TRANSPORT mUsbCbi1Transport; +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.c b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.c new file mode 100644 index 0000000000..a7c3e82f98 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.c @@ -0,0 +1,640 @@ +/** @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: + + UsbMassImpl.c + +Abstract: + + The implementation of USB mass storage class device driver. + The command set supported is "USB Mass Storage Specification + for Bootability". + +Revision History + + +**/ + +#include "UsbMassImpl.h" + +// +// The underlying transport protocol. CBI support isn't included +// in the current build. It is being obseleted by the standard +// body. If you want to enable it, remove the if directive here, +// then add the UsbMassCbi.c/.h to the driver's inf file. +// +STATIC +USB_MASS_TRANSPORT *mUsbMassTransport[] = { + &mUsbCbi0Transport, + &mUsbCbi1Transport, + &mUsbBotTransport, + NULL +}; + +UINTN mUsbMscInfo = DEBUG_INFO; +UINTN mUsbMscError = DEBUG_ERROR; + + +/** + Retrieve the media parameters such as disk gemotric for the + device's BLOCK IO protocol. + + @param UsbMass The USB mass storage device + + @retval EFI_SUCCESS The media parameters is updated successfully. + @retval Others Failed to get the media parameters. + +**/ +EFI_STATUS +UsbMassInitMedia ( + IN USB_MASS_DEVICE *UsbMass + ) +{ + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + UINTN Index; + + Media = &UsbMass->BlockIoMedia; + + // + // Initialize the MediaPrsent/ReadOnly and others to the default. + // We are not forced to get it right at this time, check UEFI2.0 + // spec for more information: + // + // MediaPresent: This field shows the media present status as + // of the most recent ReadBlocks or WriteBlocks call. + // + // ReadOnly : This field shows the read-only status as of the + // recent WriteBlocks call. + // + // but remember to update MediaId/MediaPresent/ReadOnly status + // after ReadBlocks and WriteBlocks + // + Media->MediaPresent = FALSE; + Media->LogicalPartition = FALSE; + Media->ReadOnly = FALSE; + Media->WriteCaching = FALSE; + Media->IoAlign = 0; + + // + // Some device may spend several seconds before it is ready. + // Try several times before giving up. Wait 5s at most. + // + Status = EFI_SUCCESS; + + for (Index = 0; Index < USB_BOOT_WAIT_RETRY; Index++) { + + Status = UsbBootGetParams (UsbMass); + if ((Status != EFI_MEDIA_CHANGED) + && (Status != EFI_NOT_READY) + && (Status != EFI_TIMEOUT)) { + break; + } + + Status = UsbBootIsUnitReady (UsbMass); + if (EFI_ERROR (Status)) { + gBS->Stall (USB_BOOT_UNIT_READY_STALL * (Index + 1)); + } + + } + + return Status; +} + + +/** + Reset the block device. ExtendedVerification is ignored for this. + + @param This The BLOCK IO protocol + @param ExtendedVerification Whether to execute extended verfication. + + @retval EFI_SUCCESS The device is successfully resetted. + @retval Others Failed to reset the device. + +**/ +EFI_STATUS +UsbMassReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + USB_MASS_DEVICE *UsbMass; + + UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This); + return UsbMass->Transport->Reset (UsbMass->Context, ExtendedVerification); +} + + +/** + Read some blocks of data from the block device. + + @param This The Block IO protocol + @param MediaId The media's ID of the device for current request + @param Lba The start block number + @param BufferSize The size of buffer to read data in + @param Buffer The buffer to read data to + + @retval EFI_SUCCESS The data is successfully read + @retval EFI_NO_MEDIA Media isn't present + @retval EFI_MEDIA_CHANGED The device media has been changed, that is, + MediaId changed + @retval EFI_INVALID_PARAMETER Some parameters are invalid, such as Buffer is + NULL. + @retval EFI_BAD_BUFFER_SIZE The buffer size isn't a multiple of media's block + size, or overflow the last block number. + +**/ +EFI_STATUS +UsbMassReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + USB_MASS_DEVICE *UsbMass; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + UINTN TotalBlock; + + UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This); + Media = &UsbMass->BlockIoMedia; + + // + // First, validate the parameters + // + if ((Buffer == NULL) || (BufferSize == 0)) { + return EFI_INVALID_PARAMETER; + } + + // + // If it is a remoable media, such as CD-Rom or Usb-Floppy, + // if, need to detect the media before each rw, while Usb-Flash + // needn't. However, it's hard to identify Usb-Floppy between + // Usb-Flash by now, so detect media every time. + // + Status = UsbBootDetectMedia (UsbMass); + if (EFI_ERROR (Status)) { + DEBUG ((mUsbMscError, "UsbMassReadBlocks: UsbBootDetectMedia (%r)\n", Status)); + return Status; + } + + // + // Make sure BlockSize and LBA is consistent with BufferSize + // + if ((BufferSize % Media->BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + TotalBlock = BufferSize / Media->BlockSize; + + if (Lba + TotalBlock - 1 > Media->LastBlock) { + return EFI_BAD_BUFFER_SIZE; + } + + Status = UsbBootReadBlocks (UsbMass, (UINT32) Lba, TotalBlock, Buffer); + if (EFI_ERROR (Status)) { + DEBUG ((mUsbMscError, "UsbMassReadBlocks: UsbBootReadBlocks (%r) -> Reset\n", Status)); + UsbMassReset (This, TRUE); + } + + return Status; +} + + +/** + Write some blocks of data to the block device. + + @param This The Block IO protocol + @param MediaId The media's ID of the device for current request + @param Lba The start block number + @param BufferSize The size of buffer to write data to + @param Buffer The buffer to write data to + + @retval EFI_SUCCESS The data is successfully written + @retval EFI_NO_MEDIA Media isn't present + @retval EFI_MEDIA_CHANGED The device media has been changed, that is, + MediaId changed + @retval EFI_INVALID_PARAMETER Some parameters are invalid, such as Buffer is + NULL. + @retval EFI_BAD_BUFFER_SIZE The buffer size isn't a multiple of media's block + size, + +**/ +EFI_STATUS +UsbMassWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + USB_MASS_DEVICE *UsbMass; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + UINTN TotalBlock; + + UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This); + Media = &UsbMass->BlockIoMedia; + + // + // First, validate the parameters + // + if ((Buffer == NULL) || (BufferSize == 0)) { + return EFI_INVALID_PARAMETER; + } + + // + // If it is a remoable media, such as CD-Rom or Usb-Floppy, + // if, need to detect the media before each rw, while Usb-Flash + // needn't. However, it's hard to identify Usb-Floppy between + // Usb-Flash by now, so detect media every time. + // + Status = UsbBootDetectMedia (UsbMass); + if (EFI_ERROR (Status)) { + DEBUG ((mUsbMscError, "UsbMassWriteBlocks: UsbBootDetectMedia (%r)\n", Status)); + return Status; + } + + // + // Make sure BlockSize and LBA is consistent with BufferSize + // + if ((BufferSize % Media->BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + TotalBlock = BufferSize / Media->BlockSize; + + if (Lba + TotalBlock - 1 > Media->LastBlock) { + return EFI_BAD_BUFFER_SIZE; + } + + // + // Try to write the data even the device is marked as ReadOnly, + // and clear the status should the write succeed. + // + Status = UsbBootWriteBlocks (UsbMass, (UINT32) Lba, TotalBlock, Buffer); + if (EFI_ERROR (Status)) { + DEBUG ((mUsbMscError, "UsbMassWriteBlocks: UsbBootWriteBlocks (%r) -> Reset\n", Status)); + UsbMassReset (This, TRUE); + } + + return Status; +} + + +/** + Flush the cached writes to disks. USB mass storage device doesn't + support write cache, so return EFI_SUCCESS directly. + + @param This The BLOCK IO protocol + + @retval EFI_SUCCESS Always returns success + +**/ +EFI_STATUS +UsbMassFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ) +{ + return EFI_SUCCESS; +} + + +/** + Check whether the controller is a supported USB mass storage. + + @param This The USB mass driver's driver binding. + @param Controller The device to test against. + @param RemainingDevicePath The remaining device path + + @retval EFI_SUCCESS This device is a supported USB mass storage. + @retval EFI_UNSUPPORTED The device isn't supported + @retval Others Some error happened. + +**/ +EFI_STATUS +EFIAPI +USBMassDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_USB_IO_PROTOCOL *UsbIo; + EFI_USB_INTERFACE_DESCRIPTOR Interface; + USB_MASS_TRANSPORT *Transport; + EFI_STATUS Status; + INTN Index; + + // + // Check whether the controlelr support USB_IO + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + &UsbIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the interface to check the USB class and find a transport + // protocol handler. + // + Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = EFI_UNSUPPORTED; + + if (Interface.InterfaceClass != USB_MASS_STORE_CLASS) { + goto ON_EXIT; + } + + for (Index = 0; mUsbMassTransport[Index] != NULL; Index++) { + Transport = mUsbMassTransport[Index]; + if (Interface.InterfaceProtocol == Transport->Protocol) { + Status = Transport->Init (UsbIo, Controller, NULL); + break; + } + } + + DEBUG ((mUsbMscInfo, "Found a USB mass store device %r\n", Status)); + +ON_EXIT: + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + Start the USB mass storage device on the controller. It will + install a BLOCK_IO protocol on the device if everything is OK. + + @param This The USB mass storage driver binding. + @param Controller The USB mass storage device to start on + @param RemainingDevicePath The remaining device path. + + @retval EFI_SUCCESS The driver has started on the device. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory + @retval Others Failed to start the driver on the device. + +**/ +EFI_STATUS +EFIAPI +USBMassDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_USB_IO_PROTOCOL *UsbIo; + EFI_USB_INTERFACE_DESCRIPTOR Interface; + USB_MASS_DEVICE *UsbMass; + USB_MASS_TRANSPORT *Transport; + EFI_STATUS Status; + UINTN Index; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + &UsbIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + UsbMass = AllocateZeroPool (sizeof (USB_MASS_DEVICE)); + if (UsbMass == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Initialize the transport protocols + // + Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface); + if (EFI_ERROR (Status)) { + DEBUG ((mUsbMscError, "USBMassDriverBindingStart: UsbIo->UsbGetInterfaceDescriptor (%r)\n", Status)); + goto ON_ERROR; + } + + Status = EFI_UNSUPPORTED; + + for (Index = 0; mUsbMassTransport[Index] != NULL; Index++) { + Transport = mUsbMassTransport[Index]; + + if (Interface.InterfaceProtocol == Transport->Protocol) { + UsbMass->Transport = Transport; + Status = Transport->Init (UsbIo, Controller, &UsbMass->Context); + break; + } + } + + if (EFI_ERROR (Status)) { + DEBUG ((mUsbMscError, "USBMassDriverBindingStart: Transport->Init (%r)\n", Status)); + goto ON_ERROR; + } + + UsbMass->Signature = USB_MASS_SIGNATURE; + UsbMass->Controller = Controller; + UsbMass->UsbIo = UsbIo; + UsbMass->BlockIo.Media = &UsbMass->BlockIoMedia; + UsbMass->BlockIo.Reset = UsbMassReset; + UsbMass->BlockIo.ReadBlocks = UsbMassReadBlocks; + UsbMass->BlockIo.WriteBlocks = UsbMassWriteBlocks; + UsbMass->BlockIo.FlushBlocks = UsbMassFlushBlocks; + UsbMass->OpticalStorage = FALSE; + + // + // Get the storage's parameters, such as last block number. + // then install the BLOCK_IO + // + Status = UsbMassInitMedia (UsbMass); + if (!EFI_ERROR (Status)) { + if ((UsbMass->Pdt != USB_PDT_DIRECT_ACCESS) && + (UsbMass->Pdt != USB_PDT_CDROM) && + (UsbMass->Pdt != USB_PDT_OPTICAL) && + (UsbMass->Pdt != USB_PDT_SIMPLE_DIRECT)) { + DEBUG ((mUsbMscError, "USBMassDriverBindingStart: Found an unsupported peripheral type[%d]\n", UsbMass->Pdt)); + goto ON_ERROR; + } + } else if (Status != EFI_NO_MEDIA){ + DEBUG ((mUsbMscError, "USBMassDriverBindingStart: UsbMassInitMedia (%r)\n", Status)); + goto ON_ERROR; + } + + Status = gBS->InstallProtocolInterface ( + &Controller, + &gEfiBlockIoProtocolGuid, + EFI_NATIVE_INTERFACE, + &UsbMass->BlockIo + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + gBS->FreePool (UsbMass); + + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + Stop controlling the device. + + @param This The USB mass storage driver binding + @param Controller The device controller controlled by the driver. + @param NumberOfChildren The number of children of this device + @param ChildHandleBuffer The buffer of children handle. + + @retval EFI_SUCCESS The driver stopped from controlling the device. + @retval Others Failed to stop the driver + +**/ +EFI_STATUS +EFIAPI +USBMassDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + USB_MASS_DEVICE *UsbMass; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + + // + // First, get our context back from the BLOCK_IO + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiBlockIoProtocolGuid, + &BlockIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (BlockIo); + + // + // Uninstall Block I/O protocol from the device handle, + // then call the transport protocol to stop itself. + // + Status = gBS->UninstallProtocolInterface ( + Controller, + &gEfiBlockIoProtocolGuid, + &UsbMass->BlockIo + ); + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + UsbMass->Transport->Fini (UsbMass->Context); + gBS->FreePool (UsbMass); + + return EFI_SUCCESS; +} + +EFI_DRIVER_BINDING_PROTOCOL gUSBMassDriverBinding = { + USBMassDriverBindingSupported, + USBMassDriverBindingStart, + USBMassDriverBindingStop, + 0x11, + NULL, + NULL +}; + +//@MT: EFI_DRIVER_ENTRY_POINT (USBMassStorageEntryPoint) + +EFI_STATUS +EFIAPI +USBMassStorageEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +/*++ + +Routine Description: + + The entry point for the driver, which will install the driver binding and + component name protocol + +Arguments: + + ImageHandle - The image handle of this driver + SystemTable - The system table + +Returns: + + EFI_SUCCESS - the protocols are installed OK + Others - Failed to install protocols. + +--*/ +{ + EFI_STATUS Status; + + // + // Install driver binding protocol + // + Status = EfiLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &gUSBMassDriverBinding, + ImageHandle, + &gUsbMassStorageComponentName, + NULL, + NULL + ); + + return Status; +} diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.h b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.h new file mode 100644 index 0000000000..39bc87ca9e --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.h @@ -0,0 +1,57 @@ +/** @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: + + UsbMassImpl.h + +Abstract: + + The implementation of USB mass storage class device driver. + +Revision History + + +**/ + +#ifndef _EFI_USBMASS_IMPL_H_ +#define _EFI_USBMASS_IMPL_H_ + +typedef struct _USB_MASS_DEVICE USB_MASS_DEVICE; + +#include "UsbMass.h" +#include "UsbMassBot.h" +#include "UsbMassCbi.h" +#include "UsbMassBoot.h" + +enum { + USB_MASS_SIGNATURE= EFI_SIGNATURE_32 ('U', 's', 'b', 'K'), +}; + +typedef struct _USB_MASS_DEVICE { + UINT32 Signature; + EFI_HANDLE Controller; + EFI_USB_IO_PROTOCOL *UsbIo; + EFI_BLOCK_IO_PROTOCOL BlockIo; + EFI_BLOCK_IO_MEDIA BlockIoMedia; + BOOLEAN OpticalStorage; + UINT8 Lun; // Logical Unit Number + UINT8 Pdt; // Peripheral Device Type + USB_MASS_TRANSPORT *Transport; // USB mass storage transport protocol + VOID *Context; // Opaque storage for mass transport +}; + +#define USB_MASS_DEVICE_FROM_BLOCKIO(a) \ + CR (a, USB_MASS_DEVICE, BlockIo, USB_MASS_SIGNATURE) + +extern EFI_COMPONENT_NAME_PROTOCOL gUsbMassStorageComponentName; + +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf new file mode 100644 index 0000000000..7636db7028 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf @@ -0,0 +1,95 @@ +#/** @file +# Component name for module UsbMassStorage +# +# 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 = UsbMassStorageDxe + FILE_GUID = 9FB4B4A7-42C0-4bcd-8540-9BCC6711F83E + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = USBMassStorageEntryPoint + +# +# 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] + UsbMassBoot.h + UsbMassImpl.h + UsbMassBot.h + UsbMassBot.c + ComponentName.c + UsbMassImpl.c + UsbMassBoot.c + UsbMassCbi.h + UsbMass.h + UsbMassCbi.c + +################################################################################ +# +# 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 + 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 + gEfiBlockIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.msa b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.msa new file mode 100644 index 0000000000..ca7f3db911 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.msa @@ -0,0 +1,75 @@ + + + UsbMassStorageDxe + DXE_DRIVER + 9FB4B4A7-42C0-4bcd-8540-9BCC6711F83E + 1.0 + Component name for module UsbMassStorage + 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 + UsbMassStorageDxe + + + + DebugLib + + + BaseMemoryLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + UefiLib + + + MemoryAllocationLib + + + + UsbMassCbi.c + UsbMass.h + UsbMassCbi.h + UsbMassBoot.c + UsbMassImpl.c + ComponentName.c + UsbMassBot.c + UsbMassBot.h + UsbMassImpl.h + UsbMassBoot.h + + + + + + + + gEfiBlockIoProtocolGuid + + + gEfiUsbIoProtocolGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + USBMassStorageEntryPoint + + + \ No newline at end of file