/*++

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:

    EhciSched.c

Abstract:


Revision History
--*/

#include "Ehci.h"

STATIC
EFI_STATUS
SetAndWaitDoorBell (
  IN  USB2_HC_DEV     *HcDev,
  IN  UINTN           Timeout
  )
/*++

Routine Description:

  Set DoorBell and wait it to complete

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  EFI_STATUS  Status;
  UINT32      Data;
  UINTN       Delay;

  Status = ReadEhcOperationalReg (
             HcDev,
             USBCMD,
             &Data
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto exit;
  }

  Data |= USBCMD_IAAD;
  Status = WriteEhcOperationalReg (
             HcDev,
             USBCMD,
             Data
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
  }

  //
  // Timeout is in US unit
  //
  Delay = (Timeout / 50) + 1;
  do {
    Status = ReadEhcOperationalReg (
               HcDev,
               USBSTS,
               &Data
               );
    if (EFI_ERROR (Status)) {
      Status = EFI_DEVICE_ERROR;
      goto exit;
    }

    if ((Data & USBSTS_IAA) == USBSTS_IAA) {
      break;
    }

    gBS->Stall (EHCI_GENERIC_RECOVERY_TIME);

  } while (Delay--);

  Data = Data & 0xFFFFFFC0;
  Data |= USBSTS_IAA;
  Status = WriteEhcOperationalReg (
             HcDev,
             USBSTS,
             Data
             );

exit:
  return Status;
}





EFI_STATUS
CreateNULLQH (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Create the NULL QH to make it as the Async QH header

Arguments:

  HcDev   - USB2_HC_DEV

Returns:

  EFI_SUCCESS        Success
--*/
{
  EFI_STATUS            Status;
  EHCI_QH_ENTITY        *NULLQhPtr;
  //
  // Allocate  memory for Qh structure
  //
  Status = EhciAllocatePool (
             HcDev,
             (UINT8 **) &NULLQhPtr,
             sizeof (EHCI_QH_ENTITY)
             );
  if (EFI_ERROR (Status)) {
     return Status;
  }

  NULLQhPtr->Qh.Status = QTD_STATUS_HALTED;
  NULLQhPtr->Qh.HeadReclamationFlag = 1;
  NULLQhPtr->Qh.QhHorizontalPointer = (UINT32) (GET_0B_TO_31B (&(NULLQhPtr->Qh) >> 5));
  NULLQhPtr->Qh.SelectType = QH_SELECT_TYPE;
  NULLQhPtr->Qh.NextQtdTerminate = 1;

  NULLQhPtr->Next = NULLQhPtr;
  NULLQhPtr->Prev = NULLQhPtr;

  HcDev->NULLQH = NULLQhPtr;

  return Status;
}



VOID
DestroyNULLQH (
  IN  USB2_HC_DEV     *HcDev
  )
{

  if (HcDev->NULLQH != NULL) {
    EhciFreePool (HcDev, (UINT8 *)HcDev->NULLQH, sizeof (EHCI_QH_ENTITY));
    HcDev->NULLQH = NULL;
  }
}



EFI_STATUS
InitialPeriodicFrameList (
  IN  USB2_HC_DEV     *HcDev,
  IN  UINTN           Length
  )
/*++

Routine Description:

  Initialize Periodic Schedule Frame List

Arguments:

  HcDev   - USB2_HC_DEV
  Length  - Frame List Length

Returns:

  EFI_SUCCESS        Success
  EFI_DEVICE_ERROR   Fail

--*/
{
  EFI_STATUS            Status;
  VOID                  *CommonBuffer;
  EFI_PHYSICAL_ADDRESS  FrameBuffer;
  VOID                  *Map;
  UINTN                 BufferSizeInPages;
  UINTN                 BufferSizeInBytes;
  UINTN                 FrameIndex;
  FRAME_LIST_ENTRY      *FrameEntryPtr;

  //
  // The Frame List is a common buffer that will be
  // accessed by both the cpu and the usb bus master
  // at the same time.
  // The Frame List ocupies 4K bytes,
  // and must be aligned on 4-Kbyte boundaries.
  //
  if (EHCI_MAX_FRAME_LIST_LENGTH != Length && IsFrameListProgrammable (HcDev)) {
    Status = SetFrameListLen (HcDev, Length);
    if (EFI_ERROR (Status)) {
      Status = EFI_INVALID_PARAMETER;
      goto exit;
    }
  }

  BufferSizeInBytes = EFI_PAGE_SIZE;
  BufferSizeInPages = EFI_SIZE_TO_PAGES (BufferSizeInBytes);
  Status = HcDev->PciIo->AllocateBuffer (
                          HcDev->PciIo,
                          AllocateAnyPages,
                          EfiBootServicesData,
                          BufferSizeInPages,
                          &CommonBuffer,
                          0
                          );
  if (EFI_ERROR (Status)) {
    DEBUG ((gEHCErrorLevel, "EHCI: PciIo->AllocateBuffer Failed\n"));
    Status = EFI_OUT_OF_RESOURCES;
    goto exit;
  }

  Status = HcDev->PciIo->Map (
                          HcDev->PciIo,
                          EfiPciIoOperationBusMasterCommonBuffer,
                          CommonBuffer,
                          &BufferSizeInBytes,
                          &FrameBuffer,
                          &Map
                          );
  if (EFI_ERROR (Status) || (BufferSizeInBytes != EFI_PAGE_SIZE)) {
    DEBUG ((gEHCErrorLevel, "EHCI: PciIo->MapBuffer Failed\n"));
    Status = EFI_OUT_OF_RESOURCES;
    goto free_buffer;
  }

  //
  // Put high 32bit into CtrlDataStructSeg reg
  // when 64bit addressing range capability
  //
  if (HcDev->Is64BitCapable != 0) {
  	HcDev->High32BitAddr = (UINT32) GET_32B_TO_63B (FrameBuffer);

  	Status = SetCtrlDataStructSeg (HcDev);
    if (EFI_ERROR (Status)) {
      DEBUG ((gEHCErrorLevel, "EHCI: SetCtrlDataStructSeg Failed\n"));
      Status = EFI_DEVICE_ERROR;
      goto unmap_buffer;
    }
  }

  //
  // Tell the Host Controller where the Frame List lies,
  // by set the Frame List Base Address Register.
  //
  Status = SetFrameListBaseAddr (HcDev, (UINT32) FrameBuffer);
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto unmap_buffer;
  }

  HcDev->PeriodicFrameListLength  = Length;
  HcDev->PeriodicFrameListBuffer  = (VOID *) ((UINTN) FrameBuffer);
  HcDev->PeriodicFrameListMap     = Map;

  //
  // Init Frame List Array fields
  //
  FrameEntryPtr = (FRAME_LIST_ENTRY *) HcDev->PeriodicFrameListBuffer;
  for (FrameIndex = 0; FrameIndex < HcDev->PeriodicFrameListLength; FrameIndex++) {
    FrameEntryPtr->LinkPointer    = 0;
    FrameEntryPtr->Rsvd           = 0;
    FrameEntryPtr->SelectType     = 0;
    FrameEntryPtr->LinkTerminate  = TRUE;
    FrameEntryPtr++;
  }

  goto exit;

unmap_buffer:
  HcDev->PciIo->Unmap (HcDev->PciIo, Map);
free_buffer:
  HcDev->PciIo->FreeBuffer (HcDev->PciIo, BufferSizeInPages, CommonBuffer);
exit:
  return Status;
}

