/** @file

  The UHCI driver model and HC protocol routines.

Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "Uhci.h"


EFI_DRIVER_BINDING_PROTOCOL gUhciDriverBinding = {
  UhciDriverBindingSupported,
  UhciDriverBindingStart,
  UhciDriverBindingStop,
  0x20,
  NULL,
  NULL
};

/**
  Provides software reset for the USB host controller according to UEFI 2.0 spec.

  @param  This                   A pointer to the EFI_USB2_HC_PROTOCOL instance.
  @param  Attributes             A bit mask of the reset operation to perform.  See
                                 below for a list of the supported bit mask values.

  @return EFI_SUCCESS            The reset operation succeeded.
  @return EFI_INVALID_PARAMETER  Attributes is not valid.
  @return EFI_UNSUPPORTED        This type of reset is not currently supported.
  @return EFI_DEVICE_ERROR       Other errors.

**/
EFI_STATUS
EFIAPI
Uhci2Reset (
  IN EFI_USB2_HC_PROTOCOL   *This,
  IN UINT16                 Attributes
  )
{
  USB_HC_DEV          *Uhc;
  EFI_TPL             OldTpl;

  if ((Attributes == EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG) ||
      (Attributes == EFI_USB_HC_RESET_HOST_WITH_DEBUG)) {
    return EFI_UNSUPPORTED;
  }

  Uhc = UHC_FROM_USB2_HC_PROTO (This);

  if (Uhc->DevicePath != NULL) {
    //
    // Report Status Code to indicate reset happens
    //
    REPORT_STATUS_CODE_WITH_DEVICE_PATH (
      EFI_PROGRESS_CODE,
      (EFI_IO_BUS_USB | EFI_IOB_PC_RESET),
      Uhc->DevicePath
      );
  }

  OldTpl  = gBS->RaiseTPL (UHCI_TPL);

  switch (Attributes) {
  case EFI_USB_HC_RESET_GLOBAL:
    //
    // Stop schedule and set the Global Reset bit in the command register
    //
    UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT);
    UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_GRESET);

    gBS->Stall (UHC_ROOT_PORT_RESET_STALL);

    //
    // Clear the Global Reset bit to zero.
    //
    UhciClearRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_GRESET);

    gBS->Stall (UHC_ROOT_PORT_RECOVERY_STALL);
    break;

  case EFI_USB_HC_RESET_HOST_CONTROLLER:
    //
    // Stop schedule and set Host Controller Reset bit to 1
    //
    UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT);
    UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_HCRESET);

    gBS->Stall (UHC_ROOT_PORT_RECOVERY_STALL);
    break;

  default:
    goto ON_INVAILD_PARAMETER;
  }

  //
  // Delete all old transactions on the USB bus, then
  // reinitialize the frame list
  //
  UhciFreeAllAsyncReq (Uhc);
  UhciDestoryFrameList (Uhc);
  UhciInitFrameList (Uhc);

  gBS->RestoreTPL (OldTpl);

  return EFI_SUCCESS;

ON_INVAILD_PARAMETER:

  gBS->RestoreTPL (OldTpl);

  return EFI_INVALID_PARAMETER;
}


/**
  Retrieves current state of the USB host controller according to UEFI 2.0 spec.

  @param  This                   A pointer to the EFI_USB2_HC_PROTOCOL instance.
  @param  State                  Variable to receive current device state.

  @return EFI_SUCCESS            The state is returned.
  @return EFI_INVALID_PARAMETER  State is not valid.
  @return EFI_DEVICE_ERROR       Other errors.

**/
EFI_STATUS
EFIAPI
Uhci2GetState (
  IN   EFI_USB2_HC_PROTOCOL   *This,
  OUT  EFI_USB_HC_STATE       *State
  )
{
  USB_HC_DEV          *Uhc;
  UINT16              UsbSts;
  UINT16              UsbCmd;

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

  Uhc     = UHC_FROM_USB2_HC_PROTO (This);

  UsbCmd  = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET);
  UsbSts  = UhciReadReg (Uhc->PciIo, USBSTS_OFFSET);

  if ((UsbCmd & USBCMD_EGSM) !=0 ) {
    *State = EfiUsbHcStateSuspend;

  } else if ((UsbSts & USBSTS_HCH) != 0) {
    *State = EfiUsbHcStateHalt;

  } else {
    *State = EfiUsbHcStateOperational;
  }

  return EFI_SUCCESS;
}


/**
  Sets the USB host controller to a specific state according to UEFI 2.0 spec.

  @param  This                   A pointer to the EFI_USB2_HC_PROTOCOL instance.
  @param  State                  Indicates the state of the host controller that will
                                 be set.

  @return EFI_SUCCESS            Host controller was successfully placed in the state.
  @return EFI_INVALID_PARAMETER  State is invalid.
  @return EFI_DEVICE_ERROR       Failed to set the state.

**/
EFI_STATUS
EFIAPI
Uhci2SetState (
  IN EFI_USB2_HC_PROTOCOL    *This,
  IN EFI_USB_HC_STATE        State
  )
{
  EFI_USB_HC_STATE    CurState;
  USB_HC_DEV          *Uhc;
  EFI_TPL             OldTpl;
  EFI_STATUS          Status;
  UINT16              UsbCmd;

  Uhc     = UHC_FROM_USB2_HC_PROTO (This);
  Status  = Uhci2GetState (This, &CurState);

  if (EFI_ERROR (Status)) {
    return EFI_DEVICE_ERROR;
  }

  if (CurState == State) {
    return EFI_SUCCESS;
  }

  Status  = EFI_SUCCESS;
  OldTpl  = gBS->RaiseTPL (UHCI_TPL);

  switch (State) {
  case EfiUsbHcStateHalt:
    Status = UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT);
    break;

  case EfiUsbHcStateOperational:
    UsbCmd = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET);

    if (CurState == EfiUsbHcStateHalt) {
      //
      // Set Run/Stop bit to 1, also set the bandwidht reclamation
      // point to 64 bytes
      //
      UsbCmd |= USBCMD_RS | USBCMD_MAXP;
      UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd);

    } else if (CurState == EfiUsbHcStateSuspend) {
      //
      // If FGR(Force Global Resume) bit is 0, set it
      //
      if ((UsbCmd & USBCMD_FGR) == 0) {
        UsbCmd |= USBCMD_FGR;
        UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd);
      }

      //
      // wait 20ms to let resume complete (20ms is specified by UHCI spec)
      //
      gBS->Stall (UHC_FORCE_GLOBAL_RESUME_STALL);

      //
      // Write FGR bit to 0 and EGSM(Enter Global Suspend Mode) bit to 0
      //
      UsbCmd &= ~USBCMD_FGR;
      UsbCmd &= ~USBCMD_EGSM;
      UsbCmd |= USBCMD_RS;
      UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd);
    }

    break;

  case EfiUsbHcStateSuspend:
    Status = Uhci2SetState (This, EfiUsbHcStateHalt);

    if (EFI_ERROR (Status)) {
      Status = EFI_DEVICE_ERROR;
      goto ON_EXIT;
    }

    //
    // Set Enter Global Suspend Mode bit to 1.
    //
    UsbCmd = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET);
    UsbCmd |= USBCMD_EGSM;
    UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd);
    break;

  default:
    Status = EFI_INVALID_PARAMETER;
    break;
  }

