/*++

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:

    UhcHlp.c
    
Abstract: 
    

Revision History
--*/

#include "uhci.h"

STATIC
EFI_STATUS
USBReadPortW (
  IN       EFI_PCI_IO_PROTOCOL     *PciIo,
  IN       UINT32                  PortOffset,
  IN OUT   UINT16                  *Data
  )
/*++

Routine Description:

  USBReadPort Word

Arguments:

  PciIo       - EFI_PCI_IO_PROTOCOL
  PortOffset  - Port offset
  Data        - Data to reutrn

Returns:

  EFI_SUCCESS

--*/
{
  //
  // Perform 16bit Read in PCI IO Space
  //
  return PciIo->Io.Read (
                     PciIo,
                     EfiPciIoWidthUint16,
                     USB_BAR_INDEX,
                     (UINT64) PortOffset,
                     1,
                     Data
                     );
}

STATIC
EFI_STATUS
USBWritePortW (
  IN EFI_PCI_IO_PROTOCOL     *PciIo,
  IN UINT32                  PortOffset,
  IN UINT16                  Data
  )
/*++

Routine Description:

  USB Write Port Word

Arguments:

  PciIo       - EFI_PCI_IO_PROTOCOL
  PortOffset  - Port offset
  Data        - Data to write

Returns:

  EFI_SUCCESS

--*/
{
  //
  // Perform 16bit Write in PCI IO Space
  //
  return PciIo->Io.Write (
                     PciIo,
                     EfiPciIoWidthUint16,
                     USB_BAR_INDEX,
                     (UINT64) PortOffset,
                     1,
                     &Data
                     );
}

STATIC
EFI_STATUS
USBWritePortDW (
  IN EFI_PCI_IO_PROTOCOL     *PciIo,
  IN UINT32                  PortOffset,
  IN UINT32                  Data
  )
/*++

Routine Description:

  USB Write Port DWord

Arguments:

  PciIo       - EFI_PCI_IO_PROTOCOL
  PortOffset  - Port offset
  Data        - Data to write

Returns:

  EFI_SUCCESS

--*/
{
  //
  // Perform 32bit Write in PCI IO Space
  //
  return PciIo->Io.Write (
                     PciIo,
                     EfiPciIoWidthUint32,
                     USB_BAR_INDEX,
                     (UINT64) PortOffset,
                     1,
                     &Data
                     );
}
//
//  USB register-base helper functions
//
EFI_STATUS
WriteUHCCommandReg (
  IN EFI_PCI_IO_PROTOCOL     *PciIo,
  IN UINT32                  CmdAddrOffset,
  IN UINT16                  UsbCmd
  )
/*++

Routine Description:

  Write UHCI Command Register

Arguments:

  PciIo         - EFI_PCI_IO_PROTOCOL
  CmdAddrOffset - Command address offset
  UsbCmd        - Data to write

Returns:

  EFI_SUCCESS

--*/
{
  //
  // Write to UHC's Command Register
  //
  return USBWritePortW (PciIo, CmdAddrOffset, UsbCmd);
}

EFI_STATUS
ReadUHCCommandReg (
  IN       EFI_PCI_IO_PROTOCOL     *PciIo,
  IN       UINT32                  CmdAddrOffset,
  IN OUT   UINT16                  *Data
  )
/*++

Routine Description:

  Read UHCI Command Register

Arguments:

  PciIo         - EFI_PCI_IO_PROTOCOL
  CmdAddrOffset - Command address offset
  Data          - Data to return

Returns:

  EFI_SUCCESS

--*/
{
  //
  // Read from UHC's Command Register
  //
  return USBReadPortW (PciIo, CmdAddrOffset, Data);
}

EFI_STATUS
WriteUHCStatusReg (
  IN EFI_PCI_IO_PROTOCOL     *PciIo,
  IN UINT32                  StatusAddrOffset,
  IN UINT16                  UsbSts
  )
/*++

Routine Description:

  Write UHCI Staus Register

Arguments:

  PciIo            - EFI_PCI_IO_PROTOCOL
  StatusAddrOffset - Status address offset
  UsbSts           - Data to write

Returns:

  EFI_SUCCESS

--*/
{
  //
  // Write to UHC's Status Register
  //
  return USBWritePortW (PciIo, StatusAddrOffset, UsbSts);
}

EFI_STATUS
ReadUHCStatusReg (
  IN     EFI_PCI_IO_PROTOCOL     *PciIo,
  IN     UINT32                  StatusAddrOffset,
  IN OUT UINT16                  *Data
  )
/*++

Routine Description:

  Read UHCI Staus Register

Arguments:

  PciIo            - EFI_PCI_IO_PROTOCOL
  StatusAddrOffset - Status address offset
  UsbSts           - Data to return

Returns:

  EFI_SUCCESS

--*/
{
  //
  // Read from UHC's Status Register
  //
  return USBReadPortW (PciIo, StatusAddrOffset, Data);
}


EFI_STATUS
ClearStatusReg (
  IN EFI_PCI_IO_PROTOCOL     *PciIo,
  IN UINT32                  StatusAddrOffset
  )
/*++

Routine Description:

  Clear the content of UHC's Status Register

Arguments:

  PciIo             - EFI_PCI_IO_PROTOCOL
  StatusAddrOffset  - Status address offset
  
Returns:

  EFI_SUCCESS

--*/
{
 
  return WriteUHCStatusReg (PciIo, StatusAddrOffset, 0x003F);
}

EFI_STATUS
ReadUHCFrameNumberReg (
  IN       EFI_PCI_IO_PROTOCOL     *PciIo,
  IN       UINT32                  FrameNumAddrOffset,
  IN OUT   UINT16                  *Data
  )
/*++

Routine Description:

  Read from UHC's Frame Number Register

Arguments:

  PciIo              - EFI_PCI_IO_PROTOCOL
  FrameNumAddrOffset - Frame number register offset
  Data               - Data to return 
Returns:

  EFI_SUCCESS

--*/
{
  
  return USBReadPortW (PciIo, FrameNumAddrOffset, Data);
}

EFI_STATUS
WriteUHCFrameListBaseReg (
  IN EFI_PCI_IO_PROTOCOL     *PciIo,
  IN UINT32                  FlBaseAddrOffset,
  IN UINT32                  UsbFrameListBaseAddr
  )
/*++

Routine Description:

  Write to UHC's Frame List Base Register

Arguments:

  PciIo                - EFI_PCI_IO_PROTOCOL
  FlBaseAddrOffset     - Frame Base address register
  UsbFrameListBaseAddr - Address to write

Returns:

  EFI_SUCCESS

--*/
{
  
  return USBWritePortDW (PciIo, FlBaseAddrOffset, UsbFrameListBaseAddr);
}

EFI_STATUS
ReadRootPortReg (
  IN     EFI_PCI_IO_PROTOCOL     *PciIo,
  IN     UINT32                  PortAddrOffset,
  IN OUT UINT16                  *Data
  )
/*++

Routine Description:

  Read from UHC's Root Port Register

Arguments:

  PciIo           - EFI_PCI_IO_PROTOCOL
  PortAddrOffset  - Port Addrress Offset,
  Data            - Data to return
Returns:

  EFI_SUCCESS

--*/
{
 
  return USBReadPortW (PciIo, PortAddrOffset, Data);
}

EFI_STATUS
WriteRootPortReg (
  IN EFI_PCI_IO_PROTOCOL     *PciIo,
  IN UINT32                  PortAddrOffset,
  IN UINT16                  ControlBits
  )
/*++

Routine Description:

   Write to UHC's Root Port Register

Arguments:

  PciIo           - EFI_PCI_IO_PROTOCOL
  PortAddrOffset  - Port Addrress Offset,
  ControlBits     - Data to write
Returns:

  EFI_SUCCESS

--*/
{
 
  return USBWritePortW (PciIo, PortAddrOffset, ControlBits);
}



EFI_STATUS
WaitForUHCHalt (
  IN EFI_PCI_IO_PROTOCOL     *PciIo,
  IN UINT32                  StatusRegAddr,
  IN UINTN                   Timeout
  )
/*++

Routine Description:

  Wait until UHCI halt or timeout

Arguments:

  PciIo         - EFI_PCI_IO_PROTOCOL
  StatusRegAddr - Status Register Address
  Timeout       - Time out value in us

Returns:

  EFI_DEVICE_ERROR - Unable to read the status register
  EFI_TIMEOUT      - Time out
  EFI_SUCCESS      - Success

--*/
{
  UINTN       Delay;
  EFI_STATUS  Status;
  UINT16      HcStatus;

  //
  // Timeout is in us unit
  //
  Delay = (Timeout / 50) + 1;
  do {
    Status = ReadUHCStatusReg (PciIo, StatusRegAddr, &HcStatus);
    if (EFI_ERROR (Status)) {
      return EFI_DEVICE_ERROR;
    }

    if ((HcStatus & USBSTS_HCH) == USBSTS_HCH) {
      break;
    }
    //
    // Stall for 50 us
    //
    gBS->Stall (50);

  } while (Delay--);

  if (Delay == 0) {
    return EFI_TIMEOUT;
  }

  return EFI_SUCCESS;
}

BOOLEAN
IsStatusOK (
  IN EFI_PCI_IO_PROTOCOL     *PciIo,
  IN UINT32                  StatusRegAddr
  )
/*++

Routine Description:

  Judge whether the host controller operates well

Arguments:

  PciIo         - EFI_PCI_IO_PROTOCOL
  StatusRegAddr - Status register address

Returns:

   TRUE  -  Status is good
   FALSE -  Status is bad

--*/
{
  EFI_STATUS  Status;
  UINT16      HcStatus;
  //
  // Detect whether the interrupt is caused by fatal error.
  // see "UHCI Design Guid".
  //
  Status = ReadUHCStatusReg (PciIo, StatusRegAddr, &HcStatus);
  if (EFI_ERROR (Status)) {
    return FALSE;
  }

  if (HcStatus & (USBSTS_HCPE | USBSTS_HSE | USBSTS_HCH)) {
    return FALSE;
  } else {
    return TRUE;
  }

}


BOOLEAN 
IsHostSysOrProcessErr (
  IN EFI_PCI_IO_PROTOCOL     *PciIo,
  IN UINT32                  StatusRegAddr
  )
/*++

Routine Description:

  Judge the status is HostSys,ProcessErr error or good

Arguments:

  PciIo         - EFI_PCI_IO_PROTOCOL
  StatusRegAddr - Status register address

Returns:

   TRUE  -  Status is good
   FALSE -  Status is bad

--*/
{
  EFI_STATUS  Status;
  UINT16      HcStatus;
  //
  // Detect whether the interrupt is caused by serious error.
  // see "UHCI Design Guid".
  //
  Status = ReadUHCStatusReg (PciIo, StatusRegAddr, &HcStatus);
  if (EFI_ERROR (Status)) {
    return FALSE;
  }

  if (HcStatus & (USBSTS_HSE | USBSTS_HCPE)) {
    return TRUE;
  } else {
    return FALSE;
  }
}


UINT16
GetCurrentFrameNumber (
  IN EFI_PCI_IO_PROTOCOL     *PciIo,
  IN UINT32                  FrameNumAddrOffset
  )
/*++

Routine Description:

  Get Current Frame Number

Arguments:

  PciIo               - EFI_PCI_IO_PROTOCOL
  FrameNumAddrOffset  - FrameNum register AddrOffset

Returns:

  Frame number 

--*/
{
  //
  // Gets value in the USB frame number register.
  //
  UINT16  FrameNumber;

  ReadUHCFrameNumberReg (PciIo, FrameNumAddrOffset, &FrameNumber);

  return (UINT16) (FrameNumber & 0x03FF);
}

EFI_STATUS
SetFrameListBaseAddress (
  IN EFI_PCI_IO_PROTOCOL     *PciIo,
  IN UINT32                  FlBaseAddrReg,
  IN UINT32                  Addr
  )
/*++

Routine Description:

  Set FrameListBase Address

Arguments:

  PciIo         - EFI_PCI_IO_PROTOCOL
  FlBaseAddrReg - FrameListBase register
  Addr          - Address to set

Returns:

  EFI_SUCCESS

--*/
{
  //
  // Sets value in the USB Frame List Base Address register.
  //
  return WriteUHCFrameListBaseReg (PciIo, FlBaseAddrReg, (UINT32) (Addr & 0xFFFFF000));
}