VOID
DeinitialPeriodicFrameList (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Deinitialize Periodic Schedule Frame List

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  VOID

--*/
{
  HcDev->PciIo->Unmap (HcDev->PciIo, HcDev->PeriodicFrameListMap);
  HcDev->PciIo->FreeBuffer (HcDev->PciIo, EFI_SIZE_TO_PAGES (EFI_PAGE_SIZE), HcDev->PeriodicFrameListBuffer);
  return ;
}

EFI_STATUS
CreatePollingTimer (
  IN  USB2_HC_DEV      *HcDev,
  IN  EFI_EVENT_NOTIFY NotifyFunction
  )
/*++

Routine Description:

  Create Async Request Polling Timer

Arguments:

  HcDev          - USB2_HC_DEV
  NotifyFunction - Timer Notify Function

Returns:

  EFI_SUCCESS        Success
  EFI_DEVICE_ERROR   Fail

--*/
{
  return gBS->CreateEvent (
                EVT_TIMER | EVT_NOTIFY_SIGNAL,
                TPL_NOTIFY,
                NotifyFunction,
                HcDev,
                &HcDev->AsyncRequestEvent
                );
}

EFI_STATUS
DestoryPollingTimer (
  IN  USB2_HC_DEV *HcDev
  )
/*++

Routine Description:

  Destory Async Request Polling Timer

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS        Success
  EFI_DEVICE_ERROR   Fail

--*/
{
  return gBS->CloseEvent (HcDev->AsyncRequestEvent);
}

EFI_STATUS
StartPollingTimer (
  IN  USB2_HC_DEV *HcDev
  )
/*++

Routine Description:

  Start Async Request Polling Timer

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS        Success
  EFI_DEVICE_ERROR   Fail

--*/
{
  return gBS->SetTimer (
                HcDev->AsyncRequestEvent,
                TimerPeriodic,
                EHCI_ASYNC_REQUEST_POLLING_TIME
                );
}

EFI_STATUS
StopPollingTimer (
  IN  USB2_HC_DEV *HcDev
  )
/*++

Routine Description:

  Stop Async Request Polling Timer

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS        Success
  EFI_DEVICE_ERROR   Fail

--*/
{
  return gBS->SetTimer (
                HcDev->AsyncRequestEvent,
                TimerCancel,
                EHCI_ASYNC_REQUEST_POLLING_TIME
                );
}

EFI_STATUS
CreateQh (
  IN  USB2_HC_DEV         *HcDev,
  IN  UINT8               DeviceAddr,
  IN  UINT8               Endpoint,
  IN  UINT8               DeviceSpeed,
  IN  UINTN               MaxPacketLen,
  OUT EHCI_QH_ENTITY      **QhPtrPtr
  )
/*++

Routine Description:

  Create Qh Structure and Pre-Initialize

Arguments:

  HcDev        - USB2_HC_DEV
  DeviceAddr   - Address of Device
  Endpoint     - Endpoint Number
  DeviceSpeed  - Device Speed
  MaxPacketLen - Max Length of one Packet
  QhPtrPtr     - A pointer of pointer to Qh for return

Returns:

  EFI_SUCCESS            Success
  EFI_OUT_OF_RESOURCES   Cannot allocate resources

--*/
{
  EFI_STATUS  Status;
  EHCI_QH_HW  *QhHwPtr;

  ASSERT (HcDev);
  ASSERT (QhPtrPtr);

  *QhPtrPtr = NULL;

  //
  // Allocate  memory for Qh structure
  //
  Status = EhciAllocatePool (
             HcDev,
             (UINT8 **) QhPtrPtr,
             sizeof (EHCI_QH_ENTITY)
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_OUT_OF_RESOURCES;
    goto exit;
  }

  //
  // Software field
  //
  (*QhPtrPtr)->Next         = NULL;
  (*QhPtrPtr)->Prev         = NULL;
  (*QhPtrPtr)->FirstQtdPtr  = NULL;
  (*QhPtrPtr)->AltQtdPtr    = NULL;
  (*QhPtrPtr)->LastQtdPtr   = NULL;

  //
  // Hardware field
  //
  QhHwPtr                       = &((*QhPtrPtr)->Qh);
  QhHwPtr->QhHorizontalPointer  = 0;
  QhHwPtr->SelectType           = 0;
  QhHwPtr->MaxPacketLen         = (UINT32) MaxPacketLen;
  QhHwPtr->EndpointSpeed        = (DeviceSpeed & 0x3);
  QhHwPtr->EndpointNum          = (Endpoint & 0x0F);
  QhHwPtr->DeviceAddr           = (DeviceAddr & 0x7F);
  QhHwPtr->Multiplier           = HIGH_BANDWIDTH_PIPE_MULTIPLIER;
  QhHwPtr->Rsvd1                = 0;
  QhHwPtr->Rsvd2                = 0;
  QhHwPtr->Rsvd3                = 0;
  QhHwPtr->Rsvd4                = 0;
  QhHwPtr->Rsvd5                = 0;
  QhHwPtr->Rsvd6                = 0;

exit:
  return Status;
}

VOID
DestoryQh (
  IN USB2_HC_DEV         *HcDev,
  IN EHCI_QH_ENTITY      *QhPtr
  )
/*++

Routine Description:

  Destory Qh Structure

Arguments:

  HcDev - USB2_HC_DEV
  QhPtr - A pointer to Qh

Returns:

  EFI_SUCCESS        Success
  EFI_DEVICE_ERROR   Fail

--*/
{
  ASSERT (HcDev);
  ASSERT (QhPtr);

  EhciFreePool (HcDev, (UINT8 *) QhPtr, sizeof (EHCI_QH_ENTITY));
  return ;
}

EFI_STATUS
CreateControlQh (
  IN  USB2_HC_DEV                         *HcDev,
  IN  UINT8                               DeviceAddr,
  IN  UINT8                               DeviceSpeed,
  IN  UINTN                               MaxPacketLen,
  IN  EFI_USB2_HC_TRANSACTION_TRANSLATOR  *Translator,
  OUT EHCI_QH_ENTITY                      **QhPtrPtr
  )
/*++

Routine Description:

  Create Qh for Control Transfer

Arguments:

  HcDev        - USB2_HC_DEV
  DeviceAddr   - Address of Device
  DeviceSpeed  - Device Speed
  MaxPacketLen - Max Length of one Packet
  Translator   - Translator Transaction for SplitX
  QhPtrPtr     - A pointer of pointer to Qh for return

Returns:

  EFI_SUCCESS            Success
  EFI_OUT_OF_RESOURCES   Cannot allocate resources

--*/
{
  EFI_STATUS  Status;

  //
  // Create and init Control Qh
  //
  Status = CreateQh (
             HcDev,
             DeviceAddr,
             0,
             DeviceSpeed,
             MaxPacketLen,
             QhPtrPtr
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_OUT_OF_RESOURCES;
    goto exit;
  }
  //
  // Software field
  //
  (*QhPtrPtr)->Next         = (*QhPtrPtr);
  (*QhPtrPtr)->Prev         = (*QhPtrPtr);
  (*QhPtrPtr)->TransferType = CONTROL_TRANSFER;

  //
  // Hardware field
  //
  // Control Transfer use DataToggleControl
  //
  (*QhPtrPtr)->Qh.DataToggleControl   = TRUE;
  (*QhPtrPtr)->Qh.QhHorizontalPointer = (UINT32) (GET_0B_TO_31B (&((*QhPtrPtr)->Qh)) >> 5);
  (*QhPtrPtr)->Qh.SelectType          = QH_SELECT_TYPE;
  (*QhPtrPtr)->Qh.QhTerminate         = FALSE;
  if (EFI_USB_SPEED_HIGH != DeviceSpeed)  {
    (*QhPtrPtr)->Qh.ControlEndpointFlag = TRUE;
  }
  (*QhPtrPtr)->Qh.NakCountReload      = NAK_COUNT_RELOAD;
  if (NULL != Translator) {
    (*QhPtrPtr)->Qh.PortNum = Translator->TranslatorPortNumber;
    (*QhPtrPtr)->Qh.HubAddr = Translator->TranslatorHubAddress;
    (*QhPtrPtr)->Qh.Status |= QTD_STATUS_DO_START_SPLIT;
  }

exit:
  return Status;
}

EFI_STATUS
CreateBulkQh (
  IN  USB2_HC_DEV                         *HcDev,
  IN  UINT8                               DeviceAddr,
  IN  UINT8                               EndPointAddr,
  IN  UINT8                               DeviceSpeed,
  IN  UINT8                               DataToggle,
  IN  UINTN                               MaxPacketLen,
  IN  EFI_USB2_HC_TRANSACTION_TRANSLATOR  *Translator,
  OUT EHCI_QH_ENTITY                      **QhPtrPtr
  )
/*++

Routine Description:

  Create Qh for Bulk Transfer

Arguments:

  HcDev        - USB2_HC_DEV
  DeviceAddr   - Address of Device
  EndPointAddr - Address of Endpoint
  DeviceSpeed  - Device Speed
  MaxPacketLen - Max Length of one Packet
  Translator   - Translator Transaction for SplitX
  QhPtrPtr     - A pointer of pointer to Qh for return

Returns:

  EFI_SUCCESS            Success
  EFI_OUT_OF_RESOURCES   Cannot allocate resources

--*/
{
  EFI_STATUS  Status;

  //
  // Create and init Bulk Qh
  //
  Status = CreateQh (
             HcDev,
             DeviceAddr,
             EndPointAddr,
             DeviceSpeed,
             MaxPacketLen,
             QhPtrPtr
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_OUT_OF_RESOURCES;
    goto exit;
  }

  //
  // Software fields
  //
  (*QhPtrPtr)->Next         = (*QhPtrPtr);
  (*QhPtrPtr)->Prev         = (*QhPtrPtr);
  (*QhPtrPtr)->TransferType = BULK_TRANSFER;

  //
  // Hardware fields
  //
  // BulkTransfer don't use DataToggleControl
  //
  (*QhPtrPtr)->Qh.DataToggleControl   = FALSE;
  (*QhPtrPtr)->Qh.QhHorizontalPointer = (UINT32) (GET_0B_TO_31B (&((*QhPtrPtr)->Qh)) >> 5);
  (*QhPtrPtr)->Qh.SelectType          = QH_SELECT_TYPE;
  (*QhPtrPtr)->Qh.QhTerminate         = FALSE;
  (*QhPtrPtr)->Qh.NakCountReload      = NAK_COUNT_RELOAD;
  (*QhPtrPtr)->Qh.DataToggle          = DataToggle;
  if (NULL != Translator) {
    (*QhPtrPtr)->Qh.PortNum = Translator->TranslatorPortNumber;
    (*QhPtrPtr)->Qh.HubAddr = Translator->TranslatorHubAddress;
    (*QhPtrPtr)->Qh.Status |= QTD_STATUS_DO_START_SPLIT;
  }

exit:
  return Status;
}

EFI_STATUS
CreateInterruptQh (
  IN  USB2_HC_DEV                         *HcDev,
  IN  UINT8                               DeviceAddr,
  IN  UINT8                               EndPointAddr,
  IN  UINT8                               DeviceSpeed,
  IN  UINT8                               DataToggle,
  IN  UINTN                               MaxPacketLen,
  IN  UINTN                               Interval,
  IN  EFI_USB2_HC_TRANSACTION_TRANSLATOR  *Translator,
  OUT EHCI_QH_ENTITY                      **QhPtrPtr
  )
/*++

Routine Description:

  Create Qh for Control Transfer

Arguments:

  HcDev        - USB2_HC_DEV
  DeviceAddr   - Address of Device
  EndPointAddr - Address of Endpoint
  DeviceSpeed  - Device Speed
  MaxPacketLen - Max Length of one Packet
  Interval     - value of interval
  Translator   - Translator Transaction for SplitX
  QhPtrPtr     - A pointer of pointer to Qh for return

Returns:

  EFI_SUCCESS            Success
  EFI_OUT_OF_RESOURCES   Cannot allocate resources

--*/
{
  EFI_STATUS  Status;

  //
  // Create and init InterruptQh
  //
  Status = CreateQh (
             HcDev,
             DeviceAddr,
             EndPointAddr,
             DeviceSpeed,
             MaxPacketLen,
             QhPtrPtr
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_OUT_OF_RESOURCES;
    goto exit;
  }

  //
  // Software fields
  //
  if (Interval == 0) {
    (*QhPtrPtr)->TransferType = SYNC_INTERRUPT_TRANSFER;
  } else {
    (*QhPtrPtr)->TransferType = ASYNC_INTERRUPT_TRANSFER;
  }
  (*QhPtrPtr)->Interval = GetApproxiOfInterval (Interval);

  //
  // Hardware fields
  //
  // InterruptTranfer don't use DataToggleControl
  //
  (*QhPtrPtr)->Qh.DataToggleControl     = FALSE;
  (*QhPtrPtr)->Qh.QhHorizontalPointer   = 0;
  (*QhPtrPtr)->Qh.QhTerminate           = TRUE;
  (*QhPtrPtr)->Qh.NakCountReload        = 0;
  (*QhPtrPtr)->Qh.InerruptScheduleMask  = MICRO_FRAME_0_CHANNEL;
  (*QhPtrPtr)->Qh.SplitComletionMask    = (MICRO_FRAME_2_CHANNEL | MICRO_FRAME_3_CHANNEL | MICRO_FRAME_4_CHANNEL);
  (*QhPtrPtr)->Qh.DataToggle            = DataToggle;
  if (NULL != Translator) {
    (*QhPtrPtr)->Qh.PortNum = Translator->TranslatorPortNumber;
    (*QhPtrPtr)->Qh.HubAddr = Translator->TranslatorHubAddress;
    (*QhPtrPtr)->Qh.Status |= QTD_STATUS_DO_START_SPLIT;
  }

exit:
  return Status;
}

EFI_STATUS
CreateQtd (
  IN  USB2_HC_DEV          *HcDev,
  IN  UINT8                *DataPtr,
  IN  UINTN                DataLen,
  IN  UINT8                PktId,
  IN  UINT8                Toggle,
  IN  UINT8                QtdStatus,
  OUT EHCI_QTD_ENTITY      **QtdPtrPtr
  )
/*++

Routine Description:

  Create Qtd Structure and Pre-Initialize it

Arguments:

  HcDev       - USB2_HC_DEV
  DataPtr     - A pointer to user data buffer to transfer
  DataLen     - Length of user data to transfer
  PktId       - Packet Identification of this Qtd
  Toggle      - Data Toggle of this Qtd
  QtdStatus   - Default value of status of this Qtd
  QtdPtrPtr   - A pointer of pointer to Qtd for return

Returns:

  EFI_SUCCESS            Success
  EFI_OUT_OF_RESOURCES   Cannot allocate resources

--*/
{
  EFI_STATUS  Status;
  EHCI_QTD_HW *QtdHwPtr;

  ASSERT (HcDev);
  ASSERT (QtdPtrPtr);

  //
  // Create memory for Qtd structure
  //
  Status = EhciAllocatePool (
             HcDev,
             (UINT8 **) QtdPtrPtr,
             sizeof (EHCI_QTD_ENTITY)
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_OUT_OF_RESOURCES;
    goto exit;
  }

  //
  // Software field
  //
  (*QtdPtrPtr)->TotalBytes        = (UINT32) DataLen;
  (*QtdPtrPtr)->StaticTotalBytes  = (UINT32) DataLen;
  (*QtdPtrPtr)->Prev              = NULL;
  (*QtdPtrPtr)->Next              = NULL;

  //
  // Hardware field
  //
  QtdHwPtr                      = &((*QtdPtrPtr)->Qtd);
  QtdHwPtr->NextQtdPointer      = 0;
  QtdHwPtr->NextQtdTerminate    = TRUE;
  QtdHwPtr->AltNextQtdPointer   = 0;
  QtdHwPtr->AltNextQtdTerminate = TRUE;
  QtdHwPtr->DataToggle          = Toggle;
  QtdHwPtr->TotalBytes          = (UINT32) DataLen;
  QtdHwPtr->CurrentPage         = 0;
  QtdHwPtr->ErrorCount          = QTD_ERROR_COUNTER;
  QtdHwPtr->Status              = QtdStatus;
  QtdHwPtr->Rsvd1               = 0;
  QtdHwPtr->Rsvd2               = 0;
  QtdHwPtr->Rsvd3               = 0;
  QtdHwPtr->Rsvd4               = 0;
  QtdHwPtr->Rsvd5               = 0;
  QtdHwPtr->Rsvd6               = 0;

  //
  // Set PacketID [Setup/Data/Status]
  //
  switch (PktId) {
  case SETUP_PACKET_ID:
    QtdHwPtr->PidCode = SETUP_PACKET_PID_CODE;
    break;

  case INPUT_PACKET_ID:
    QtdHwPtr->PidCode = INPUT_PACKET_PID_CODE;
    break;

  case OUTPUT_PACKET_ID:
    QtdHwPtr->PidCode = OUTPUT_PACKET_PID_CODE;
    break;

  default:
    Status = EFI_INVALID_PARAMETER;
    goto exit;
  }

  //
  // Set Data Buffer Pointers
  //
  if (NULL != DataPtr) {
    SetQtdBufferPointer (
      QtdHwPtr,
      DataPtr,
      DataLen
      );
    (*QtdPtrPtr)->StaticCurrentOffset = QtdHwPtr->CurrentOffset;
  }

exit:
  return Status;
}

EFI_STATUS
CreateSetupQtd (
  IN  USB2_HC_DEV          *HcDev,
  IN  UINT8                *DevReqPtr,
  OUT EHCI_QTD_ENTITY      **QtdPtrPtr
  )
/*++

Routine Description:

  Create Qtd Structure for Setup

Arguments:

  HcDev      - USB2_HC_DEV
  DevReqPtr  - A pointer to Device Request Data
  QtdPtrPtr  - A pointer of pointer to Qtd for return

Returns:

  EFI_SUCCESS            Success
  EFI_OUT_OF_RESOURCES   Cannot allocate resources

--*/
{
  return CreateQtd (
           HcDev,
           DevReqPtr,
           sizeof (EFI_USB_DEVICE_REQUEST),
           SETUP_PACKET_ID,
           DATA0,
           QTD_STATUS_ACTIVE,
           QtdPtrPtr
           );
}

EFI_STATUS
CreateDataQtd (
  IN  USB2_HC_DEV           *HcDev,
  IN  UINT8                 *DataPtr,
  IN  UINTN                 DataLen,
  IN  UINT8                 PktId,
  IN  UINT8                 Toggle,
  OUT EHCI_QTD_ENTITY       **QtdPtrPtr
  )
/*++

Routine Description:

  Create Qtd Structure for data

Arguments:

  HcDev       - USB2_HC_DEV
  DataPtr     - A pointer to user data buffer to transfer
  DataLen     - Length of user data to transfer
  PktId       - Packet Identification of this Qtd
  Toggle      - Data Toggle of this Qtd
  QtdPtrPtr   - A pointer of pointer to Qtd for return

Returns:

  EFI_SUCCESS            Success
  EFI_OUT_OF_RESOURCES   Cannot allocate resources

--*/
{
  return CreateQtd (
           HcDev,
           DataPtr,
           DataLen,
           PktId,
           Toggle,
           QTD_STATUS_ACTIVE,
           QtdPtrPtr
           );
}

EFI_STATUS
CreateAltQtd (
  IN  USB2_HC_DEV           *HcDev,
  IN  UINT8                 PktId,
  OUT EHCI_QTD_ENTITY       **QtdPtrPtr
  )
/*++

Routine Description:

  Create Qtd Structure for Alternative

Arguments:

  HcDev      - USB2_HC_DEV
  PktId      - Packet Identification of this Qtd
  QtdPtrPtr  - A pointer of pointer to Qtd for return

Returns:

  EFI_SUCCESS            Success
  EFI_OUT_OF_RESOURCES   Cannot allocate resources

--*/
{
  return CreateQtd (
           HcDev,
           NULL,
           0,
           PktId,
           0,
           QTD_STATUS_ACTIVE,
           QtdPtrPtr
           );
}

EFI_STATUS
CreateStatusQtd (
  IN  USB2_HC_DEV           *HcDev,
  IN  UINT8                 PktId,
  OUT EHCI_QTD_ENTITY       **QtdPtrPtr
  )
/*++

Routine Description:

  Create Qtd Structure for status

Arguments:

  HcDev       - USB2_HC_DEV
  PktId       - Packet Identification of this Qtd
  QtdPtrPtr   - A pointer of pointer to Qtd for return

Returns:

  EFI_SUCCESS            Success
  EFI_OUT_OF_RESOURCES   Cannot allocate resources

--*/
{
  return CreateQtd (
           HcDev,
           NULL,
           0,
           PktId,
           DATA1,
           QTD_STATUS_ACTIVE,
           QtdPtrPtr
           );
}

EFI_STATUS
CreateControlQtds (
  IN  USB2_HC_DEV                         *HcDev,
  IN UINT8                                DataPktId,
  IN UINT8                                *RequestCursor,
  IN UINT8                                *DataCursor,
  IN UINTN                                DataLen,
  IN  EFI_USB2_HC_TRANSACTION_TRANSLATOR  *Translator,
  OUT EHCI_QTD_ENTITY                     **ControlQtdsHead
  )
/*++

Routine Description:

  Create Qtds list for Control Transfer

Arguments:

  HcDev           - USB2_HC_DEV
  DataPktId       - Packet Identification of Data Qtds
  RequestCursor   - A pointer to request structure buffer to transfer
  DataCursor      - A pointer to user data buffer to transfer
  DataLen         - Length of user data to transfer
  ControlQtdsHead - A pointer of pointer to first Qtd for control tranfer for return

Returns:

  EFI_SUCCESS            Success
  EFI_OUT_OF_RESOURCES   Cannot allocate resources

--*/
{
  EFI_STATUS      Status;
  EHCI_QTD_ENTITY *QtdPtr;
  EHCI_QTD_ENTITY *PreQtdPtr;
  EHCI_QTD_ENTITY *SetupQtdPtr;
  EHCI_QTD_ENTITY *FirstDataQtdPtr;
  EHCI_QTD_ENTITY *LastDataQtdPtr;
  EHCI_QTD_ENTITY *StatusQtdPtr;
  UINT8           DataToggle;
  UINT8           StatusPktId;
  UINTN           CapacityOfQtd;
  UINTN           SizePerQtd;
  UINTN           DataCount;

  QtdPtr          = NULL;
  PreQtdPtr       = NULL;
  SetupQtdPtr     = NULL;
  FirstDataQtdPtr = NULL;
  LastDataQtdPtr  = NULL;
  StatusQtdPtr    = NULL;
  CapacityOfQtd = 0;

  //
  //  Setup Stage of Control Transfer
  //
  Status = CreateSetupQtd (
             HcDev,
             RequestCursor,
             &SetupQtdPtr
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_OUT_OF_RESOURCES;
    goto exit;
  }

  //
  //  Data Stage of Control Transfer
  //
  DataToggle  = 1;
  DataCount   = DataLen;

  //
  // Create Qtd structure and link together
  //
  while (DataCount > 0) {
    //
    // PktSize is the data load size that each Qtd.
    //
    CapacityOfQtd = GetCapacityOfQtd (DataCursor);
    SizePerQtd    = DataCount;
    if (DataCount > CapacityOfQtd) {
      SizePerQtd = CapacityOfQtd;
    }

    Status = CreateDataQtd (
               HcDev,
               DataCursor,
               SizePerQtd,
               DataPktId,
               DataToggle,
               &QtdPtr
               );
    if (EFI_ERROR (Status)) {
      Status = EFI_OUT_OF_RESOURCES;
      if (NULL == FirstDataQtdPtr) {
        goto destory_setup_qtd;
      } else {
        goto destory_qtds;
      }
    }

    if (NULL == FirstDataQtdPtr) {
      FirstDataQtdPtr = QtdPtr;
    } else {
      LinkQtdToQtd (PreQtdPtr, QtdPtr);
    }

    DataToggle ^= 1;

    PreQtdPtr = QtdPtr;
    DataCursor += SizePerQtd;
    DataCount -= SizePerQtd;
  }

  LastDataQtdPtr = QtdPtr;

  //
  // Status Stage of Control Transfer
  //
  if (OUTPUT_PACKET_ID == DataPktId) {
    StatusPktId = INPUT_PACKET_ID;
  } else {
    StatusPktId = OUTPUT_PACKET_ID;
  }

  Status = CreateStatusQtd (
            HcDev,
            StatusPktId,
            &StatusQtdPtr
            );
  if (EFI_ERROR (Status)) {
    Status = EFI_OUT_OF_RESOURCES;
    goto destory_qtds;
  }

  //
  // Link setup Qtd -> data Qtds -> status Qtd
  //
  if (FirstDataQtdPtr != NULL) {
    LinkQtdToQtd (SetupQtdPtr, FirstDataQtdPtr);
    LinkQtdToQtd (LastDataQtdPtr, StatusQtdPtr);
  } else {
    LinkQtdToQtd (SetupQtdPtr, StatusQtdPtr);
  }

  *ControlQtdsHead = SetupQtdPtr;

  goto exit;

destory_qtds:
  DestoryQtds (HcDev, FirstDataQtdPtr);
destory_setup_qtd:
  DestoryQtds (HcDev, SetupQtdPtr);
exit:
  return Status;
}

EFI_STATUS
CreateBulkOrInterruptQtds (
  IN  USB2_HC_DEV                         *HcDev,
  IN  UINT8                               PktId,
  IN  UINT8                               *DataCursor,
  IN  UINTN                               DataLen,
  IN  EFI_USB2_HC_TRANSACTION_TRANSLATOR  *Translator,
  OUT EHCI_QTD_ENTITY                     **QtdsHead
  )
/*++

Routine Description:

  Create Qtds list for Bulk or Interrupt Transfer

Arguments:

  HcDev        - USB2_HC_DEV
  PktId        - Packet Identification of Qtds
  DataCursor   - A pointer to user data buffer to transfer
  DataLen      - Length of user data to transfer
  DataToggle   - Data Toggle to start
  Translator   - Translator Transaction for SplitX
  QtdsHead     - A pointer of pointer to first Qtd for control tranfer for return

Returns:

  EFI_SUCCESS            Success
  EFI_OUT_OF_RESOURCES   Cannot allocate resources

--*/
{
  EFI_STATUS      Status;
  EHCI_QTD_ENTITY *QtdPtr;
  EHCI_QTD_ENTITY *PreQtdPtr;
  EHCI_QTD_ENTITY *FirstQtdPtr;
  EHCI_QTD_ENTITY *AltQtdPtr;
  UINTN           DataCount;
  UINTN           CapacityOfQtd;
  UINTN           SizePerQtd;

  Status        = EFI_SUCCESS;
  QtdPtr        = NULL;
  PreQtdPtr     = NULL;
  FirstQtdPtr   = NULL;
  AltQtdPtr     = NULL;
  CapacityOfQtd = 0;

  DataCount   = DataLen;
  while (DataCount > 0) {

    CapacityOfQtd = GetCapacityOfQtd (DataCursor);
    SizePerQtd    = DataCount;
    if (DataCount > CapacityOfQtd) {
      SizePerQtd = CapacityOfQtd;
    }

    Status = CreateDataQtd (
              HcDev,
              DataCursor,
              SizePerQtd,
              PktId,
              0,
              &QtdPtr
              );
    if (EFI_ERROR (Status)) {
      Status = EFI_OUT_OF_RESOURCES;
      if (NULL == FirstQtdPtr) {
        goto exit;
      } else {
        goto destory_qtds;
      }
    }

    if (NULL == FirstQtdPtr) {
      FirstQtdPtr = QtdPtr;
    } else {
      LinkQtdToQtd (PreQtdPtr, QtdPtr);
    }

    PreQtdPtr = QtdPtr;
    DataCursor += SizePerQtd;
    DataCount -= SizePerQtd;
  }

  //
  // Set Alternate Qtd
  //
  if (INPUT_PACKET_ID == PktId && 0 < GetNumberOfQtd (FirstQtdPtr)) {
    Status = CreateAltQtd (
              HcDev,
              PktId,
              &AltQtdPtr
              );
    if (EFI_ERROR (Status)) {
      Status = EFI_OUT_OF_RESOURCES;
      goto destory_qtds;
    }

    LinkQtdsToAltQtd (FirstQtdPtr, AltQtdPtr);
  }

  *QtdsHead = FirstQtdPtr;
  goto exit;

destory_qtds:
  DestoryQtds (HcDev, FirstQtdPtr);
exit:
  return Status;
}

VOID
DestoryQtds (
  IN USB2_HC_DEV          *HcDev,
  IN EHCI_QTD_ENTITY      *FirstQtdPtr
  )
/*++

Routine Description:

  Destory all Qtds in the list

Arguments:

  HcDev         - USB2_HC_DEV
  FirstQtdPtr   - A pointer to first Qtd in the list

Returns:

  VOID

--*/
{
  EHCI_QTD_ENTITY *PrevQtd;
  EHCI_QTD_ENTITY *NextQtd;

  if (!FirstQtdPtr) {
    goto exit;
  }

  PrevQtd = FirstQtdPtr;

  //
  // Delete all the Qtds.
  //
  do {
    NextQtd = PrevQtd->Next;
    EhciFreePool (HcDev, (UINT8 *) PrevQtd, sizeof (EHCI_QTD_ENTITY));
    PrevQtd = NextQtd;
  } while (NULL != PrevQtd);

exit:
  return ;
}

UINTN
GetNumberOfQtd (
  IN EHCI_QTD_ENTITY    *FirstQtdPtr
  )
/*++

Routine Description:

  Number of Qtds in the list

Arguments:

  FirstQtdPtr - A pointer to first Qtd in the list

Returns:

  Number of Qtds in the list

--*/
{
  UINTN           Count;
  EHCI_QTD_ENTITY *QtdPtr;
  Count   = 0;
  QtdPtr  = FirstQtdPtr;

  while (NULL != QtdPtr) {
    Count++;
    QtdPtr = QtdPtr->Next;
  }

  return Count;
}

UINTN
GetCapacityOfQtd (
  IN UINT8    *BufferCursor
  )
/*++

Routine Description:

  Get Size of First Qtd

Arguments:

  BufferCursor       - BufferCursor of the Qtd

Returns:

  Size of First Qtd

--*/
{

 if (EFI_PAGE_MASK & GET_0B_TO_31B (BufferCursor)) {
   return EFI_PAGE_SIZE * 4;
 } else {
   return EFI_PAGE_SIZE * 5;
 }

}

UINTN
GetApproxiOfInterval (
  IN UINTN  Interval
  )
/*++

Routine Description:

  Get the approximate value in the 2 index sequence

Arguments:

  Interval  - the value of interval

Returns:

  approximate value of interval in the 2 index sequence

--*/
{
  UINTN Orignate;
  UINTN Approxi;

  Orignate  = Interval;
  Approxi   = 1;

  while (Orignate != 1 && Orignate != 0) {
    Orignate  = Orignate >> 1;
    Approxi   = Approxi << 1;
  }

  if (Interval & (Approxi >> 1)) {
    Approxi = Approxi << 1;
  }

  return Approxi;
}

EHCI_QTD_HW *
GetQtdAlternateNextPointer (
  IN EHCI_QTD_HW  *HwQtdPtr
  )
/*++

Routine Description:

  Get Qtd alternate next pointer field

Arguments:

  HwQtdPtr - A pointer to hardware Qtd structure

Returns:

  A pointer to hardware alternate Qtd

--*/
{
  EHCI_QTD_HW *Value;

  Value = NULL;

  if (!HwQtdPtr->AltNextQtdTerminate) {
    Value = (EHCI_QTD_HW *) GET_0B_TO_31B (HwQtdPtr->AltNextQtdPointer << 5);
  }

  return Value;
}

EHCI_QTD_HW *
GetQtdNextPointer (
  IN EHCI_QTD_HW  *HwQtdPtr
  )
/*++

Routine Description:

  Get Qtd next pointer field

Arguments:

  HwQtdPtr - A pointer to hardware Qtd structure

Returns:

  A pointer to next hardware Qtd structure

--*/
{
  EHCI_QTD_HW *Value;

  Value = NULL;

  if (!HwQtdPtr->NextQtdTerminate) {
    Value = (EHCI_QTD_HW *) GET_0B_TO_31B (HwQtdPtr->NextQtdPointer << 5);
  }

  return Value;
}

VOID
LinkQtdToQtd (
  IN EHCI_QTD_ENTITY * PreQtdPtr,
  IN EHCI_QTD_ENTITY * QtdPtr
  )
/*++

Routine Description:

  Link Qtds together

Arguments:

  PreQtdPtr   - A pointer to pre Qtd
  QtdPtr      - A pointer to next Qtd

Returns:

  VOID

--*/
{
  EHCI_QTD_HW *QtdHwPtr;

  ASSERT(PreQtdPtr);
  ASSERT(QtdPtr);

  //
  // Software link
  //
  PreQtdPtr->Next = QtdPtr;
  QtdPtr->Prev    = PreQtdPtr;

  //
  // Hardware link
  //
  QtdHwPtr                        = &(QtdPtr->Qtd);
  PreQtdPtr->Qtd.NextQtdPointer   = (UINT32) (GET_0B_TO_31B(QtdHwPtr) >> 5);
  PreQtdPtr->Qtd.NextQtdTerminate = FALSE;

  return ;
}



VOID
LinkQtdsToAltQtd (
  IN EHCI_QTD_ENTITY  * FirstQtdPtr,
  IN EHCI_QTD_ENTITY  * AltQtdPtr
  )
/*++

Routine Description:

  Link AlterQtds together

Arguments:

  FirstQtdPtr  - A pointer to first Qtd in the list
  AltQtdPtr    - A pointer to alternative Qtd

Returns:

  VOID

--*/
{
  EHCI_QTD_ENTITY *QtdPtr;
  EHCI_QTD_HW     *AltQtdHwPtr;

  ASSERT(FirstQtdPtr);
  ASSERT(AltQtdPtr);

  AltQtdHwPtr = &(AltQtdPtr->Qtd);
  QtdPtr      = FirstQtdPtr;

  while (NULL != QtdPtr) {
    //
    // Software link
    //
    QtdPtr->AltNext = AltQtdPtr;
    //
    // Hardware link
    //
    QtdPtr->Qtd.AltNextQtdPointer   = (UINT32) (GET_0B_TO_31B(AltQtdHwPtr) >> 5);
    QtdPtr->Qtd.AltNextQtdTerminate = FALSE;
    QtdPtr                          = QtdPtr->Next;
  }

  return ;
}

VOID
LinkQtdToQh (
  IN EHCI_QH_ENTITY      *QhPtr,
  IN EHCI_QTD_ENTITY     *QtdPtr
  )
/*++

Routine Description:

  Link Qtds list to Qh

Arguments:

  QhPtr    - A pointer to Qh
  QtdPtr   - A pointer to first Qtd in the list

Returns:

  VOID

--*/
{
  EHCI_QTD_ENTITY *Cursor;
  EHCI_QTD_HW     *QtdHwPtr;

  ASSERT (QhPtr);
  ASSERT (QtdPtr);

  QhPtr->FirstQtdPtr = QtdPtr;
  if (NULL != QtdPtr->AltNext) {
    QhPtr->AltQtdPtr = QtdPtr->AltNext;
  }

  Cursor = QtdPtr;
  while (NULL != Cursor) {
    Cursor->SelfQh = QhPtr;
    if (NULL == Cursor->Next) {
      QhPtr->LastQtdPtr = Cursor;
    }

    Cursor = Cursor->Next;
  }

  QtdHwPtr                    = &(QtdPtr->Qtd);
  QhPtr->Qh.NextQtdPointer    = (UINT32) (GET_0B_TO_31B (QtdHwPtr) >> 5);
  QhPtr->Qh.NextQtdTerminate  = FALSE;

  QhPtr->Qh.AltNextQtdPointer    = 0;
  QhPtr->Qh.AltNextQtdTerminate  = TRUE;


  if ((QtdPtr->Qtd.PidCode == OUTPUT_PACKET_PID_CODE) &&
      (QhPtr->TransferType == BULK_TRANSFER)) {
      //
      //Start PING first
      //
      QhPtr->Qh.Status |= QTD_STATUS_DO_PING;
    }

  return ;
}

EFI_STATUS
LinkQhToAsyncList (
  IN  USB2_HC_DEV       *HcDev,
  IN EHCI_QH_ENTITY     *QhPtr
  )
/*++

Routine Description:

  Link Qh to Async Schedule List

Arguments:

  HcDev  - USB2_HC_DEV
  QhPtr  - A pointer to Qh

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  EFI_STATUS  Status;

  ASSERT (HcDev);
  ASSERT (QhPtr);


  //
  // NULL QH created before
  //

  HcDev->NULLQH->Next = QhPtr;
  HcDev->NULLQH->Prev = QhPtr;

  QhPtr->Next = HcDev->NULLQH;
  QhPtr->Prev = HcDev->NULLQH;


  HcDev->NULLQH->Qh.QhHorizontalPointer = (UINT32) (GET_0B_TO_31B (&(QhPtr->Qh) >> 5));
  QhPtr->Qh.QhHorizontalPointer = (UINT32) (GET_0B_TO_31B (&(HcDev->NULLQH->Qh) >> 5));


  Status = SetAsyncListAddr (HcDev, HcDev->NULLQH);
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto exit;
  }

  if (!IsAsyncScheduleEnabled (HcDev)) {

    Status = EnableAsynchronousSchedule (HcDev);
    if (EFI_ERROR (Status)) {
      Status = EFI_DEVICE_ERROR;
      goto exit;
    }

    Status = WaitForAsyncScheduleEnable (HcDev, EHCI_GENERIC_TIMEOUT);
    if (EFI_ERROR (Status)) {
      DEBUG ((gEHCDebugLevel, "EHCI: WaitForAsyncScheduleEnable TimeOut"));
      Status = EFI_TIMEOUT;
      goto exit;
    }

    if (IsEhcHalted (HcDev)) {
      Status = StartScheduleExecution (HcDev);
      if (EFI_ERROR (Status)) {
        Status = EFI_DEVICE_ERROR;
      }
    }

  }

exit:
  return Status;
}

EFI_STATUS
UnlinkQhFromAsyncList (
  IN USB2_HC_DEV        *HcDev,
  IN EHCI_QH_ENTITY     *QhPtr
  )
/*++

Routine Description:

  Unlink Qh from Async Schedule List

Arguments:

  HcDev  - USB2_HC_DEV
  QhPtr  - A pointer to Qh

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  EFI_STATUS  Status;

  Status = EFI_SUCCESS;

  ASSERT (HcDev);
  ASSERT (QhPtr);


  HcDev->NULLQH->Next = HcDev->NULLQH;
  HcDev->NULLQH->Prev = HcDev->NULLQH;


  QhPtr->Next = QhPtr;
  QhPtr->Prev = QhPtr;

  HcDev->NULLQH->Qh.QhHorizontalPointer = (UINT32) (GET_0B_TO_31B (&(HcDev->NULLQH->Qh) >> 5));


  SetAndWaitDoorBell (HcDev, 2 * EHCI_GENERIC_TIMEOUT);

  QhPtr->Qh.QhTerminate         = 1;
  QhPtr->Qh.QhHorizontalPointer = 0;


  Status = DisableAsynchronousSchedule (HcDev);
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto exit;
  }

  Status = WaitForAsyncScheduleDisable (HcDev, EHCI_GENERIC_TIMEOUT);
  if (EFI_ERROR (Status)) {
    DEBUG ((gEHCErrorLevel, "EHCI: WaitForAsyncScheduleDisable TimeOut\n"));
    Status = EFI_TIMEOUT;
    goto exit;
  }


exit:
  return Status;
}

VOID
LinkQhToPeriodicList (
  IN USB2_HC_DEV        *HcDev,
  IN EHCI_QH_ENTITY     *QhPtr
  )
/*++

Routine Description:

  Link Qh to Periodic Schedule List

Arguments:

  HcDev  - USB2_HC_DEV
  QhPtr  - A pointer to Qh

Returns:

  VOID

--*/
{
  FRAME_LIST_ENTRY  *FrameEntryPtr;
  EHCI_QH_ENTITY    *FindQhPtr;
  EHCI_QH_HW        *FindQhHwPtr;
  UINTN             FrameIndex;

  ASSERT (HcDev);
  ASSERT (QhPtr);

  FindQhPtr                     = NULL;
  FindQhHwPtr                   = NULL;
  FrameIndex                    = 0;
  FrameEntryPtr                 = (FRAME_LIST_ENTRY *) HcDev->PeriodicFrameListBuffer;

  QhPtr->Qh.HeadReclamationFlag = FALSE;

  if (QhPtr->TransferType == ASYNC_INTERRUPT_TRANSFER) {

    //
    // AsyncInterruptTransfer Qh
    //

    //
    // Link to Frame[0] List
    //
    if (!FrameEntryPtr->LinkTerminate) {
      //
      // Not Null FrameList
      //
      FindQhHwPtr = (EHCI_QH_HW *) GET_0B_TO_31B (FrameEntryPtr->LinkPointer << 5);
      FindQhPtr   = (EHCI_QH_ENTITY *) GET_QH_ENTITY_ADDR (FindQhHwPtr);
      //
      // FindQh is Left/Right to Qh
      //
      while ((NULL != FindQhPtr->Next) && (FindQhPtr->Interval > QhPtr->Interval)) {
        FindQhPtr = FindQhPtr->Next;
      }

      if (FindQhPtr->Interval == QhPtr->Interval) {
        //
        // Link Qh after FindQh
        //
        if (NULL != FindQhPtr->Next) {
          FindQhPtr->Next->Prev         = QhPtr;
          QhPtr->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (&(FindQhPtr->Next->Qh) >> 5);
          QhPtr->Qh.SelectType          = QH_SELECT_TYPE;
          QhPtr->Qh.QhTerminate         = FALSE;
        }

        FindQhPtr->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
        FindQhPtr->Qh.SelectType          = QH_SELECT_TYPE;
        FindQhPtr->Qh.QhTerminate         = FALSE;

        QhPtr->Prev                       = FindQhPtr;
        QhPtr->Next                       = FindQhPtr->Next;
        FindQhPtr->Next                   = QhPtr;
      } else if (FindQhPtr->Interval < QhPtr->Interval) {
        //
        // Link Qh before FindQh
        //
        if (NULL == FindQhPtr->Prev) {
          //
          // Qh is the First one in Frame[0] List
          //
          FrameEntryPtr->LinkPointer    = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
          FrameEntryPtr->SelectType     = QH_SELECT_TYPE;
          FrameEntryPtr->LinkTerminate  = FALSE;
        } else {
          //
          // Qh is not the First one in Frame[0] List
          //
          FindQhPtr->Prev->Next                   = QhPtr;
          FindQhPtr->Prev->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
          FindQhPtr->Prev->Qh.SelectType          = QH_SELECT_TYPE;
          FindQhPtr->Prev->Qh.QhTerminate         = FALSE;
        }

        QhPtr->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (&(FindQhPtr->Qh) >> 5);
        QhPtr->Qh.SelectType          = QH_SELECT_TYPE;
        QhPtr->Qh.QhTerminate         = FALSE;

        QhPtr->Next                   = FindQhPtr;
        QhPtr->Prev                   = FindQhPtr->Prev;
        FindQhPtr->Prev               = QhPtr;
      } else {
        //
        // Link Qh after FindQh, Qh is the Last one
        //
        FindQhPtr->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
        FindQhPtr->Prev->Qh.SelectType    = QH_SELECT_TYPE;
        FindQhPtr->Qh.QhTerminate         = FALSE;

        QhPtr->Prev                       = FindQhPtr;
        QhPtr->Next                       = NULL;
        FindQhPtr->Next                   = QhPtr;
      }
    } else {
      //
      // Null FrameList
      //
      FrameEntryPtr->LinkPointer    = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
      FrameEntryPtr->SelectType     = QH_SELECT_TYPE;
      FrameEntryPtr->LinkTerminate  = FALSE;
    }
    //
    // Other Frame[X]
    //
    if (NULL == QhPtr->Prev) {
      //
      // Qh is the First one in Frame[0] List
      //
      FrameIndex += QhPtr->Interval;
      while (FrameIndex < HcDev->PeriodicFrameListLength) {
        FrameEntryPtr                 = (FRAME_LIST_ENTRY *) (FrameEntryPtr + QhPtr->Interval);
        FrameEntryPtr->LinkPointer    = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
        FrameEntryPtr->SelectType     = QH_SELECT_TYPE;
        FrameEntryPtr->LinkTerminate  = FALSE;
        FrameIndex += QhPtr->Interval;
      }
    } else if (QhPtr->Interval < QhPtr->Prev->Interval) {
      //
      // Qh is not the First one in Frame[0] List, and Prev.interval > Qh.interval
      //
      FrameIndex += QhPtr->Interval;
      while (FrameIndex < HcDev->PeriodicFrameListLength) {
        FrameEntryPtr = (FRAME_LIST_ENTRY *) (FrameEntryPtr + QhPtr->Interval);
        if ((FrameIndex % QhPtr->Prev->Interval) != 0) {
          FrameEntryPtr->LinkPointer    = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
          FrameEntryPtr->SelectType     = QH_SELECT_TYPE;
          FrameEntryPtr->LinkTerminate  = FALSE;
        }

        FrameIndex += QhPtr->Interval;
      }
    }
  } else {

    //
    // SyncInterruptTransfer Qh
    //

    if (!FrameEntryPtr->LinkTerminate) {
      //
      // Not Null FrameList
      //
      FindQhHwPtr = (EHCI_QH_HW *) GET_0B_TO_31B (FrameEntryPtr->LinkPointer << 5);
      FindQhPtr   = (EHCI_QH_ENTITY *) GET_QH_ENTITY_ADDR (FindQhHwPtr);
      //
      // FindQh is Last Qh in the Asynchronous List, Link Qh after FindQh
      //
      while (NULL != FindQhPtr->Next) {
        FindQhPtr = FindQhPtr->Next;
      }

      FindQhPtr->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
      FindQhPtr->Qh.SelectType          = QH_SELECT_TYPE;
      FindQhPtr->Qh.QhTerminate         = FALSE;

      FindQhPtr->Next                   = QhPtr;
      QhPtr->Prev                       = FindQhPtr;
    } else {
      //
      // Null FrameList
      //
      FrameEntryPtr->LinkPointer    = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
      FrameEntryPtr->SelectType     = QH_SELECT_TYPE;
      FrameEntryPtr->LinkTerminate  = FALSE;
    }
  }

  return ;
}

VOID
UnlinkQhFromPeriodicList (
  IN USB2_HC_DEV        *HcDev,
  IN EHCI_QH_ENTITY     *QhPtr,
  IN UINTN              Interval
  )
/*++

Routine Description:

  Unlink Qh from Periodic Schedule List

Arguments:

  HcDev     - USB2_HC_DEV
  QhPtr     - A pointer to Qh
  Interval  - Interval of this periodic transfer

Returns:

  VOID

--*/
{
  FRAME_LIST_ENTRY  *FrameEntryPtr;
  UINTN             FrameIndex;

  FrameIndex = 0;

  ASSERT (HcDev);
  ASSERT (QhPtr);

  FrameIndex    = 0;
  FrameEntryPtr = (FRAME_LIST_ENTRY *) HcDev->PeriodicFrameListBuffer;

  if (QhPtr->TransferType == ASYNC_INTERRUPT_TRANSFER) {

    //
    // AsyncInterruptTransfer Qh
    //

    if (NULL == QhPtr->Prev) {
      //
      // Qh is the First one on  Frame[0] List
      //
      if (NULL == QhPtr->Next) {
        //
        // Only one on  Frame[0] List
        //
        while (FrameIndex < HcDev->PeriodicFrameListLength) {
          FrameEntryPtr->LinkPointer    = 0;
          FrameEntryPtr->SelectType     = 0;
          FrameEntryPtr->LinkTerminate  = TRUE;
          FrameEntryPtr                += Interval;
          FrameIndex                   += Interval;
        }
      } else {
        while (FrameIndex < HcDev->PeriodicFrameListLength) {
          FrameEntryPtr->LinkPointer    = (UINT32) GET_0B_TO_31B (&(QhPtr->Next->Qh) >> 5);
          FrameEntryPtr->SelectType     = QH_SELECT_TYPE;
          FrameEntryPtr->LinkTerminate  = FALSE;
          FrameEntryPtr += Interval;
          FrameIndex += Interval;
        }
      }
    } else {

      //
      // Not First one on  Frame[0] List
      //
      if (NULL == QhPtr->Next) {
        //
        // Qh is the Last one on  Frame[0] List
        //
        QhPtr->Prev->Qh.QhHorizontalPointer = 0;
        QhPtr->Prev->Qh.SelectType          = 0;
        QhPtr->Prev->Qh.QhTerminate         = TRUE;
      } else {
        QhPtr->Prev->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Next->Qh) >> 5);
        QhPtr->Prev->Qh.SelectType          = QH_SELECT_TYPE;
        QhPtr->Prev->Qh.QhTerminate         = FALSE;
      }

      if (Interval == QhPtr->Prev->Interval) {
        //
        // Interval is the same as Prev
        // Not involed Frame[X]
        //
      } else {
        //
        // Other Frame[X]
        //
        while (FrameIndex < HcDev->PeriodicFrameListLength) {
          if ((FrameIndex % QhPtr->Prev->Interval) != 0) {
            FrameEntryPtr->LinkPointer    = QhPtr->Prev->Qh.QhHorizontalPointer;
            FrameEntryPtr->SelectType     = QhPtr->Prev->Qh.SelectType;
            FrameEntryPtr->LinkTerminate  = QhPtr->Prev->Qh.QhTerminate;
          }
          FrameEntryPtr += Interval;
          FrameIndex += Interval;
        }
      }
    }

    if (NULL != QhPtr->Next) {
      QhPtr->Next->Prev = QhPtr->Prev;
    }

    if (NULL != QhPtr->Prev) {
      QhPtr->Prev->Next = QhPtr->Next;
    }
  } else {
    //
    // SyncInterruptTransfer Qh
    //
    if (NULL == QhPtr->Prev) {
      //
      // Qh is the only one Qh on  Frame[0] List
      //
      FrameEntryPtr->LinkPointer    = 0;
      FrameEntryPtr->SelectType     = 0;
      FrameEntryPtr->LinkTerminate  = TRUE;
    } else {
      QhPtr->Prev->Qh.QhHorizontalPointer = 0;
      QhPtr->Prev->Qh.SelectType          = 0;
      QhPtr->Prev->Qh.QhTerminate         = TRUE;
    }

    if (NULL != QhPtr->Prev) {
      QhPtr->Prev->Next = NULL;
    }
  }

  return ;
}