ON_EXIT:
  gBS->RestoreTPL (OldTpl);
  return Status;
}

/**
  Retrieves capabilities of USB host controller according to UEFI 2.0 spec.

  @param  This                   A pointer to the EFI_USB2_HC_PROTOCOL instance.
  @param  MaxSpeed               A pointer to the max speed USB host controller
                                 supports.
  @param  PortNumber             A pointer to the number of root hub ports.
  @param  Is64BitCapable         A pointer to an integer to show whether USB host
                                 controller supports 64-bit memory addressing.

  @return EFI_SUCCESS            capabilities were retrieved successfully.
  @return EFI_INVALID_PARAMETER  MaxSpeed or PortNumber or Is64BitCapable is NULL.
  @return EFI_DEVICE_ERROR       An error was encountered.

**/
EFI_STATUS
EFIAPI
Uhci2GetCapability (
  IN  EFI_USB2_HC_PROTOCOL  *This,
  OUT UINT8                 *MaxSpeed,
  OUT UINT8                 *PortNumber,
  OUT UINT8                 *Is64BitCapable
  )
{
  USB_HC_DEV          *Uhc;
  UINT32              Offset;
  UINT16              PortSC;
  UINT32              Index;

  Uhc = UHC_FROM_USB2_HC_PROTO (This);

  if ((NULL == MaxSpeed) || (NULL == PortNumber) || (NULL == Is64BitCapable)) {
    return EFI_INVALID_PARAMETER;
  }

  *MaxSpeed       = EFI_USB_SPEED_FULL;
  *Is64BitCapable = (UINT8) FALSE;

  *PortNumber = 0;

  for (Index = 0; Index < USB_MAX_ROOTHUB_PORT; Index++) {
    Offset  = USBPORTSC_OFFSET + Index * 2;
    PortSC  = UhciReadReg (Uhc->PciIo, Offset);

    //
    // Port status's bit 7 is reserved and always returns 1 if
    // the port number is valid. Intel's UHCI (in EHCI controller)
    // returns 0 in this bit if port number is invalid. Also, if
    // PciIo IoRead returns error, 0xFFFF is returned to caller.
    //
    if (((PortSC & 0x80) == 0) || (PortSC == 0xFFFF)) {
      break;
    }
    (*PortNumber)++;
  }

  Uhc->RootPorts = *PortNumber;

  DEBUG ((EFI_D_INFO, "Uhci2GetCapability: %d ports\n", (UINT32)Uhc->RootPorts));
  return EFI_SUCCESS;
}


/**
  Retrieves the current status of a USB root hub port according to UEFI 2.0 spec.

  @param  This                    A pointer to the EFI_USB2_HC_PROTOCOL.
  @param  PortNumber              The port to get status.
  @param  PortStatus              A pointer to the current port status bits and  port
                                  status change bits.

  @return EFI_SUCCESS             status of the USB root hub port was returned in PortStatus.
  @return EFI_INVALID_PARAMETER   PortNumber is invalid.
  @return EFI_DEVICE_ERROR        Can't read register.

**/
EFI_STATUS
EFIAPI
Uhci2GetRootHubPortStatus (
  IN   EFI_USB2_HC_PROTOCOL   *This,
  IN   UINT8                  PortNumber,
  OUT  EFI_USB_PORT_STATUS    *PortStatus
  )
{
  USB_HC_DEV          *Uhc;
  UINT32              Offset;
  UINT16              PortSC;

  Uhc = UHC_FROM_USB2_HC_PROTO (This);

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

  if (PortNumber >= Uhc->RootPorts) {
    return EFI_INVALID_PARAMETER;
  }

  Offset                        = USBPORTSC_OFFSET + PortNumber * 2;
  PortStatus->PortStatus        = 0;
  PortStatus->PortChangeStatus  = 0;

  PortSC                        = UhciReadReg (Uhc->PciIo, Offset);

  if ((PortSC & USBPORTSC_CCS) != 0) {
    PortStatus->PortStatus |= USB_PORT_STAT_CONNECTION;
  }

  if ((PortSC & USBPORTSC_PED) != 0) {
    PortStatus->PortStatus |= USB_PORT_STAT_ENABLE;
  }

  if ((PortSC & USBPORTSC_SUSP) != 0) {
    DEBUG ((EFI_D_INFO, "Uhci2GetRootHubPortStatus: port %d is suspended\n", PortNumber));
    PortStatus->PortStatus |= USB_PORT_STAT_SUSPEND;
  }

  if ((PortSC & USBPORTSC_PR) != 0) {
    PortStatus->PortStatus |= USB_PORT_STAT_RESET;
  }

  if ((PortSC & USBPORTSC_LSDA) != 0) {
    PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED;
  }

  //
  // CHC will always return one in port owner bit
  //
  PortStatus->PortStatus |= USB_PORT_STAT_OWNER;

  if ((PortSC & USBPORTSC_CSC) != 0) {
    PortStatus->PortChangeStatus |= USB_PORT_STAT_C_CONNECTION;
  }

  if ((PortSC & USBPORTSC_PEDC) != 0) {
    PortStatus->PortChangeStatus |= USB_PORT_STAT_C_ENABLE;
  }

  return EFI_SUCCESS;
}


/**
  Sets a feature for the specified root hub port according to UEFI 2.0 spec.

  @param  This                   A pointer to the EFI_USB2_HC_PROTOCOL.
  @param  PortNumber             Specifies the root hub port whose feature  is
                                 requested to be set.
  @param  PortFeature            Indicates the feature selector associated  with the
                                 feature set request.

  @return EFI_SUCCESS            PortFeature was set for the root port.
  @return EFI_INVALID_PARAMETER  PortNumber is invalid or PortFeature is invalid.
  @return EFI_DEVICE_ERROR       Can't read register.

**/
EFI_STATUS
EFIAPI
Uhci2SetRootHubPortFeature (
  IN EFI_USB2_HC_PROTOCOL    *This,
  IN UINT8                   PortNumber,
  IN EFI_USB_PORT_FEATURE    PortFeature
  )
{
  USB_HC_DEV          *Uhc;
  EFI_TPL             OldTpl;
  UINT32              Offset;
  UINT16              PortSC;
  UINT16              Command;

  Uhc = UHC_FROM_USB2_HC_PROTO (This);

  if (PortNumber >= Uhc->RootPorts) {
    return EFI_INVALID_PARAMETER;
  }

  Offset  = USBPORTSC_OFFSET + PortNumber * 2;

  OldTpl  = gBS->RaiseTPL (UHCI_TPL);
  PortSC  = UhciReadReg (Uhc->PciIo, Offset);

  switch (PortFeature) {
  case EfiUsbPortSuspend:
    Command = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET);
    if ((Command & USBCMD_EGSM) == 0) {
      //
      // if global suspend is not active, can set port suspend
      //
      PortSC &= 0xfff5;
      PortSC |= USBPORTSC_SUSP;
    }
    break;

  case EfiUsbPortReset:
    PortSC &= 0xfff5;
    PortSC |= USBPORTSC_PR;
    break;

  case EfiUsbPortPower:
    //
    // No action
    //
    break;

  case EfiUsbPortEnable:
    PortSC &= 0xfff5;
    PortSC |= USBPORTSC_PED;
    break;

  default:
    gBS->RestoreTPL (OldTpl);
    return EFI_INVALID_PARAMETER;
  }

  UhciWriteReg (Uhc->PciIo, Offset, PortSC);
  gBS->RestoreTPL (OldTpl);

  return EFI_SUCCESS;
}


