/** @file Manage Usb Descriptor List Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
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; }