VOID
LinkToAsyncReqeust (
  IN  USB2_HC_DEV        *HcDev,
  IN  EHCI_ASYNC_REQUEST *AsyncRequestPtr
  )
/*++

Routine Description:

  Llink AsyncRequest Entry to Async Request List

Arguments:

  HcDev             - USB2_HC_DEV
  AsyncRequestPtr   - A pointer to Async Request Entry

Returns:

  VOID

--*/
{
  EHCI_ASYNC_REQUEST  *CurrentPtr;

  CurrentPtr              = HcDev->AsyncRequestList;
  HcDev->AsyncRequestList = AsyncRequestPtr;
  AsyncRequestPtr->Prev   = NULL;
  AsyncRequestPtr->Next   = CurrentPtr;

  if (NULL != CurrentPtr) {
    CurrentPtr->Prev = AsyncRequestPtr;
  }

  return ;
}

VOID
UnlinkFromAsyncReqeust (
  IN  USB2_HC_DEV        *HcDev,
  IN  EHCI_ASYNC_REQUEST *AsyncRequestPtr
  )
/*++

Routine Description:

  Unlink AsyncRequest Entry from Async Request List

Arguments:

  HcDev            - USB2_HC_DEV
  AsyncRequestPtr  - A pointer to Async Request Entry

Returns:

  VOID

--*/
{
  if (NULL == AsyncRequestPtr->Prev) {
    HcDev->AsyncRequestList = AsyncRequestPtr->Next;
    if (NULL != AsyncRequestPtr->Next) {
      AsyncRequestPtr->Next->Prev = NULL;
    }
  } else {
    AsyncRequestPtr->Prev->Next = AsyncRequestPtr->Next;
    if (NULL != AsyncRequestPtr->Next) {
      AsyncRequestPtr->Next->Prev = AsyncRequestPtr->Prev;
    }
  }

  return ;
}