VOID
EnableMaxPacketSize (
  IN USB_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Enable Max Packet Size

Arguments:

  HcDev - USB_HC_DEV

Returns:

  VOID

--*/
{
  UINT16      CommandContent;

  ReadUHCCommandReg (
    HcDev->PciIo,
    (UINT32) (USBCMD),
    &CommandContent
    );

  if ((CommandContent & USBCMD_MAXP) != USBCMD_MAXP) {
    CommandContent |= USBCMD_MAXP;
    WriteUHCCommandReg (
      HcDev->PciIo,
      (UINT32) (USBCMD),
      CommandContent
      );
  }

  return ;
}

EFI_STATUS
CreateFrameList (
  IN USB_HC_DEV     *HcDev,
  IN UINT32         FlBaseAddrReg
  )
/*++

Routine Description:

  CreateFrameList

Arguments:

  HcDev         - USB_HC_DEV
  FlBaseAddrReg - Frame List register

Returns:

  EFI_OUT_OF_RESOURCES - Can't allocate memory resources
  EFI_UNSUPPORTED      - Map memory fail
  EFI_SUCCESS          - Success

--*/
{
  EFI_STATUS            Status;
  VOID                  *CommonBuffer;
  EFI_PHYSICAL_ADDRESS  MappedAddress;
  VOID                  *Mapping;
  UINTN                 BufferSizeInPages;
  UINTN                 BufferSizeInBytes;

  //
  // 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.
  //
  BufferSizeInBytes = 4096;
  BufferSizeInPages = EFI_SIZE_TO_PAGES (BufferSizeInBytes);
  Status = HcDev->PciIo->AllocateBuffer (
                           HcDev->PciIo,
                           AllocateAnyPages,
                           EfiBootServicesData,
                           BufferSizeInPages,
                           &CommonBuffer,
                           0
                           );
  if (EFI_ERROR (Status)) {
    return EFI_OUT_OF_RESOURCES;
  }

  Status = HcDev->PciIo->Map (
                           HcDev->PciIo,
                           EfiPciIoOperationBusMasterCommonBuffer,
                           CommonBuffer,
                           &BufferSizeInBytes,
                           &MappedAddress,
                           &Mapping
                           );
  if (EFI_ERROR (Status) || (BufferSizeInBytes != 4096)) {
    HcDev->PciIo->FreeBuffer (HcDev->PciIo, BufferSizeInPages, CommonBuffer);
    return EFI_UNSUPPORTED;
  }

  HcDev->FrameListEntry   = (FRAMELIST_ENTRY *) ((UINTN) MappedAddress);

  HcDev->FrameListMapping = Mapping;

  InitFrameList (HcDev);

  //
  // Tell the Host Controller where the Frame List lies,
  // by set the Frame List Base Address Register.
  //
  SetFrameListBaseAddress (
    HcDev->PciIo,
    FlBaseAddrReg,
    (UINT32) ((UINTN) HcDev->FrameListEntry)
    );

  return EFI_SUCCESS;
}

EFI_STATUS
FreeFrameListEntry (
  IN USB_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Free FrameList buffer

Arguments:

  HcDev - USB_HC_DEV

Returns:

  EFI_SUCCESS - success

--*/
{
  //
  // Unmap the common buffer for framelist entry,
  // and free the common buffer.
  // Uhci's frame list occupy 4k memory.
  //
  HcDev->PciIo->Unmap (HcDev->PciIo, HcDev->FrameListMapping);
  HcDev->PciIo->FreeBuffer (
                  HcDev->PciIo,
                  EFI_SIZE_TO_PAGES (4096),
                  (VOID *) (HcDev->FrameListEntry)
                  );
  return EFI_SUCCESS;
}

VOID
InitFrameList (
  IN USB_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Initialize FrameList

Arguments:

  HcDev - USB_HC_DEV

Returns:
   VOID

--*/  
{
  FRAMELIST_ENTRY *FrameListPtr;
  UINTN           Index;

  //
  // Validate each Frame List Entry
  //
  FrameListPtr = HcDev->FrameListEntry;
  for (Index = 0; Index < 1024; Index++) {
    FrameListPtr->FrameListPtrTerminate = 1;
    FrameListPtr->FrameListPtr          = 0;
    FrameListPtr->FrameListPtrQSelect   = 0;
    FrameListPtr->FrameListRsvd         = 0;
    FrameListPtr++;
  }
}
//
// //////////////////////////////////////////////////////////////
//
// QH TD related Helper Functions
//
////////////////////////////////////////////////////////////////
//
// functions for QH
//
STATIC
EFI_STATUS
AllocateQHStruct (
  IN  USB_HC_DEV     *HcDev,
  OUT QH_STRUCT      **ppQHStruct
  )
/*++

Routine Description:

  Allocate QH Struct

Arguments:

  HcDev       - USB_HC_DEV
  ppQHStruct  - QH_STRUCT content to return
Returns:

  EFI_SUCCESS

--*/
{
  *ppQHStruct = NULL;

  //
  // QH must align on 16 bytes alignment,
  // since the memory allocated by UhciAllocatePool ()
  // is aligned on 32 bytes, it is no need to adjust
  // the allocated memory returned.
  //
  return UhciAllocatePool (HcDev, (UINT8 **) ppQHStruct, sizeof (QH_STRUCT));
}


EFI_STATUS
CreateQH (
  IN  USB_HC_DEV     *HcDev,
  OUT QH_STRUCT      **pptrQH
  )
/*++

Routine Description:

  CreateQH

Arguments:

  HcDev       - USB_HC_DEV
  ppQHStruct  - QH_STRUCT content to return
Returns:

  EFI_SUCCESS          - Success
  EFI_OUT_OF_RESOURCES - Can't allocate memory
--*/
{
  EFI_STATUS  Status;

  //
  // allocate align memory for QH_STRUCT
  //
  Status = AllocateQHStruct (HcDev, pptrQH);
  if (EFI_ERROR (Status)) {
    return EFI_OUT_OF_RESOURCES;
  }
  //
  // init each field of the QH_STRUCT
  //
  //
  // Make QH ready
  //
  SetQHHorizontalValidorInvalid (*pptrQH, FALSE);
  SetQHVerticalValidorInvalid   (*pptrQH, FALSE);

  return EFI_SUCCESS;
}

VOID
SetQHHorizontalLinkPtr (
  IN QH_STRUCT     *PtrQH,
  IN VOID          *ptrNext
  )
/*++

Routine Description:

  Set QH Horizontal Link Pointer

Arguments:

  PtrQH   - QH_STRUCT
  ptrNext - Data to write 

Returns:

  VOID

--*/
{
  //
  // Since the QH_STRUCT is aligned on 16-byte boundaries,
  // Only the highest 28bit of the address is valid
  // (take 32bit address as an example).
  //
  PtrQH->QH.QHHorizontalPtr = (UINT32) ((UINTN) ptrNext >> 4);
}

VOID *
GetQHHorizontalLinkPtr (
  IN QH_STRUCT     *PtrQH
  )
/*++

Routine Description:

  Get QH Horizontal Link Pointer

Arguments:

  PtrQH   - QH_STRUCT
  

Returns:

  Data to return 

--*/
{
  //
  // Restore the 28bit address to 32bit address
  // (take 32bit address as an example)
  //
  return (VOID *) ((UINTN) (PtrQH->QH.QHHorizontalPtr << 4));
}

VOID
SetQHHorizontalQHorTDSelect (
  IN QH_STRUCT     *PtrQH,
  IN BOOLEAN       bQH
  )
/*++

Routine Description:

  Set QH Horizontal QH or TD 

Arguments:

  PtrQH - QH_STRUCT
  bQH   - TRUE is QH FALSE is TD

Returns:
  VOID

--*/
{
  //
  // if QH is connected, the specified bit is set,
  // if TD is connected, the specified bit is cleared.
  //
  PtrQH->QH.QHHorizontalQSelect = bQH ? 1 : 0;
}


VOID
SetQHHorizontalValidorInvalid (
  IN QH_STRUCT     *PtrQH,
  IN BOOLEAN       bValid
  )
/*++

Routine Description:

  Set QH Horizontal Valid or Invalid

Arguments:

  PtrQH  - QH_STRUCT
  bValid - TRUE is Valid FALSE is Invalid

Returns:
  VOID

--*/
{
  //
  // Valid means the horizontal link pointer is valid,
  // else, it's invalid.
  //
  PtrQH->QH.QHHorizontalTerminate = bValid ? 0 : 1;
}

VOID
SetQHVerticalLinkPtr (
  IN QH_STRUCT     *PtrQH,
  IN VOID          *ptrNext
  )
/*++

Routine Description:

  Set QH Vertical Link Pointer
  
Arguments:

  PtrQH   - QH_STRUCT
  ptrNext - Data to write
Returns:

  VOID

--*/
{
  //
  // Since the QH_STRUCT is aligned on 16-byte boundaries,
  // Only the highest 28bit of the address is valid
  // (take 32bit address as an example).
  //
  PtrQH->QH.QHVerticalPtr = (UINT32) ((UINTN) ptrNext >> 4);
}

VOID *
GetQHVerticalLinkPtr (
  IN QH_STRUCT     *PtrQH
  )
/*++

Routine Description:

  Get QH Vertical Link Pointer
  
Arguments:

  PtrQH   - QH_STRUCT
 
Returns:

   Data to return

--*/
{
  //
  // Restore the 28bit address to 32bit address
  // (take 32bit address as an example)
  //
  return (VOID *) ((UINTN) (PtrQH->QH.QHVerticalPtr << 4));
}

VOID
SetQHVerticalQHorTDSelect (
  IN QH_STRUCT     *PtrQH,
  IN BOOLEAN       bQH
  )
/*++

Routine Description:

  Set QH Vertical QH or TD

Arguments:

  PtrQH - QH_STRUCT
  bQH   - TRUE is QH FALSE is TD

Returns:

  VOID

--*/
{
  //
  // Set the specified bit if the Vertical Link Pointer pointing to a QH,
  // Clear the specified bit if the Vertical Link Pointer pointing to a TD.
  //
  PtrQH->QH.QHVerticalQSelect = bQH ? 1 : 0;
}

BOOLEAN
IsQHHorizontalQHSelect (
  IN QH_STRUCT     *PtrQH
  )
/*++

Routine Description:

  Is QH Horizontal QH Select

Arguments:

  PtrQH - QH_STRUCT
 
Returns:

  TRUE  - QH
  FALSE - TD

--*/
{
  //
  // Retrieve the information about whether the Horizontal Link Pointer
  // pointing to a QH or TD.
  //
  return (BOOLEAN) (PtrQH->QH.QHHorizontalQSelect ? TRUE : FALSE);
}

VOID
SetQHVerticalValidorInvalid (
  IN QH_STRUCT     *PtrQH,
  IN BOOLEAN       IsValid
  )
/*++

Routine Description:

  Set QH Vertical Valid or Invalid

Arguments:

  PtrQH   - QH_STRUCT
  IsValid - TRUE is valid FALSE is invalid

Returns:

  VOID

--*/
{
  //
  // If TRUE, indicates the Vertical Link Pointer field is valid,
  // else, the field is invalid.
  //
  PtrQH->QH.QHVerticalTerminate = IsValid ? 0 : 1;
}


BOOLEAN
GetQHVerticalValidorInvalid (
  IN QH_STRUCT     *PtrQH
  )
/*++

Routine Description:

  Get QH Vertical Valid or Invalid

Arguments:

  PtrQH - QH_STRUCT

Returns:

  TRUE  - Valid
  FALSE - Invalid

--*/
{
  //
  // If TRUE, indicates the Vertical Link Pointer field is valid,
  // else, the field is invalid.
  //
  return (BOOLEAN) (!(PtrQH->QH.QHVerticalTerminate));
}

STATIC
BOOLEAN
GetQHHorizontalValidorInvalid (
  IN QH_STRUCT     *PtrQH
  )
/*++

Routine Description:

  Get QH Horizontal Valid or Invalid

Arguments:

  PtrQH - QH_STRUCT

Returns:

  TRUE  - Valid
  FALSE - Invalid

--*/
{
  //
  // If TRUE, meaning the Horizontal Link Pointer field is valid,
  // else, the field is invalid.
  //
  return (BOOLEAN) (!(PtrQH->QH.QHHorizontalTerminate));
}
//
// functions for TD
//
EFI_STATUS
AllocateTDStruct (
  IN  USB_HC_DEV     *HcDev,
  OUT TD_STRUCT      **ppTDStruct
  )
/*++

Routine Description:

  Allocate TD Struct

Arguments:

  HcDev       - USB_HC_DEV
  ppTDStruct  - place to store TD_STRUCT pointer
Returns:

  EFI_SUCCESS

--*/
{
  *ppTDStruct = NULL;

  //
  // TD must align on 16 bytes alignment,
  // since the memory allocated by UhciAllocatePool ()
  // is aligned on 32 bytes, it is no need to adjust
  // the allocated memory returned.
  //
  return UhciAllocatePool (
          HcDev,
          (UINT8 **) ppTDStruct,
          sizeof (TD_STRUCT)
          );
}

EFI_STATUS
CreateTD (
  IN  USB_HC_DEV     *HcDev,
  OUT TD_STRUCT      **pptrTD
  )
/*++

Routine Description:

  Create TD

Arguments:

  HcDev   - USB_HC_DEV
  pptrTD  - TD_STRUCT pointer to store

Returns:

  EFI_OUT_OF_RESOURCES - Can't allocate resources
  EFI_SUCCESS          - Success

--*/
{
  EFI_STATUS  Status;
  //
  // create memory for TD_STRUCT, and align the memory.
  //
  Status = AllocateTDStruct (HcDev, pptrTD);
  if (EFI_ERROR (Status)) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Make TD ready.
  //
  SetTDLinkPtrValidorInvalid (*pptrTD, FALSE);


  return EFI_SUCCESS;
}

EFI_STATUS
GenSetupStageTD (
  IN  USB_HC_DEV     *HcDev,
  IN  UINT8          DevAddr,
  IN  UINT8          Endpoint,
  IN  BOOLEAN        bSlow,
  IN  UINT8          *pDevReq,
  IN  UINT8          RequestLen,
  OUT TD_STRUCT      **ppTD
  )
/*++

Routine Description:

  Generate Setup Stage TD

Arguments:

  HcDev       - USB_HC_DEV
  DevAddr     - Device address
  Endpoint    - Endpoint number 
  bSlow       - Full speed or low speed
  pDevReq     - Device request
  RequestLen  - Request length
  ppTD        - TD_STRUCT to return
Returns:

  EFI_OUT_OF_RESOURCES - Can't allocate memory
  EFI_SUCCESS          - Success

--*/
{
  EFI_STATUS  Status;
  TD_STRUCT   *pTDStruct;

  Status = CreateTD (HcDev, &pTDStruct);
  if (EFI_ERROR (Status)) {
    return EFI_OUT_OF_RESOURCES;
  }

  SetTDLinkPtr (pTDStruct, NULL);

  //
  // Depth first fashion
  //
  SetTDLinkPtrDepthorBreadth (pTDStruct, TRUE);

  //
  // initialize as the last TD in the QH context,
  // this field will be updated in the TD linkage process.
  //
  SetTDLinkPtrValidorInvalid (pTDStruct, FALSE);

  //
  // Disable Short Packet Detection by default
  //
  EnableorDisableTDShortPacket (pTDStruct, FALSE);

  //
  // Max error counter is 3, retry 3 times when error encountered.
  //
  SetTDControlErrorCounter (pTDStruct, 3);

  //
  // set device speed attribute
  // (TRUE - Slow Device; FALSE - Full Speed Device)
  //
  SetTDLoworFullSpeedDevice (pTDStruct, bSlow);

  //
  // Non isochronous transfer TD
  //
  SetTDControlIsochronousorNot (pTDStruct, FALSE);

  //
  // Interrupt On Complete bit be set to zero,
  // Disable IOC interrupt.
  //
  SetorClearTDControlIOC (pTDStruct, FALSE);

  //
  // Set TD Active bit
  //
  SetTDStatusActiveorInactive (pTDStruct, TRUE);

  SetTDTokenMaxLength (pTDStruct, RequestLen);

  SetTDTokenDataToggle0 (pTDStruct);

  SetTDTokenEndPoint (pTDStruct, Endpoint);

  SetTDTokenDeviceAddress (pTDStruct, DevAddr);

  SetTDTokenPacketID (pTDStruct, SETUP_PACKET_ID);

  pTDStruct->pTDBuffer      = (UINT8 *) pDevReq;
  pTDStruct->TDBufferLength = RequestLen;
  SetTDDataBuffer (pTDStruct);

  *ppTD = pTDStruct;

  return EFI_SUCCESS;
}

EFI_STATUS
GenDataTD (
  IN  USB_HC_DEV     *HcDev,
  IN  UINT8          DevAddr,
  IN  UINT8          Endpoint,
  IN  UINT8          *pData,
  IN  UINT8          Len,
  IN  UINT8          PktID,
  IN  UINT8          Toggle,
  IN  BOOLEAN        bSlow,
  OUT TD_STRUCT      **ppTD
  )
/*++

Routine Description:

  Generate Data Stage TD

Arguments:

  HcDev       - USB_HC_DEV
  DevAddr     - Device address
  Endpoint    - Endpoint number 
  pData       - Data buffer 
  Len         - Data length
  PktID       - Packet ID
  Toggle      - Data toggle value
  bSlow       - Full speed or low speed
  ppTD        - TD_STRUCT to return
Returns:

  EFI_OUT_OF_RESOURCES - Can't allocate memory
  EFI_SUCCESS          - Success

--*/
{
  TD_STRUCT   *pTDStruct;
  EFI_STATUS  Status;

  Status = CreateTD (HcDev, &pTDStruct);
  if (EFI_ERROR (Status)) {
    return EFI_OUT_OF_RESOURCES;
  }

  SetTDLinkPtr (pTDStruct, NULL);

  //
  // Depth first fashion
  //
  SetTDLinkPtrDepthorBreadth (pTDStruct, TRUE);

  //
  // Link pointer pointing to TD struct
  //
  SetTDLinkPtrQHorTDSelect (pTDStruct, FALSE);

  //
  // initialize as the last TD in the QH context,
  // this field will be updated in the TD linkage process.
  //
  SetTDLinkPtrValidorInvalid (pTDStruct, FALSE);

  //
  // Disable short packet detect
  //
  EnableorDisableTDShortPacket (pTDStruct, FALSE);
  //
  // Max error counter is 3
  //
  SetTDControlErrorCounter (pTDStruct, 3);

  //
  // set device speed attribute
  // (TRUE - Slow Device; FALSE - Full Speed Device)
  //
  SetTDLoworFullSpeedDevice (pTDStruct, bSlow);

  //
  // Non isochronous transfer TD
  //
  SetTDControlIsochronousorNot (pTDStruct, FALSE);

  //
  // Disable Interrupt On Complete
  // Disable IOC interrupt.
  //
  SetorClearTDControlIOC (pTDStruct, FALSE);

  //
  // Set Active bit
  //
  SetTDStatusActiveorInactive (pTDStruct, TRUE);

  SetTDTokenMaxLength (pTDStruct, Len);

  if (Toggle) {
    SetTDTokenDataToggle1 (pTDStruct);
  } else {
    SetTDTokenDataToggle0 (pTDStruct);
  }

  SetTDTokenEndPoint (pTDStruct, Endpoint);

  SetTDTokenDeviceAddress (pTDStruct, DevAddr);

  SetTDTokenPacketID (pTDStruct, PktID);

  pTDStruct->pTDBuffer      = (UINT8 *) pData;
  pTDStruct->TDBufferLength = Len;
  SetTDDataBuffer (pTDStruct);
  *ppTD = pTDStruct;

  return EFI_SUCCESS;
}


EFI_STATUS
CreateStatusTD (
  IN  USB_HC_DEV     *HcDev,
  IN  UINT8          DevAddr,
  IN  UINT8          Endpoint,
  IN  UINT8          PktID,
  IN  BOOLEAN        bSlow,
  OUT TD_STRUCT      **ppTD
  )
/*++

Routine Description:

  Generate Status Stage TD

Arguments:

  HcDev       - USB_HC_DEV
  DevAddr     - Device address
  Endpoint    - Endpoint number 
  PktID       - Packet ID
  bSlow       - Full speed or low speed
  ppTD        - TD_STRUCT to return
Returns:

  EFI_OUT_OF_RESOURCES - Can't allocate memory
  EFI_SUCCESS          - Success

--*/
{
  TD_STRUCT   *ptrTDStruct;
  EFI_STATUS  Status;

  Status = CreateTD (HcDev, &ptrTDStruct);
  if (EFI_ERROR (Status)) {
    return EFI_OUT_OF_RESOURCES;
  }

  SetTDLinkPtr (ptrTDStruct, NULL);

  //
  // Depth first fashion
  //
  SetTDLinkPtrDepthorBreadth (ptrTDStruct, TRUE);

  //
  // initialize as the last TD in the QH context,
  // this field will be updated in the TD linkage process.
  //
  SetTDLinkPtrValidorInvalid (ptrTDStruct, FALSE);

  //
  // Disable short packet detect
  //
  EnableorDisableTDShortPacket (ptrTDStruct, FALSE);

  //
  // Max error counter is 3
  //
  SetTDControlErrorCounter (ptrTDStruct, 3);

  //
  // set device speed attribute
  // (TRUE - Slow Device; FALSE - Full Speed Device)
  //
  SetTDLoworFullSpeedDevice (ptrTDStruct, bSlow);

  //
  // Non isochronous transfer TD
  //
  SetTDControlIsochronousorNot (ptrTDStruct, FALSE);

  //
  // Disable Interrupt On Complete
  // Disable IOC interrupt.
  //
  SetorClearTDControlIOC (ptrTDStruct, FALSE);

  //
  // Set TD Active bit
  //
  SetTDStatusActiveorInactive (ptrTDStruct, TRUE);

  SetTDTokenMaxLength (ptrTDStruct, 0);

  SetTDTokenDataToggle1 (ptrTDStruct);

  SetTDTokenEndPoint (ptrTDStruct, Endpoint);

  SetTDTokenDeviceAddress (ptrTDStruct, DevAddr);

  SetTDTokenPacketID (ptrTDStruct, PktID);

  ptrTDStruct->pTDBuffer      = NULL;
  ptrTDStruct->TDBufferLength = 0;
  SetTDDataBuffer (ptrTDStruct);

  *ppTD = ptrTDStruct;

  return EFI_SUCCESS;
}


VOID
SetTDLinkPtrValidorInvalid (
  IN TD_STRUCT     *ptrTDStruct,
  IN BOOLEAN       bValid
  )
/*++

Routine Description:

  Set TD Link Pointer Valid or Invalid

Arguments:

  ptrTDStruct - TD_STRUCT
  bValid      - TRUE is valid FALSE is invalid

Returns:

  VOID

--*/
{
  //
  // Valid means the link pointer is valid,
  // else, it's invalid.
  //
  ptrTDStruct->TDData.TDLinkPtrTerminate = (bValid ? 0 : 1);
}

VOID
SetTDLinkPtrQHorTDSelect (
  IN TD_STRUCT     *ptrTDStruct,
  IN BOOLEAN       bQH
  )
/*++

Routine Description:

  Set TD Link Pointer QH or TD Select

Arguments:

  ptrTDStruct - TD_STRUCT
  bQH         -  TRUE is QH FALSE is TD
  
Returns:

  VOID

--*/
{
  //
  // Indicate whether the Link Pointer pointing to a QH or TD
  //
  ptrTDStruct->TDData.TDLinkPtrQSelect = (bQH ? 1 : 0);
}

VOID
SetTDLinkPtrDepthorBreadth (
  IN TD_STRUCT     *ptrTDStruct,
  IN BOOLEAN       bDepth
  )
/*++

Routine Description:

  Set TD Link Pointer depth or bread priority

Arguments:

  ptrTDStruct - TD_STRUCT
  bDepth      -  TRUE is Depth  FALSE is Breadth
  
Returns:

  VOID

--*/
{
  //
  // If TRUE, indicating the host controller should process in depth first
  // fashion,
  // else, the host controller should process in breadth first fashion
  //
  ptrTDStruct->TDData.TDLinkPtrDepthSelect = (bDepth ? 1 : 0);
}

VOID
SetTDLinkPtr (
  IN TD_STRUCT     *ptrTDStruct,
  IN VOID          *ptrNext
  )
/*++

Routine Description:

  Set TD Link Pointer

Arguments:

  ptrTDStruct - TD_STRUCT
  ptrNext     - Pointer to set
  
Returns:

  VOID

--*/
{
  //
  // Set TD Link Pointer. Since QH,TD align on 16-byte boundaries,
  // only the highest 28 bits are valid. (if take 32bit address as an example)
  //
  ptrTDStruct->TDData.TDLinkPtr = (UINT32) ((UINTN) ptrNext >> 4);
}

VOID *
GetTDLinkPtr (
  IN TD_STRUCT     *ptrTDStruct
  )
/*++

Routine Description:

  Get TD Link Pointer

Arguments:

  ptrTDStruct - TD_STRUCT
   
Returns:

  Pointer to get

--*/
{
  //
  // Get TD Link Pointer. Restore it back to 32bit
  // (if take 32bit address as an example)
  //
  return (VOID *) ((UINTN) (ptrTDStruct->TDData.TDLinkPtr << 4));
}

STATIC
BOOLEAN
IsTDLinkPtrQHOrTD (
  IN TD_STRUCT     *ptrTDStruct
  )
/*++

Routine Description:

  Is TD Link Pointer is QH Or TD

Arguments:

  ptrTDStruct - TODO: add argument description

Returns:

  TRUE  - QH
  FALSE - TD 

--*/
{
  //
  // Get the information about whether the Link Pointer field pointing to
  // a QH or a TD.
  //
  return (BOOLEAN) (ptrTDStruct->TDData.TDLinkPtrQSelect);
}

VOID
EnableorDisableTDShortPacket (
  IN TD_STRUCT     *ptrTDStruct,
  IN BOOLEAN       bEnable
  )
/*++

Routine Description:

  Enable or Disable TD ShortPacket

Arguments:

  ptrTDStruct - TD_STRUCT
  bEnable     - TRUE is Enanble FALSE is Disable

Returns:

  VOID

--*/
{
  //
  // TRUE means enable short packet detection mechanism.
  //
  ptrTDStruct->TDData.TDStatusSPD = (bEnable ? 1 : 0);
}

VOID
SetTDControlErrorCounter (
  IN TD_STRUCT     *ptrTDStruct,
  IN UINT8         nMaxErrors
  )
/*++

Routine Description:

  Set TD Control ErrorCounter

Arguments:

  ptrTDStruct - TD_STRUCT
  nMaxErrors  - Error counter number
  
Returns:

  VOID

--*/
{
  //
  // valid value of nMaxErrors is 0,1,2,3
  //
  if (nMaxErrors > 3) {
    nMaxErrors = 3;
  }

  ptrTDStruct->TDData.TDStatusErr = nMaxErrors;
}


VOID
SetTDLoworFullSpeedDevice (
  IN TD_STRUCT     *ptrTDStruct,
  IN BOOLEAN       bLowSpeedDevice
  )
{
  //
  // TRUE means the TD is targeting at a Low-speed device
  //
  ptrTDStruct->TDData.TDStatusLS = (bLowSpeedDevice ? 1 : 0);
}

VOID
SetTDControlIsochronousorNot (
  IN TD_STRUCT     *ptrTDStruct,
  IN BOOLEAN       IsIsochronous
  )
{
  //
  // TRUE means the TD belongs to Isochronous transfer type.
  //
  ptrTDStruct->TDData.TDStatusIOS = (IsIsochronous ? 1 : 0);
}

VOID
SetorClearTDControlIOC (
  IN TD_STRUCT     *ptrTDStruct,
  IN BOOLEAN       IsSet
  )
{
  //
  // If this bit is set, it indicates that the host controller should issue
  // an interrupt on completion of the frame in which this TD is executed.
  //
  ptrTDStruct->TDData.TDStatusIOC = IsSet ? 1 : 0;
}

VOID
SetTDStatusActiveorInactive (
  IN TD_STRUCT     *ptrTDStruct,
  IN BOOLEAN       IsActive
  )
{
  //
  // If this bit is set, it indicates that the TD is active and can be
  // executed.
  //
  if (IsActive) {
    ptrTDStruct->TDData.TDStatus |= 0x80;
  } else {
    ptrTDStruct->TDData.TDStatus &= 0x7F;
  }
}

UINT16
SetTDTokenMaxLength (
  IN TD_STRUCT     *ptrTDStruct,
  IN UINT16        MaximumLength
  )
{
  //
  // Specifies the maximum number of data bytes allowed for the transfer.
  // the legal value extent is 0 ~ 0x500.
  //
  if (MaximumLength > 0x500) {
    MaximumLength = 0x500;
  }
  ptrTDStruct->TDData.TDTokenMaxLen = MaximumLength - 1;

  return MaximumLength;
}

VOID
SetTDTokenDataToggle1 (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  //
  // Set the data toggle bit to DATA1
  //
  ptrTDStruct->TDData.TDTokenDataToggle = 1;
}

VOID
SetTDTokenDataToggle0 (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  //
  // Set the data toggle bit to DATA0
  //
  ptrTDStruct->TDData.TDTokenDataToggle = 0;
}

UINT8
GetTDTokenDataToggle (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  //
  // Get the data toggle value.
  //
  return (UINT8) (ptrTDStruct->TDData.TDTokenDataToggle);
}

VOID
SetTDTokenEndPoint (
  IN TD_STRUCT     *ptrTDStruct,
  IN UINTN         EndPoint
  )
{
  //
  // Set EndPoint Number the TD is targeting at.
  //
  ptrTDStruct->TDData.TDTokenEndPt = (UINT8) EndPoint;
}

VOID
SetTDTokenDeviceAddress (
  IN TD_STRUCT     *ptrTDStruct,
  IN UINTN         DeviceAddress
  )
{
  //
  // Set Device Address the TD is targeting at.
  //
  ptrTDStruct->TDData.TDTokenDevAddr = (UINT8) DeviceAddress;
}

VOID
SetTDTokenPacketID (
  IN TD_STRUCT     *ptrTDStruct,
  IN UINT8         PID
  )
{
  //
  // Set the Packet Identification to be used for this transaction.
  //
  ptrTDStruct->TDData.TDTokenPID = PID;
}

VOID
SetTDDataBuffer (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  //
  // Set the beginning address of the data buffer that will be used
  // during the transaction.
  //
  ptrTDStruct->TDData.TDBufferPtr = (UINT32) ((UINTN) (ptrTDStruct->pTDBuffer));
}

BOOLEAN
IsTDStatusActive (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  UINT8 TDStatus;

  //
  // Detect whether the TD is active.
  //
  TDStatus = (UINT8) (ptrTDStruct->TDData.TDStatus);
  return (BOOLEAN) (TDStatus & 0x80);
}

BOOLEAN
IsTDStatusStalled (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  UINT8 TDStatus;

  //
  // Detect whether the device/endpoint addressed by this TD is stalled.
  //
  TDStatus = (UINT8) (ptrTDStruct->TDData.TDStatus);
  return (BOOLEAN) (TDStatus & 0x40);
}

BOOLEAN
IsTDStatusBufferError (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  UINT8 TDStatus;
  //
  // Detect whether Data Buffer Error is happened.
  //
  TDStatus = (UINT8) (ptrTDStruct->TDData.TDStatus);
  return (BOOLEAN) (TDStatus & 0x20);
}

BOOLEAN
IsTDStatusBabbleError (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  UINT8 TDStatus;

  //
  // Detect whether Babble Error is happened.
  //
  TDStatus = (UINT8) (ptrTDStruct->TDData.TDStatus);
  return (BOOLEAN) (TDStatus & 0x10);
}

BOOLEAN
IsTDStatusNAKReceived (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  UINT8 TDStatus;

  //
  // Detect whether NAK is received.
  //
  TDStatus = (UINT8) (ptrTDStruct->TDData.TDStatus);
  return (BOOLEAN) (TDStatus & 0x08);
}

BOOLEAN
IsTDStatusCRCTimeOutError (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  UINT8 TDStatus;

  //
  // Detect whether CRC/Time Out Error is encountered.
  //
  TDStatus = (UINT8) (ptrTDStruct->TDData.TDStatus);
  return (BOOLEAN) (TDStatus & 0x04);
}

BOOLEAN
IsTDStatusBitStuffError (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  UINT8 TDStatus;

  //
  // Detect whether Bitstuff Error is received.
  //
  TDStatus = (UINT8) (ptrTDStruct->TDData.TDStatus);
  return (BOOLEAN) (TDStatus & 0x02);
}

UINT16
GetTDStatusActualLength (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  //
  // Retrieve the actual number of bytes that were tansferred.
  // the value is encoded as n-1. so return the decoded value.
  //
  return (UINT16) ((ptrTDStruct->TDData.TDStatusActualLength) + 1);
}

UINT16
GetTDTokenMaxLength (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  //
  // Retrieve the maximum number of data bytes allowed for the trnasfer.
  //
  return (UINT16) ((ptrTDStruct->TDData.TDTokenMaxLen) + 1);
}

UINT8
GetTDTokenEndPoint (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  //
  // Retrieve the endpoint number the transaction is targeting at.
  //
  return (UINT8) (ptrTDStruct->TDData.TDTokenEndPt);
}

UINT8
GetTDTokenDeviceAddress (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  //
  // Retrieve the device address the transaction is targeting at.
  //
  return (UINT8) (ptrTDStruct->TDData.TDTokenDevAddr);
}

UINT8
GetTDTokenPacketID (
  IN  TD_STRUCT *ptrTDStruct
  )
{
  //
  // Retrieve the Packet Identification information.
  //
  return (UINT8) (ptrTDStruct->TDData.TDTokenPID);
}

UINT8 *
GetTDDataBuffer (
  IN TD_STRUCT    *ptrTDStruct
  )
{
  //
  // Retrieve the beginning address of the data buffer
  // that involved in this transaction.
  //
  return ptrTDStruct->pTDBuffer;
}

BOOLEAN
GetTDLinkPtrValidorInvalid (
  IN TD_STRUCT     *ptrTDStruct
  )
{
  //
  // Retrieve the information of whether the Link Pointer field
  // is valid or not.
  //
  if (ptrTDStruct->TDData.TDLinkPtrTerminate) {
    return FALSE;
  } else {
    return TRUE;
  }

}

UINTN
CountTDsNumber (
  IN TD_STRUCT     *PtrFirstTD
  )
{
  UINTN     Number;
  TD_STRUCT *ptr;
  //
  // Count the queued TDs number.
  //
  Number  = 0;
  ptr     = PtrFirstTD;
  while (ptr) {
    ptr = (TD_STRUCT *) ptr->ptrNextTD;
    Number++;
  }

  return Number;
}



VOID
LinkTDToQH (
  IN QH_STRUCT     *PtrQH,
  IN TD_STRUCT     *PtrTD
  )
/*++

Routine Description:

  Link TD To QH

Arguments:

  PtrQH - QH_STRUCT
  PtrTD - TD_STRUCT
Returns:

  VOID

--*/
{
  if (PtrQH == NULL || PtrTD == NULL) {
    return ;
  }
  //
  //  Validate QH Vertical Ptr field
  //
  SetQHVerticalValidorInvalid (PtrQH, TRUE);

  //
  //  Vertical Ptr pointing to TD structure
  //
  SetQHVerticalQHorTDSelect (PtrQH, FALSE);

  SetQHVerticalLinkPtr (PtrQH, (VOID *) PtrTD);

  PtrQH->ptrDown = (VOID *) PtrTD;
}

VOID
LinkTDToTD (
  IN TD_STRUCT     *ptrPreTD,
  IN TD_STRUCT     *PtrTD
  )
/*++

Routine Description:

  Link TD To TD

Arguments:

  ptrPreTD - Previous TD_STRUCT to be linked
  PtrTD    - TD_STRUCT to link
Returns:

  VOID

--*/
{
  if (ptrPreTD == NULL || PtrTD == NULL) {
    return ;
  }
  //
  // Depth first fashion
  //
  SetTDLinkPtrDepthorBreadth (ptrPreTD, TRUE);

  //
  // Link pointer pointing to TD struct
  //
  SetTDLinkPtrQHorTDSelect (ptrPreTD, FALSE);

  //
  // Validate the link pointer valid bit
  //
  SetTDLinkPtrValidorInvalid (ptrPreTD, TRUE);

  SetTDLinkPtr (ptrPreTD, PtrTD);

  ptrPreTD->ptrNextTD = (VOID *) PtrTD;
}
//
// Transfer Schedule related Helper Functions
//
VOID
SetorClearCurFrameListTerminate (
  IN FRAMELIST_ENTRY     *pCurEntry,
  IN BOOLEAN             IsSet
  )
{
  //
  // If TRUE, empty the frame. If FALSE, indicate the Pointer field is valid.
  //
  pCurEntry->FrameListPtrTerminate = (IsSet ? 1 : 0);
}

VOID
SetCurFrameListQHorTD (
  IN FRAMELIST_ENTRY     *pCurEntry,
  IN BOOLEAN             IsQH
  )
{
  //
  // This bit indicates to the hardware whether the item referenced by the
  // link pointer is a TD or a QH.
  //
  pCurEntry->FrameListPtrQSelect = (IsQH ? 1 : 0);
}

STATIC
BOOLEAN
IsCurFrameListQHorTD (
  IN FRAMELIST_ENTRY     *pCurEntry
  )
{
  //
  // TRUE is QH
  // FALSE is TD
  //
  return (BOOLEAN) (pCurEntry->FrameListPtrQSelect);
}

BOOLEAN
GetCurFrameListTerminate (
  IN FRAMELIST_ENTRY     *pCurEntry
  )
{
  //
  // TRUE means the frame is empty,
  // FALSE means the link pointer field is valid.
  //
  return (BOOLEAN) (pCurEntry->FrameListPtrTerminate);
}

VOID
SetCurFrameListPointer (
  IN FRAMELIST_ENTRY     *pCurEntry,
  IN UINT8               *ptr
  )
{
  //
  // Set the pointer field of the frame.
  //
  pCurEntry->FrameListPtr = (UINT32) ((UINTN) ptr >> 4);
}

VOID *
GetCurFrameListPointer (
  IN FRAMELIST_ENTRY     *pCurEntry
  )
{
  //
  // Get the link pointer of the frame.
  //
  return (VOID *) ((UINTN) (pCurEntry->FrameListPtr << 4));

}

VOID
LinkQHToFrameList (
  IN FRAMELIST_ENTRY     *pEntry,
  IN UINT16              FrameListIndex,
  IN QH_STRUCT           *PtrQH
  )
/*++

Routine Description:

  Link QH To Frame List

Arguments:

  pEntry           - FRAMELIST_ENTRY
  FrameListIndex   - Frame List Index
  PtrQH            - QH to link 
Returns:

  VOID

--*/
{
  FRAMELIST_ENTRY *pCurFrame;
  QH_STRUCT       *TempQH;
  QH_STRUCT       *NextTempQH;
  TD_STRUCT       *TempTD;
  BOOLEAN         LINK;

  //
  // Get frame list entry that the link process will begin from.
  //
  pCurFrame = pEntry + FrameListIndex;

  //
  // if current frame is empty
  // then link the specified QH directly to the Frame List.
  //
  if (GetCurFrameListTerminate (pCurFrame)) {
    
    //
    // Link new QH to the frame list entry.
    //
    SetCurFrameListQHorTD (pCurFrame, TRUE);

    SetCurFrameListPointer (pCurFrame, (UINT8 *) PtrQH);

    //
    // clear T bit in the Frame List, indicating that the frame list entry
    // is no longer empty.
    //
    SetorClearCurFrameListTerminate (pCurFrame, FALSE);

    return ;

  } else {
    //
    // current frame list has link pointer
    //
    if (!IsCurFrameListQHorTD (pCurFrame)) {
      //
      //  a TD is linked to the framelist entry
      //
      TempTD = (TD_STRUCT *) GetCurFrameListPointer (pCurFrame);

      while (GetTDLinkPtrValidorInvalid (TempTD)) {

        if (IsTDLinkPtrQHOrTD (TempTD)) {
          //
          // QH linked next to the TD
          //
          break;
        }

        TempTD = (TD_STRUCT *) GetTDLinkPtr (TempTD);
      }
      
      //
      // either no ptr linked next to the TD or QH is linked next to the TD
      //
      if (!GetTDLinkPtrValidorInvalid (TempTD)) {
        
        //
        // no ptr linked next to the TD
        //
        TempTD->ptrNextQH = PtrQH;
        SetTDLinkPtrQHorTDSelect (TempTD, TRUE);
        SetTDLinkPtr (TempTD, PtrQH);
        SetTDLinkPtrValidorInvalid (TempTD, TRUE);
        return ;

      } else {
        //
        //  QH is linked next to the TD
        //
        TempQH = (QH_STRUCT *) GetTDLinkPtr (TempTD);
      }
    } else {
      //
      // a QH is linked to the framelist entry
      //
      TempQH = (QH_STRUCT *) GetCurFrameListPointer (pCurFrame);
    }
    
    //
    // Set up Flag
    //
    LINK = TRUE;

    //
    // Avoid the same qh repeated linking in one frame entry
    //
    if (TempQH == PtrQH) {
      LINK = FALSE;
      return ;
    }
    //
    // if current QH has next QH connected
    //
    while (GetQHHorizontalValidorInvalid (TempQH)) {
      //
      // Get next QH pointer
      //
      NextTempQH = (QH_STRUCT *) GetQHHorizontalLinkPtr (TempQH);

      //
      // Bulk transfer qh may be self-linked,
      // so, the code below is to aVOID dead-loop when meeting self-linked qh
      //
      if (NextTempQH == TempQH) {
        LINK = FALSE;
        break;
      }

      TempQH = NextTempQH;

      //
      // Avoid the same qh repeated linking in one frame entry
      //
      if (TempQH == PtrQH) {
        LINK = FALSE;
      }
    }

    if (LINK) {
      TempQH->ptrNext = PtrQH;
      SetQHHorizontalQHorTDSelect (TempQH, TRUE);
      SetQHHorizontalLinkPtr (TempQH, PtrQH);
      SetQHHorizontalValidorInvalid (TempQH, TRUE);
    }

    return ;
  }
}

EFI_STATUS
ExecuteControlTransfer (
  IN  USB_HC_DEV     *HcDev,
  IN  TD_STRUCT      *PtrTD,
  IN  UINT32         wIndex,
  OUT UINTN          *ActualLen,
  IN  UINTN          TimeOut,
  OUT UINT32         *TransferResult
  )
/*++

Routine Description:

  Execute Control Transfer

Arguments:

  HcDev            - USB_HC_DEV
  PtrTD            - TD_STRUCT
  wIndex           - No use
  ActualLen        - Actual transfered Len 
  TimeOut          - TimeOut value in milliseconds
  TransferResult   - Transfer result
Returns:

  EFI_SUCCESS      - Sucess
  EFI_DEVICE_ERROR - Error
  

--*/
{
  UINTN   ErrTDPos;
  UINTN   Delay;
  UINTN   RequiredLen;
  BOOLEAN TransferFinished;

  ErrTDPos        = 0;
  *TransferResult = EFI_USB_NOERROR;
  RequiredLen     = *ActualLen;
  *ActualLen      = 0;

  Delay           = (TimeOut * STALL_1_MILLI_SECOND / 50) + 1;

  do {
    TransferFinished = CheckTDsResults (
                         PtrTD,
                         RequiredLen,
                         TransferResult,
                         &ErrTDPos,
                         ActualLen
                         );

    if (TransferFinished) {
      break;
    }
       
    //
    // TD is inactive, which means the control transfer is end.
    //
    if ((*TransferResult & EFI_USB_ERR_NOTEXECUTE) != EFI_USB_ERR_NOTEXECUTE) {
      break;
    } 

    gBS->Stall (50);

  } while (Delay--);

  if (*TransferResult != EFI_USB_NOERROR) {
    return EFI_DEVICE_ERROR;
  }

  return EFI_SUCCESS;
}

EFI_STATUS
ExecBulkorSyncInterruptTransfer (
  IN  USB_HC_DEV     *HcDev,
  IN  TD_STRUCT      *PtrTD,
  IN  UINT32         wIndex,
  OUT UINTN          *ActualLen,
  OUT UINT8          *DataToggle,
  IN  UINTN          TimeOut,
  OUT UINT32         *TransferResult
  )
/*++

Routine Description:

  Execute Bulk or SyncInterrupt Transfer

Arguments:

  HcDev            - USB_HC_DEV
  PtrTD            - TD_STRUCT
  wIndex           - No use
  ActualLen        - Actual transfered Len 
  DataToggle       - Data Toggle
  TimeOut          - TimeOut value in milliseconds
  TransferResult   - Transfer result
Returns:

  EFI_SUCCESS      - Sucess
  EFI_DEVICE_ERROR - Error
--*/
{
  UINTN   ErrTDPos;
  UINTN   ScrollNum;
  UINTN   Delay;
  UINTN   RequiredLen;
  BOOLEAN TransferFinished;

  ErrTDPos        = 0;
  *TransferResult = EFI_USB_NOERROR;
  RequiredLen     = *ActualLen;
  *ActualLen      = 0;

  Delay           = (TimeOut * STALL_1_MILLI_SECOND / 50) + 1;

  do {

    TransferFinished = CheckTDsResults (
                         PtrTD,
                         RequiredLen,
                         TransferResult,
                         &ErrTDPos,
                         ActualLen
                         );
                        
    if (TransferFinished) {
      break;
    }
       
    //
    // TD is inactive, which means bulk or interrupt transfer's end.
    //    
    if ((*TransferResult & EFI_USB_ERR_NOTEXECUTE) != EFI_USB_ERR_NOTEXECUTE) {
      break;
    }

    gBS->Stall (50);

  } while (Delay--);

  //
  // has error
  //
  if (*TransferResult != EFI_USB_NOERROR) {
  
    //
    // scroll the Data Toggle back to the last success TD
    //
    ScrollNum = CountTDsNumber (PtrTD) - ErrTDPos;
    if (ScrollNum & 0x1) {
      *DataToggle ^= 1;
    }

    return EFI_DEVICE_ERROR;
  }

  return EFI_SUCCESS;
}

VOID
DelLinkSingleQH (
  IN  USB_HC_DEV     *HcDev,
  IN  QH_STRUCT      *PtrQH,
  IN  UINT16         FrameListIndex,
  IN  BOOLEAN        SearchOther,
  IN  BOOLEAN        Delete
  )
/*++

Routine Description:

  Unlink from frame list and delete single QH
Arguments:

  HcDev            - USB_HC_DEV
  PtrQH            - QH_STRUCT
  FrameListIndex   - Frame List Index
  SearchOther      - Search Other QH
  Delete           - TRUE is to delete the QH
Returns:
  VOID
--*/
{
  FRAMELIST_ENTRY *pCurFrame;
  UINTN           Index;
  UINTN           BeginFrame;
  UINTN           EndFrame;
  QH_STRUCT       *CurrentQH;
  QH_STRUCT       *NextQH;
  TD_STRUCT       *CurrentTD;
  VOID            *PtrPreQH;
  BOOLEAN         Found;

  NextQH    = NULL;
  PtrPreQH  = NULL;
  Found     = FALSE;

  if (PtrQH == NULL) {
    return ;
  }

  if (SearchOther) {
    BeginFrame  = 0;
    EndFrame    = 1024;
  } else {
    BeginFrame  = FrameListIndex;
    EndFrame    = FrameListIndex + 1;
  }

  for (Index = BeginFrame; Index < EndFrame; Index++) {

    pCurFrame = HcDev->FrameListEntry + (Index & 0x3FF);

    if (GetCurFrameListTerminate (pCurFrame)) {
      //
      // current frame list is empty,search next frame list entry
      //
      continue;
    }

    if (!IsCurFrameListQHorTD (pCurFrame)) {
      //
      // TD linked to current framelist
      //
      CurrentTD = (TD_STRUCT *) GetCurFrameListPointer (pCurFrame);

      while (GetTDLinkPtrValidorInvalid (CurrentTD)) {

        if (IsTDLinkPtrQHOrTD (CurrentTD)) {
          //
          // QH linked next to the TD,break while ()
          //
          break;
        }

        CurrentTD = (TD_STRUCT *) GetTDLinkPtr (CurrentTD);
      }

      if (!GetTDLinkPtrValidorInvalid (CurrentTD)) {
        //
        // no QH linked next to the last TD,
        // search next frame list
        //
        continue;
      }
      
      //
      // a QH linked next to the last TD
      //
      CurrentQH = (QH_STRUCT *) GetTDLinkPtr (CurrentTD);

      PtrPreQH  = CurrentTD;

    } else {
      //
      // a QH linked to current framelist
      //
      CurrentQH = (QH_STRUCT *) GetCurFrameListPointer (pCurFrame);

      PtrPreQH  = NULL;
    }

    if (CurrentQH == PtrQH) {

      if (GetQHHorizontalValidorInvalid (PtrQH)) {
        //
        // there is QH connected after the QH found
        //
        //
        // retrieve nex qh pointer of the qh found.
        //
        NextQH = GetQHHorizontalLinkPtr (PtrQH);
      } else {
        NextQH = NULL;
      }

      if (PtrPreQH) {
        //
        // QH linked to a TD struct
        //
        CurrentTD = (TD_STRUCT *) PtrPreQH;

        SetTDLinkPtrValidorInvalid (CurrentTD, (BOOLEAN) ((NextQH == NULL) ? FALSE : TRUE));
        SetTDLinkPtr (CurrentTD, NextQH);
        CurrentTD->ptrNextQH = NextQH;

      } else {
        //
        // QH linked directly to current framelist entry
        //
        SetorClearCurFrameListTerminate (pCurFrame, (BOOLEAN) ((NextQH == NULL) ? TRUE : FALSE));
        SetCurFrameListPointer (pCurFrame, (UINT8 *) NextQH);
      }

      Found = TRUE;
      //
      // search next framelist entry
      //
      continue;
    }

    while (GetQHHorizontalValidorInvalid (CurrentQH)) {

      PtrPreQH = CurrentQH;
      //
      // Get next horizontal linked QH
      //
      CurrentQH = (QH_STRUCT *) GetQHHorizontalLinkPtr (CurrentQH);
      //
      // the qh is found
      //
      if (CurrentQH == PtrQH) {
        break;
      }
    }
    
    //
    // search next frame list entry
    //
    if (CurrentQH != PtrQH) {
      //
      // Not find the QH
      //
      continue;
    }
    //
    // find the specified qh, then delink it from
    // the horizontal QH list in the frame entry.
    //
    
    if (GetQHHorizontalValidorInvalid (PtrQH)) {
      //
      // there is QH connected after the QH found
      //
      //
      // retrieve nex qh pointer of the qh found.
      //
      NextQH = GetQHHorizontalLinkPtr (PtrQH);

    } else {
      //
      // NO QH connected after the QH found
      //
      NextQH = NULL;
      //
      // NULL the previous QH's link ptr and set Terminate field.
      //
      SetQHHorizontalValidorInvalid ((QH_STRUCT *) PtrPreQH, FALSE);
    }

    SetQHHorizontalLinkPtr ((QH_STRUCT *) PtrPreQH, NextQH);
    ((QH_STRUCT *) PtrPreQH)->ptrNext = NextQH;

    Found = TRUE;
  }

  if (Found && Delete) {
    //
    // free memory once used by the specific QH
    //
    UhciFreePool (HcDev, (UINT8 *) PtrQH, sizeof (QH_STRUCT));
  }

  return ;
}


VOID
DeleteQueuedTDs (
  IN USB_HC_DEV     *HcDev,
  IN TD_STRUCT      *PtrFirstTD
  )
/*++
Routine Description:

  Delete Queued TDs
Arguments:

  HcDev       - USB_HC_DEV
  PtrFirstTD  - TD link list head

Returns:
  VOID

--*/
{
  TD_STRUCT *Tptr1;
  TD_STRUCT *Tptr2;

  Tptr1 = PtrFirstTD;
  //
  // Delete all the TDs in a queue.
  //
  while (Tptr1) {

    Tptr2 = Tptr1;

    if (!GetTDLinkPtrValidorInvalid (Tptr2)) {
      Tptr1 = NULL;
    } else {

      Tptr1 = GetTDLinkPtr (Tptr2);

      //
      // TD link to itself
      //
      if (Tptr1 == Tptr2) {
        Tptr1 = NULL;
      }
    }

    UhciFreePool (HcDev, (UINT8 *) Tptr2, sizeof (TD_STRUCT));
  }

  return ;
}

VOID
InsertQHTDToINTList (
  IN USB_HC_DEV                          *HcDev,
  IN QH_STRUCT                           *PtrQH,
  IN TD_STRUCT                           *PtrFirstTD,
  IN UINT8                               DeviceAddress,
  IN UINT8                               EndPointAddress,
  IN UINT8                               DataToggle,
  IN UINTN                               DataLength,
  IN UINTN                               PollingInterval,
  IN VOID                                *Mapping,
  IN UINT8                               *DataBuffer,
  IN EFI_ASYNC_USB_TRANSFER_CALLBACK     CallBackFunction,
  IN VOID                                *Context
  )
/*++
Routine Description:
  Insert QH and TD To Interrupt List
Arguments:

  HcDev           - USB_HC_DEV
  PtrQH           - QH_STRUCT
  PtrFirstTD      - First TD_STRUCT
  DeviceAddress   - Device Address
  EndPointAddress - EndPoint Address
  DataToggle      - Data Toggle
  DataLength      - Data length 
  PollingInterval - Polling Interval when inserted to frame list
  Mapping         - Mapping alue  
  DataBuffer      - Data buffer
  CallBackFunction- CallBackFunction after interrupt transfeer
  Context         - CallBackFunction Context passed as function parameter
Returns:
  EFI_SUCCESS            - Sucess
  EFI_INVALID_PARAMETER  - Paremeter is error 

--*/
{
  INTERRUPT_LIST  *Node;

  Node = AllocatePool (sizeof (INTERRUPT_LIST));
  if (Node == NULL) {
    return ;
  }
  
  //
  // Fill Node field
  //
  Node->Signature     = INTERRUPT_LIST_SIGNATURE;
  Node->DevAddr       = DeviceAddress;
  Node->EndPoint      = EndPointAddress;
  Node->PtrQH         = PtrQH;
  Node->PtrFirstTD    = PtrFirstTD;
  Node->DataToggle    = DataToggle;
  Node->DataLen       = DataLength;
  Node->PollInterval  = PollingInterval;
  Node->Mapping       = Mapping;
  //
  // DataBuffer is allocated host memory, not mapped memory
  //
  Node->DataBuffer        = DataBuffer;
  Node->InterruptCallBack = CallBackFunction;
  Node->InterruptContext  = Context;

  //
  // insert the new interrupt transfer to the head of the list.
  // The interrupt transfer's monitor function scans the whole list from head
  // to tail. The new interrupt transfer MUST be added to the head of the list
  // for the sake of error recovery.
  //
  InsertHeadList (&(HcDev->InterruptListHead), &(Node->Link));

  return ;
}


EFI_STATUS
DeleteAsyncINTQHTDs (
  IN  USB_HC_DEV     *HcDev,
  IN  UINT8          DeviceAddress,
  IN  UINT8          EndPointAddress,
  OUT UINT8          *DataToggle
  )
/*++
Routine Description:

  Delete Async INT QH and TDs
Arguments:

  HcDev           - USB_HC_DEV
  DeviceAddress   - Device Address
  EndPointAddress - EndPoint Address
  DataToggle      - Data Toggle

Returns:
  EFI_SUCCESS            - Sucess
  EFI_INVALID_PARAMETER  - Paremeter is error 

--*/
{
  QH_STRUCT       *MatchQH;
  QH_STRUCT       *ptrNextQH;
  TD_STRUCT       *MatchTD;
  LIST_ENTRY      *Link;
  INTERRUPT_LIST  *MatchList;
  INTERRUPT_LIST  *PtrList;
  BOOLEAN         Found;

  UINT32          Result;
  UINTN           ErrTDPos;
  UINTN           ActualLen;

  MatchQH   = NULL;
  MatchTD   = NULL;
  MatchList = NULL;

  //
  // no interrupt transaction exists
  //
  if (IsListEmpty (&(HcDev->InterruptListHead))) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // find the correct QH-TD that need to delete
  // (by matching Device address and EndPoint number to match QH-TD )
  //
  Found = FALSE;
  Link  = &(HcDev->InterruptListHead);
  do {

    Link    = Link->ForwardLink;
    PtrList = INTERRUPT_LIST_FROM_LINK (Link);

    if ((PtrList->DevAddr == DeviceAddress) && ((PtrList->EndPoint & 0x0f) == (EndPointAddress & 0x0f))) {
      MatchList = PtrList;

      Found     = TRUE;
      break;
    }

  } while (Link->ForwardLink != &(HcDev->InterruptListHead));

  if (!Found) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // get current endpoint's data toggle bit and save.
  //
  ExecuteAsyncINTTDs (HcDev, MatchList, &Result, &ErrTDPos, &ActualLen);
  UpdateAsyncINTQHTDs (MatchList, Result, (UINT32) ErrTDPos);
  *DataToggle = MatchList->DataToggle;

  MatchTD     = MatchList->PtrFirstTD;
  MatchQH     = MatchList->PtrQH;
  //
  // find the first matching QH position in the FrameList
  //
  while (MatchQH) {

    ptrNextQH = MatchQH->ptrNextIntQH;

    //
    // Search all the entries
    //
    DelLinkSingleQH (HcDev, MatchQH, 0, TRUE, TRUE);

    MatchQH = ptrNextQH;
  }
  
  //
  // Call PciIo->Unmap() to unmap the busmaster read/write
  //
  HcDev->PciIo->Unmap (HcDev->PciIo, MatchList->Mapping);

  //
  // free host data buffer allocated,
  // mapped data buffer is freed by Unmap
  //
  if (MatchList->DataBuffer != NULL) {
    gBS->FreePool (MatchList->DataBuffer);
  }
  
  //
  // at last delete the TDs, to aVOID problems
  //
  DeleteQueuedTDs (HcDev, MatchTD);

  //
  // remove Match node from interrupt list
  //
  RemoveEntryList (&(MatchList->Link));
  gBS->FreePool (MatchList);
  return EFI_SUCCESS;
}

BOOLEAN
CheckTDsResults (
  IN  TD_STRUCT     *PtrTD,
  IN  UINTN         RequiredLen,
  OUT UINT32        *Result,
  OUT UINTN         *ErrTDPos,
  OUT UINTN         *ActualTransferSize
  )
/*++

Routine Description:

  Check TDs Results

Arguments:

  PtrTD               - TD_STRUCT to check
  RequiredLen         - Required Len
  Result              - Transfer result
  ErrTDPos            - Error TD Position
  ActualTransferSize  - Actual Transfer Size

Returns:

  TRUE  - Sucess
  FALSE - Fail

--*/
{
  UINTN Len;

  *Result   = EFI_USB_NOERROR;
  *ErrTDPos = 0;

  //
  // Init to zero.
  //
  *ActualTransferSize = 0;

  while (PtrTD) {

    if (IsTDStatusActive (PtrTD)) {
      *Result |= EFI_USB_ERR_NOTEXECUTE;
    }

    if (IsTDStatusStalled (PtrTD)) {
      *Result |= EFI_USB_ERR_STALL;
    }

    if (IsTDStatusBufferError (PtrTD)) {
      *Result |= EFI_USB_ERR_BUFFER;
    }

    if (IsTDStatusBabbleError (PtrTD)) {
      *Result |= EFI_USB_ERR_BABBLE;
    }

    if (IsTDStatusNAKReceived (PtrTD)) {
      *Result |= EFI_USB_ERR_NAK;
    }

    if (IsTDStatusCRCTimeOutError (PtrTD)) {
      *Result |= EFI_USB_ERR_TIMEOUT;
    }

    if (IsTDStatusBitStuffError (PtrTD)) {
      *Result |= EFI_USB_ERR_BITSTUFF;
    }
   
    //
    // if any error encountered, stop processing the left TDs.
    //
    if (*Result) {
      return FALSE;
    }

    Len = GetTDStatusActualLength (PtrTD) & 0x7FF;
    *ActualTransferSize += Len;

    if (*ActualTransferSize <= RequiredLen && Len < PtrTD->TDData.TDTokenMaxLen) {
      //
      // transter finished and actural length less than required length
      //
      goto Done;
    }
    //
    // Accumulate actual transferred data length in each TD.
    //
    PtrTD = (TD_STRUCT *) (PtrTD->ptrNextTD);
    //
    // Record the first Error TD's position in the queue,
    // this value is zero-based.
    //
    (*ErrTDPos)++;
  }

Done:
  return TRUE;
}


VOID
ExecuteAsyncINTTDs (
  IN  USB_HC_DEV         *HcDev,
  IN  INTERRUPT_LIST     *PtrList,
  OUT UINT32             *Result,
  OUT UINTN              *ErrTDPos,
  OUT UINTN              *ActualLen
  )
/*++

Routine Description:

  Execute Async Interrupt TDs

Arguments:

  HcDev     - USB_HC_DEV
  PtrList   - INTERRUPT_LIST
  Result    - Transfer result
  ErrTDPos  - Error TD Position
  ActualTransferSize  - Actual Transfer Size
  
Returns:

  VOID

--*/
{
  //
  // *ErrTDPos is zero-based value, indicating the first error TD's position
  // in the TDs' sequence.
  // *ErrTDPos value is only valid when *Result is not equal NOERROR.
  //
  UINTN RequiredLen;

  RequiredLen = *ActualLen;
  CheckTDsResults (PtrList->PtrFirstTD, RequiredLen, Result, ErrTDPos, ActualLen);

  return ;
}


VOID
UpdateAsyncINTQHTDs (
  IN INTERRUPT_LIST     *PtrList,
  IN UINT32             Result,
  IN UINT32             ErrTDPos
  )
/*++

Routine Description:

  Update Async Interrupt QH and TDs

Arguments:

  PtrList   - INTERRUPT_LIST
  Result    - Transfer reslut
  ErrTDPos  - Error TD Position

Returns:

  VOID

--*/
{
  QH_STRUCT *PtrFirstQH;
  QH_STRUCT *PtrQH;
  TD_STRUCT *PtrFirstTD;
  TD_STRUCT *PtrTD;
  UINT8     DataToggle;
  UINT32    Index;

  PtrFirstQH  = PtrList->PtrQH;
  PtrFirstTD  = PtrList->PtrFirstTD;

  DataToggle  = 0;

  if (Result == EFI_USB_NOERROR) {

    PtrTD = PtrFirstTD;
    while (PtrTD) {
      DataToggle  = GetTDTokenDataToggle (PtrTD);
      PtrTD       = PtrTD->ptrNextTD;
    }
    
    //
    // save current DataToggle value to interrupt list.
    // this value is used for tracing the interrupt endpoint DataToggle.
    // when this interrupt transfer is deleted, the last DataToggle is saved
    //
    PtrList->DataToggle = DataToggle;

    PtrTD               = PtrFirstTD;

    //
    // Since DataToggle bit should toggle after each success transaction,
    // the First TD's DataToggle bit will be updated to XOR of Last TD's
    // DataToggle bit. If the First TD's DataToggle bit is not equal Last
    // TD's DataToggle bit, that means it already be the XOR of Last TD's,
    // so no update is needed.
    //
    if (DataToggle == GetTDTokenDataToggle (PtrFirstTD)) {
      PtrTD = PtrFirstTD;
      while (PtrTD) {

        DataToggle ^= 1;
        if (DataToggle) {
          SetTDTokenDataToggle1 (PtrTD);
        } else {
          SetTDTokenDataToggle0 (PtrTD);
        }

        PtrTD = PtrTD->ptrNextTD;
      }
    }
    //
    // restore Link Pointer of QH to First TD
    // (because QH's Link Pointer will change during TD execution)
    //
    PtrQH = PtrFirstQH;
    while (PtrQH) {

      LinkTDToQH (PtrQH, PtrFirstTD);
      PtrQH = PtrQH->ptrNextIntQH;
    }
    
    //
    // set all the TDs active
    //
    PtrTD = PtrFirstTD;
    while (PtrTD) {
      SetTDStatusActiveorInactive (PtrTD, TRUE);
      PtrTD = PtrTD->ptrNextTD;
    }

  } else if (((Result & EFI_USB_ERR_NOTEXECUTE) == EFI_USB_ERR_NOTEXECUTE) ||
           ((Result & EFI_USB_ERR_NAK) == EFI_USB_ERR_NAK)
          ) {
    //
    // no update
    //
  } else {
    //
    // Have Errors
    //
    PtrTD = PtrFirstTD;
    //
    // not first TD error
    //
    if (ErrTDPos != 0) {
      //
      // get the last success TD
      //
      for (Index = 1; Index < ErrTDPos; Index++) {
        PtrTD = PtrTD->ptrNextTD;
      }
      //
      // update Data Toggle in the interrupt list node
      //
      PtrList->DataToggle = GetTDTokenDataToggle (PtrTD);

      //
      // get the error TD
      //
      PtrTD = PtrTD->ptrNextTD;

    } else {
      PtrList->DataToggle = GetTDTokenDataToggle (PtrTD);
    }
    //
    // do not restore the QH's vertical link pointer,
    // let the callback function do the rest of error handling.
    //
  }

  return ;
}

VOID
ReleaseInterruptList (
  IN USB_HC_DEV         *HcDev,
  IN LIST_ENTRY         *ListHead
  )
/*++

Routine Description:

  Release Interrupt List
Arguments:

  HcDev     - USB_HC_DEV
  ListHead  - List head

Returns:

  VOID

--*/
{
  LIST_ENTRY      *Link;
  LIST_ENTRY      *SavedLink;
  INTERRUPT_LIST  *pNode;
  TD_STRUCT       *PtrTD;
  TD_STRUCT       *ptrNextTD;
  QH_STRUCT       *PtrQH;
  QH_STRUCT       *SavedQH;

  if (ListHead == NULL) {
    return ;
  }

  Link = ListHead;

  //
  // Free all the resources in the interrupt list
  //
  SavedLink = Link->ForwardLink;
  while (!IsListEmpty (ListHead)) {

    Link      = SavedLink;

    SavedLink = Link->ForwardLink;

    pNode     = INTERRUPT_LIST_FROM_LINK (Link);

    RemoveEntryList (&pNode->Link);

    SavedQH = pNode->PtrQH;
    for (PtrQH = SavedQH; PtrQH != NULL; PtrQH = SavedQH) {
      SavedQH = PtrQH->ptrNextIntQH;
      UhciFreePool (HcDev, (UINT8 *) PtrQH, sizeof (QH_STRUCT));
    }

    PtrTD = pNode->PtrFirstTD;
    while (PtrTD != NULL) {

      ptrNextTD = PtrTD->ptrNextTD;
      UhciFreePool (HcDev, (UINT8 *) PtrTD, sizeof (TD_STRUCT));
      PtrTD = ptrNextTD;
    }

    gBS->FreePool (pNode);
  }
}


EFI_STATUS
InitializeMemoryManagement (
  IN USB_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Initialize Memory Management

Arguments:

  HcDev - USB_HC_DEV

Returns:

  EFI_SUCCESS -  Success
--*/
{
  MEMORY_MANAGE_HEADER  *MemoryHeader;
  EFI_STATUS            Status;
  UINTN                 MemPages;

  MemPages  = NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES;
  Status    = CreateMemoryBlock (HcDev, &MemoryHeader, MemPages);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  HcDev->MemoryHeader = MemoryHeader;

  return EFI_SUCCESS;
}

EFI_STATUS
CreateMemoryBlock (
  IN  USB_HC_DEV               *HcDev,
  OUT MEMORY_MANAGE_HEADER     **MemoryHeader,
  IN  UINTN                    MemoryBlockSizeInPages
  )
/*++

Routine Description:

  Use PciIo->AllocateBuffer to allocate common buffer for the memory block,
  and use PciIo->Map to map the common buffer for Bus Master Read/Write.


Arguments:

  HcDev        - USB_HC_DEV
  MemoryHeader - MEMORY_MANAGE_HEADER to output
  MemoryBlockSizeInPages - MemoryBlockSizeInPages
Returns:

  EFI_SUCCESS -  Success
--*/
{
  EFI_STATUS            Status;
  VOID                  *CommonBuffer;
  EFI_PHYSICAL_ADDRESS  MappedAddress;
  UINTN                 MemoryBlockSizeInBytes;
  VOID                  *Mapping;

  //
  // Allocate memory for MemoryHeader
  //
  *MemoryHeader = AllocateZeroPool (sizeof (MEMORY_MANAGE_HEADER));
  if (*MemoryHeader == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  (*MemoryHeader)->Next = NULL;

  //
  // set Memory block size
  //
  (*MemoryHeader)->MemoryBlockSizeInBytes = EFI_PAGES_TO_SIZE (MemoryBlockSizeInPages);

  //
  // each bit in Bit Array will manage 32 bytes memory in memory block
  //
  (*MemoryHeader)->BitArraySizeInBytes = ((*MemoryHeader)->MemoryBlockSizeInBytes / 32) / 8;

  //
  // Allocate memory for BitArray
  //
  (*MemoryHeader)->BitArrayPtr = AllocateZeroPool ((*MemoryHeader)->BitArraySizeInBytes);
  if ((*MemoryHeader)->BitArrayPtr == NULL) {
    gBS->FreePool (*MemoryHeader);
    return EFI_OUT_OF_RESOURCES;
  }
  
  //
  // Memory Block uses MemoryBlockSizeInPages pages,
  // and it is allocated as common buffer use.
  //
  Status = HcDev->PciIo->AllocateBuffer (
                           HcDev->PciIo,
                           AllocateAnyPages,
                           EfiBootServicesData,
                           MemoryBlockSizeInPages,
                           &CommonBuffer,
                           0
                           );
  if (EFI_ERROR (Status)) {
    gBS->FreePool ((*MemoryHeader)->BitArrayPtr);
    gBS->FreePool (*MemoryHeader);
    return Status;
  }

  MemoryBlockSizeInBytes = EFI_PAGES_TO_SIZE (MemoryBlockSizeInPages);
  Status = HcDev->PciIo->Map (
                           HcDev->PciIo,
                           EfiPciIoOperationBusMasterCommonBuffer,
                           CommonBuffer,
                           &MemoryBlockSizeInBytes,
                           &MappedAddress,
                           &Mapping
                           );
  //
  // if returned Mapped size is less than the size we request,do not support.
  //
  if (EFI_ERROR (Status) || (MemoryBlockSizeInBytes != EFI_PAGES_TO_SIZE (MemoryBlockSizeInPages))) {
    HcDev->PciIo->FreeBuffer (HcDev->PciIo, MemoryBlockSizeInPages, CommonBuffer);
    gBS->FreePool ((*MemoryHeader)->BitArrayPtr);
    gBS->FreePool (*MemoryHeader);
    return EFI_UNSUPPORTED;
  }
  //
  // Set Memory block initial address
  //
  (*MemoryHeader)->MemoryBlockPtr = (UINT8 *) ((UINTN) MappedAddress);
  (*MemoryHeader)->Mapping        = Mapping;

  ZeroMem (
    (*MemoryHeader)->MemoryBlockPtr,
    EFI_PAGES_TO_SIZE (MemoryBlockSizeInPages)
    );

  return EFI_SUCCESS;
}

EFI_STATUS
FreeMemoryHeader (
  IN USB_HC_DEV               *HcDev,
  IN MEMORY_MANAGE_HEADER     *MemoryHeader
  )
/*++

Routine Description:

  Free Memory Header

Arguments:

  HcDev         - USB_HC_DEV
  MemoryHeader  - MemoryHeader to be freed

Returns:

  EFI_INVALID_PARAMETER - Parameter is error
  EFI_SUCCESS           - Success

--*/
{
  if ((MemoryHeader == NULL) || (HcDev == NULL)) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // unmap the common buffer used by the memory block
  //
  HcDev->PciIo->Unmap (HcDev->PciIo, MemoryHeader->Mapping);

  //
  // free common buffer
  //
  HcDev->PciIo->FreeBuffer (
                  HcDev->PciIo,
                  EFI_SIZE_TO_PAGES (MemoryHeader->MemoryBlockSizeInBytes),
                  MemoryHeader->MemoryBlockPtr
                  );
  //
  // free bit array
  //
  gBS->FreePool (MemoryHeader->BitArrayPtr);
  //
  // free memory header
  //
  gBS->FreePool (MemoryHeader);

  return EFI_SUCCESS;
}

EFI_STATUS
UhciAllocatePool (
  IN  USB_HC_DEV     *HcDev,
  OUT UINT8          **Pool,
  IN  UINTN          AllocSize
  )
/*++

Routine Description:

  Uhci Allocate Pool

Arguments:

  HcDev     - USB_HC_DEV
  Pool      - Place to store pointer to the memory buffer
  AllocSize - Alloc Size

Returns:

  EFI_SUCCESS - Success

--*/
{
  MEMORY_MANAGE_HEADER  *MemoryHeader;
  MEMORY_MANAGE_HEADER  *TempHeaderPtr;
  MEMORY_MANAGE_HEADER  *NewMemoryHeader;
  UINTN                 RealAllocSize;
  UINTN                 MemoryBlockSizeInPages;
  EFI_STATUS            Status;

  *Pool         = NULL;

  MemoryHeader  = HcDev->MemoryHeader;
  ASSERT (MemoryHeader != NULL);

  //
  // allocate unit is 32 bytes (align on 32 byte)
  //
  if (AllocSize & 0x1F) {
    RealAllocSize = (AllocSize / 32 + 1) * 32;
  } else {
    RealAllocSize = AllocSize;
  }
  
  //
  // There may be linked MemoryHeaders.
  // To allocate a free pool in Memory blocks,
  // must search in the MemoryHeader link list
  // until enough free pool is found.
  //
  Status = EFI_NOT_FOUND;
  for (TempHeaderPtr = MemoryHeader; TempHeaderPtr != NULL;
       TempHeaderPtr = TempHeaderPtr->Next) {

    Status = AllocMemInMemoryBlock (
               TempHeaderPtr,
               (VOID **) Pool,
               RealAllocSize / 32
               );
    if (!EFI_ERROR (Status)) {
      ZeroMem (*Pool, AllocSize);
      return EFI_SUCCESS;
    }
  }
  
  //
  // There is no enough memory,
  // Create a new Memory Block
  //
  
  //
  // if pool size is larger than NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES,
  // just allocate a large enough memory block.
  //
  if (RealAllocSize > EFI_PAGES_TO_SIZE (NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES)) {
    MemoryBlockSizeInPages = EFI_SIZE_TO_PAGES (RealAllocSize) + 1;
  } else {
    MemoryBlockSizeInPages = NORMAL_MEMORY_BLOCK_UNIT_IN_PAGES;
  }

  Status = CreateMemoryBlock (HcDev, &NewMemoryHeader, MemoryBlockSizeInPages);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  
  //
  // Link the new Memory Block to the Memory Header list
  //
  InsertMemoryHeaderToList (MemoryHeader, NewMemoryHeader);

  Status = AllocMemInMemoryBlock (
             NewMemoryHeader,
             (VOID **) Pool,
             RealAllocSize / 32
             );

  if (!EFI_ERROR (Status)) {
    ZeroMem (*Pool, AllocSize);
  }

  return Status;
}

VOID
UhciFreePool (
  IN USB_HC_DEV     *HcDev,
  IN UINT8          *Pool,
  IN UINTN          AllocSize
  )
/*++

Routine Description:

  Uhci Free Pool

Arguments:

  HcDev     - USB_HC_DEV
  Pool      - Pool to free
  AllocSize - Pool size

Returns:

  VOID

--*/
{
  MEMORY_MANAGE_HEADER  *MemoryHeader;
  MEMORY_MANAGE_HEADER  *TempHeaderPtr;
  UINTN                 StartBytePos;
  UINTN                 Index;
  UINT8                 StartBitPos;
  UINT8                 Index2;
  UINTN                 Count;
  UINTN                 RealAllocSize;

  MemoryHeader = HcDev->MemoryHeader;

  //
  // allocate unit is 32 byte (align on 32 byte)
  //
  if (AllocSize & 0x1F) {
    RealAllocSize = (AllocSize / 32 + 1) * 32;
  } else {
    RealAllocSize = AllocSize;
  }
  //
  // scan the memory header linked list for
  // the asigned memory to free.
  //
  for (TempHeaderPtr = MemoryHeader;TempHeaderPtr != NULL;
       TempHeaderPtr = TempHeaderPtr->Next) {

    if ((Pool >= TempHeaderPtr->MemoryBlockPtr) &&
        ((Pool + RealAllocSize) <= (TempHeaderPtr->MemoryBlockPtr + TempHeaderPtr->MemoryBlockSizeInBytes))
        ) {
      //
      // Pool is in the Memory Block area,
      // find the start byte and bit in the bit array
      //
      StartBytePos  = ((Pool - TempHeaderPtr->MemoryBlockPtr) / 32) / 8;
      StartBitPos   = (UINT8) (((Pool - TempHeaderPtr->MemoryBlockPtr) / 32) & 0x7);

      //
      // reset associated bits in bit arry
      //
      for (Index = StartBytePos, Index2 = StartBitPos, Count = 0; Count < (RealAllocSize / 32); Count++) {

        TempHeaderPtr->BitArrayPtr[Index] = (UINT8) (TempHeaderPtr->BitArrayPtr[Index] ^ bit (Index2));
        Index2++;
        if (Index2 == 8) {
          Index += 1;
          Index2 = 0;
        }
      }
      //
      // break the loop
      //
      break;
    }
  }
  
  //
  // Release emptied memory blocks (only if the memory block is not
  // the first one in the memory header list
  //
  for (TempHeaderPtr = MemoryHeader->Next; TempHeaderPtr != NULL;) {
    //
    // Debug
    //
    ASSERT (MemoryHeader->Next != NULL);

    if (IsMemoryBlockEmptied (TempHeaderPtr)) {

      DelinkMemoryBlock (MemoryHeader, TempHeaderPtr);
      //
      // when the TempHeaderPtr is freed in FreeMemoryHeader(),
      // the TempHeaderPtr is pointing to nonsense content.
      //
      FreeMemoryHeader (HcDev, TempHeaderPtr);
      //
      // reset the TempHeaderPtr, continue search for
      // another empty memory block.
      //
      TempHeaderPtr = MemoryHeader->Next;
      continue;
    }

    TempHeaderPtr = TempHeaderPtr->Next;
  }
}

VOID
InsertMemoryHeaderToList (
  IN MEMORY_MANAGE_HEADER     *MemoryHeader,
  IN MEMORY_MANAGE_HEADER     *NewMemoryHeader
  )
/*++

Routine Description:

  Insert Memory Header To List

Arguments:

  MemoryHeader    - MEMORY_MANAGE_HEADER
  NewMemoryHeader - MEMORY_MANAGE_HEADER

Returns:

  VOID

--*/
{
  MEMORY_MANAGE_HEADER  *TempHeaderPtr;

  for (TempHeaderPtr = MemoryHeader; TempHeaderPtr != NULL;
       TempHeaderPtr = TempHeaderPtr->Next) {
    if (TempHeaderPtr->Next == NULL) {
      TempHeaderPtr->Next = NewMemoryHeader;
      break;
    }
  }
}

EFI_STATUS
AllocMemInMemoryBlock (
  IN  MEMORY_MANAGE_HEADER     *MemoryHeader,
  OUT VOID                     **Pool,
  IN  UINTN                    NumberOfMemoryUnit
  )
/*++

Routine Description:

  Alloc Memory In MemoryBlock

Arguments:

  MemoryHeader        - MEMORY_MANAGE_HEADER
  Pool                - Place to store pointer to memory
  NumberOfMemoryUnit  - Number Of Memory Unit

Returns:

  EFI_NOT_FOUND  - Can't find the free memory 
  EFI_SUCCESS    - Success

--*/
{
  UINTN TempBytePos;
  UINTN FoundBytePos;
  UINT8 Index;
  UINT8 FoundBitPos;
  UINT8 ByteValue;
  UINT8 BitValue;
  UINTN NumberOfZeros;
  UINTN Count;

  FoundBytePos  = 0;
  FoundBitPos   = 0;
  ByteValue     = MemoryHeader->BitArrayPtr[0];
  NumberOfZeros = 0;
  Index         = 0;

  for (TempBytePos = 0; TempBytePos < MemoryHeader->BitArraySizeInBytes;) {
    
    //
    // Pop out BitValue from a byte in TempBytePos.
    //
    BitValue = (UINT8) (ByteValue & 0x1);
    //
    // right shift the byte
    //
    ByteValue /= 2;

    if (BitValue == 0) {
      //
      // Found a free bit, the NumberOfZeros only record the number
      // of those consecutive zeros
      //
      NumberOfZeros++;
      //
      // Found enough consecutive free space, break the loop
      //
      if (NumberOfZeros >= NumberOfMemoryUnit) {
        break;
      }
    } else {
      //
      // Encountering a '1', meant the bit is ocupied.
      //
      if (NumberOfZeros >= NumberOfMemoryUnit) {
        //
        // Found enough consecutive free space,break the loop
        //
        break;
      } else {
        //
        // the NumberOfZeros only record the number of those consecutive zeros,
        // so reset the NumberOfZeros to 0 when encountering '1' before finding
        // enough consecutive '0's
        //
        NumberOfZeros = 0;
        //
        // reset the (FoundBytePos,FoundBitPos) to the position of '1'
        //
        FoundBytePos  = TempBytePos;
        FoundBitPos   = Index;
      }
    }
    //
    // step forward a bit
    //
    Index++;
    if (Index == 8) {
      //
      // step forward a byte, getting the byte value,
      // and reset the bit pos.
      //
      TempBytePos += 1;
      ByteValue = MemoryHeader->BitArrayPtr[TempBytePos];
      Index     = 0;
    }
  }

  if (NumberOfZeros < NumberOfMemoryUnit) {
    return EFI_NOT_FOUND;
  }
  
  //
  // Found enough free space.
  //
  
  //
  // The values recorded in (FoundBytePos,FoundBitPos) have two conditions:
  //  1)(FoundBytePos,FoundBitPos) record the position
  //    of the last '1' before the consecutive '0's, it must
  //    be adjusted to the start position of the consecutive '0's.
  //  2)the start address of the consecutive '0's is just the start of
  //    the bitarray. so no need to adjust the values of
  //    (FoundBytePos,FoundBitPos).
  //
  if ((MemoryHeader->BitArrayPtr[0] & bit (0)) != 0) {
    FoundBitPos += 1;
  }
  
  //
  // Have the (FoundBytePos,FoundBitPos) make sense.
  //
  if (FoundBitPos > 7) {
    FoundBytePos += 1;
    FoundBitPos -= 8;
  }
  
  //
  // Set the memory as allocated
  //
  for (TempBytePos = FoundBytePos, Index = FoundBitPos,Count = 0;
       Count < NumberOfMemoryUnit; Count ++) {

    MemoryHeader->BitArrayPtr[TempBytePos] = (UINT8) (MemoryHeader->BitArrayPtr[TempBytePos] | bit (Index));
    Index++;
    if (Index == 8) {
      TempBytePos += 1;
      Index = 0;
    }
  }

  *Pool = MemoryHeader->MemoryBlockPtr + (FoundBytePos * 8 + FoundBitPos) * 32;

  return EFI_SUCCESS;
}

BOOLEAN
IsMemoryBlockEmptied (
  IN MEMORY_MANAGE_HEADER     *MemoryHeaderPtr
  )
/*++

Routine Description:

  Is Memory Block Emptied

Arguments:

  MemoryHeaderPtr - MEMORY_MANAGE_HEADER

Returns:

  TRUE  - Empty
  FALSE - Not Empty 

--*/
{
  UINTN Index;

  for (Index = 0; Index < MemoryHeaderPtr->BitArraySizeInBytes; Index++) {
    if (MemoryHeaderPtr->BitArrayPtr[Index] != 0) {
      return FALSE;
    }
  }

  return TRUE;
}

VOID
DelinkMemoryBlock (
  IN MEMORY_MANAGE_HEADER     *FirstMemoryHeader,
  IN MEMORY_MANAGE_HEADER     *NeedFreeMemoryHeader
  )
/*++

Routine Description:

  Delink Memory Block

Arguments:

  FirstMemoryHeader     - MEMORY_MANAGE_HEADER
  NeedFreeMemoryHeader  - MEMORY_MANAGE_HEADER

Returns:

  VOID

--*/
{
  MEMORY_MANAGE_HEADER  *TempHeaderPtr;

  if ((FirstMemoryHeader == NULL) || (NeedFreeMemoryHeader == NULL)) {
    return ;
  }
  for (TempHeaderPtr = FirstMemoryHeader; TempHeaderPtr != NULL;
       TempHeaderPtr = TempHeaderPtr->Next) {

    if (TempHeaderPtr->Next == NeedFreeMemoryHeader) {
      //
      // Link the before and after
      //
      TempHeaderPtr->Next = NeedFreeMemoryHeader->Next;
      break;
    }
  }
}

EFI_STATUS
DelMemoryManagement (
  IN USB_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Delete Memory Management

Arguments:

  HcDev - USB_HC_DEV

Returns:

  EFI_SUCCESS - Success

--*/
{
  MEMORY_MANAGE_HEADER  *TempHeaderPtr;

  for (TempHeaderPtr = HcDev->MemoryHeader->Next; TempHeaderPtr != NULL;) {

    DelinkMemoryBlock (HcDev->MemoryHeader, TempHeaderPtr);
    //
    // when the TempHeaderPtr is freed in FreeMemoryHeader(),
    // the TempHeaderPtr is pointing to nonsense content.
    //
    FreeMemoryHeader (HcDev, TempHeaderPtr);
    //
    // reset the TempHeaderPtr,continue free another memory block.
    //
    TempHeaderPtr = HcDev->MemoryHeader->Next;
  }

  FreeMemoryHeader (HcDev, HcDev->MemoryHeader);

  return EFI_SUCCESS;
}


VOID
CleanUsbTransactions (
  IN USB_HC_DEV     *HcDev
  )
{
  //
  // only asynchronous interrupt transfers are always alive on the bus
  //
  ReleaseInterruptList (HcDev, &(HcDev->InterruptListHead));
}

VOID
TurnOffUSBEmulation (
  IN EFI_PCI_IO_PROTOCOL     *PciIo
  )
/*++
  
  Routine Description:
    Disable USB Emulation
  Arguments:
    PciIo  -  EFI_PCI_IO_PROTOCOL
  Returns:
    VOID
--*/
{
  UINT16  Command;

  //
  // Disable USB Emulation
  //
  Command = 0;
  PciIo->Pci.Write (
               PciIo,
               EfiPciIoWidthUint16,
               USB_EMULATION,
               1,
               &Command
               );

  return ;
}
