/*++

Copyright (c) 2006, Intel Corporation                                                         
All rights reserved. This program and the accompanying materials                          
are licensed and made available under the terms and conditions of the BSD License         
which accompanies this distribution.  The full text of the license may be found at        
http://opensource.org/licenses/bsd-license.php                                            
                                                                                          
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             

  Module Name:

    UsbIo.c

  Abstract:

    USB I/O Abstraction Driver

  Revision History

--*/

#include "usbbus.h"

//
// USB I/O Support Function Prototypes
//
STATIC
EFI_STATUS
EFIAPI
UsbControlTransfer (
  IN       EFI_USB_IO_PROTOCOL        *This,
  IN       EFI_USB_DEVICE_REQUEST     *Request,
  IN       EFI_USB_DATA_DIRECTION     Direction,
  IN       UINT32                     Timeout,
  IN OUT   VOID                       *Data, OPTIONAL
  IN       UINTN                      DataLength, OPTIONAL
  OUT      UINT32                     *Status
  );

STATIC
EFI_STATUS
EFIAPI
UsbBulkTransfer (
  IN       EFI_USB_IO_PROTOCOL     *This,
  IN       UINT8                   DeviceEndpoint,
  IN OUT   VOID                    *Data,
  IN OUT   UINTN                   *DataLength,
  IN       UINTN                   Timeout,
  OUT      UINT32                  *Status
  );

STATIC
EFI_STATUS
EFIAPI
UsbAsyncInterruptTransfer (
  IN EFI_USB_IO_PROTOCOL                 * This,
  IN UINT8                               DeviceEndpoint,
  IN BOOLEAN                             IsNewTransfer,
  IN UINTN                               PollingInterval, OPTIONAL
  IN UINTN                               DataLength, OPTIONAL
  IN EFI_ASYNC_USB_TRANSFER_CALLBACK     InterruptCallBack, OPTIONAL
  IN VOID                                *Context OPTIONAL
  );

STATIC
EFI_STATUS
EFIAPI
UsbSyncInterruptTransfer (
  IN       EFI_USB_IO_PROTOCOL     *This,
  IN       UINT8                   DeviceEndpoint,
  IN OUT   VOID                    *Data,
  IN OUT   UINTN                   *DataLength,
  IN       UINTN                   Timeout,
  OUT      UINT32                  *Status
  );

STATIC
EFI_STATUS
EFIAPI
UsbIsochronousTransfer (
  IN       EFI_USB_IO_PROTOCOL     *This,
  IN       UINT8                   DeviceEndpoint,
  IN OUT   VOID                    *Data,
  IN       UINTN                   DataLength,
  OUT      UINT32                  *Status
  );

STATIC
EFI_STATUS
EFIAPI
UsbAsyncIsochronousTransfer (
  IN        EFI_USB_IO_PROTOCOL                 * This,
  IN        UINT8                               DeviceEndpoint,
  IN OUT    VOID                                *Data,
  IN        UINTN                               DataLength,
  IN        EFI_ASYNC_USB_TRANSFER_CALLBACK     IsochronousCallBack,
  IN        VOID                                *Context OPTIONAL
  );

extern
EFI_STATUS
EFIAPI
UsbPortReset (
  IN EFI_USB_IO_PROTOCOL     *This
  );

STATIC
EFI_STATUS
EFIAPI
UsbGetDeviceDescriptor (
  IN  EFI_USB_IO_PROTOCOL           *This,
  OUT EFI_USB_DEVICE_DESCRIPTOR     *DeviceDescriptor
  );

STATIC
EFI_STATUS
EFIAPI
UsbGetActiveConfigDescriptor (
  IN  EFI_USB_IO_PROTOCOL           *This,
  OUT EFI_USB_CONFIG_DESCRIPTOR     *ConfigurationDescriptor
  );

STATIC
EFI_STATUS
EFIAPI
UsbGetInterfaceDescriptor (
  IN  EFI_USB_IO_PROTOCOL              *This,
  OUT EFI_USB_INTERFACE_DESCRIPTOR     *InterfaceDescriptor
  );