VOID
SetQtdBufferPointer (
  IN EHCI_QTD_HW  *QtdHwPtr,
  IN VOID         *DataPtr,
  IN UINTN        DataLen
  )
/*++

Routine Description:

  Set data buffer pointers in Qtd

Arguments:

  QtdHwPtr  - A pointer to Qtd hardware structure
  DataPtr   - A pointer to user data buffer
  DataLen   - Length of the user data buffer

Returns:

  VOID

--*/
{
  UINTN RemainLen;

  ASSERT (QtdHwPtr);
  ASSERT (DataLen <= 5 * EFI_PAGE_SIZE);

  RemainLen = DataLen;
  //
  // Allow buffer address range across 4G.
  // But EFI_USB_MAX_BULK_BUFFER_NUM = 1, so don't allow
  // seperate buffer array.
  //
  //
  // Set BufferPointer0, ExtBufferPointer0 and Offset
  //
  QtdHwPtr->BufferPointer0    = (UINT32) (GET_0B_TO_31B (DataPtr) >> EFI_PAGE_SHIFT);
  QtdHwPtr->CurrentOffset     = (UINT32) (GET_0B_TO_31B (DataPtr) & EFI_PAGE_MASK);

  //
  // Set BufferPointer1 and ExtBufferPointer1
  //
  RemainLen = RemainLen > (EFI_PAGE_SIZE - QtdHwPtr->CurrentOffset) ? (RemainLen - (EFI_PAGE_SIZE - QtdHwPtr->CurrentOffset)) : 0;
  if (RemainLen == 0) {
    goto exit;
  }

  QtdHwPtr->BufferPointer1    = QtdHwPtr->BufferPointer0 + 1;

  //
  // Set BufferPointer2 and ExtBufferPointer2
  //
  RemainLen = RemainLen > EFI_PAGE_SIZE ? (RemainLen - EFI_PAGE_SIZE) : 0;
  if (RemainLen == 0) {
    goto exit;
  }

  QtdHwPtr->BufferPointer2    = QtdHwPtr->BufferPointer1 + 1;

  //
  // Set BufferPointer3 and ExtBufferPointer3
  //
  RemainLen = RemainLen > EFI_PAGE_SIZE ? (RemainLen - EFI_PAGE_SIZE) : 0;
  if (RemainLen == 0) {
    goto exit;
  }

  QtdHwPtr->BufferPointer3    = QtdHwPtr->BufferPointer2 + 1;

  //
  // Set BufferPointer4 and ExtBufferPointer4
  //
  RemainLen = RemainLen > EFI_PAGE_SIZE ? (RemainLen - EFI_PAGE_SIZE) : 0;
  if (RemainLen == 0) {
    goto exit;
  }

  QtdHwPtr->BufferPointer4    = QtdHwPtr->BufferPointer3 + 1;

exit:
  return ;
}

