mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-24 16:27:42 +01:00
7c0aa811ec
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
1018 lines
26 KiB
C
1018 lines
26 KiB
C
/** @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 (( EFI_D_ERROR, "UsbParseInterfaceDesc: failed to create interface descriptor\n"));
|
|
return NULL;
|
|
}
|
|
|
|
Offset = Used;
|
|
|
|
//
|
|
// Create an array to hold the interface's endpoints
|
|
//
|
|
NumEp = Setting->Desc.NumEndpoints;
|
|
|
|
DEBUG (( EFI_D_INFO, "UsbParseInterfaceDesc: interface %d(setting %d) has %d endpoints\n",
|
|
Setting->Desc.InterfaceNumber, Setting->Desc.AlternateSetting, (UINT32)NumEp));
|
|
|
|
if (NumEp == 0) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Setting->Endpoints = AllocateZeroPool (sizeof (USB_ENDPOINT_DESC *) * NumEp);
|
|
|
|
if (Setting->Endpoints == NULL) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
//
|
|
// Create the endpoints for this interface
|
|
//
|
|
for (Index = 0; (Index < NumEp) && (Offset < Len); Index++) {
|
|
Ep = UsbCreateDesc (DescBuf + Offset, Len - Offset, USB_DESC_TYPE_ENDPOINT, &Used);
|
|
|
|
if (Ep == NULL) {
|
|
DEBUG (( EFI_D_ERROR, "UsbParseInterfaceDesc: failed to create endpoint(index %d)\n", (UINT32)Index));
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Setting->Endpoints[Index] = Ep;
|
|
Offset += Used;
|
|
}
|
|
|
|
|
|
ON_EXIT:
|
|
*Consumed = Offset;
|
|
return Setting;
|
|
|
|
ON_ERROR:
|
|
UsbFreeInterfaceDesc (Setting);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
Parse the configuration descriptor and its interfaces.
|
|
|
|
@param DescBuf The buffer of raw descriptor.
|
|
@param Len The length of the raw descriptor buffer.
|
|
|
|
@return The created configuration descriptor.
|
|
|
|
**/
|
|
USB_CONFIG_DESC *
|
|
UsbParseConfigDesc (
|
|
IN UINT8 *DescBuf,
|
|
IN UINTN Len
|
|
)
|
|
{
|
|
USB_CONFIG_DESC *Config;
|
|
USB_INTERFACE_SETTING *Setting;
|
|
USB_INTERFACE_DESC *Interface;
|
|
UINTN Index;
|
|
UINTN NumIf;
|
|
UINTN Consumed;
|
|
|
|
ASSERT (DescBuf != NULL);
|
|
|
|
Config = UsbCreateDesc (DescBuf, Len, USB_DESC_TYPE_CONFIG, &Consumed);
|
|
|
|
if (Config == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Initialize an array of setting for the configuration's interfaces.
|
|
//
|
|
NumIf = Config->Desc.NumInterfaces;
|
|
Config->Interfaces = AllocateZeroPool (sizeof (USB_INTERFACE_DESC *) * NumIf);
|
|
|
|
if (Config->Interfaces == NULL) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
DEBUG (( EFI_D_INFO, "UsbParseConfigDesc: config %d has %d interfaces\n",
|
|
Config->Desc.ConfigurationValue, (UINT32)NumIf));
|
|
|
|
for (Index = 0; Index < NumIf; Index++) {
|
|
Interface = AllocateZeroPool (sizeof (USB_INTERFACE_DESC));
|
|
|
|
if (Interface == NULL) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Config->Interfaces[Index] = Interface;
|
|
}
|
|
|
|
//
|
|
// If a configuration has several interfaces, these interfaces are
|
|
// numbered from zero to n. If a interface has several settings,
|
|
// these settings are also number from zero to m. The interface
|
|
// setting must be organized as |interface 0, setting 0|interface 0
|
|
// setting 1|interface 1, setting 0|interface 2, setting 0|. Check
|
|
// USB2.0 spec, page 267.
|
|
//
|
|
DescBuf += Consumed;
|
|
Len -= Consumed;
|
|
|
|
//
|
|
// Make allowances for devices that return extra data at the
|
|
// end of their config descriptors
|
|
//
|
|
while (Len >= sizeof (EFI_USB_INTERFACE_DESCRIPTOR)) {
|
|
Setting = UsbParseInterfaceDesc (DescBuf, Len, &Consumed);
|
|
|
|
if (Setting == NULL) {
|
|
DEBUG (( EFI_D_ERROR, "UsbParseConfigDesc: warning: failed to get interface setting, stop parsing now.\n"));
|
|
break;
|
|
|
|
} else if (Setting->Desc.InterfaceNumber >= NumIf) {
|
|
DEBUG (( EFI_D_ERROR, "UsbParseConfigDesc: mal-formated interface descriptor\n"));
|
|
|
|
UsbFreeInterfaceDesc (Setting);
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
//
|
|
// Insert the descriptor to the corresponding set.
|
|
//
|
|
Interface = Config->Interfaces[Setting->Desc.InterfaceNumber];
|
|
|
|
if (Interface->NumOfSetting >= USB_MAX_INTERFACE_SETTING) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Interface->Settings[Interface->NumOfSetting] = Setting;
|
|
Interface->NumOfSetting++;
|
|
|
|
DescBuf += Consumed;
|
|
Len -= Consumed;
|
|
}
|
|
|
|
return Config;
|
|
|
|
ON_ERROR:
|
|
UsbFreeConfigDesc (Config);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
USB standard control transfer support routine. This
|
|
function is used by USB device. It is possible that
|
|
the device's interfaces are still waiting to be
|
|
enumerated.
|
|
|
|
@param UsbDev The usb device.
|
|
@param Direction The direction of data transfer.
|
|
@param Type Standard / class specific / vendor specific.
|
|
@param Target The receiving target.
|
|
@param Request Which request.
|
|
@param Value The wValue parameter of the request.
|
|
@param Index The wIndex parameter of the request.
|
|
@param Buf The buffer to receive data into / transmit from.
|
|
@param Length The length of the buffer.
|
|
|
|
@retval EFI_SUCCESS The control request is executed.
|
|
@retval EFI_DEVICE_ERROR Failed to execute the control transfer.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
UsbCtrlRequest (
|
|
IN USB_DEVICE *UsbDev,
|
|
IN EFI_USB_DATA_DIRECTION Direction,
|
|
IN UINTN Type,
|
|
IN UINTN Target,
|
|
IN UINTN Request,
|
|
IN UINT16 Value,
|
|
IN UINT16 Index,
|
|
IN OUT VOID *Buf,
|
|
IN UINTN Length
|
|
)
|
|
{
|
|
EFI_USB_DEVICE_REQUEST DevReq;
|
|
EFI_STATUS Status;
|
|
UINT32 Result;
|
|
UINTN Len;
|
|
|
|
ASSERT ((UsbDev != NULL) && (UsbDev->Bus != NULL));
|
|
|
|
DevReq.RequestType = USB_REQUEST_TYPE (Direction, Type, Target);
|
|
DevReq.Request = (UINT8) Request;
|
|
DevReq.Value = Value;
|
|
DevReq.Index = Index;
|
|
DevReq.Length = (UINT16) Length;
|
|
|
|
Len = Length;
|
|
Status = UsbHcControlTransfer (
|
|
UsbDev->Bus,
|
|
UsbDev->Address,
|
|
UsbDev->Speed,
|
|
UsbDev->MaxPacket0,
|
|
&DevReq,
|
|
Direction,
|
|
Buf,
|
|
&Len,
|
|
USB_GENERAL_DEVICE_REQUEST_TIMEOUT,
|
|
&UsbDev->Translator,
|
|
&Result
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Get the standard descriptors.
|
|
|
|
@param UsbDev The USB device to read descriptor from.
|
|
@param DescType The type of descriptor to read.
|
|
@param DescIndex The index of descriptor to read.
|
|
@param LangId Language ID, only used to get string, otherwise set
|
|
it to 0.
|
|
@param Buf The buffer to hold the descriptor read.
|
|
@param Length The length of the buffer.
|
|
|
|
@retval EFI_SUCCESS The descriptor is read OK.
|
|
@retval Others Failed to retrieve the descriptor.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
UsbCtrlGetDesc (
|
|
IN USB_DEVICE *UsbDev,
|
|
IN UINTN DescType,
|
|
IN UINTN DescIndex,
|
|
IN UINT16 LangId,
|
|
OUT VOID *Buf,
|
|
IN UINTN Length
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = UsbCtrlRequest (
|
|
UsbDev,
|
|
EfiUsbDataIn,
|
|
USB_REQ_TYPE_STANDARD,
|
|
USB_TARGET_DEVICE,
|
|
USB_REQ_GET_DESCRIPTOR,
|
|
(UINT16) ((DescType << 8) | DescIndex),
|
|
LangId,
|
|
Buf,
|
|
Length
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Return the max packet size for endpoint zero. This function
|
|
is the first function called to get descriptors during bus
|
|
enumeration.
|
|
|
|
@param UsbDev The usb device.
|
|
|
|
@retval EFI_SUCCESS The max packet size of endpoint zero is retrieved.
|
|
@retval EFI_DEVICE_ERROR Failed to retrieve it.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
UsbGetMaxPacketSize0 (
|
|
IN USB_DEVICE *UsbDev
|
|
)
|
|
{
|
|
EFI_USB_DEVICE_DESCRIPTOR DevDesc;
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
|
|
|
|
//
|
|
// Get the first 8 bytes of the device descriptor which contains
|
|
// max packet size for endpoint 0, which is at least 8.
|
|
//
|
|
for (Index = 0; Index < 3; Index++) {
|
|
Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_DEVICE, 0, 0, &DevDesc, 8);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
if ((DevDesc.BcdUSB >= 0x0300) && (DevDesc.MaxPacketSize0 == 9)) {
|
|
UsbDev->MaxPacket0 = 1 << 9;
|
|
return EFI_SUCCESS;
|
|
}
|
|
UsbDev->MaxPacket0 = DevDesc.MaxPacketSize0;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
gBS->Stall (USB_RETRY_MAX_PACK_SIZE_STALL);
|
|
}
|
|
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
|
|
/**
|
|
Get the device descriptor for the device.
|
|
|
|
@param UsbDev The Usb device to retrieve descriptor from.
|
|
|
|
@retval EFI_SUCCESS The device descriptor is returned.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
UsbGetDevDesc (
|
|
IN USB_DEVICE *UsbDev
|
|
)
|
|
{
|
|
USB_DEVICE_DESC *DevDesc;
|
|
EFI_STATUS Status;
|
|
|
|
DevDesc = AllocateZeroPool (sizeof (USB_DEVICE_DESC));
|
|
|
|
if (DevDesc == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = UsbCtrlGetDesc (
|
|
UsbDev,
|
|
USB_DESC_TYPE_DEVICE,
|
|
0,
|
|
0,
|
|
DevDesc,
|
|
sizeof (EFI_USB_DEVICE_DESCRIPTOR)
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->FreePool (DevDesc);
|
|
} else {
|
|
UsbDev->DevDesc = DevDesc;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Retrieve the indexed string for the language. It requires two
|
|
steps to get a string, first to get the string's length. Then
|
|
the string itself.
|
|
|
|
@param UsbDev The usb device.
|
|
@param Index The index the string to retrieve.
|
|
@param LangId Language ID.
|
|
|
|
@return The created string descriptor or NULL.
|
|
|
|
**/
|
|
EFI_USB_STRING_DESCRIPTOR *
|
|
UsbGetOneString (
|
|
IN USB_DEVICE *UsbDev,
|
|
IN UINT8 Index,
|
|
IN UINT16 LangId
|
|
)
|
|
{
|
|
EFI_USB_STRING_DESCRIPTOR Desc;
|
|
EFI_STATUS Status;
|
|
UINT8 *Buf;
|
|
|
|
//
|
|
// First get two bytes which contains the string length.
|
|
//
|
|
Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_STRING, Index, LangId, &Desc, 2);
|
|
|
|
//
|
|
// 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 (( EFI_D_ERROR, "UsbGetOneConfig: failed to get descript length(%d) %r\n",
|
|
Desc.TotalLength, Status));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
DEBUG (( EFI_D_INFO, "UsbGetOneConfig: total length is %d\n", Desc.TotalLength));
|
|
|
|
//
|
|
// 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 (( EFI_D_ERROR, "UsbGetOneConfig: failed to get full descript %r\n", Status));
|
|
|
|
FreePool (Buf);
|
|
return NULL;
|
|
}
|
|
|
|
return Buf;
|
|
}
|
|
|
|
|
|
/**
|
|
Build the whole array of descriptors. This function must
|
|
be called after UsbGetMaxPacketSize0 returns the max packet
|
|
size correctly for endpoint 0.
|
|
|
|
@param UsbDev The Usb device.
|
|
|
|
@retval EFI_SUCCESS The descriptor table is build.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the descriptor.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
UsbBuildDescTable (
|
|
IN USB_DEVICE *UsbDev
|
|
)
|
|
{
|
|
EFI_USB_CONFIG_DESCRIPTOR *Config;
|
|
USB_DEVICE_DESC *DevDesc;
|
|
USB_CONFIG_DESC *ConfigDesc;
|
|
UINT8 NumConfig;
|
|
EFI_STATUS Status;
|
|
UINT8 Index;
|
|
|
|
//
|
|
// Get the device descriptor, then allocate the configure
|
|
// descriptor pointer array to hold configurations.
|
|
//
|
|
Status = UsbGetDevDesc (UsbDev);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG (( EFI_D_ERROR, "UsbBuildDescTable: failed to get device descriptor - %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
DevDesc = UsbDev->DevDesc;
|
|
NumConfig = DevDesc->Desc.NumConfigurations;
|
|
if (NumConfig == 0) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
DevDesc->Configs = AllocateZeroPool (NumConfig * sizeof (USB_CONFIG_DESC *));
|
|
if (DevDesc->Configs == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
DEBUG (( EFI_D_INFO, "UsbBuildDescTable: device has %d configures\n", NumConfig));
|
|
|
|
//
|
|
// Read each configurations, then parse them
|
|
//
|
|
for (Index = 0; Index < NumConfig; Index++) {
|
|
Config = UsbGetOneConfig (UsbDev, Index);
|
|
|
|
if (Config == NULL) {
|
|
DEBUG (( EFI_D_ERROR, "UsbBuildDescTable: failed to get configure (index %d)\n", Index));
|
|
|
|
//
|
|
// If we can get the default descriptor, it is likely that the
|
|
// device is still operational.
|
|
//
|
|
if (Index == 0) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
ConfigDesc = UsbParseConfigDesc ((UINT8 *) Config, Config->TotalLength);
|
|
|
|
FreePool (Config);
|
|
|
|
if (ConfigDesc == NULL) {
|
|
DEBUG (( EFI_D_ERROR, "UsbBuildDescTable: failed to parse configure (index %d)\n", Index));
|
|
|
|
//
|
|
// If we can get the default descriptor, it is likely that the
|
|
// device is still operational.
|
|
//
|
|
if (Index == 0) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
DevDesc->Configs[Index] = ConfigDesc;
|
|
}
|
|
|
|
//
|
|
// Don't return error even this function failed because
|
|
// it is possible for the device to not support strings.
|
|
//
|
|
Status = UsbBuildLangTable (UsbDev);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG (( EFI_D_INFO, "UsbBuildDescTable: get language ID table %r\n", Status));
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Set the device's address.
|
|
|
|
@param UsbDev The device to set address to.
|
|
@param Address The address to set.
|
|
|
|
@retval EFI_SUCCESS The device is set to the address.
|
|
@retval Others Failed to set the device address.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
UsbSetAddress (
|
|
IN USB_DEVICE *UsbDev,
|
|
IN UINT8 Address
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = UsbCtrlRequest (
|
|
UsbDev,
|
|
EfiUsbNoData,
|
|
USB_REQ_TYPE_STANDARD,
|
|
USB_TARGET_DEVICE,
|
|
USB_REQ_SET_ADDRESS,
|
|
Address,
|
|
0,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Set the device's configuration. This function changes
|
|
the device's internal state. UsbSelectConfig changes
|
|
the Usb bus's internal state.
|
|
|
|
@param UsbDev The USB device to set configure to.
|
|
@param ConfigIndex The configure index to set.
|
|
|
|
@retval EFI_SUCCESS The device is configured now.
|
|
@retval Others Failed to set the device configure.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
UsbSetConfig (
|
|
IN USB_DEVICE *UsbDev,
|
|
IN UINT8 ConfigIndex
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = UsbCtrlRequest (
|
|
UsbDev,
|
|
EfiUsbNoData,
|
|
USB_REQ_TYPE_STANDARD,
|
|
USB_TARGET_DEVICE,
|
|
USB_REQ_SET_CONFIG,
|
|
ConfigIndex,
|
|
0,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Usb UsbIo interface to clear the feature. This is should
|
|
only be used by HUB which is considered a device driver
|
|
on top of the UsbIo interface.
|
|
|
|
@param UsbIo The UsbIo interface.
|
|
@param Target The target of the transfer: endpoint/device.
|
|
@param Feature The feature to clear.
|
|
@param Index The wIndex parameter.
|
|
|
|
@retval EFI_SUCCESS The device feature is cleared.
|
|
@retval Others Failed to clear the feature.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
UsbIoClearFeature (
|
|
IN EFI_USB_IO_PROTOCOL *UsbIo,
|
|
IN UINTN Target,
|
|
IN UINT16 Feature,
|
|
IN UINT16 Index
|
|
)
|
|
{
|
|
EFI_USB_DEVICE_REQUEST DevReq;
|
|
UINT32 UsbResult;
|
|
EFI_STATUS Status;
|
|
|
|
DevReq.RequestType = USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD, Target);
|
|
DevReq.Request = USB_REQ_CLEAR_FEATURE;
|
|
DevReq.Value = Feature;
|
|
DevReq.Index = Index;
|
|
DevReq.Length = 0;
|
|
|
|
Status = UsbIo->UsbControlTransfer (
|
|
UsbIo,
|
|
&DevReq,
|
|
EfiUsbNoData,
|
|
USB_CLEAR_FEATURE_REQUEST_TIMEOUT,
|
|
NULL,
|
|
0,
|
|
&UsbResult
|
|
);
|
|
|
|
return Status;
|
|
}
|