/**
  Clears a feature for the specified root hub port according to Uefi 2.0 spec.

  @param  This                   A pointer to the EFI_USB2_HC_PROTOCOL instance.
  @param  PortNumber             Specifies the root hub port whose feature  is
                                 requested to be cleared.
  @param  PortFeature            Indicates the feature selector associated with the
                                 feature clear request.

  @return EFI_SUCCESS            PortFeature was cleared for the USB root hub port.
  @return EFI_INVALID_PARAMETER  PortNumber is invalid or PortFeature is invalid.
  @return EFI_DEVICE_ERROR       Can't read register.

**/
EFI_STATUS
EFIAPI
Uhci2ClearRootHubPortFeature (
  IN EFI_USB2_HC_PROTOCOL    *This,
  IN UINT8                   PortNumber,
  IN EFI_USB_PORT_FEATURE    PortFeature
  )
{
  USB_HC_DEV          *Uhc;
  EFI_TPL             OldTpl;
  UINT32              Offset;
  UINT16              PortSC;

  Uhc = UHC_FROM_USB2_HC_PROTO (This);

  if (PortNumber >= Uhc->RootPorts) {
    return EFI_INVALID_PARAMETER;
  }

  Offset  = USBPORTSC_OFFSET + PortNumber * 2;

  OldTpl  = gBS->RaiseTPL (UHCI_TPL);
  PortSC  = UhciReadReg (Uhc->PciIo, Offset);

  switch (PortFeature) {
  case EfiUsbPortEnable:
    PortSC &= 0xfff5;
    PortSC &= ~USBPORTSC_PED;
    break;

  case EfiUsbPortSuspend:
    //
    // Cause a resume on the specified port if in suspend mode.
    //
    PortSC &= 0xfff5;
    PortSC &= ~USBPORTSC_SUSP;
    break;

  case EfiUsbPortPower:
    //
    // No action
    //
    break;

  case EfiUsbPortReset:
    PortSC &= 0xfff5;
    PortSC &= ~USBPORTSC_PR;
    break;

  case EfiUsbPortConnectChange:
    PortSC &= 0xfff5;
    PortSC |= USBPORTSC_CSC;
    break;

  case EfiUsbPortEnableChange:
    PortSC &= 0xfff5;
    PortSC |= USBPORTSC_PEDC;
    break;

  case EfiUsbPortSuspendChange:
    //
    // Root hub does not support this
    //
    break;

  case EfiUsbPortOverCurrentChange:
    //
    // Root hub does not support this
    //
    break;

  case EfiUsbPortResetChange:
    //
    // Root hub does not support this
    //
    break;

  default:
    gBS->RestoreTPL (OldTpl);
    return EFI_INVALID_PARAMETER;
  }

  UhciWriteReg (Uhc->PciIo, Offset, PortSC);
  gBS->RestoreTPL (OldTpl);

  return EFI_SUCCESS;
}