BOOLEAN
IsQtdStatusActive (
  IN EHCI_QTD_HW  *HwQtdPtr
  )
/*++

Routine Description:

  Whether Qtd status is active or not

Arguments:

  HwQtdPtr - A pointer to hardware Qtd structure

Returns:

  TRUE    Active
  FALSE   Inactive

--*/
{
  UINT8   QtdStatus;
  BOOLEAN Value;

  QtdStatus = (UINT8) (HwQtdPtr->Status);
  Value     = (BOOLEAN) (QtdStatus & QTD_STATUS_ACTIVE);
  //DEBUG ((gEHCErrorLevel, "EHCI: IsQtdStatusActive 0x%X, Address = %x\r\n",HwQtdPtr->Status, HwQtdPtr));

  return Value;
}

BOOLEAN
IsQtdStatusHalted (
  IN EHCI_QTD_HW  *HwQtdPtr
  )
/*++

Routine Description:

  Whether Qtd status is halted or not

Arguments:

  HwQtdPtr - A pointer to hardware Qtd structure

Returns:

  TRUE    Halted
  FALSE   Not halted

--*/
{
  UINT8   QtdStatus;
  BOOLEAN Value;

  QtdStatus = (UINT8) (HwQtdPtr->Status);
  Value     = (BOOLEAN) (QtdStatus & QTD_STATUS_HALTED);

  return Value;
}