STATIC
EFI_STATUS
EFIAPI
UsbGetEndpointDescriptor (
  IN  EFI_USB_IO_PROTOCOL             *This,
  IN  UINT8                           EndpointIndex,
  OUT EFI_USB_ENDPOINT_DESCRIPTOR     *EndpointDescriptor
  );

STATIC
EFI_STATUS
EFIAPI
UsbGetStringDescriptor (
  IN  EFI_USB_IO_PROTOCOL     *This,
  IN  UINT16                  LangID,
  IN  UINT8                   StringIndex,
  OUT CHAR16                  **String
  );

STATIC
EFI_STATUS
EFIAPI
UsbGetSupportedLanguages (
  IN  EFI_USB_IO_PROTOCOL      *This,
  OUT UINT16                   **LangIDTable,
  OUT UINT16                   *TableSize
  );

//
// USB I/O Interface structure
//
STATIC EFI_USB_IO_PROTOCOL  UsbIoInterface = {
  UsbControlTransfer,
  UsbBulkTransfer,
  UsbAsyncInterruptTransfer,
  UsbSyncInterruptTransfer,
  UsbIsochronousTransfer,
  UsbAsyncIsochronousTransfer,
  UsbGetDeviceDescriptor,
  UsbGetActiveConfigDescriptor,
  UsbGetInterfaceDescriptor,
  UsbGetEndpointDescriptor,
  UsbGetStringDescriptor,
  UsbGetSupportedLanguages,
  UsbPortReset
};

VOID
InitializeUsbIoInstance (
  IN USB_IO_CONTROLLER_DEVICE     *UsbIoController
  )
{
  //
  // Copy EFI_USB_IO protocol instance
  //
  CopyMem (
    &UsbIoController->UsbIo,
    &UsbIoInterface,
    sizeof (EFI_USB_IO_PROTOCOL)
    );
}
//
// Implementation
//
STATIC
EFI_STATUS
EFIAPI
UsbControlTransfer (
  IN       EFI_USB_IO_PROTOCOL        *This,
  IN       EFI_USB_DEVICE_REQUEST     *Request,
  IN       EFI_USB_DATA_DIRECTION     Direction,
  IN       UINT32                     Timeout,
  IN OUT   VOID                       *Data, OPTIONAL
  IN       UINTN                      DataLength, OPTIONAL
  OUT      UINT32                     *Status
  )