/**
  Submits control transfer to a target USB device according to UEFI 2.0 spec.

  @param  This                   A pointer to the EFI_USB2_HC_PROTOCOL instance.
  @param  DeviceAddress          Target device address.
  @param  DeviceSpeed            Device speed.
  @param  MaximumPacketLength    Maximum packet size of the target endpoint.
  @param  Request                USB device request to send.
  @param  TransferDirection      Data direction of the Data stage in control transfer.
  @param  Data                   Data to transmit/receive in data stage.
  @param  DataLength             Length of the data.
  @param  TimeOut                Maximum time, in microseconds, for transfer to complete.
  @param  Translator             Transaction translator to be used by this device.
  @param  TransferResult         Variable to receive the transfer result.

  @return EFI_SUCCESS            The control transfer was completed successfully.
  @return EFI_OUT_OF_RESOURCES   Failed due to lack of resource.
  @return EFI_INVALID_PARAMETER  Some parameters are invalid.
  @return EFI_TIMEOUT            Failed due to timeout.
  @return EFI_DEVICE_ERROR       Failed due to host controller or device error.

**/
EFI_STATUS
EFIAPI
Uhci2ControlTransfer (
  IN     EFI_USB2_HC_PROTOCOL                 *This,
  IN     UINT8                                DeviceAddress,
  IN     UINT8                                DeviceSpeed,
  IN     UINTN                                MaximumPacketLength,
  IN     EFI_USB_DEVICE_REQUEST               *Request,
  IN     EFI_USB_DATA_DIRECTION               TransferDirection,
  IN OUT VOID                                 *Data,
  IN OUT UINTN                                *DataLength,
  IN     UINTN                                TimeOut,
  IN     EFI_USB2_HC_TRANSACTION_TRANSLATOR   *Translator,
  OUT    UINT32                               *TransferResult
  )
{
  USB_HC_DEV              *Uhc;
  UHCI_TD_SW              *TDs;
  EFI_TPL                 OldTpl;
  EFI_STATUS              Status;
  UHCI_QH_RESULT          QhResult;
  UINT8                   PktId;
  UINT8                   *RequestPhy;
  VOID                    *RequestMap;
  UINT8                   *DataPhy;
  VOID                    *DataMap;
  BOOLEAN                 IsSlowDevice;
  UINTN                   TransferDataLength;

  Uhc         = UHC_FROM_USB2_HC_PROTO (This);
  TDs         = NULL;
  DataPhy     = NULL;
  DataMap     = NULL;
  RequestPhy  = NULL;
  RequestMap  = NULL;

  IsSlowDevice  = (BOOLEAN) ((EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE);

  //
  // Parameters Checking
  //
  if (Request == NULL || TransferResult == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (IsSlowDevice && (MaximumPacketLength != 8)) {
    return EFI_INVALID_PARAMETER;
  }

  if ((MaximumPacketLength != 8) &&  (MaximumPacketLength != 16) &&
      (MaximumPacketLength != 32) && (MaximumPacketLength != 64)) {

    return EFI_INVALID_PARAMETER;
  }

  if ((TransferDirection != EfiUsbNoData) && (Data == NULL || DataLength == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if (TransferDirection == EfiUsbNoData) {
    TransferDataLength = 0;
  } else {
    TransferDataLength = *DataLength;
  }

  *TransferResult = EFI_USB_ERR_SYSTEM;
  Status          = EFI_DEVICE_ERROR;

  //
  // If errors exist that cause host controller halt,
  // clear status then return EFI_DEVICE_ERROR.
  //
  UhciAckAllInterrupt (Uhc);

  if (!UhciIsHcWorking (Uhc->PciIo)) {
    return EFI_DEVICE_ERROR;
  }

  OldTpl = gBS->RaiseTPL (UHCI_TPL);

  //
  // Map the Request and data for bus master access,
  // then create a list of TD for this transfer
  //
  Status = UhciMapUserRequest (Uhc, Request, &RequestPhy, &RequestMap);

  if (EFI_ERROR (Status)) {
    goto ON_EXIT;
  }

  Status = UhciMapUserData (Uhc, TransferDirection, Data, DataLength, &PktId, &DataPhy, &DataMap);

  if (EFI_ERROR (Status)) {
    Uhc->PciIo->Unmap (Uhc->PciIo, RequestMap);
    goto ON_EXIT;
  }

  TDs = UhciCreateCtrlTds (
          Uhc,
          DeviceAddress,
          PktId,
          (UINT8*)Request,
          RequestPhy,
          (UINT8*)Data,
          DataPhy,
          TransferDataLength,
          (UINT8) MaximumPacketLength,
          IsSlowDevice
          );

  if (TDs == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto UNMAP_DATA;
  }

  //
  // According to the speed of the end point, link
  // the TD to corrosponding queue head, then check
  // the execution result
  //
  UhciLinkTdToQh (Uhc, Uhc->CtrlQh, TDs);
  Status = UhciExecuteTransfer (Uhc, Uhc->CtrlQh, TDs, TimeOut, IsSlowDevice, &QhResult);
  UhciUnlinkTdFromQh (Uhc->CtrlQh, TDs);

  Uhc->PciIo->Flush (Uhc->PciIo);

  *TransferResult = QhResult.Result;

  if (DataLength != NULL) {
    *DataLength = QhResult.Complete;
  }

  UhciDestoryTds (Uhc, TDs);

UNMAP_DATA:
  Uhc->PciIo->Unmap (Uhc->PciIo, DataMap);
  Uhc->PciIo->Unmap (Uhc->PciIo, RequestMap);

ON_EXIT:
  gBS->RestoreTPL (OldTpl);
  return Status;
}


/**
  Submits bulk transfer to a bulk endpoint of a USB device.

  @param  This                   A pointer to the EFI_USB2_HC_PROTOCOL instance.
  @param  DeviceAddress          Target device address.
  @param  EndPointAddress        Endpoint number and direction.
  @param  DeviceSpeed            Device speed.
  @param  MaximumPacketLength    Maximum packet size of the target endpoint.
  @param  DataBuffersNumber      Number of data buffers prepared for the transfer.
  @param  Data                   Array of pointers to the buffers of data.
  @param  DataLength             On input, size of the data buffer, On output,
                                 actually transferred data size.
  @param  DataToggle             On input, data toggle to use; On output, next data toggle.
  @param  TimeOut                Maximum time out, in microseconds.
  @param  Translator             A pointr to the transaction translator data.
  @param  TransferResult         Variable to receive transfer result.

  @return EFI_SUCCESS            The bulk transfer was completed successfully.
  @return EFI_OUT_OF_RESOURCES   Failed due to lack of resource.
  @return EFI_INVALID_PARAMETER  Some parameters are invalid.
  @return EFI_TIMEOUT            Failed due to timeout.
  @return EFI_DEVICE_ERROR       Failed due to host controller or device error.

**/
EFI_STATUS
EFIAPI
Uhci2BulkTransfer (
  IN     EFI_USB2_HC_PROTOCOL               *This,
  IN     UINT8                              DeviceAddress,
  IN     UINT8                              EndPointAddress,
  IN     UINT8                              DeviceSpeed,
  IN     UINTN                              MaximumPacketLength,
  IN     UINT8                              DataBuffersNumber,
  IN OUT VOID                               *Data[EFI_USB_MAX_BULK_BUFFER_NUM],
  IN OUT UINTN                              *DataLength,
  IN OUT UINT8                              *DataToggle,
  IN     UINTN                              TimeOut,
  IN     EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
  OUT    UINT32                             *TransferResult
  )
{
  EFI_USB_DATA_DIRECTION  Direction;
  EFI_TPL                 OldTpl;
  USB_HC_DEV              *Uhc;
  UHCI_TD_SW              *TDs;
  UHCI_QH_SW              *BulkQh;
  UHCI_QH_RESULT          QhResult;
  EFI_STATUS              Status;
  UINT8                   PktId;
  UINT8                   *DataPhy;
  VOID                    *DataMap;

  Uhc     = UHC_FROM_USB2_HC_PROTO (This);
  DataPhy = NULL;
  DataMap = NULL;

  if (DeviceSpeed == EFI_USB_SPEED_LOW) {
    return EFI_INVALID_PARAMETER;
  }

  if ((DataLength == NULL) || (*DataLength == 0) || (Data == NULL) || (TransferResult == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if ((*DataToggle != 1) && (*DataToggle != 0)) {
    return EFI_INVALID_PARAMETER;
  }

  if ((MaximumPacketLength != 8) && (MaximumPacketLength != 16) &&
      (MaximumPacketLength != 32) && (MaximumPacketLength != 64)) {
    return EFI_INVALID_PARAMETER;
  }

  *TransferResult = EFI_USB_ERR_SYSTEM;
  Status          = EFI_OUT_OF_RESOURCES;

  //
  // If has errors that cause host controller halt,
  // then return EFI_DEVICE_ERROR directly.
  //
  UhciAckAllInterrupt (Uhc);

  if (!UhciIsHcWorking (Uhc->PciIo)) {
    return EFI_DEVICE_ERROR;
  }

  OldTpl = gBS->RaiseTPL (UHCI_TPL);

  //
  // Map the source data buffer for bus master access,
  // then create a list of TDs
  //
  if ((EndPointAddress & 0x80) != 0) {
    Direction = EfiUsbDataIn;
  } else {
    Direction = EfiUsbDataOut;
  }

  Status = UhciMapUserData (Uhc, Direction, *Data, DataLength, &PktId, &DataPhy, &DataMap);

  if (EFI_ERROR (Status)) {
    goto ON_EXIT;
  }

  Status = EFI_OUT_OF_RESOURCES;
  TDs    = UhciCreateBulkOrIntTds (
             Uhc,
             DeviceAddress,
             EndPointAddress,
             PktId,
             (UINT8 *)*Data,
             DataPhy,
             *DataLength,
             DataToggle,
             (UINT8) MaximumPacketLength,
             FALSE
             );

  if (TDs == NULL) {
    Uhc->PciIo->Unmap (Uhc->PciIo, DataMap);
    goto ON_EXIT;
  }


  //
  // Link the TDs to bulk queue head. According to the platfore
  // defintion of UHCI_NO_BW_RECLAMATION, BulkQh is either configured
  // to do full speed bandwidth reclamation or not.
  //
  BulkQh = Uhc->BulkQh;

  UhciLinkTdToQh (Uhc, BulkQh, TDs);
  Status = UhciExecuteTransfer (Uhc, BulkQh, TDs, TimeOut, FALSE, &QhResult);
  UhciUnlinkTdFromQh (BulkQh, TDs);

  Uhc->PciIo->Flush (Uhc->PciIo);

  *TransferResult = QhResult.Result;
  *DataToggle     = QhResult.NextToggle;
  *DataLength     = QhResult.Complete;

  UhciDestoryTds (Uhc, TDs);
  Uhc->PciIo->Unmap (Uhc->PciIo, DataMap);

ON_EXIT:
  gBS->RestoreTPL (OldTpl);
  return Status;
}


/**
  Submits an asynchronous interrupt transfer to an
  interrupt endpoint of a USB device according to UEFI 2.0 spec.

  @param  This                   A pointer to the EFI_USB2_HC_PROTOCOL instance.
  @param  DeviceAddress          Target device address.
  @param  EndPointAddress        Endpoint number and direction.
  @param  DeviceSpeed            Device speed.
  @param  MaximumPacketLength    Maximum packet size of the target endpoint.
  @param  IsNewTransfer          If TRUE, submit a new transfer, if FALSE cancel old transfer.
  @param  DataToggle             On input, data toggle to use; On output, next data toggle.
  @param  PollingInterval        Interrupt poll rate in milliseconds.
  @param  DataLength             On input, size of the data buffer, On output,
                                 actually transferred data size.
  @param  Translator             A pointr to the transaction translator data.
  @param  CallBackFunction       Function to call periodically.
  @param  Context                User context.

  @return EFI_SUCCESS            Transfer was submitted.
  @return EFI_INVALID_PARAMETER  Some parameters are invalid.
  @return EFI_OUT_OF_RESOURCES   Failed due to a lack of resources.
  @return EFI_DEVICE_ERROR       Can't read register.

**/
EFI_STATUS
EFIAPI
Uhci2AsyncInterruptTransfer (
  IN     EFI_USB2_HC_PROTOCOL               *This,
  IN     UINT8                              DeviceAddress,
  IN     UINT8                              EndPointAddress,
  IN     UINT8                              DeviceSpeed,
  IN     UINTN                              MaximumPacketLength,
  IN     BOOLEAN                            IsNewTransfer,
  IN OUT UINT8                              *DataToggle,
  IN     UINTN                              PollingInterval,
  IN     UINTN                              DataLength,
  IN     EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
  IN     EFI_ASYNC_USB_TRANSFER_CALLBACK    CallBackFunction,
  IN     VOID                               *Context
  )
{
  USB_HC_DEV          *Uhc;
  BOOLEAN             IsSlowDevice;
  UHCI_QH_SW          *Qh;
  UHCI_TD_SW          *IntTds;
  EFI_TPL             OldTpl;
  EFI_STATUS          Status;
  UINT8               *DataPtr;
  UINT8               *DataPhy;
  UINT8               PktId;

  Uhc       = UHC_FROM_USB2_HC_PROTO (This);
  Qh        = NULL;
  IntTds    = NULL;
  DataPtr   = NULL;
  DataPhy   = NULL;

  IsSlowDevice  = (BOOLEAN) ((EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE);

  if ((EndPointAddress & 0x80) == 0) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Delete Async interrupt transfer request
  //
  if (!IsNewTransfer) {
    OldTpl = gBS->RaiseTPL (UHCI_TPL);
    Status = UhciRemoveAsyncReq (Uhc, DeviceAddress, EndPointAddress, DataToggle);

    gBS->RestoreTPL (OldTpl);
    return Status;
  }

  if (PollingInterval < 1 || PollingInterval > 255) {
    return EFI_INVALID_PARAMETER;
  }

  if (DataLength == 0) {
    return EFI_INVALID_PARAMETER;
  }

  if ((*DataToggle != 1) && (*DataToggle != 0)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // If has errors that cause host controller halt,
  // then return EFI_DEVICE_ERROR directly.
  //
  UhciAckAllInterrupt (Uhc);

  if (!UhciIsHcWorking (Uhc->PciIo)) {
    return EFI_DEVICE_ERROR;
  }

  if ((EndPointAddress & 0x80) == 0) {
    PktId = OUTPUT_PACKET_ID;
  } else {
    PktId = INPUT_PACKET_ID;
  }

  //
  // Allocate and map source data buffer for bus master access.
  //
  DataPtr = UsbHcAllocateMem (Uhc->MemPool, DataLength);

  if (DataPtr == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  DataPhy = (UINT8 *) (UINTN) UsbHcGetPciAddressForHostMem (Uhc->MemPool, DataPtr, DataLength);

  OldTpl = gBS->RaiseTPL (UHCI_TPL);

  Qh = UhciCreateQh (Uhc, PollingInterval);

  if (Qh == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto FREE_DATA;
  }

  IntTds = UhciCreateBulkOrIntTds (
             Uhc,
             DeviceAddress,
             EndPointAddress,
             PktId,
             DataPtr,
             DataPhy,
             DataLength,
             DataToggle,
             (UINT8) MaximumPacketLength,
             IsSlowDevice
             );

  if (IntTds == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto DESTORY_QH;
  }

  UhciLinkTdToQh (Uhc, Qh, IntTds);

  //
  // Save QH-TD structures to async Interrupt transfer list,
  // for monitor interrupt transfer execution routine use.
  //
  Status = UhciCreateAsyncReq (
             Uhc,
             Qh,
             IntTds,
             DeviceAddress,
             EndPointAddress,
             DataLength,
             PollingInterval,
             DataPtr,
             CallBackFunction,
             Context,
             IsSlowDevice
             );

  if (EFI_ERROR (Status)) {
    goto DESTORY_QH;
  }

  UhciLinkQhToFrameList (Uhc, Qh);

  gBS->RestoreTPL (OldTpl);
  return EFI_SUCCESS;

DESTORY_QH:
  UsbHcFreeMem (Uhc->MemPool, Qh, sizeof (UHCI_QH_SW));

FREE_DATA:
  UsbHcFreeMem (Uhc->MemPool, DataPtr, DataLength);
  Uhc->PciIo->Flush (Uhc->PciIo);

  gBS->RestoreTPL (OldTpl);
  return Status;
}

/**
  Submits synchronous interrupt transfer to an interrupt endpoint
  of a USB device according to UEFI 2.0 spec.


  @param  This                   A pointer to the EFI_USB2_HC_PROTOCOL instance.
  @param  DeviceAddress          Target device address.
  @param  EndPointAddress        Endpoint number and direction.
  @param  DeviceSpeed            Device speed.
  @param  MaximumPacketLength    Maximum packet size of the target endpoint.
  @param  Data                   Array of pointers to the buffers of data.
  @param  DataLength             On input, size of the data buffer, On output,
                                 actually transferred data size.
  @param  DataToggle             On input, data toggle to use; On output, next data toggle.
  @param  TimeOut                Maximum time out, in microseconds.
  @param  Translator             A pointr to the transaction translator data.
  @param  TransferResult         Variable to receive transfer result.

  @return EFI_SUCCESS            The transfer was completed successfully.
  @return EFI_OUT_OF_RESOURCES   Failed due to lack of resource.
  @return EFI_INVALID_PARAMETER  Some parameters are invalid.
  @return EFI_TIMEOUT            Failed due to timeout.
  @return EFI_DEVICE_ERROR       Failed due to host controller or device error.

**/
EFI_STATUS
EFIAPI
Uhci2SyncInterruptTransfer (
  IN     EFI_USB2_HC_PROTOCOL                      *This,
  IN     UINT8                                     DeviceAddress,
  IN     UINT8                                     EndPointAddress,
  IN     UINT8                                     DeviceSpeed,
  IN     UINTN                                     MaximumPacketLength,
  IN OUT VOID                                      *Data,
  IN OUT UINTN                                     *DataLength,
  IN OUT UINT8                                     *DataToggle,
  IN     UINTN                                     TimeOut,
  IN     EFI_USB2_HC_TRANSACTION_TRANSLATOR        *Translator,
  OUT    UINT32                                    *TransferResult
  )
{
  EFI_STATUS          Status;
  USB_HC_DEV          *Uhc;
  UHCI_TD_SW          *TDs;
  UHCI_QH_RESULT      QhResult;
  EFI_TPL             OldTpl;
  UINT8               *DataPhy;
  VOID                *DataMap;
  UINT8               PktId;
  BOOLEAN             IsSlowDevice;

  Uhc     = UHC_FROM_USB2_HC_PROTO (This);
  DataPhy = NULL;
  DataMap = NULL;
  TDs     = NULL;

  if (DeviceSpeed == EFI_USB_SPEED_HIGH) {
    return EFI_INVALID_PARAMETER;
  }

  IsSlowDevice  = (BOOLEAN) ((EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE);

  if ((DataLength == NULL) || (Data == NULL) || (TransferResult == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if ((*DataToggle != 1) && (*DataToggle != 0)) {
    return EFI_INVALID_PARAMETER;
  }

  if ((*DataLength == 0) || (MaximumPacketLength > 64)) {
    return EFI_INVALID_PARAMETER;
  }

  if (IsSlowDevice && (MaximumPacketLength > 8)) {
    return EFI_INVALID_PARAMETER;
  }

  *TransferResult = EFI_USB_ERR_SYSTEM;
  Status          = EFI_DEVICE_ERROR;


  UhciAckAllInterrupt (Uhc);

  if (!UhciIsHcWorking (Uhc->PciIo)) {
    return Status;
  }

  OldTpl = gBS->RaiseTPL (UHCI_TPL);

  //
  // Map the source data buffer for bus master access.
  // Create Tds list, then link it to the UHC's interrupt list
  //
  Status = UhciMapUserData (
             Uhc,
             EfiUsbDataIn,
             Data,
             DataLength,
             &PktId,
             &DataPhy,
             &DataMap
             );

  if (EFI_ERROR (Status)) {
    goto ON_EXIT;
  }

  TDs = UhciCreateBulkOrIntTds (
          Uhc,
          DeviceAddress,
          EndPointAddress,
          PktId,
          (UINT8 *)Data,
          DataPhy,
          *DataLength,
          DataToggle,
          (UINT8) MaximumPacketLength,
          IsSlowDevice
          );

  if (TDs == NULL) {
    Uhc->PciIo->Unmap (Uhc->PciIo, DataMap);

    Status = EFI_OUT_OF_RESOURCES;
    goto ON_EXIT;
  }


  UhciLinkTdToQh (Uhc, Uhc->SyncIntQh, TDs);

  Status = UhciExecuteTransfer (Uhc, Uhc->SyncIntQh, TDs, TimeOut, IsSlowDevice, &QhResult);

  UhciUnlinkTdFromQh (Uhc->SyncIntQh, TDs);
  Uhc->PciIo->Flush (Uhc->PciIo);

  *TransferResult = QhResult.Result;
  *DataToggle     = QhResult.NextToggle;
  *DataLength     = QhResult.Complete;

  UhciDestoryTds (Uhc, TDs);
  Uhc->PciIo->Unmap (Uhc->PciIo, DataMap);

ON_EXIT:
  gBS->RestoreTPL (OldTpl);
  return Status;
}


/**
  Submits isochronous transfer to a target USB device according to UEFI 2.0 spec.

  @param  This                  A pointer to the EFI_USB2_HC_PROTOCOL instance.
  @param  DeviceAddress         Target device address.
  @param  EndPointAddress       Endpoint number and direction.
  @param  DeviceSpeed           Device speed.
  @param  MaximumPacketLength   Maximum packet size of the target endpoint.
  @param  DataBuffersNumber     Number of data buffers prepared for the transfer.
  @param  Data                  Array of pointers to the buffers of data.
  @param  DataLength            On input, size of the data buffer, On output,
                                actually transferred data size.
  @param  Translator            A pointr to the transaction translator data.
  @param  TransferResult        Variable to receive transfer result.

  @return EFI_UNSUPPORTED

**/
EFI_STATUS
EFIAPI
Uhci2IsochronousTransfer (
  IN     EFI_USB2_HC_PROTOCOL               *This,
  IN     UINT8                              DeviceAddress,
  IN     UINT8                              EndPointAddress,
  IN     UINT8                              DeviceSpeed,
  IN     UINTN                              MaximumPacketLength,
  IN     UINT8                              DataBuffersNumber,
  IN OUT VOID                               *Data[EFI_USB_MAX_ISO_BUFFER_NUM],
  IN     UINTN                              DataLength,
  IN     EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
  OUT    UINT32                             *TransferResult
  )
{
  return EFI_UNSUPPORTED;
}


/**
  Submits Async isochronous transfer to a target USB device according to UEFI 2.0 spec.

  @param  This                  A pointer to the EFI_USB2_HC_PROTOCOL instance.
  @param  DeviceAddress         Target device address.
  @param  EndPointAddress       Endpoint number and direction.
  @param  DeviceSpeed           Device speed.
  @param  MaximumPacketLength   Maximum packet size of the target endpoint.
  @param  DataBuffersNumber     Number of data buffers prepared for the transfer.
  @param  Data                  Array of pointers to the buffers of data.
  @param  DataLength            On input, size of the data buffer, On output,
                                actually transferred data size.
  @param  Translator            A pointr to the transaction translator data.
  @param  IsochronousCallBack   Function to call when the transfer complete.
  @param  Context               Pass to the call back function as parameter.

  @return EFI_UNSUPPORTED

**/
EFI_STATUS
EFIAPI
Uhci2AsyncIsochronousTransfer (
  IN     EFI_USB2_HC_PROTOCOL                *This,
  IN     UINT8                               DeviceAddress,
  IN     UINT8                               EndPointAddress,
  IN     UINT8                               DeviceSpeed,
  IN     UINTN                               MaximumPacketLength,
  IN     UINT8                               DataBuffersNumber,
  IN OUT VOID                                *Data[EFI_USB_MAX_ISO_BUFFER_NUM],
  IN     UINTN                               DataLength,
  IN     EFI_USB2_HC_TRANSACTION_TRANSLATOR  *Translator,
  IN     EFI_ASYNC_USB_TRANSFER_CALLBACK     IsochronousCallBack,
  IN     VOID                                *Context
  )
{
  return EFI_UNSUPPORTED;
}

/**
  Entry point for EFI drivers.

  @param  ImageHandle      EFI_HANDLE.
  @param  SystemTable      EFI_SYSTEM_TABLE.

  @retval EFI_SUCCESS      Driver is successfully loaded.
  @return Others           Failed.

**/
EFI_STATUS
EFIAPI
UhciDriverEntryPoint (
  IN EFI_HANDLE           ImageHandle,
  IN EFI_SYSTEM_TABLE     *SystemTable
  )
{
  return EfiLibInstallDriverBindingComponentName2 (
           ImageHandle,
           SystemTable,
           &gUhciDriverBinding,
           ImageHandle,
           &gUhciComponentName,
           &gUhciComponentName2
           );
}


/**
  Test to see if this driver supports ControllerHandle. Any
  ControllerHandle that has UsbHcProtocol installed will be supported.

  @param  This                 Protocol instance pointer.
  @param  Controller           Handle of device to test.
  @param  RemainingDevicePath  Not used.

  @return EFI_SUCCESS          This driver supports this device.
  @return EFI_UNSUPPORTED      This driver does not support this device.

**/
EFI_STATUS
EFIAPI
UhciDriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL     *This,
  IN EFI_HANDLE                      Controller,
  IN EFI_DEVICE_PATH_PROTOCOL        *RemainingDevicePath
  )
{
  EFI_STATUS            OpenStatus;
  EFI_STATUS            Status;
  EFI_PCI_IO_PROTOCOL   *PciIo;
  USB_CLASSC            UsbClassCReg;

  //
  // Test whether there is PCI IO Protocol attached on the controller handle.
  //
  OpenStatus = gBS->OpenProtocol (
                      Controller,
                      &gEfiPciIoProtocolGuid,
                      (VOID **) &PciIo,
                      This->DriverBindingHandle,
                      Controller,
                      EFI_OPEN_PROTOCOL_BY_DRIVER
                      );

  if (EFI_ERROR (OpenStatus)) {
    return OpenStatus;
  }

  Status = PciIo->Pci.Read (
                        PciIo,
                        EfiPciIoWidthUint8,
                        PCI_CLASSCODE_OFFSET,
                        sizeof (USB_CLASSC) / sizeof (UINT8),
                        &UsbClassCReg
                        );

  if (EFI_ERROR (Status)) {
    Status = EFI_UNSUPPORTED;
    goto ON_EXIT;
  }

  //
  // Test whether the controller belongs to UHCI type
  //
  if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) ||
      (UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB) ||
      (UsbClassCReg.ProgInterface != PCI_IF_UHCI)
      ) {

    Status = EFI_UNSUPPORTED;
  }

ON_EXIT:
  gBS->CloseProtocol (
         Controller,
         &gEfiPciIoProtocolGuid,
         This->DriverBindingHandle,
         Controller
         );

  return Status;

}


/**
  Allocate and initialize the empty UHCI device.

  @param  PciIo                  The PCIIO to use.
  @param  DevicePath             The device path of host controller.
  @param  OriginalPciAttributes  The original PCI attributes.

  @return Allocated UHCI device. If err, return NULL.

**/
USB_HC_DEV *
UhciAllocateDev (
  IN EFI_PCI_IO_PROTOCOL       *PciIo,
  IN EFI_DEVICE_PATH_PROTOCOL  *DevicePath,
  IN UINT64                    OriginalPciAttributes
  )
{
  USB_HC_DEV  *Uhc;
  EFI_STATUS  Status;

  Uhc = AllocateZeroPool (sizeof (USB_HC_DEV));

  if (Uhc == NULL) {
    return NULL;
  }

  //
  // This driver supports both USB_HC_PROTOCOL and USB2_HC_PROTOCOL.
  // USB_HC_PROTOCOL is for EFI 1.1 backward compability.
  //
  Uhc->Signature                        = USB_HC_DEV_SIGNATURE;
  Uhc->Usb2Hc.GetCapability             = Uhci2GetCapability;
  Uhc->Usb2Hc.Reset                     = Uhci2Reset;
  Uhc->Usb2Hc.GetState                  = Uhci2GetState;
  Uhc->Usb2Hc.SetState                  = Uhci2SetState;
  Uhc->Usb2Hc.ControlTransfer           = Uhci2ControlTransfer;
  Uhc->Usb2Hc.BulkTransfer              = Uhci2BulkTransfer;
  Uhc->Usb2Hc.AsyncInterruptTransfer    = Uhci2AsyncInterruptTransfer;
  Uhc->Usb2Hc.SyncInterruptTransfer     = Uhci2SyncInterruptTransfer;
  Uhc->Usb2Hc.IsochronousTransfer       = Uhci2IsochronousTransfer;
  Uhc->Usb2Hc.AsyncIsochronousTransfer  = Uhci2AsyncIsochronousTransfer;
  Uhc->Usb2Hc.GetRootHubPortStatus      = Uhci2GetRootHubPortStatus;
  Uhc->Usb2Hc.SetRootHubPortFeature     = Uhci2SetRootHubPortFeature;
  Uhc->Usb2Hc.ClearRootHubPortFeature   = Uhci2ClearRootHubPortFeature;
  Uhc->Usb2Hc.MajorRevision             = 0x1;
  Uhc->Usb2Hc.MinorRevision             = 0x1;

  Uhc->PciIo                 = PciIo;
  Uhc->DevicePath            = DevicePath;
  Uhc->OriginalPciAttributes = OriginalPciAttributes;
  Uhc->MemPool               = UsbHcInitMemPool (PciIo, TRUE, 0);

  if (Uhc->MemPool == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_ERROR;
  }

  InitializeListHead (&Uhc->AsyncIntList);

  Status = gBS->CreateEvent (
                  EVT_TIMER | EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  UhciMonitorAsyncReqList,
                  Uhc,
                  &Uhc->AsyncIntMonitor
                  );

  if (EFI_ERROR (Status)) {
    UsbHcFreeMemPool (Uhc->MemPool);
    goto ON_ERROR;
  }

  return Uhc;

ON_ERROR:
  FreePool (Uhc);
  return NULL;
}


/**
  Free the UHCI device and release its associated resources.

  @param  Uhc     The UHCI device to release.

**/
VOID
UhciFreeDev (
  IN USB_HC_DEV           *Uhc
  )
{
  if (Uhc->AsyncIntMonitor != NULL) {
    gBS->CloseEvent (Uhc->AsyncIntMonitor);
  }

  if (Uhc->ExitBootServiceEvent != NULL) {
    gBS->CloseEvent (Uhc->ExitBootServiceEvent);
  }

  if (Uhc->MemPool != NULL) {
    UsbHcFreeMemPool (Uhc->MemPool);
  }

  if (Uhc->CtrlNameTable != NULL) {
    FreeUnicodeStringTable (Uhc->CtrlNameTable);
  }

  FreePool (Uhc);
}


/**
  Uninstall all Uhci Interface.

  @param  Controller           Controller handle.
  @param  This                 Protocol instance pointer.

**/
VOID
UhciCleanDevUp (
  IN  EFI_HANDLE           Controller,
  IN  EFI_USB2_HC_PROTOCOL *This
  )
{
  USB_HC_DEV          *Uhc;
  EFI_STATUS          Status;

  //
  // Uninstall the USB_HC and USB_HC2 protocol, then disable the controller
  //
  Uhc = UHC_FROM_USB2_HC_PROTO (This);


  Status = gBS->UninstallProtocolInterface (
                  Controller,
                  &gEfiUsb2HcProtocolGuid,
                  &Uhc->Usb2Hc
                  );
  if (EFI_ERROR (Status)) {
    return ;
  }

  UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT);
  UhciFreeAllAsyncReq (Uhc);
  UhciDestoryFrameList (Uhc);

  //
  // Restore original PCI attributes
  //
  Uhc->PciIo->Attributes (
                  Uhc->PciIo,
                  EfiPciIoAttributeOperationSet,
                  Uhc->OriginalPciAttributes,
                  NULL
                  );

  UhciFreeDev (Uhc);
}

/**
  One notified function to stop the Host Controller when gBS->ExitBootServices() called.

  @param  Event                   Pointer to this event
  @param  Context                 Event handler private data

**/
VOID
EFIAPI
UhcExitBootService (
  EFI_EVENT                      Event,
  VOID                           *Context
  )
{
  USB_HC_DEV   *Uhc;

  Uhc = (USB_HC_DEV *) Context;

  //
  // Stop the Host Controller
  //
  UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT);

  //
  // Reset the Host Controller
  //
  UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_HCRESET);
  gBS->Stall (UHC_ROOT_PORT_RECOVERY_STALL);
}

/**
  Starting the Usb UHCI Driver.

  @param  This                 Protocol instance pointer.
  @param  Controller           Handle of device to test.
  @param  RemainingDevicePath  Not used.

  @retval EFI_SUCCESS          This driver supports this device.
  @retval EFI_UNSUPPORTED      This driver does not support this device.
  @retval EFI_DEVICE_ERROR     This driver cannot be started due to device Error.
                               EFI_OUT_OF_RESOURCES- Failed due to resource shortage.

**/
EFI_STATUS
EFIAPI
UhciDriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL     *This,
  IN EFI_HANDLE                      Controller,
  IN EFI_DEVICE_PATH_PROTOCOL        *RemainingDevicePath
  )
{
  EFI_STATUS          Status;
  EFI_PCI_IO_PROTOCOL *PciIo;
  USB_HC_DEV          *Uhc;
  UINT64              Supports;
  UINT64              OriginalPciAttributes;
  BOOLEAN             PciAttributesSaved;
  EFI_DEVICE_PATH_PROTOCOL  *HcDevicePath;

  //
  // Open PCIIO, then enable the EHC device and turn off emulation
  //
  Uhc = NULL;
  Status = gBS->OpenProtocol (
                  Controller,
                  &gEfiPciIoProtocolGuid,
                  (VOID **) &PciIo,
                  This->DriverBindingHandle,
                  Controller,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );

  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Open Device Path Protocol for on USB host controller
  //
  HcDevicePath = NULL;
  Status = gBS->OpenProtocol (
                  Controller,
                  &gEfiDevicePathProtocolGuid,
                  (VOID **) &HcDevicePath,
                  This->DriverBindingHandle,
                  Controller,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );

  PciAttributesSaved = FALSE;
  //
  // Save original PCI attributes
  //
  Status = PciIo->Attributes (
                    PciIo,
                    EfiPciIoAttributeOperationGet,
                    0,
                    &OriginalPciAttributes
                    );

  if (EFI_ERROR (Status)) {
    goto CLOSE_PCIIO;
  }
  PciAttributesSaved = TRUE;

  //
  // Robustnesss improvement such as for UoL
  // Default is not required.
  //
  if (FeaturePcdGet (PcdTurnOffUsbLegacySupport)) {
    UhciTurnOffUsbEmulation (PciIo);
  }

  Status = PciIo->Attributes (
                    PciIo,
                    EfiPciIoAttributeOperationSupported,
                    0,
                    &Supports
                    );
  if (!EFI_ERROR (Status)) {
    Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE;
    Status = PciIo->Attributes (
                      PciIo,
                      EfiPciIoAttributeOperationEnable,
                      Supports,
                      NULL
                      );
  }

  if (EFI_ERROR (Status)) {
    goto CLOSE_PCIIO;
  }

  Uhc = UhciAllocateDev (PciIo, HcDevicePath, OriginalPciAttributes);

  if (Uhc == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto CLOSE_PCIIO;
  }

  //
  // Allocate and Init Host Controller's Frame List Entry
  //
  Status = UhciInitFrameList (Uhc);

  if (EFI_ERROR (Status)) {
    Status = EFI_OUT_OF_RESOURCES;
    goto FREE_UHC;
  }

  Status = gBS->SetTimer (
                  Uhc->AsyncIntMonitor,
                  TimerPeriodic,
                  UHC_ASYNC_POLL_INTERVAL
                  );

  if (EFI_ERROR (Status)) {
    goto FREE_UHC;
  }

  //
  // Install USB2_HC_PROTOCOL
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &Controller,
                  &gEfiUsb2HcProtocolGuid,
                  &Uhc->Usb2Hc,
                  NULL
                  );

  if (EFI_ERROR (Status)) {
    goto FREE_UHC;
  }

  //
  // Create event to stop the HC when exit boot service.
  //
  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  UhcExitBootService,
                  Uhc,
                  &gEfiEventExitBootServicesGuid,
                  &Uhc->ExitBootServiceEvent
                  );
  if (EFI_ERROR (Status)) {
    goto UNINSTALL_USBHC;
  }

  //
  // Install the component name protocol
  //
  Uhc->CtrlNameTable = NULL;

  AddUnicodeString2 (
    "eng",
    gUhciComponentName.SupportedLanguages,
    &Uhc->CtrlNameTable,
    L"Usb Universal Host Controller",
    TRUE
    );
  AddUnicodeString2 (
    "en",
    gUhciComponentName2.SupportedLanguages,
    &Uhc->CtrlNameTable,
    L"Usb Universal Host Controller",
    FALSE
    );


  //
  // Start the UHCI hardware, also set its reclamation point to 64 bytes
  //
  UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, USBCMD_RS | USBCMD_MAXP);

  return EFI_SUCCESS;

UNINSTALL_USBHC:
  gBS->UninstallMultipleProtocolInterfaces (
         Controller,
         &gEfiUsb2HcProtocolGuid,
         &Uhc->Usb2Hc,
         NULL
         );

FREE_UHC:
  UhciFreeDev (Uhc);

CLOSE_PCIIO:
  if (PciAttributesSaved) {
    //
    // Restore original PCI attributes
    //
    PciIo->Attributes (
                    PciIo,
                    EfiPciIoAttributeOperationSet,
                    OriginalPciAttributes,
                    NULL
                    );
  }

  gBS->CloseProtocol (
        Controller,
        &gEfiPciIoProtocolGuid,
        This->DriverBindingHandle,
        Controller
        );

  return Status;
}


/**
  Stop this driver on ControllerHandle. Support stopping any child handles
  created by this driver.

  @param  This                 Protocol instance pointer.
  @param  Controller           Handle of device to stop driver on.
  @param  NumberOfChildren     Number of Children in the ChildHandleBuffer.
  @param  ChildHandleBuffer    List of handles for the children we need to stop.

  @return EFI_SUCCESS
  @return others

**/
EFI_STATUS
EFIAPI
UhciDriverBindingStop (
  IN EFI_DRIVER_BINDING_PROTOCOL     *This,
  IN EFI_HANDLE                      Controller,
  IN UINTN                           NumberOfChildren,
  IN EFI_HANDLE                      *ChildHandleBuffer
  )
{
  EFI_USB2_HC_PROTOCOL  *Usb2Hc;
  EFI_STATUS            Status;

   Status = gBS->OpenProtocol (
                  Controller,
                  &gEfiUsb2HcProtocolGuid,
                  (VOID **) &Usb2Hc,
                  This->DriverBindingHandle,
                  Controller,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );

  //
  // Test whether the Controller handler passed in is a valid
  // Usb controller handle that should be supported, if not,
  // return the error status directly
  //
  if (EFI_ERROR (Status)) {
    return Status;
  }

  UhciCleanDevUp (Controller, Usb2Hc);

  gBS->CloseProtocol (
        Controller,
        &gEfiPciIoProtocolGuid,
        This->DriverBindingHandle,
        Controller
        );

  return EFI_SUCCESS;
}