BOOLEAN
IsQtdStatusBufferError (
  IN EHCI_QTD_HW  *HwQtdPtr
  )
/*++

Routine Description:

  Whether Qtd status is buffer error or not

Arguments:

  HwQtdPtr - A pointer to hardware Qtd structure

Returns:

  TRUE    Buffer error
  FALSE   No buffer error

--*/
{
  UINT8   QtdStatus;
  BOOLEAN Value;

  QtdStatus = (UINT8) (HwQtdPtr->Status);
  Value     = (BOOLEAN) (QtdStatus & QTD_STATUS_BUFFER_ERR);

  return Value;
}

BOOLEAN
IsQtdStatusBabbleError (
  IN EHCI_QTD_HW  *HwQtdPtr
  )
/*++

Routine Description:

  Whether Qtd status is babble error or not

Arguments:

  HwQtdPtr - A pointer to hardware Qtd structure

Returns:

  TRUE    Babble error
  FALSE   No babble error

--*/
{
  UINT8   QtdStatus;
  BOOLEAN Value;

  QtdStatus = (UINT8) (HwQtdPtr->Status);
  Value     = (BOOLEAN) (QtdStatus & QTD_STATUS_BABBLE_ERR);

  return Value;
}

BOOLEAN
IsQtdStatusTransactionError (
  IN EHCI_QTD_HW  *HwQtdPtr
  )
/*++

Routine Description:

  Whether Qtd status is transaction error or not

Arguments:

  HwQtdPtr - A pointer to hardware Qtd structure

Returns:

  TRUE    Transaction error
  FALSE   No transaction error

--*/
{
  UINT8   QtdStatus;
  BOOLEAN Value;

  QtdStatus = (UINT8) (HwQtdPtr->Status);
  Value     = (BOOLEAN) (QtdStatus & QTD_STATUS_TRANSACTION_ERR);

  return Value;
}

BOOLEAN
IsDataInTransfer (
  IN UINT8     EndPointAddress
  )
/*++

Routine Description:

  Whether is a DataIn direction transfer

Arguments:

  EndPointAddress - address of the endpoint

Returns:

  TRUE    DataIn
  FALSE   DataOut

--*/
{
  BOOLEAN Value;

  if (EndPointAddress & 0x80) {
    Value = TRUE;
  } else {
    Value = FALSE;
  }

  return Value;
}

