/** @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;
}
