audk/EdkModulePkg/Bus/Pci/Uhci/Dxe/uhchlp.c

4238 lines
84 KiB
C
Raw Normal View History

/*++
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"
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
);
}
EFI_STATUS
USBReadPortDW (
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN UINT32 PortOffset,
IN OUT UINT32 *Data
)
/*++
Routine Description:
USBReadPort DWord
Arguments:
PciIo - EFI_PCI_IO_PROTOCOL
PortOffset - Port offset
Data - Data to reutrn
Returns:
EFI_SUCCESS
--*/
{
//
// Perform 32bit Read in PCI IO Space
//
return PciIo->Io.Read (
PciIo,
EfiPciIoWidthUint32,
USB_BAR_INDEX,
(UINT64) PortOffset,
1,
Data
);
}
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
);
}
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;
EFI_STATUS Status;
Status = 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
//
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));
}
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));
}
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);
}
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) (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] |= 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 ;
}