EFI_STATUS
MapDataBuffer (
  IN  USB2_HC_DEV             *HcDev,
  IN  EFI_USB_DATA_DIRECTION  TransferDirection,
  IN  VOID                    *Data,
  IN  OUT UINTN               *DataLength,
  OUT UINT8                   *PktId,
  OUT UINT8                   **DataCursor,
  OUT VOID                    **DataMap
  )
/*++

Routine Description:

  Map address of user data buffer

Arguments:

  HcDev              - USB2_HC_DEV
  TransferDirection  - direction of transfer
  Data               - A pointer to user data buffer
  DataLength         - length of user data
  PktId              - Packte Identificaion
  DataCursor         - mapped address to return
  DataMap            - identificaion of this mapping to return

Returns:

  EFI_SUCCESS        Success
  EFI_DEVICE_ERROR   Fail

--*/
{
  EFI_STATUS            Status;
  EFI_PHYSICAL_ADDRESS  TempPhysicalAddr;

  Status = EFI_SUCCESS;

  switch (TransferDirection) {

  case EfiUsbDataIn:

    *PktId = INPUT_PACKET_ID;
    //
    // BusMasterWrite means cpu read
    //
    Status = HcDev->PciIo->Map (
                            HcDev->PciIo,
                            EfiPciIoOperationBusMasterWrite,
                            Data,
                            DataLength,
                            &TempPhysicalAddr,
                            DataMap
                            );
    if (EFI_ERROR (Status)) {
      DEBUG ((gEHCDebugLevel, "EHCI: MapDataBuffer Failed\n"));
      Status = EFI_DEVICE_ERROR;
      goto exit;
    }

    *DataCursor = (UINT8 *) ((UINTN) TempPhysicalAddr);
    break;

  case EfiUsbDataOut:

    *PktId = OUTPUT_PACKET_ID;
    //
    // BusMasterRead means cpu write
    //
    Status = HcDev->PciIo->Map (
                            HcDev->PciIo,
                            EfiPciIoOperationBusMasterRead,
                            Data,
                            DataLength,
                            &TempPhysicalAddr,
                            DataMap
                            );
    if (EFI_ERROR (Status)) {
      Status = EFI_DEVICE_ERROR;
      goto exit;
    }

    *DataCursor = (UINT8 *) ((UINTN) TempPhysicalAddr);
    break;

  case EfiUsbNoData:

    *PktId      = OUTPUT_PACKET_ID;
    Data        = NULL;
    *DataLength = 0;
    *DataCursor = NULL;
    *DataMap    = NULL;
    break;

  default:

    Status = EFI_INVALID_PARAMETER;
  }

exit:
  return Status;
}

EFI_STATUS
MapRequestBuffer (
  IN  USB2_HC_DEV             *HcDev,
  IN  OUT VOID                *Request,
  OUT UINT8                   **RequestCursor,
  OUT VOID                    **RequestMap
  )
/*++

Routine Description:

  Map address of request structure buffer

Arguments:

  HcDev           - USB2_HC_DEV
  Request         - A pointer to request structure
  RequestCursor   - Mapped address of request structure to return
  RequestMap      - Identificaion of this mapping to return

Returns:

  EFI_SUCCESS        Success
  EFI_DEVICE_ERROR   Fail

--*/
{
  EFI_STATUS            Status;
  UINTN                 RequestLen;
  EFI_PHYSICAL_ADDRESS  TempPhysicalAddr;

  RequestLen = sizeof (EFI_USB_DEVICE_REQUEST);
  Status = HcDev->PciIo->Map (
                           HcDev->PciIo,
                           EfiPciIoOperationBusMasterRead,
                           (UINT8 *) Request,
                           (UINTN *) &RequestLen,
                           &TempPhysicalAddr,
                           RequestMap
                           );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto exit;
  }

  *RequestCursor = (UINT8 *) ((UINTN) TempPhysicalAddr);

exit:
  return Status;
}

EFI_STATUS
DeleteAsyncRequestTransfer (
  IN  USB2_HC_DEV     *HcDev,
  IN  UINT8           DeviceAddress,
  IN  UINT8           EndPointAddress,
  OUT UINT8           *DataToggle
  )
/*++

Routine Description:

  Delete all asynchronous request transfer

Arguments:

  HcDev           - USB2_HC_DEV
  DeviceAddress   - address of usb device
  EndPointAddress - address of endpoint
  DataToggle      - stored data toggle

Returns:

  EFI_SUCCESS        Success
  EFI_DEVICE_ERROR   Fail

--*/
{
  EFI_STATUS          Status;
  EHCI_ASYNC_REQUEST  *AsyncRequestPtr;
  EHCI_ASYNC_REQUEST  *MatchPtr;
  EHCI_QH_HW          *QhHwPtr;
  UINT8               EndPointNum;

  if (NULL == HcDev->AsyncRequestList) {
    Status = EFI_INVALID_PARAMETER;
    goto exit;
  }

  MatchPtr        = NULL;
  QhHwPtr         = NULL;
  EndPointNum     = (UINT8) (EndPointAddress & 0x0f);
  AsyncRequestPtr = HcDev->AsyncRequestList;

  //
  // Find QH of AsyncRequest by DeviceAddress and EndPointNum
  //
  do {

    QhHwPtr = &(AsyncRequestPtr->QhPtr->Qh);
    if (QhHwPtr->DeviceAddr == DeviceAddress && QhHwPtr->EndpointNum == EndPointNum) {
      MatchPtr = AsyncRequestPtr;
      break;
    }

    AsyncRequestPtr = AsyncRequestPtr->Next;

  } while (NULL != AsyncRequestPtr);

  if (NULL == MatchPtr) {
    Status = EFI_INVALID_PARAMETER;
    goto exit;
  }

  Status = DisablePeriodicSchedule (HcDev);
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto exit;
  }

  Status = WaitForPeriodicScheduleDisable (HcDev, EHCI_GENERIC_TIMEOUT);
  if (EFI_ERROR (Status)) {
    DEBUG ((gEHCErrorLevel, "EHCI: WaitForPeriodicScheduleDisable TimeOut\n"));
    Status = EFI_TIMEOUT;
    goto exit;
  }

  *DataToggle = (UINT8) MatchPtr->QhPtr->Qh.DataToggle;
  UnlinkQhFromPeriodicList (HcDev, MatchPtr->QhPtr, MatchPtr->QhPtr->Interval);
  UnlinkFromAsyncReqeust (HcDev, MatchPtr);

  if (NULL == HcDev->AsyncRequestList) {

    Status = StopPollingTimer (HcDev);
    if (EFI_ERROR (Status)) {
      Status = EFI_DEVICE_ERROR;
      goto exit;
    }

  } else {

    Status = EnablePeriodicSchedule (HcDev);
    if (EFI_ERROR (Status)) {
      Status = EFI_DEVICE_ERROR;
      goto exit;
    }

    Status = WaitForPeriodicScheduleEnable (HcDev, EHCI_GENERIC_TIMEOUT);
    if (EFI_ERROR (Status)) {
      DEBUG ((gEHCErrorLevel, "EHCI: WaitForPeriodicScheduleEnable TimeOut\n"));
      Status = EFI_TIMEOUT;
      goto exit;
    }

    if (IsEhcHalted (HcDev)) {
      Status = StartScheduleExecution (HcDev);
      if (EFI_ERROR (Status)) {
        Status = EFI_DEVICE_ERROR;
        goto exit;
      }
    }

  }

  DestoryQtds (HcDev, MatchPtr->QhPtr->FirstQtdPtr);
  DestoryQh (HcDev, MatchPtr->QhPtr);
  EhciFreePool (HcDev, (UINT8 *) MatchPtr, sizeof (EHCI_ASYNC_REQUEST));

exit:
  return Status;
}

VOID
CleanUpAllAsyncRequestTransfer (
  IN USB2_HC_DEV  *HcDev
  )
/*++

Routine Description:

  Clean up all asynchronous request transfer

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  VOID

--*/
{
  EHCI_ASYNC_REQUEST  *AsyncRequestPtr;
  EHCI_ASYNC_REQUEST  *FreePtr;

  AsyncRequestPtr = NULL;
  FreePtr         = NULL;

  StopPollingTimer (HcDev);

  AsyncRequestPtr = HcDev->AsyncRequestList;
  while (NULL != AsyncRequestPtr) {

    FreePtr         = AsyncRequestPtr;
    AsyncRequestPtr = AsyncRequestPtr->Next;
    UnlinkFromAsyncReqeust (HcDev, FreePtr);
    UnlinkQhFromPeriodicList (HcDev, FreePtr->QhPtr, FreePtr->QhPtr->Interval);
    DestoryQtds (HcDev, FreePtr->QhPtr->FirstQtdPtr);
    DestoryQh (HcDev, FreePtr->QhPtr);
    EhciFreePool (HcDev, (UINT8 *) FreePtr, sizeof (EHCI_ASYNC_REQUEST));

  }

  return ;
}

VOID
ZeroOutQhOverlay (
  IN EHCI_QH_ENTITY  *QhPtr
  )
/*++

Routine Description:

  Zero out the fields in Qh structure

Arguments:

  QhPtr - A pointer to Qh structure

Returns:

  VOID

--*/
{
  QhPtr->Qh.CurrentQtdPointer   = 0;
  QhPtr->Qh.AltNextQtdPointer   = 0;
  QhPtr->Qh.NakCount            = 0;
  QhPtr->Qh.AltNextQtdTerminate = 0;
  QhPtr->Qh.TotalBytes          = 0;
  QhPtr->Qh.InterruptOnComplete = 0;
  QhPtr->Qh.CurrentPage         = 0;
  QhPtr->Qh.ErrorCount          = 0;
  QhPtr->Qh.PidCode             = 0;
  QhPtr->Qh.Status              = 0;
  QhPtr->Qh.BufferPointer0      = 0;
  QhPtr->Qh.CurrentOffset       = 0;
  QhPtr->Qh.BufferPointer1      = 0;
  QhPtr->Qh.CompleteSplitMask   = 0;
  QhPtr->Qh.BufferPointer2      = 0;
  QhPtr->Qh.SplitBytes          = 0;
  QhPtr->Qh.FrameTag            = 0;
  QhPtr->Qh.BufferPointer3      = 0;
  QhPtr->Qh.BufferPointer4      = 0;
}

VOID
UpdateAsyncRequestTransfer (
  IN EHCI_ASYNC_REQUEST *AsyncRequestPtr,
  IN UINT32             TransferResult,
  IN UINTN              ErrQtdPos
  )
