mirror of https://github.com/acidanthera/audk.git
4207 lines
83 KiB
C
4207 lines
83 KiB
C
/*++
|
|
|
|
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 ;
|
|
}
|