/** @file | |
Manage Usb Descriptor List | |
Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "UsbBus.h" | |
/** | |
Free the interface setting descriptor. | |
@param Setting The descriptor to free. | |
**/ | |
VOID | |
UsbFreeInterfaceDesc ( | |
IN USB_INTERFACE_SETTING *Setting | |
) | |
{ | |
USB_ENDPOINT_DESC *Ep; | |
UINTN Index; | |
if (Setting->Endpoints != NULL) { | |
// | |
// Each interface setting may have several endpoints, free them first. | |
// | |
for (Index = 0; Index < Setting->Desc.NumEndpoints; Index++) { | |
Ep = Setting->Endpoints[Index]; | |
if (Ep != NULL) { | |
FreePool (Ep); | |
} | |
} | |
// | |
// Only call FreePool() if NumEndpoints > 0. | |
// | |
if (Setting->Desc.NumEndpoints > 0) { | |
FreePool (Setting->Endpoints); | |
} | |
} | |
FreePool (Setting); | |
} | |
/** | |
Free a configuration descriptor with its interface | |
descriptors. It may be initialized partially. | |
@param Config The configuration descriptor to free. | |
**/ | |
VOID | |
UsbFreeConfigDesc ( | |
IN USB_CONFIG_DESC *Config | |
) | |
{ | |
USB_INTERFACE_DESC *Interface; | |
UINTN Index; | |
UINTN SetIndex; | |
if (Config->Interfaces != NULL) { | |
// | |
// A configuration may have several interfaces, free the interface | |
// | |
for (Index = 0; Index < Config->Desc.NumInterfaces; Index++) { | |
Interface = Config->Interfaces[Index]; | |
if (Interface == NULL) { | |
continue; | |
} | |
// | |
// Each interface may have several settings, free the settings | |
// | |
for (SetIndex = 0; SetIndex < Interface->NumOfSetting; SetIndex++) { | |
if (Interface->Settings[SetIndex] != NULL) { | |
UsbFreeInterfaceDesc (Interface->Settings[SetIndex]); | |
} | |
} | |
FreePool (Interface); | |
} | |
FreePool (Config->Interfaces); | |
} | |
FreePool (Config); | |
} | |
/** | |
Free a device descriptor with its configurations. | |
@param DevDesc The device descriptor. | |
**/ | |
VOID | |
UsbFreeDevDesc ( | |
IN USB_DEVICE_DESC *DevDesc | |
) | |
{ | |
UINTN Index; | |
if (DevDesc->Configs != NULL) { | |
for (Index = 0; Index < DevDesc->Desc.NumConfigurations; Index++) { | |
if (DevDesc->Configs[Index] != NULL) { | |
UsbFreeConfigDesc (DevDesc->Configs[Index]); | |
} | |
} | |
FreePool (DevDesc->Configs); | |
} | |
FreePool (DevDesc); | |
} | |
/** | |
Create a descriptor. | |
@param DescBuf The buffer of raw descriptor. | |
@param Len The length of the raw descriptor buffer. | |
@param Type The type of descriptor to create. | |
@param Consumed Number of bytes consumed. | |
@return Created descriptor or NULL. | |
**/ | |
VOID * | |
UsbCreateDesc ( | |
IN UINT8 *DescBuf, | |
IN UINTN Len, | |
IN UINT8 Type, | |
OUT UINTN *Consumed | |
) | |
{ | |
USB_DESC_HEAD *Head; | |
UINTN DescLen; | |
UINTN CtrlLen; | |
UINTN Offset; | |
VOID *Desc; | |
DescLen = 0; | |
CtrlLen = 0; | |
*Consumed = 0; | |
switch (Type) { | |
case USB_DESC_TYPE_DEVICE: | |
DescLen = sizeof (EFI_USB_DEVICE_DESCRIPTOR); | |
CtrlLen = sizeof (USB_DEVICE_DESC); | |
break; | |
case USB_DESC_TYPE_CONFIG: | |
DescLen = sizeof (EFI_USB_CONFIG_DESCRIPTOR); | |
CtrlLen = sizeof (USB_CONFIG_DESC); | |
break; | |
case USB_DESC_TYPE_INTERFACE: | |
DescLen = sizeof (EFI_USB_INTERFACE_DESCRIPTOR); | |
CtrlLen = sizeof (USB_INTERFACE_SETTING); | |
break; | |
case USB_DESC_TYPE_ENDPOINT: | |
DescLen = sizeof (EFI_USB_ENDPOINT_DESCRIPTOR); | |
CtrlLen = sizeof (USB_ENDPOINT_DESC); | |
break; | |
default: | |
ASSERT (FALSE); | |
return NULL; | |
} | |
// | |
// Total length is too small that cannot hold the single descriptor header plus data. | |
// | |
if (Len <= sizeof (USB_DESC_HEAD)) { | |
DEBUG ((DEBUG_ERROR, "UsbCreateDesc: met mal-format descriptor, total length = %d!\n", Len)); | |
return NULL; | |
} | |
// | |
// 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 - sizeof (USB_DESC_HEAD)) { | |
// | |
// Above condition make sure Head->Len and Head->Type are safe to access | |
// | |
Head = (USB_DESC_HEAD *)&DescBuf[Offset]; | |
if (Head->Len == 0) { | |
DEBUG ((DEBUG_ERROR, "UsbCreateDesc: met mal-format descriptor, Head->Len = 0!\n")); | |
return NULL; | |
} | |
// | |
// Make sure no overflow when adding Head->Len to Offset. | |
// | |
if (Head->Len > MAX_UINTN - Offset) { | |
DEBUG ((DEBUG_ERROR, "UsbCreateDesc: met mal-format descriptor, Head->Len = %d!\n", Head->Len)); | |
return NULL; | |
} | |
Offset += Head->Len; | |
if (Head->Type == Type) { | |
break; | |
} | |
} | |
// | |
// Head->Len is invalid resulting data beyond boundary, or | |
// Descriptor cannot be found: No such type. | |
// | |
if (Len < Offset) { | |
DEBUG ((DEBUG_ERROR, "UsbCreateDesc: met mal-format descriptor, Offset/Len = %d/%d!\n", Offset, Len)); | |
return NULL; | |
} | |
if ((Head->Type != Type) || (Head->Len < DescLen)) { | |
DEBUG ((DEBUG_ERROR, "UsbCreateDesc: descriptor cannot be found, Header(T/L) = %d/%d!\n", Head->Type, Head->Len)); | |
return NULL; | |
} | |
Desc = AllocateZeroPool ((UINTN)CtrlLen); | |
if (Desc == NULL) { | |
return NULL; | |
} | |
CopyMem (Desc, Head, (UINTN)DescLen); | |
*Consumed = Offset; | |
return Desc; | |
} | |
/** | |
Parse an interface descriptor and its endpoints. | |
@param DescBuf The buffer of raw descriptor. | |
@param Len The length of the raw descriptor buffer. | |
@param Consumed The number of raw descriptor consumed. | |
@return The create interface setting or NULL if failed. | |
**/ | |
USB_INTERFACE_SETTING * | |
UsbParseInterfaceDesc ( | |
IN UINT8 *DescBuf, | |
IN UINTN Len, | |
OUT UINTN *Consumed | |
) | |
{ | |
USB_INTERFACE_SETTING *Setting; | |
USB_ENDPOINT_DESC *Ep; | |
UINTN Index; | |
UINTN NumEp; | |
UINTN Used; | |
UINTN Offset; | |
*Consumed = 0; | |
Setting = UsbCreateDesc (DescBuf, Len, USB_DESC_TYPE_INTERFACE, &Used); | |
if (Setting == NULL) { | |
DEBUG ((DEBUG_ERROR, "UsbParseInterfaceDesc: failed to create interface descriptor\n")); | |
return NULL; | |
} | |
Offset = Used; | |
// | |
// Create an array to hold the interface's endpoints | |
// | |
NumEp = Setting->Desc.NumEndpoints; | |
DEBUG (( | |
DEBUG_INFO, | |
"UsbParseInterfaceDesc: interface %d(setting %d) has %d endpoints\n", | |
Setting->Desc.InterfaceNumber, | |
Setting->Desc.AlternateSetting, | |
(UINT32)NumEp | |
)); | |
if (NumEp == 0) { | |
goto ON_EXIT; | |
} | |
Setting->Endpoints = AllocateZeroPool (sizeof (USB_ENDPOINT_DESC *) * NumEp); | |
if (Setting->Endpoints == NULL) { | |
goto ON_ERROR; | |
} | |
// | |
// Create the endpoints for this interface | |
// | |
for (Index = 0; (Index < NumEp) && (Offset < Len); Index++) { | |
Ep = UsbCreateDesc (DescBuf + Offset, Len - Offset, USB_DESC_TYPE_ENDPOINT, &Used); | |
if (Ep == NULL) { | |
DEBUG ((DEBUG_ERROR, "UsbParseInterfaceDesc: failed to create endpoint(index %d)\n", (UINT32)Index)); | |
goto ON_ERROR; | |
} | |
Setting->Endpoints[Index] = Ep; | |
Offset += Used; | |
} | |
ON_EXIT: | |
*Consumed = Offset; | |
return Setting; | |
ON_ERROR: | |
UsbFreeInterfaceDesc (Setting); | |
return NULL; | |
} | |
/** | |
Parse the configuration descriptor and its interfaces. | |
@param DescBuf The buffer of raw descriptor. | |
@param Len The length of the raw descriptor buffer. | |
@return The created configuration descriptor. | |
**/ | |
USB_CONFIG_DESC * | |
UsbParseConfigDesc ( | |
IN UINT8 *DescBuf, | |
IN UINTN Len | |
) | |
{ | |
USB_CONFIG_DESC *Config; | |
USB_INTERFACE_SETTING *Setting; | |
USB_INTERFACE_DESC *Interface; | |
UINTN Index; | |
UINTN NumIf; | |
UINTN Consumed; | |
ASSERT (DescBuf != NULL); | |
Config = UsbCreateDesc (DescBuf, Len, USB_DESC_TYPE_CONFIG, &Consumed); | |
if (Config == NULL) { | |
return NULL; | |
} | |
// | |
// Initialize an array of setting for the configuration's interfaces. | |
// | |
NumIf = Config->Desc.NumInterfaces; | |
Config->Interfaces = AllocateZeroPool (sizeof (USB_INTERFACE_DESC *) * NumIf); | |
if (Config->Interfaces == NULL) { | |
goto ON_ERROR; | |
} | |
DEBUG (( | |
DEBUG_INFO, | |
"UsbParseConfigDesc: config %d has %d interfaces\n", | |
Config->Desc.ConfigurationValue, | |
(UINT32)NumIf | |
)); | |
for (Index = 0; Index < NumIf; Index++) { | |
Interface = AllocateZeroPool (sizeof (USB_INTERFACE_DESC)); | |
if (Interface == NULL) { | |
goto ON_ERROR; | |
} | |
Config->Interfaces[Index] = Interface; | |
} | |
// | |
// If a configuration has several interfaces, these interfaces are | |
// numbered from zero to n. If a interface has several settings, | |
// these settings are also number from zero to m. The interface | |
// setting must be organized as |interface 0, setting 0|interface 0 | |
// setting 1|interface 1, setting 0|interface 2, setting 0|. Check | |
// USB2.0 spec, page 267. | |
// | |
DescBuf += Consumed; | |
Len -= Consumed; | |
// | |
// Make allowances for devices that return extra data at the | |
// end of their config descriptors | |
// | |
while (Len >= sizeof (EFI_USB_INTERFACE_DESCRIPTOR)) { | |
Setting = UsbParseInterfaceDesc (DescBuf, Len, &Consumed); | |
if (Setting == NULL) { | |
DEBUG ((DEBUG_ERROR, "UsbParseConfigDesc: warning: failed to get interface setting, stop parsing now.\n")); | |
break; | |
} else if (Setting->Desc.InterfaceNumber >= NumIf) { | |
DEBUG ((DEBUG_ERROR, "UsbParseConfigDesc: malformatted interface descriptor\n")); | |
UsbFreeInterfaceDesc (Setting); | |
goto ON_ERROR; | |
} | |
// | |
// Insert the descriptor to the corresponding set. | |
// | |
Interface = Config->Interfaces[Setting->Desc.InterfaceNumber]; | |
if (Interface->NumOfSetting >= USB_MAX_INTERFACE_SETTING) { | |
goto ON_ERROR; | |
} | |
Interface->Settings[Interface->NumOfSetting] = Setting; | |
Interface->NumOfSetting++; | |
DescBuf += Consumed; | |
Len -= Consumed; | |
} | |
return Config; | |
ON_ERROR: | |
UsbFreeConfigDesc (Config); | |
return NULL; | |
} | |
/** | |
USB standard control transfer support routine. This | |
function is used by USB device. It is possible that | |
the device's interfaces are still waiting to be | |
enumerated. | |
@param UsbDev The usb device. | |
@param Direction The direction of data transfer. | |
@param Type Standard / class specific / vendor specific. | |
@param Target The receiving target. | |
@param Request Which request. | |
@param Value The wValue parameter of the request. | |
@param Index The wIndex parameter of the request. | |
@param Buf The buffer to receive data into / transmit from. | |
@param Length The length of the buffer. | |
@retval EFI_SUCCESS The control request is executed. | |
@retval EFI_DEVICE_ERROR Failed to execute the control transfer. | |
**/ | |
EFI_STATUS | |
UsbCtrlRequest ( | |
IN USB_DEVICE *UsbDev, | |
IN EFI_USB_DATA_DIRECTION Direction, | |
IN UINTN Type, | |
IN UINTN Target, | |
IN UINTN Request, | |
IN UINT16 Value, | |
IN UINT16 Index, | |
IN OUT VOID *Buf, | |
IN UINTN Length | |
) | |
{ | |
EFI_USB_DEVICE_REQUEST DevReq; | |
EFI_STATUS Status; | |
UINT32 Result; | |
UINTN Len; | |
ASSERT ((UsbDev != NULL) && (UsbDev->Bus != NULL)); | |
DevReq.RequestType = USB_REQUEST_TYPE (Direction, Type, Target); | |
DevReq.Request = (UINT8)Request; | |
DevReq.Value = Value; | |
DevReq.Index = Index; | |
DevReq.Length = (UINT16)Length; | |
Len = Length; | |
Status = UsbHcControlTransfer ( | |
UsbDev->Bus, | |
UsbDev->Address, | |
UsbDev->Speed, | |
UsbDev->MaxPacket0, | |
&DevReq, | |
Direction, | |
Buf, | |
&Len, | |
USB_GENERAL_DEVICE_REQUEST_TIMEOUT, | |
&UsbDev->Translator, | |
&Result | |
); | |
return Status; | |
} | |
/** | |
Get the standard descriptors. | |
@param UsbDev The USB device to read descriptor from. | |
@param DescType The type of descriptor to read. | |
@param DescIndex The index of descriptor to read. | |
@param LangId Language ID, only used to get string, otherwise set | |
it to 0. | |
@param Buf The buffer to hold the descriptor read. | |
@param Length The length of the buffer. | |
@retval EFI_SUCCESS The descriptor is read OK. | |
@retval Others Failed to retrieve the descriptor. | |
**/ | |
EFI_STATUS | |
UsbCtrlGetDesc ( | |
IN USB_DEVICE *UsbDev, | |
IN UINTN DescType, | |
IN UINTN DescIndex, | |
IN UINT16 LangId, | |
OUT VOID *Buf, | |
IN UINTN Length | |
) | |
{ | |
EFI_STATUS Status; | |
Status = UsbCtrlRequest ( | |
UsbDev, | |
EfiUsbDataIn, | |
USB_REQ_TYPE_STANDARD, | |
USB_TARGET_DEVICE, | |
USB_REQ_GET_DESCRIPTOR, | |
(UINT16)((DescType << 8) | DescIndex), | |
LangId, | |
Buf, | |
Length | |
); | |
return Status; | |
} | |
/** | |
Return the max packet size for endpoint zero. This function | |
is the first function called to get descriptors during bus | |
enumeration. | |
@param UsbDev The usb device. | |
@retval EFI_SUCCESS The max packet size of endpoint zero is retrieved. | |
@retval EFI_DEVICE_ERROR Failed to retrieve it. | |
**/ | |
EFI_STATUS | |
UsbGetMaxPacketSize0 ( | |
IN USB_DEVICE *UsbDev | |
) | |
{ | |
EFI_USB_DEVICE_DESCRIPTOR DevDesc; | |
EFI_STATUS Status; | |
UINTN Index; | |
// | |
// Get the first 8 bytes of the device descriptor which contains | |
// max packet size for endpoint 0, which is at least 8. | |
// | |
for (Index = 0; Index < 3; Index++) { | |
Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_DEVICE, 0, 0, &DevDesc, 8); | |
if (!EFI_ERROR (Status)) { | |
if ((DevDesc.BcdUSB >= 0x0300) && (DevDesc.MaxPacketSize0 == 9)) { | |
UsbDev->MaxPacket0 = 1 << 9; | |
return EFI_SUCCESS; | |
} | |
UsbDev->MaxPacket0 = DevDesc.MaxPacketSize0; | |
return EFI_SUCCESS; | |
} | |
gBS->Stall (USB_RETRY_MAX_PACK_SIZE_STALL); | |
} | |
return EFI_DEVICE_ERROR; | |
} | |
/** | |
Get the device descriptor for the device. | |
@param UsbDev The Usb device to retrieve descriptor from. | |
@retval EFI_SUCCESS The device descriptor is returned. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory. | |
**/ | |
EFI_STATUS | |
UsbGetDevDesc ( | |
IN USB_DEVICE *UsbDev | |
) | |
{ | |
USB_DEVICE_DESC *DevDesc; | |
EFI_STATUS Status; | |
DevDesc = AllocateZeroPool (sizeof (USB_DEVICE_DESC)); | |
if (DevDesc == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Status = UsbCtrlGetDesc ( | |
UsbDev, | |
USB_DESC_TYPE_DEVICE, | |
0, | |
0, | |
DevDesc, | |
sizeof (EFI_USB_DEVICE_DESCRIPTOR) | |
); | |
if (EFI_ERROR (Status)) { | |
gBS->FreePool (DevDesc); | |
} else { | |
UsbDev->DevDesc = DevDesc; | |
} | |
return Status; | |
} | |
/** | |
Retrieve the indexed string for the language. It requires two | |
steps to get a string, first to get the string's length. Then | |
the string itself. | |
@param UsbDev The usb device. | |
@param Index The index the string to retrieve. | |
@param LangId Language ID. | |
@return The created string descriptor or NULL. | |
**/ | |
EFI_USB_STRING_DESCRIPTOR * | |
UsbGetOneString ( | |
IN USB_DEVICE *UsbDev, | |
IN UINT8 Index, | |
IN UINT16 LangId | |
) | |
{ | |
EFI_USB_STRING_DESCRIPTOR Desc; | |
EFI_STATUS Status; | |
UINT8 *Buf; | |
// | |
// First get two bytes which contains the string length. | |
// | |
Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_STRING, Index, LangId, &Desc, 2); | |
// | |
// Reject if Length even cannot cover itself, or odd because Unicode string byte length should be even. | |
// | |
if (EFI_ERROR (Status) || | |
(Desc.Length < OFFSET_OF (EFI_USB_STRING_DESCRIPTOR, Length) + sizeof (Desc.Length)) || | |
(Desc.Length % 2 != 0) | |
) | |
{ | |
return NULL; | |
} | |
Buf = AllocateZeroPool (Desc.Length); | |
if (Buf == NULL) { | |
return NULL; | |
} | |
Status = UsbCtrlGetDesc ( | |
UsbDev, | |
USB_DESC_TYPE_STRING, | |
Index, | |
LangId, | |
Buf, | |
Desc.Length | |
); | |
if (EFI_ERROR (Status)) { | |
FreePool (Buf); | |
return NULL; | |
} | |
return (EFI_USB_STRING_DESCRIPTOR *)Buf; | |
} | |
/** | |
Build the language ID table for string descriptors. | |
@param UsbDev The Usb device. | |
@retval EFI_UNSUPPORTED This device doesn't support string table. | |
**/ | |
EFI_STATUS | |
UsbBuildLangTable ( | |
IN USB_DEVICE *UsbDev | |
) | |
{ | |
EFI_USB_STRING_DESCRIPTOR *Desc; | |
EFI_STATUS Status; | |
UINTN Index; | |
UINTN Max; | |
UINT16 *Point; | |
// | |
// The string of language ID zero returns the supported languages | |
// | |
Desc = UsbGetOneString (UsbDev, 0, 0); | |
if (Desc == NULL) { | |
return EFI_UNSUPPORTED; | |
} | |
if (Desc->Length < 4) { | |
Status = EFI_UNSUPPORTED; | |
goto ON_EXIT; | |
} | |
Status = EFI_SUCCESS; | |
Max = (Desc->Length - 2) / 2; | |
Max = MIN (Max, USB_MAX_LANG_ID); | |
Point = Desc->String; | |
for (Index = 0; Index < Max; Index++) { | |
UsbDev->LangId[Index] = *Point; | |
Point++; | |
} | |
UsbDev->TotalLangId = (UINT16)Max; | |
ON_EXIT: | |
gBS->FreePool (Desc); | |
return Status; | |
} | |
/** | |
Retrieve the indexed configure for the device. USB device | |
returns the configuration together with the interfaces for | |
this configuration. Configuration descriptor is also of | |
variable length. | |
@param UsbDev The Usb interface. | |
@param Index The index of the configuration. | |
@return The created configuration descriptor. | |
**/ | |
EFI_USB_CONFIG_DESCRIPTOR * | |
UsbGetOneConfig ( | |
IN USB_DEVICE *UsbDev, | |
IN UINT8 Index | |
) | |
{ | |
EFI_USB_CONFIG_DESCRIPTOR Desc; | |
EFI_STATUS Status; | |
VOID *Buf; | |
// | |
// First get four bytes which contains the total length | |
// for this configuration. | |
// | |
Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_CONFIG, Index, 0, &Desc, 8); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"UsbGetOneConfig: failed to get descript length(%d) - %r\n", | |
Desc.TotalLength, | |
Status | |
)); | |
return NULL; | |
} | |
DEBUG ((DEBUG_INFO, "UsbGetOneConfig: total length is %d\n", Desc.TotalLength)); | |
// | |
// Reject if TotalLength even cannot cover itself. | |
// | |
if (Desc.TotalLength < OFFSET_OF (EFI_USB_CONFIG_DESCRIPTOR, TotalLength) + sizeof (Desc.TotalLength)) { | |
return NULL; | |
} | |
Buf = AllocateZeroPool (Desc.TotalLength); | |
if (Buf == NULL) { | |
return NULL; | |
} | |
Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_CONFIG, Index, 0, Buf, Desc.TotalLength); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "UsbGetOneConfig: failed to get full descript - %r\n", Status)); | |
FreePool (Buf); | |
return NULL; | |
} | |
return Buf; | |
} | |
/** | |
Build the whole array of descriptors. This function must | |
be called after UsbGetMaxPacketSize0 returns the max packet | |
size correctly for endpoint 0. | |
@param UsbDev The Usb device. | |
@retval EFI_SUCCESS The descriptor table is build. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the descriptor. | |
**/ | |
EFI_STATUS | |
UsbBuildDescTable ( | |
IN USB_DEVICE *UsbDev | |
) | |
{ | |
EFI_USB_CONFIG_DESCRIPTOR *Config; | |
USB_DEVICE_DESC *DevDesc; | |
USB_CONFIG_DESC *ConfigDesc; | |
UINT8 NumConfig; | |
EFI_STATUS Status; | |
UINT8 Index; | |
// | |
// Get the device descriptor, then allocate the configure | |
// descriptor pointer array to hold configurations. | |
// | |
Status = UsbGetDevDesc (UsbDev); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "UsbBuildDescTable: failed to get device descriptor - %r\n", Status)); | |
return Status; | |
} | |
DevDesc = UsbDev->DevDesc; | |
NumConfig = DevDesc->Desc.NumConfigurations; | |
if (NumConfig == 0) { | |
return EFI_DEVICE_ERROR; | |
} | |
DevDesc->Configs = AllocateZeroPool (NumConfig * sizeof (USB_CONFIG_DESC *)); | |
if (DevDesc->Configs == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
DEBUG ((DEBUG_INFO, "UsbBuildDescTable: device has %d configures\n", NumConfig)); | |
// | |
// Read each configurations, then parse them | |
// | |
for (Index = 0; Index < NumConfig; Index++) { | |
Config = UsbGetOneConfig (UsbDev, Index); | |
if (Config == NULL) { | |
DEBUG ((DEBUG_ERROR, "UsbBuildDescTable: failed to get configure (index %d)\n", Index)); | |
// | |
// If we can get the default descriptor, it is likely that the | |
// device is still operational. | |
// | |
if (Index == 0) { | |
return EFI_DEVICE_ERROR; | |
} | |
break; | |
} | |
ConfigDesc = UsbParseConfigDesc ((UINT8 *)Config, Config->TotalLength); | |
FreePool (Config); | |
if (ConfigDesc == NULL) { | |
DEBUG ((DEBUG_ERROR, "UsbBuildDescTable: failed to parse configure (index %d)\n", Index)); | |
// | |
// If we can get the default descriptor, it is likely that the | |
// device is still operational. | |
// | |
if (Index == 0) { | |
return EFI_DEVICE_ERROR; | |
} | |
break; | |
} | |
DevDesc->Configs[Index] = ConfigDesc; | |
} | |
// | |
// Don't return error even this function failed because | |
// it is possible for the device to not support strings. | |
// | |
Status = UsbBuildLangTable (UsbDev); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_INFO, "UsbBuildDescTable: get language ID table - %r\n", Status)); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Set the device's address. | |
@param UsbDev The device to set address to. | |
@param Address The address to set. | |
@retval EFI_SUCCESS The device is set to the address. | |
@retval Others Failed to set the device address. | |
**/ | |
EFI_STATUS | |
UsbSetAddress ( | |
IN USB_DEVICE *UsbDev, | |
IN UINT8 Address | |
) | |
{ | |
EFI_STATUS Status; | |
Status = UsbCtrlRequest ( | |
UsbDev, | |
EfiUsbNoData, | |
USB_REQ_TYPE_STANDARD, | |
USB_TARGET_DEVICE, | |
USB_REQ_SET_ADDRESS, | |
Address, | |
0, | |
NULL, | |
0 | |
); | |
return Status; | |
} | |
/** | |
Set the device's configuration. This function changes | |
the device's internal state. UsbSelectConfig changes | |
the Usb bus's internal state. | |
@param UsbDev The USB device to set configure to. | |
@param ConfigIndex The configure index to set. | |
@retval EFI_SUCCESS The device is configured now. | |
@retval Others Failed to set the device configure. | |
**/ | |
EFI_STATUS | |
UsbSetConfig ( | |
IN USB_DEVICE *UsbDev, | |
IN UINT8 ConfigIndex | |
) | |
{ | |
EFI_STATUS Status; | |
Status = UsbCtrlRequest ( | |
UsbDev, | |
EfiUsbNoData, | |
USB_REQ_TYPE_STANDARD, | |
USB_TARGET_DEVICE, | |
USB_REQ_SET_CONFIG, | |
ConfigIndex, | |
0, | |
NULL, | |
0 | |
); | |
return Status; | |
} | |
/** | |
Usb UsbIo interface to clear the feature. This is should | |
only be used by HUB which is considered a device driver | |
on top of the UsbIo interface. | |
@param UsbIo The UsbIo interface. | |
@param Target The target of the transfer: endpoint/device. | |
@param Feature The feature to clear. | |
@param Index The wIndex parameter. | |
@retval EFI_SUCCESS The device feature is cleared. | |
@retval Others Failed to clear the feature. | |
**/ | |
EFI_STATUS | |
UsbIoClearFeature ( | |
IN EFI_USB_IO_PROTOCOL *UsbIo, | |
IN UINTN Target, | |
IN UINT16 Feature, | |
IN UINT16 Index | |
) | |
{ | |
EFI_USB_DEVICE_REQUEST DevReq; | |
UINT32 UsbResult; | |
EFI_STATUS Status; | |
DevReq.RequestType = USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD, Target); | |
DevReq.Request = USB_REQ_CLEAR_FEATURE; | |
DevReq.Value = Feature; | |
DevReq.Index = Index; | |
DevReq.Length = 0; | |
Status = UsbIo->UsbControlTransfer ( | |
UsbIo, | |
&DevReq, | |
EfiUsbNoData, | |
USB_CLEAR_FEATURE_REQUEST_TIMEOUT, | |
NULL, | |
0, | |
&UsbResult | |
); | |
return Status; | |
} |