/*++

Routine Description:

  Update asynchronous request transfer

Arguments:

  AsyncRequestPtr  - A pointer to async request
  TransferResult   - transfer result
  ErrQtdPos        - postion of error Qtd

Returns:

  VOID

--*/
{
  EHCI_QTD_ENTITY *QtdPtr;

  QtdPtr      = NULL;

  if (EFI_USB_NOERROR == TransferResult) {

    //
    // Update Qh for next trigger
    //

    QtdPtr = AsyncRequestPtr->QhPtr->FirstQtdPtr;

    //
    // Update fields in Qh
    //

    //
    // Get DataToggle from Overlay in Qh
    //
    // ZeroOut Overlay in Qh except DataToggle, HostController will update this field
    //
    ZeroOutQhOverlay (AsyncRequestPtr->QhPtr);
    AsyncRequestPtr->QhPtr->Qh.NextQtdPointer   = (UINT32) (GET_0B_TO_31B (&(QtdPtr->Qtd)) >> 5);
    AsyncRequestPtr->QhPtr->Qh.NextQtdTerminate = FALSE;

    //
    // Update fields in Qtd
    //
    while (NULL != QtdPtr) {
      QtdPtr->Qtd.TotalBytes    = QtdPtr->StaticTotalBytes;
      QtdPtr->Qtd.CurrentOffset = QtdPtr->StaticCurrentOffset;
      QtdPtr->Qtd.CurrentPage   = 0;
      QtdPtr->Qtd.ErrorCount    = QTD_ERROR_COUNTER;
      QtdPtr->Qtd.Status        = QTD_STATUS_ACTIVE;

      QtdPtr->TotalBytes        = QtdPtr->StaticTotalBytes;
      QtdPtr                    = QtdPtr->Next;
    }
  }

  return ;
}

BOOLEAN
CheckQtdsTransferResult (
  IN  BOOLEAN            IsControl,
  IN  EHCI_QH_ENTITY     *QhPtr,
  OUT UINT32             *Result,
  OUT UINTN              *ErrQtdPos,
  OUT UINTN              *ActualLen
  )
/*++

Routine Description:

  Check transfer result of Qtds

Arguments:

  IsControl    - Is control transfer or not
  QhPtr        - A pointer to Qh
  Result       - Transfer result
  ErrQtdPos    - Error TD Position
  ActualLen    - Actual Transfer Size

Returns:

  TRUE    Qtds finished
  FALSE   Not finish

--*/
{
  UINTN           ActualLenPerQtd;
  EHCI_QTD_ENTITY *QtdPtr;
  EHCI_QTD_HW     *QtdHwPtr;
  BOOLEAN         Value;

  ASSERT (QhPtr);
  ASSERT (Result);
  ASSERT (ErrQtdPos);
  ASSERT (ActualLen);

  Value     = TRUE;
  QtdPtr    = QhPtr->FirstQtdPtr;
  QtdHwPtr  = &(QtdPtr->Qtd);

  while (NULL != QtdHwPtr) {
    if (IsQtdStatusActive (QtdHwPtr)) {
      *Result |= EFI_USB_ERR_NOTEXECUTE;
    }

    if (IsQtdStatusHalted (QtdHwPtr)) {

      DEBUG ((gEHCErrorLevel, "EHCI: QTD_STATUS_HALTED 0x%X\n", QtdHwPtr->Status));
      *Result |= EFI_USB_ERR_STALL;
    }

    if (IsQtdStatusBufferError (QtdHwPtr)) {
      DEBUG ((gEHCErrorLevel, "EHCI: QTD_STATUS_BUFFER_ERR 0x%X\n", QtdHwPtr->Status));
      *Result |= EFI_USB_ERR_BUFFER;
    }

    if (IsQtdStatusBabbleError (QtdHwPtr)) {
      DEBUG ((gEHCErrorLevel, "EHCI: StatusBufferError 0x%X\n", QtdHwPtr->Status));
      *Result |= EFI_USB_ERR_BABBLE;
    }

    if (IsQtdStatusTransactionError (QtdHwPtr)) {

      //
      //Exclude Special Case
      //
      if (((QtdHwPtr->Status & QTD_STATUS_HALTED) == QTD_STATUS_HALTED) ||
          ((QtdHwPtr->Status & QTD_STATUS_ACTIVE) == QTD_STATUS_ACTIVE) ||
          ((QtdHwPtr->ErrorCount != QTD_ERROR_COUNTER))) {
        *Result |= EFI_USB_ERR_TIMEOUT;
        DEBUG ((gEHCErrorLevel, "EHCI: QTD_STATUS_TRANSACTION_ERR: 0x%X\n", QtdHwPtr->Status));
      }
    }

    ActualLenPerQtd     = QtdPtr->TotalBytes - QtdHwPtr->TotalBytes;
    QtdPtr->TotalBytes  = QtdHwPtr->TotalBytes;
    //
    // Accumulate actual transferred data length in each DataQtd.

    //
    if (SETUP_PACKET_PID_CODE != QtdHwPtr->PidCode) {
      *ActualLen += ActualLenPerQtd;
    }

    if (*Result) {
      Value = FALSE;
      break;
    }

    if ((INPUT_PACKET_PID_CODE == QtdHwPtr->PidCode)&& (QtdPtr->TotalBytes > 0)) {
      //
      // Short Packet: IN, Short
      //
      DEBUG ((gEHCDebugLevel, "EHCI: Short Packet Status: 0x%x\n", QtdHwPtr->Status));
      break;
    }

    if (QtdPtr->Next != NULL) {
      (*ErrQtdPos)++;
      QtdPtr   = QtdPtr->Next;
      QtdHwPtr = &(QtdPtr->Qtd);
    } else {
      QtdHwPtr = NULL;
    }

  }

  return Value;
}

EFI_STATUS
ExecuteTransfer (
  IN  USB2_HC_DEV         *HcDev,
  IN  BOOLEAN             IsControl,
  IN  EHCI_QH_ENTITY      *QhPtr,
  IN  OUT UINTN           *ActualLen,
  OUT UINT8               *DataToggle,
  IN  UINTN               TimeOut,
  OUT UINT32              *TransferResult
  )
/*++

Routine Description:

  Execute Bulk or SyncInterrupt Transfer

Arguments:

  HcDev            - USB2_HC_DEV
  IsControl        - Is control transfer or not
  QhPtr            - A pointer to Qh
  ActualLen        - Actual transfered Len
  DataToggle       - Data Toggle
  TimeOut          - TimeOut threshold
  TransferResult   - Transfer result

Returns:

  EFI_SUCCESS      Sucess
  EFI_DEVICE_ERROR Fail

--*/
{
  EFI_STATUS  Status;
  UINTN       ErrQtdPos;
  UINTN       Delay;
  BOOLEAN     Finished;

  Status          = EFI_SUCCESS;
  ErrQtdPos       = 0;
  *TransferResult = EFI_USB_NOERROR;
  *ActualLen      = 0;
  Finished        = FALSE;

  Delay           = (TimeOut * STALL_1_MILLI_SECOND / 50);

  do {
    *TransferResult = 0;
    Finished = CheckQtdsTransferResult (
                 IsControl,
                 QhPtr,
                 TransferResult,
                 &ErrQtdPos,
                 ActualLen
                 );
    if (Finished) {
      break;
    }
    //
    // Qtd is inactive, which means bulk or interrupt transfer's end.
    //
    if (!(*TransferResult & EFI_USB_ERR_NOTEXECUTE)) {
      break;
    }

    gBS->Stall (EHCI_SYNC_REQUEST_POLLING_TIME);

  } while (--Delay);

  if (EFI_USB_NOERROR != *TransferResult) {
    if (0 == Delay) {
      DEBUG((gEHCErrorLevel, "EHCI: QTDS TimeOut\n"));
      Status = EFI_TIMEOUT;
    } else {
      Status = EFI_DEVICE_ERROR;
    }
  }

  //
  // Special for Bulk and Interrupt Transfer
  //
  *DataToggle = (UINT8) QhPtr->Qh.DataToggle;

  return Status;
}

EFI_STATUS
AsyncRequestMoniter (
  IN EFI_EVENT     Event,
  IN VOID          *Context
  )
/*++
Routine Description:

  Interrupt transfer periodic check handler

Arguments:
  Event    - Interrupt event
  Context  - Pointer to USB2_HC_DEV

Returns:

  EFI_SUCCESS        Success
  EFI_DEVICE_ERROR   Fail

--*/
{
  EFI_STATUS          Status;
  USB2_HC_DEV         *HcDev;
  EHCI_ASYNC_REQUEST  *AsyncRequestPtr;
  EHCI_ASYNC_REQUEST  *NextPtr;
  EHCI_QTD_HW         *QtdHwPtr;
  UINTN               ErrQtdPos;
  UINTN               ActualLen;
  UINT32              TransferResult;
  UINT8               *ReceiveBuffer;
  UINT8               *ProcessBuffer;

  Status          = EFI_SUCCESS;
  QtdHwPtr        = NULL;
  ReceiveBuffer   = NULL;
  ProcessBuffer   = NULL;
  HcDev           = (USB2_HC_DEV *) Context;
  AsyncRequestPtr = HcDev->AsyncRequestList;

  while (NULL != AsyncRequestPtr) {

    TransferResult  = 0;
    ErrQtdPos       = 0;
    ActualLen       = 0;

    CheckQtdsTransferResult (
      FALSE,
      AsyncRequestPtr->QhPtr,
      &TransferResult,
      &ErrQtdPos,
      &ActualLen
      );

    if ((TransferResult & EFI_USB_ERR_NAK) || (TransferResult & EFI_USB_ERR_NOTEXECUTE)) {
      AsyncRequestPtr = AsyncRequestPtr->Next;
      continue;
    }
    //
    // Allocate memory for EHC private data structure
    //
    ProcessBuffer = AllocateZeroPool (ActualLen);
    if (NULL == ProcessBuffer) {
      Status = EFI_OUT_OF_RESOURCES;
      goto exit;
    }

    QtdHwPtr = &(AsyncRequestPtr->QhPtr->FirstQtdPtr->Qtd);
    ReceiveBuffer = (UINT8 *) GET_0B_TO_31B ((QtdHwPtr->BufferPointer0 << EFI_PAGE_SHIFT) | AsyncRequestPtr->QhPtr->FirstQtdPtr->StaticCurrentOffset);
    CopyMem (
      ProcessBuffer,
      ReceiveBuffer,
      ActualLen
      );

    UpdateAsyncRequestTransfer (AsyncRequestPtr, TransferResult, ErrQtdPos);

    NextPtr = AsyncRequestPtr->Next;

    if (EFI_USB_NOERROR == TransferResult) {

      if (AsyncRequestPtr->CallBackFunc != NULL) {
        (AsyncRequestPtr->CallBackFunc) (ProcessBuffer, ActualLen, AsyncRequestPtr->Context, TransferResult);
      }

    } else {

      //
      // leave error recovery to its related device driver. A common case of
      // the error recovery is to re-submit the interrupt transfer.
      // When an interrupt transfer is re-submitted, its position in the linked
      // list is changed. It is inserted to the head of the linked list, while
      // this function scans the whole list from head to tail. Thus, the
      // re-submitted interrupt transfer's callback function will not be called
      // again in this round.
      //
      if (AsyncRequestPtr->CallBackFunc != NULL) {
        (AsyncRequestPtr->CallBackFunc) (NULL, 0, AsyncRequestPtr->Context, TransferResult);
      }

    }

    if (NULL != ProcessBuffer) {
      gBS->FreePool (ProcessBuffer);
    }

    AsyncRequestPtr = NextPtr;
  }

exit:
  return Status;
}