/*++

  Routine Description:
    This function is used to manage a USB device with a control transfer pipe.

  Arguments:
    This        -   Indicates calling context.
    Request     -   A pointer to the USB device request that will be sent to
                    the USB device.
    Direction   -   Indicates the data direction.
    Data        -   A pointer to the buffer of data that will be transmitted
                    to USB device or received from USB device.
    Timeout     -   Indicates the transfer should be completed within this time
                    frame.
    DataLength  -   The size, in bytes, of the data buffer specified by Data.
    Status      -   A pointer to the result of the USB transfer.

  Returns:
    EFI_SUCCESS
    EFI_INVALID_PARAMETER
    EFI_OUT_OF_RESOURCES
    EFI_TIMEOUT
    EFI_DEVICE_ERROR

--*/
{
  USB_IO_CONTROLLER_DEVICE  *UsbIoController;
  EFI_USB_HC_PROTOCOL       *UsbHCInterface;
  EFI_STATUS                RetStatus;
  USB_IO_DEVICE             *UsbIoDevice;
  UINT8                     MaxPacketLength;
  UINT32                    TransferResult;
  BOOLEAN                   Disconnected;
  //
  // Parameters Checking
  //
  if (Status == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  
  //
  // leave the HostController's ControlTransfer
  // to perform other parameters checking
  //
  UsbIoController = USB_IO_CONTROLLER_DEVICE_FROM_USB_IO_THIS (This);
  UsbIoDevice     = UsbIoController->UsbDevice;
  UsbHCInterface  = UsbIoDevice->BusController->UsbHCInterface;
  MaxPacketLength = UsbIoDevice->DeviceDescriptor.MaxPacketSize0;

 
  if (Request->Request     == USB_DEV_CLEAR_FEATURE && 
      Request->RequestType == 0x02                  && 
      Request->Value       == EfiUsbEndpointHalt) {
     //
     //Reduce the remove delay time for system response
     //
     IsDeviceDisconnected (UsbIoController, &Disconnected);
     if (!EFI_ERROR (Status) && Disconnected == TRUE) {
      DEBUG ((gUSBErrorLevel, "Device is disconnected when trying reset\n"));
      return EFI_DEVICE_ERROR;
    }
  }



  //
  // using HostController's ControlTransfer to complete the request
  //
  RetStatus = UsbHCInterface->ControlTransfer (
                                UsbHCInterface,
                                UsbIoDevice->DeviceAddress,
                                UsbIoDevice->IsSlowDevice,
                                MaxPacketLength,
                                Request,
                                Direction,
                                Data,
                                &DataLength,
                                (UINTN) Timeout,
                                &TransferResult
                                );
  *Status = TransferResult;

  if (Request->Request     == USB_DEV_CLEAR_FEATURE && 
      Request->RequestType == 0x02                  && 
      Request->Value       == EfiUsbEndpointHalt) {
    //
    // This is a UsbClearEndpointHalt request
    // Need to clear data toggle
    // Request.Index == EndpointAddress
    //
    if (!EFI_ERROR (RetStatus) && TransferResult == EFI_USB_NOERROR) {
      SetDataToggleBit (
        This,
        (UINT8) Request->Index,
        0
        );
    }
  }
  return RetStatus;
}

STATIC
EFI_STATUS
EFIAPI
UsbBulkTransfer (
  IN       EFI_USB_IO_PROTOCOL     *This,
  IN       UINT8                   DeviceEndpoint,
  IN OUT   VOID                    *Data,
  IN OUT   UINTN                   *DataLength,
  IN       UINTN                   Timeout,
  OUT      UINT32                  *Status
  )
/*++

  Routine Description:
    This function is used to manage a USB device with the bulk transfer pipe.

  Arguments:
    This            - Indicates calling context.
    DeviceEndpoint  - The destination USB device endpoint to which the device
                      request is being sent.
    Data            - A pointer to the buffer of data that will be transmitted
                      to USB device or received from USB device.
    DataLength      - On input, the size, in bytes, of the data buffer
                      specified by Data.  On output, the number of bytes that
                      were actually transferred.
    Timeout         - Indicates the transfer should be completed within this
                      time frame.
    Status          - This parameter indicates the USB transfer status.

  Returns:
    EFI_SUCCESS
    EFI_INVALID_PARAMETER
    EFI_OUT_OF_RESOURCES
    EFI_TIMEOUT
    EFI_DEVICE_ERROR

--*/
{
  USB_IO_DEVICE               *UsbIoDev;
  UINT8                       MaxPacketLength;
  UINT8                       DataToggle;
  UINT8                       OldToggle;
  EFI_STATUS                  RetStatus;
  EFI_USB_HC_PROTOCOL         *UsbHCInterface;
  USB_IO_CONTROLLER_DEVICE    *UsbIoController;
  ENDPOINT_DESC_LIST_ENTRY    *EndPointListEntry;
  UINT32                      TransferResult;

  UsbIoController = USB_IO_CONTROLLER_DEVICE_FROM_USB_IO_THIS (This);
  UsbIoDev        = UsbIoController->UsbDevice;
  UsbHCInterface  = UsbIoDev->BusController->UsbHCInterface;

  //
  // Parameters Checking
  //
  if ((DeviceEndpoint & 0x7F) == 0) {
    return EFI_INVALID_PARAMETER;
  }

  if ((DeviceEndpoint & 0x7F) > 15) {
    return EFI_INVALID_PARAMETER;
  }

  if (Status == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  EndPointListEntry = FindEndPointListEntry (
                        This,
                        DeviceEndpoint
                        );

  if (EndPointListEntry == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if ((EndPointListEntry->EndpointDescriptor.Attributes & 0x03) != 0x02) {
    return EFI_INVALID_PARAMETER;
  }
                        
  //
  // leave the HostController's BulkTransfer
  // to perform other parameters checking
  //
  GetDeviceEndPointMaxPacketLength (
    This,
    DeviceEndpoint,
    &MaxPacketLength
    );

  GetDataToggleBit (
    This,
    DeviceEndpoint,
    &DataToggle
    );

  OldToggle = DataToggle;

  //
  // using HostController's BulkTransfer to complete the request
  //
  RetStatus = UsbHCInterface->BulkTransfer (
                                UsbHCInterface,
                                UsbIoDev->DeviceAddress,
                                DeviceEndpoint,
                                MaxPacketLength,
                                Data,
                                DataLength,
                                &DataToggle,
                                Timeout,
                                &TransferResult
                                );

  if (OldToggle != DataToggle) {
    //
    // Write the toggle back
    //
    SetDataToggleBit (
      This,
      DeviceEndpoint,
      DataToggle
      );
  }

  *Status = TransferResult;

  return RetStatus;
}

STATIC
EFI_STATUS
EFIAPI
UsbSyncInterruptTransfer (
  IN       EFI_USB_IO_PROTOCOL     *This,
  IN       UINT8                   DeviceEndpoint,
  IN OUT   VOID                    *Data,
  IN OUT   UINTN                   *DataLength,
  IN       UINTN                   Timeout,
  OUT      UINT32                  *Status
  )
/*++

  Routine Description:
    Usb Sync Interrupt Transfer

  Arguments:
    This            - Indicates calling context.
    DeviceEndpoint  - The destination USB device endpoint to which the device
                      request is being sent.
    Data            - A pointer to the buffer of data that will be transmitted
                      to USB device or received from USB device.
    DataLength      - On input, the size, in bytes, of the data buffer
                      specified by Data.  On output, the number of bytes that
                      were actually transferred.
    Timeout         - Indicates the transfer should be completed within this
                      time frame.
    Status          - This parameter indicates the USB transfer status.

  Returns:
    EFI_SUCCESS
    EFI_INVALID_PARAMETER
    EFI_OUT_OF_RESOURCES
    EFI_TIMEOUT
    EFI_DEVICE_ERROR

--*/
{
  USB_IO_DEVICE               *UsbIoDev;
  UINT8                       MaxPacketLength;
  UINT8                       DataToggle;
  UINT8                       OldToggle;
  EFI_STATUS                  RetStatus;
  EFI_USB_HC_PROTOCOL         *UsbHCInterface;
  USB_IO_CONTROLLER_DEVICE    *UsbIoController;
  ENDPOINT_DESC_LIST_ENTRY    *EndPointListEntry;

  //
  // Parameters Checking
  //
  if ((DeviceEndpoint & 0x7F) == 0) {
    return EFI_INVALID_PARAMETER;
  }

  if ((DeviceEndpoint & 0x7F) > 15) {
    return EFI_INVALID_PARAMETER;
  }

  if (Status == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  EndPointListEntry = FindEndPointListEntry (
                        This,
                        DeviceEndpoint
                        );

  if (EndPointListEntry == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if ((EndPointListEntry->EndpointDescriptor.Attributes & 0x03) != 0x03) {
    return EFI_INVALID_PARAMETER;
  }
  
  //
  // leave the HostController's SyncInterruptTransfer
  // to perform other parameters checking
  //
  UsbIoController = USB_IO_CONTROLLER_DEVICE_FROM_USB_IO_THIS (This);
  UsbIoDev        = UsbIoController->UsbDevice;
  UsbHCInterface  = UsbIoDev->BusController->UsbHCInterface;

  GetDeviceEndPointMaxPacketLength (
    This,
    DeviceEndpoint,
    &MaxPacketLength
    );

  GetDataToggleBit (
    This,
    DeviceEndpoint,
    &DataToggle
    );

  OldToggle = DataToggle;
  //
  // using HostController's SyncInterruptTransfer to complete the request
  //
  RetStatus = UsbHCInterface->SyncInterruptTransfer (
                                UsbHCInterface,
                                UsbIoDev->DeviceAddress,
                                DeviceEndpoint,
                                UsbIoDev->IsSlowDevice,
                                MaxPacketLength,
                                Data,
                                DataLength,
                                &DataToggle,
                                Timeout,
                                Status
                                );

  if (OldToggle != DataToggle) {
    //
    // Write the toggle back
    //
    SetDataToggleBit (
      This,
      DeviceEndpoint,
      DataToggle
      );
  }

  return RetStatus;
}

STATIC
EFI_STATUS
EFIAPI
UsbAsyncInterruptTransfer (
  IN EFI_USB_IO_PROTOCOL                 *This,
  IN UINT8                               DeviceEndpoint,
  IN BOOLEAN                             IsNewTransfer,
  IN UINTN                               PollingInterval, OPTIONAL
  IN UINTN                               DataLength, OPTIONAL
  IN EFI_ASYNC_USB_TRANSFER_CALLBACK     InterruptCallBack, OPTIONAL
  IN VOID                                *Context OPTIONAL
  )
/*++

  Routine Description:
    Usb Async Interrput Transfer

  Arguments:
    This              -   Indicates calling context.
    DeviceEndpoint    -   The destination USB device endpoint to which the
                          device request is being sent.
    IsNewTransfer     -   If TRUE, a new transfer will be submitted to USB
                          controller.  If FALSE,  the interrupt transfer is
                          deleted from the device's interrupt transfer queue.
    PollingInterval   -   Indicates the periodic rate, in milliseconds, that
                          the transfer is to be executed.
    DataLength        -   Specifies the length, in bytes, of the data to be
                          received from the USB device.
    InterruptCallback -   The Callback function.  This function is called if
                          the asynchronous interrupt transfer is completed.
    Context           -   Passed to InterruptCallback 
  Returns:
    EFI_SUCCESS
    EFI_INVALID_PARAMETER
    EFI_OUT_OF_RESOURCES

--*/
{
  USB_IO_DEVICE               *UsbIoDev;
  UINT8                       MaxPacketLength;
  UINT8                       DataToggle;
  EFI_USB_HC_PROTOCOL         *UsbHCInterface;
  EFI_STATUS                  RetStatus;
  USB_IO_CONTROLLER_DEVICE    *UsbIoController;
  ENDPOINT_DESC_LIST_ENTRY    *EndpointListEntry;

  //
  // Check endpoint
  //
  if ((DeviceEndpoint & 0x7F) == 0) {
    return EFI_INVALID_PARAMETER;
  }

  if ((DeviceEndpoint & 0x7F) > 15) {
    return EFI_INVALID_PARAMETER;
  }

  EndpointListEntry = FindEndPointListEntry (
                        This,
                        DeviceEndpoint
                        );

  if (EndpointListEntry == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if ((EndpointListEntry->EndpointDescriptor.Attributes & 0x03) != 0x03) {
    return EFI_INVALID_PARAMETER;
  }

  UsbIoController = USB_IO_CONTROLLER_DEVICE_FROM_USB_IO_THIS (This);
  UsbIoDev        = UsbIoController->UsbDevice;
  UsbHCInterface  = UsbIoDev->BusController->UsbHCInterface;

  if (!IsNewTransfer) {
    //
    // Delete this transfer
    //
    UsbHCInterface->AsyncInterruptTransfer (
                      UsbHCInterface,
                      UsbIoDev->DeviceAddress,
                      DeviceEndpoint,
                      UsbIoDev->IsSlowDevice,
                      0,
                      FALSE,
                      &DataToggle,
                      PollingInterval,
                      DataLength,
                      NULL,
                      NULL
                      );

    //
    // We need to store the toggle value
    //
    SetDataToggleBit (
      This,
      DeviceEndpoint,
      DataToggle
      );

    return EFI_SUCCESS;
  }

  GetDeviceEndPointMaxPacketLength (
    This,
    DeviceEndpoint,
    &MaxPacketLength
    );

  GetDataToggleBit (
    This,
    DeviceEndpoint,
    &DataToggle
    );

  RetStatus = UsbHCInterface->AsyncInterruptTransfer (
                                UsbHCInterface,
                                UsbIoDev->DeviceAddress,
                                DeviceEndpoint,
                                UsbIoDev->IsSlowDevice,
                                MaxPacketLength,
                                TRUE,
                                &DataToggle,
                                PollingInterval,
                                DataLength,
                                InterruptCallBack,
                                Context
                                );

  return RetStatus;
}

STATIC
EFI_STATUS
EFIAPI
UsbIsochronousTransfer (
  IN       EFI_USB_IO_PROTOCOL     *This,
  IN       UINT8                   DeviceEndpoint,
  IN OUT   VOID                    *Data,
  IN       UINTN                   DataLength,
  OUT      UINT32                  *Status
  )
/*++

  Routine Description:
    Usb Isochronous Transfer

  Arguments:
    This              -   Indicates calling context.
    DeviceEndpoint    -   The destination USB device endpoint to which the
                          device request is being sent.
    Data              -   A pointer to the buffer of data that will be
                          transmitted to USB device or received from USB device.
    DataLength        -   The size, in bytes, of the data buffer specified by
                          Data.
    Status            -   This parameter indicates the USB transfer status.

  Returns:
    EFI_SUCCESS
    EFI_INVALID_PARAMETER
    EFI_OUT_OF_RESOURCES
    EFI_TIMEOUT
    EFI_DEVICE_ERROR
    EFI_UNSUPPORTED
--*/
{
  //
  // Currently we don't support this transfer
  //
  return EFI_UNSUPPORTED;
}

STATIC
EFI_STATUS
EFIAPI
UsbAsyncIsochronousTransfer (
  IN        EFI_USB_IO_PROTOCOL                 *This,
  IN        UINT8                               DeviceEndpoint,
  IN OUT    VOID                                *Data,
  IN        UINTN                               DataLength,
  IN        EFI_ASYNC_USB_TRANSFER_CALLBACK     IsochronousCallBack,
  IN        VOID                                *Context OPTIONAL
  )
/*++

Routine Description:

  Usb Async Isochronous Transfer

Arguments:

  This                - EFI_USB_IO_PROTOCOL
  DeviceEndpoint      - DeviceEndpoint number
  Data                - Data to transfer
  DataLength          - DataLength
  IsochronousCallBack - Isochronous CallBack function
  Context             - Passed to IsochronousCallBack function
Returns:

  EFI_UNSUPPORTED     - Unsupported now

--*/
{
  //
  // Currently we don't support this transfer
  //
  return EFI_UNSUPPORTED;
}
//
// Here is new definitions
//
STATIC
EFI_STATUS
EFIAPI
UsbGetDeviceDescriptor (
  IN  EFI_USB_IO_PROTOCOL           *This,
  OUT EFI_USB_DEVICE_DESCRIPTOR     *DeviceDescriptor
  )
/*++

  Routine Description:
    Retrieves the USB Device Descriptor.

  Arguments:
    This              -   Indicates the calling context.
    DeviceDescriptor  -   A pointer to the caller allocated USB Device
                          Descriptor.

  Returns:
    EFI_SUCCESS
    EFI_INVALID_PARAMETER
    EFI_NOT_FOUND

--*/
{
  USB_IO_CONTROLLER_DEVICE  *UsbIoController;
  USB_IO_DEVICE             *UsbIoDev;

  //
  // This function just wrapps UsbGetDeviceDescriptor.
  //
  
  if (DeviceDescriptor == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  UsbIoController = USB_IO_CONTROLLER_DEVICE_FROM_USB_IO_THIS (This);
  UsbIoDev        = UsbIoController->UsbDevice;

  if (!UsbIoDev->IsConfigured) {
    return EFI_NOT_FOUND;
  }

  CopyMem (
    DeviceDescriptor,
    &UsbIoDev->DeviceDescriptor,
    sizeof (EFI_USB_DEVICE_DESCRIPTOR)
    );

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
EFIAPI
UsbGetActiveConfigDescriptor (
  IN  EFI_USB_IO_PROTOCOL           *This,
  OUT EFI_USB_CONFIG_DESCRIPTOR     *ConfigurationDescriptor
  )
/*++

  Routine Description:
    Retrieves the current USB configuration Descriptor.

  Arguments:
    This                     -   Indicates the calling context.
    ConfigurationDescriptor  -   A pointer to the caller allocated USB active
                                 Configuration Descriptor.

  Returns:
    EFI_SUCCESS
    EFI_INVALID_PARAMETER
    EFI_NOT_FOUND

--*/
{
  USB_IO_DEVICE             *UsbIoDev;
  USB_IO_CONTROLLER_DEVICE  *UsbIoController;

  //
  // This function just wrapps UsbGetActiveConfigDescriptor.
  //
  if (ConfigurationDescriptor == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  UsbIoController = USB_IO_CONTROLLER_DEVICE_FROM_USB_IO_THIS (This);
  UsbIoDev        = UsbIoController->UsbDevice;

  if (!UsbIoDev->IsConfigured) {
    return EFI_NOT_FOUND;
  }

  CopyMem (
    ConfigurationDescriptor,
    &(UsbIoDev->ActiveConfig->CongfigDescriptor),
    sizeof (EFI_USB_CONFIG_DESCRIPTOR)
    );

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
EFIAPI
UsbGetInterfaceDescriptor (
  IN  EFI_USB_IO_PROTOCOL              *This,
  OUT EFI_USB_INTERFACE_DESCRIPTOR     *InterfaceDescriptor
  )
/*++

  Routine Description:
    Retrieves the interface Descriptor for that controller.

  Arguments:
    This                  -   Indicates the calling context.
    InterfaceDescriptor   -   A pointer to the caller allocated USB interface
                              Descriptor.

  Returns:
    EFI_SUCCESS
    EFI_INVALID_PARAMETER
    EFI_NOT_FOUND

--*/
{
  INTERFACE_DESC_LIST_ENTRY *InterfaceListEntry;

  if (InterfaceDescriptor == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  InterfaceListEntry = FindInterfaceListEntry (This);

  if (InterfaceListEntry == NULL) {
    return EFI_NOT_FOUND;
  }

  CopyMem (
    InterfaceDescriptor,
    &(InterfaceListEntry->InterfaceDescriptor),
    sizeof (EFI_USB_INTERFACE_DESCRIPTOR)
    );

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
EFIAPI
UsbGetEndpointDescriptor (
  IN  EFI_USB_IO_PROTOCOL             *This,
  IN  UINT8                           EndpointIndex,
  OUT EFI_USB_ENDPOINT_DESCRIPTOR     *EndpointDescriptor
  )
/*++

  Routine Description:
    Retrieves the endpoint Descriptor for a given endpoint.

  Arguments:
    This                  -   Indicates the calling context.
    EndpointIndex         -   Indicates which endpoint descriptor to retrieve.
                              The valid range is 0..15.
    EndpointDescriptor    -   A pointer to the caller allocated USB Endpoint
                              Descriptor of a USB controller.

  Returns:
    EFI_SUCCESS           -   The endpoint descriptor was retrieved successfully.
    EFI_INVALID_PARAMETER -   EndpointIndex is not valid.
                          -   EndpointDescriptor is NULL.
    EFI_NOT_FOUND         -   The endpoint descriptor cannot be found.
                              The device may not be correctly configured.

--*/
{
  INTERFACE_DESC_LIST_ENTRY *InterfaceListEntry;
  LIST_ENTRY            *EndpointListHead;
  ENDPOINT_DESC_LIST_ENTRY  *EndpointListEntry;

  if (EndpointDescriptor == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (EndpointIndex > 15) {
    return EFI_INVALID_PARAMETER;
  }

  InterfaceListEntry = FindInterfaceListEntry (This);

  if (InterfaceListEntry == NULL) {
    return EFI_NOT_FOUND;
  }

  EndpointListHead  = (LIST_ENTRY *) (&InterfaceListEntry->EndpointDescListHead);
  EndpointListEntry = (ENDPOINT_DESC_LIST_ENTRY *) (EndpointListHead->ForwardLink);

  if (EndpointIndex >= InterfaceListEntry->InterfaceDescriptor.NumEndpoints) {
    return EFI_NOT_FOUND;
  }
  //
  // Loop all endpoint descriptor to get match one.
  //
  while (EndpointIndex != 0) {
    EndpointListEntry = (ENDPOINT_DESC_LIST_ENTRY *) (EndpointListEntry->Link.ForwardLink);
    EndpointIndex--;
  }

  CopyMem (
    EndpointDescriptor,
    &EndpointListEntry->EndpointDescriptor,
    sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)
    );

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
EFIAPI
UsbGetSupportedLanguages (
  IN  EFI_USB_IO_PROTOCOL     *This,
  OUT UINT16                  **LangIDTable,
  OUT UINT16                  *TableSize
  )
/*++

  Routine Description:
    Get all the languages that the USB device supports

  Arguments:
    This        -   Indicates the calling context.
    LangIDTable -   Language ID for the string the caller wants to get.
    TableSize   -   The size, in bytes, of the table LangIDTable.

  Returns:
    EFI_SUCCESS
    EFI_NOT_FOUND

--*/
{
  USB_IO_DEVICE             *UsbIoDev;
  USB_IO_CONTROLLER_DEVICE  *UsbIoController;
  UINTN                     Index;
  BOOLEAN                   Found;

  UsbIoController = USB_IO_CONTROLLER_DEVICE_FROM_USB_IO_THIS (This);
  UsbIoDev        = UsbIoController->UsbDevice;

  Found           = FALSE;
  Index           = 0;
  //
  // Loop language table
  //
  while (UsbIoDev->LangID[Index]) {
    Found = TRUE;
    Index++;
  }

  if (!Found) {
    return EFI_NOT_FOUND;
  }

  *LangIDTable  = UsbIoDev->LangID;
  *TableSize    = (UINT16) Index;

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
EFIAPI
UsbGetStringDescriptor (
  IN  EFI_USB_IO_PROTOCOL     *This,
  IN  UINT16                  LangID,
  IN  UINT8                   StringIndex,
  OUT CHAR16                  **String
  )
/*++

  Routine Description:
    Get a given string descriptor

  Arguments:
    This          -   Indicates the calling context.
    LangID        -   The Language ID for the string being retrieved.
    StringIndex  -   The ID of the string being retrieved.
    String        -   A pointer to a buffer allocated by this function
                      with AllocatePool() to store the string.  If this
                      function returns EFI_SUCCESS, it stores the string
                      the caller wants to get.  The caller should release
                      the string buffer with FreePool() after the string
                      is not used any more.
  Returns:
    EFI_SUCCESS
    EFI_NOT_FOUND
    EFI_OUT_OF_RESOURCES

--*/
{
  UINT32                    Status;
  EFI_STATUS                Result;
  EFI_USB_STRING_DESCRIPTOR *StrDescriptor;
  UINT8                     *Buffer;
  CHAR16                    *UsbString;
  UINT16                    TempBuffer;
  USB_IO_DEVICE             *UsbIoDev;
  UINT8                     Index;
  BOOLEAN                   Found;
  USB_IO_CONTROLLER_DEVICE  *UsbIoController;

  if (StringIndex == 0) {
    return EFI_NOT_FOUND;
  }
  //
  // Search LanguageID, check if it is supported by this device
  //
  if (LangID == 0) {
    return EFI_NOT_FOUND;
  }

  UsbIoController = USB_IO_CONTROLLER_DEVICE_FROM_USB_IO_THIS (This);
  UsbIoDev        = UsbIoController->UsbDevice;

  Found           = FALSE;
  Index           = 0;
  while (UsbIoDev->LangID[Index]) {
    if (UsbIoDev->LangID[Index] == LangID) {
      Found = TRUE;
      break;
    }

    Index++;
  }

  if (!Found) {
    return EFI_NOT_FOUND;
  }
  //
  // Get String Length
  //
  Result = UsbGetString (
            This,
            LangID,
            StringIndex,
            &TempBuffer,
            2,
            &Status
            );
  if (EFI_ERROR (Result)) {
    return EFI_NOT_FOUND;
  }

  StrDescriptor = (EFI_USB_STRING_DESCRIPTOR *) &TempBuffer;

  if (StrDescriptor->Length == 0) {
    return EFI_UNSUPPORTED;
  }

  Buffer = AllocateZeroPool (StrDescriptor->Length);
  if (Buffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Result = UsbGetString (
            This,
            LangID,
            StringIndex,
            Buffer,
            StrDescriptor->Length,
            &Status
            );

  if (EFI_ERROR (Result)) {
    gBS->FreePool (Buffer);
    return EFI_NOT_FOUND;
  }

  StrDescriptor = (EFI_USB_STRING_DESCRIPTOR *) Buffer;

  //
  // UsbString is a UNICODE string
  //
  UsbString = AllocateZeroPool (StrDescriptor->Length);
  if (UsbString == NULL) {
    gBS->FreePool (Buffer);
    return EFI_OUT_OF_RESOURCES;
  }

  CopyMem (
    (VOID *) UsbString,
    Buffer + 2,
    StrDescriptor->Length - 2
    );

  *String = UsbString;

  gBS->FreePool (Buffer);

  return EFI_SUCCESS;
}
