audk/EdkModulePkg/Bus/Pci/Ehci/Dxe/EhciSched.c

3241 lines
71 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:
EhciSched.c
Abstract:
Revision History
--*/
#include "Ehci.h"
STATIC
EFI_STATUS
SetAndWaitDoorBell (
IN USB2_HC_DEV *HcDev,
IN UINTN Timeout
)
/*++
Routine Description:
Set DoorBell and wait it to complete
Arguments:
HcDev - USB2_HC_DEV
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
EFI_STATUS Status;
UINT32 Data;
UINTN Delay;
Status = ReadEhcOperationalReg (
HcDev,
USBCMD,
&Data
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto exit;
}
Data |= USBCMD_IAAD;
Status = WriteEhcOperationalReg (
HcDev,
USBCMD,
Data
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
}
//
// Timeout is in US unit
//
Delay = (Timeout / 50) + 1;
do {
Status = ReadEhcOperationalReg (
HcDev,
USBSTS,
&Data
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto exit;
}
if ((Data & USBSTS_IAA) == USBSTS_IAA) {
break;
}
gBS->Stall (EHCI_GENERIC_RECOVERY_TIME);
} while (Delay--);
Data = Data & 0xFFFFFFC0;
Data |= USBSTS_IAA;
Status = WriteEhcOperationalReg (
HcDev,
USBSTS,
Data
);
exit:
return Status;
}
EFI_STATUS
CreateNULLQH (
IN USB2_HC_DEV *HcDev
)
/*++
Routine Description:
Create the NULL QH to make it as the Async QH header
Arguments:
HcDev - USB2_HC_DEV
Returns:
EFI_SUCCESS Success
--*/
{
EFI_STATUS Status;
EHCI_QH_ENTITY *NULLQhPtr;
//
// Allocate memory for Qh structure
//
Status = EhciAllocatePool (
HcDev,
(UINT8 **) &NULLQhPtr,
sizeof (EHCI_QH_ENTITY)
);
if (EFI_ERROR (Status)) {
return Status;
}
NULLQhPtr->Qh.Status = QTD_STATUS_HALTED;
NULLQhPtr->Qh.HeadReclamationFlag = 1;
NULLQhPtr->Qh.QhHorizontalPointer = (UINT32) (GET_0B_TO_31B (&(NULLQhPtr->Qh) >> 5));
NULLQhPtr->Qh.SelectType = QH_SELECT_TYPE;
NULLQhPtr->Qh.NextQtdTerminate = 1;
NULLQhPtr->Next = NULLQhPtr;
NULLQhPtr->Prev = NULLQhPtr;
HcDev->NULLQH = NULLQhPtr;
return Status;
}
VOID
DestroyNULLQH (
IN USB2_HC_DEV *HcDev
)
{
if (HcDev->NULLQH != NULL) {
EhciFreePool (HcDev, (UINT8 *)HcDev->NULLQH, sizeof (EHCI_QH_ENTITY));
HcDev->NULLQH = NULL;
}
}
EFI_STATUS
InitialPeriodicFrameList (
IN USB2_HC_DEV *HcDev,
IN UINTN Length
)
/*++
Routine Description:
Initialize Periodic Schedule Frame List
Arguments:
HcDev - USB2_HC_DEV
Length - Frame List Length
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
EFI_STATUS Status;
VOID *CommonBuffer;
EFI_PHYSICAL_ADDRESS FrameBuffer;
VOID *Map;
UINTN BufferSizeInPages;
UINTN BufferSizeInBytes;
UINTN FrameIndex;
FRAME_LIST_ENTRY *FrameEntryPtr;
//
// The Frame List is a common buffer that will be
// accessed by both the cpu and the usb bus master
// at the same time.
// The Frame List ocupies 4K bytes,
// and must be aligned on 4-Kbyte boundaries.
//
if (EHCI_MAX_FRAME_LIST_LENGTH != Length && IsFrameListProgrammable (HcDev)) {
Status = SetFrameListLen (HcDev, Length);
if (EFI_ERROR (Status)) {
Status = EFI_INVALID_PARAMETER;
goto exit;
}
}
BufferSizeInBytes = EFI_PAGE_SIZE;
BufferSizeInPages = EFI_SIZE_TO_PAGES (BufferSizeInBytes);
Status = HcDev->PciIo->AllocateBuffer (
HcDev->PciIo,
AllocateAnyPages,
EfiBootServicesData,
BufferSizeInPages,
&CommonBuffer,
0
);
if (EFI_ERROR (Status)) {
DEBUG ((gEHCErrorLevel, "EHCI: PciIo->AllocateBuffer Failed\n"));
Status = EFI_OUT_OF_RESOURCES;
goto exit;
}
Status = HcDev->PciIo->Map (
HcDev->PciIo,
EfiPciIoOperationBusMasterCommonBuffer,
CommonBuffer,
&BufferSizeInBytes,
&FrameBuffer,
&Map
);
if (EFI_ERROR (Status) || (BufferSizeInBytes != EFI_PAGE_SIZE)) {
DEBUG ((gEHCErrorLevel, "EHCI: PciIo->MapBuffer Failed\n"));
Status = EFI_OUT_OF_RESOURCES;
goto free_buffer;
}
//
// Put high 32bit into CtrlDataStructSeg reg
// when 64bit addressing range capability
//
if (HcDev->Is64BitCapable != 0) {
HcDev->High32BitAddr = (UINT32) GET_32B_TO_63B (FrameBuffer);
Status = SetCtrlDataStructSeg (HcDev);
if (EFI_ERROR (Status)) {
DEBUG ((gEHCErrorLevel, "EHCI: SetCtrlDataStructSeg Failed\n"));
Status = EFI_DEVICE_ERROR;
goto unmap_buffer;
}
}
//
// Tell the Host Controller where the Frame List lies,
// by set the Frame List Base Address Register.
//
Status = SetFrameListBaseAddr (HcDev, (UINT32) FrameBuffer);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto unmap_buffer;
}
HcDev->PeriodicFrameListLength = Length;
HcDev->PeriodicFrameListBuffer = (VOID *) ((UINTN) FrameBuffer);
HcDev->PeriodicFrameListMap = Map;
//
// Init Frame List Array fields
//
FrameEntryPtr = (FRAME_LIST_ENTRY *) HcDev->PeriodicFrameListBuffer;
for (FrameIndex = 0; FrameIndex < HcDev->PeriodicFrameListLength; FrameIndex++) {
FrameEntryPtr->LinkPointer = 0;
FrameEntryPtr->Rsvd = 0;
FrameEntryPtr->SelectType = 0;
FrameEntryPtr->LinkTerminate = TRUE;
FrameEntryPtr++;
}
goto exit;
unmap_buffer:
HcDev->PciIo->Unmap (HcDev->PciIo, Map);
free_buffer:
HcDev->PciIo->FreeBuffer (HcDev->PciIo, BufferSizeInPages, CommonBuffer);
exit:
return Status;
}
VOID
DeinitialPeriodicFrameList (
IN USB2_HC_DEV *HcDev
)
/*++
Routine Description:
Deinitialize Periodic Schedule Frame List
Arguments:
HcDev - USB2_HC_DEV
Returns:
VOID
--*/
{
HcDev->PciIo->Unmap (HcDev->PciIo, HcDev->PeriodicFrameListMap);
HcDev->PciIo->FreeBuffer (HcDev->PciIo, EFI_SIZE_TO_PAGES (EFI_PAGE_SIZE), HcDev->PeriodicFrameListBuffer);
return ;
}
EFI_STATUS
CreatePollingTimer (
IN USB2_HC_DEV *HcDev,
IN EFI_EVENT_NOTIFY NotifyFunction
)
/*++
Routine Description:
Create Async Request Polling Timer
Arguments:
HcDev - USB2_HC_DEV
NotifyFunction - Timer Notify Function
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
return gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
NotifyFunction,
HcDev,
&HcDev->AsyncRequestEvent
);
}
EFI_STATUS
DestoryPollingTimer (
IN USB2_HC_DEV *HcDev
)
/*++
Routine Description:
Destory Async Request Polling Timer
Arguments:
HcDev - USB2_HC_DEV
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
return gBS->CloseEvent (HcDev->AsyncRequestEvent);
}
EFI_STATUS
StartPollingTimer (
IN USB2_HC_DEV *HcDev
)
/*++
Routine Description:
Start Async Request Polling Timer
Arguments:
HcDev - USB2_HC_DEV
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
return gBS->SetTimer (
HcDev->AsyncRequestEvent,
TimerPeriodic,
EHCI_ASYNC_REQUEST_POLLING_TIME
);
}
EFI_STATUS
StopPollingTimer (
IN USB2_HC_DEV *HcDev
)
/*++
Routine Description:
Stop Async Request Polling Timer
Arguments:
HcDev - USB2_HC_DEV
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
return gBS->SetTimer (
HcDev->AsyncRequestEvent,
TimerCancel,
EHCI_ASYNC_REQUEST_POLLING_TIME
);
}
EFI_STATUS
CreateQh (
IN USB2_HC_DEV *HcDev,
IN UINT8 DeviceAddr,
IN UINT8 Endpoint,
IN UINT8 DeviceSpeed,
IN UINTN MaxPacketLen,
OUT EHCI_QH_ENTITY **QhPtrPtr
)
/*++
Routine Description:
Create Qh Structure and Pre-Initialize
Arguments:
HcDev - USB2_HC_DEV
DeviceAddr - Address of Device
Endpoint - Endpoint Number
DeviceSpeed - Device Speed
MaxPacketLen - Max Length of one Packet
QhPtrPtr - A pointer of pointer to Qh for return
Returns:
EFI_SUCCESS Success
EFI_OUT_OF_RESOURCES Cannot allocate resources
--*/
{
EFI_STATUS Status;
EHCI_QH_HW *QhHwPtr;
ASSERT (HcDev);
ASSERT (QhPtrPtr);
*QhPtrPtr = NULL;
//
// Allocate memory for Qh structure
//
Status = EhciAllocatePool (
HcDev,
(UINT8 **) QhPtrPtr,
sizeof (EHCI_QH_ENTITY)
);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
goto exit;
}
//
// Software field
//
(*QhPtrPtr)->Next = NULL;
(*QhPtrPtr)->Prev = NULL;
(*QhPtrPtr)->FirstQtdPtr = NULL;
(*QhPtrPtr)->AltQtdPtr = NULL;
(*QhPtrPtr)->LastQtdPtr = NULL;
//
// Hardware field
//
QhHwPtr = &((*QhPtrPtr)->Qh);
QhHwPtr->QhHorizontalPointer = 0;
QhHwPtr->SelectType = 0;
QhHwPtr->MaxPacketLen = (UINT32) MaxPacketLen;
QhHwPtr->EndpointSpeed = (DeviceSpeed & 0x3);
QhHwPtr->EndpointNum = (Endpoint & 0x0F);
QhHwPtr->DeviceAddr = (DeviceAddr & 0x7F);
QhHwPtr->Multiplier = HIGH_BANDWIDTH_PIPE_MULTIPLIER;
QhHwPtr->Rsvd1 = 0;
QhHwPtr->Rsvd2 = 0;
QhHwPtr->Rsvd3 = 0;
QhHwPtr->Rsvd4 = 0;
QhHwPtr->Rsvd5 = 0;
QhHwPtr->Rsvd6 = 0;
exit:
return Status;
}
VOID
DestoryQh (
IN USB2_HC_DEV *HcDev,
IN EHCI_QH_ENTITY *QhPtr
)
/*++
Routine Description:
Destory Qh Structure
Arguments:
HcDev - USB2_HC_DEV
QhPtr - A pointer to Qh
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
ASSERT (HcDev);
ASSERT (QhPtr);
EhciFreePool (HcDev, (UINT8 *) QhPtr, sizeof (EHCI_QH_ENTITY));
return ;
}
EFI_STATUS
CreateControlQh (
IN USB2_HC_DEV *HcDev,
IN UINT8 DeviceAddr,
IN UINT8 DeviceSpeed,
IN UINTN MaxPacketLen,
IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
OUT EHCI_QH_ENTITY **QhPtrPtr
)
/*++
Routine Description:
Create Qh for Control Transfer
Arguments:
HcDev - USB2_HC_DEV
DeviceAddr - Address of Device
DeviceSpeed - Device Speed
MaxPacketLen - Max Length of one Packet
Translator - Translator Transaction for SplitX
QhPtrPtr - A pointer of pointer to Qh for return
Returns:
EFI_SUCCESS Success
EFI_OUT_OF_RESOURCES Cannot allocate resources
--*/
{
EFI_STATUS Status;
//
// Create and init Control Qh
//
Status = CreateQh (
HcDev,
DeviceAddr,
0,
DeviceSpeed,
MaxPacketLen,
QhPtrPtr
);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
goto exit;
}
//
// Software field
//
(*QhPtrPtr)->Next = (*QhPtrPtr);
(*QhPtrPtr)->Prev = (*QhPtrPtr);
(*QhPtrPtr)->TransferType = CONTROL_TRANSFER;
//
// Hardware field
//
// Control Transfer use DataToggleControl
//
(*QhPtrPtr)->Qh.DataToggleControl = TRUE;
(*QhPtrPtr)->Qh.QhHorizontalPointer = (UINT32) (GET_0B_TO_31B (&((*QhPtrPtr)->Qh)) >> 5);
(*QhPtrPtr)->Qh.SelectType = QH_SELECT_TYPE;
(*QhPtrPtr)->Qh.QhTerminate = FALSE;
if (EFI_USB_SPEED_HIGH != DeviceSpeed) {
(*QhPtrPtr)->Qh.ControlEndpointFlag = TRUE;
}
(*QhPtrPtr)->Qh.NakCountReload = NAK_COUNT_RELOAD;
if (NULL != Translator) {
(*QhPtrPtr)->Qh.PortNum = Translator->TranslatorPortNumber;
(*QhPtrPtr)->Qh.HubAddr = Translator->TranslatorHubAddress;
(*QhPtrPtr)->Qh.Status |= QTD_STATUS_DO_START_SPLIT;
}
exit:
return Status;
}
EFI_STATUS
CreateBulkQh (
IN USB2_HC_DEV *HcDev,
IN UINT8 DeviceAddr,
IN UINT8 EndPointAddr,
IN UINT8 DeviceSpeed,
IN UINT8 DataToggle,
IN UINTN MaxPacketLen,
IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
OUT EHCI_QH_ENTITY **QhPtrPtr
)
/*++
Routine Description:
Create Qh for Bulk Transfer
Arguments:
HcDev - USB2_HC_DEV
DeviceAddr - Address of Device
EndPointAddr - Address of Endpoint
DeviceSpeed - Device Speed
MaxPacketLen - Max Length of one Packet
Translator - Translator Transaction for SplitX
QhPtrPtr - A pointer of pointer to Qh for return
Returns:
EFI_SUCCESS Success
EFI_OUT_OF_RESOURCES Cannot allocate resources
--*/
{
EFI_STATUS Status;
//
// Create and init Bulk Qh
//
Status = CreateQh (
HcDev,
DeviceAddr,
EndPointAddr,
DeviceSpeed,
MaxPacketLen,
QhPtrPtr
);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
goto exit;
}
//
// Software fields
//
(*QhPtrPtr)->Next = (*QhPtrPtr);
(*QhPtrPtr)->Prev = (*QhPtrPtr);
(*QhPtrPtr)->TransferType = BULK_TRANSFER;
//
// Hardware fields
//
// BulkTransfer don't use DataToggleControl
//
(*QhPtrPtr)->Qh.DataToggleControl = FALSE;
(*QhPtrPtr)->Qh.QhHorizontalPointer = (UINT32) (GET_0B_TO_31B (&((*QhPtrPtr)->Qh)) >> 5);
(*QhPtrPtr)->Qh.SelectType = QH_SELECT_TYPE;
(*QhPtrPtr)->Qh.QhTerminate = FALSE;
(*QhPtrPtr)->Qh.NakCountReload = NAK_COUNT_RELOAD;
(*QhPtrPtr)->Qh.DataToggle = DataToggle;
if (NULL != Translator) {
(*QhPtrPtr)->Qh.PortNum = Translator->TranslatorPortNumber;
(*QhPtrPtr)->Qh.HubAddr = Translator->TranslatorHubAddress;
(*QhPtrPtr)->Qh.Status |= QTD_STATUS_DO_START_SPLIT;
}
exit:
return Status;
}
EFI_STATUS
CreateInterruptQh (
IN USB2_HC_DEV *HcDev,
IN UINT8 DeviceAddr,
IN UINT8 EndPointAddr,
IN UINT8 DeviceSpeed,
IN UINT8 DataToggle,
IN UINTN MaxPacketLen,
IN UINTN Interval,
IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
OUT EHCI_QH_ENTITY **QhPtrPtr
)
/*++
Routine Description:
Create Qh for Control Transfer
Arguments:
HcDev - USB2_HC_DEV
DeviceAddr - Address of Device
EndPointAddr - Address of Endpoint
DeviceSpeed - Device Speed
MaxPacketLen - Max Length of one Packet
Interval - value of interval
Translator - Translator Transaction for SplitX
QhPtrPtr - A pointer of pointer to Qh for return
Returns:
EFI_SUCCESS Success
EFI_OUT_OF_RESOURCES Cannot allocate resources
--*/
{
EFI_STATUS Status;
//
// Create and init InterruptQh
//
Status = CreateQh (
HcDev,
DeviceAddr,
EndPointAddr,
DeviceSpeed,
MaxPacketLen,
QhPtrPtr
);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
goto exit;
}
//
// Software fields
//
if (Interval == 0) {
(*QhPtrPtr)->TransferType = SYNC_INTERRUPT_TRANSFER;
} else {
(*QhPtrPtr)->TransferType = ASYNC_INTERRUPT_TRANSFER;
}
(*QhPtrPtr)->Interval = GetApproxiOfInterval (Interval);
//
// Hardware fields
//
// InterruptTranfer don't use DataToggleControl
//
(*QhPtrPtr)->Qh.DataToggleControl = FALSE;
(*QhPtrPtr)->Qh.QhHorizontalPointer = 0;
(*QhPtrPtr)->Qh.QhTerminate = TRUE;
(*QhPtrPtr)->Qh.NakCountReload = 0;
(*QhPtrPtr)->Qh.InerruptScheduleMask = MICRO_FRAME_0_CHANNEL;
(*QhPtrPtr)->Qh.SplitComletionMask = (MICRO_FRAME_2_CHANNEL | MICRO_FRAME_3_CHANNEL | MICRO_FRAME_4_CHANNEL);
(*QhPtrPtr)->Qh.DataToggle = DataToggle;
if (NULL != Translator) {
(*QhPtrPtr)->Qh.PortNum = Translator->TranslatorPortNumber;
(*QhPtrPtr)->Qh.HubAddr = Translator->TranslatorHubAddress;
(*QhPtrPtr)->Qh.Status |= QTD_STATUS_DO_START_SPLIT;
}
exit:
return Status;
}
EFI_STATUS
CreateQtd (
IN USB2_HC_DEV *HcDev,
IN UINT8 *DataPtr,
IN UINTN DataLen,
IN UINT8 PktId,
IN UINT8 Toggle,
IN UINT8 QtdStatus,
OUT EHCI_QTD_ENTITY **QtdPtrPtr
)
/*++
Routine Description:
Create Qtd Structure and Pre-Initialize it
Arguments:
HcDev - USB2_HC_DEV
DataPtr - A pointer to user data buffer to transfer
DataLen - Length of user data to transfer
PktId - Packet Identification of this Qtd
Toggle - Data Toggle of this Qtd
QtdStatus - Default value of status of this Qtd
QtdPtrPtr - A pointer of pointer to Qtd for return
Returns:
EFI_SUCCESS Success
EFI_OUT_OF_RESOURCES Cannot allocate resources
--*/
{
EFI_STATUS Status;
EHCI_QTD_HW *QtdHwPtr;
ASSERT (HcDev);
ASSERT (QtdPtrPtr);
//
// Create memory for Qtd structure
//
Status = EhciAllocatePool (
HcDev,
(UINT8 **) QtdPtrPtr,
sizeof (EHCI_QTD_ENTITY)
);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
goto exit;
}
//
// Software field
//
(*QtdPtrPtr)->TotalBytes = (UINT32) DataLen;
(*QtdPtrPtr)->StaticTotalBytes = (UINT32) DataLen;
(*QtdPtrPtr)->Prev = NULL;
(*QtdPtrPtr)->Next = NULL;
//
// Hardware field
//
QtdHwPtr = &((*QtdPtrPtr)->Qtd);
QtdHwPtr->NextQtdPointer = 0;
QtdHwPtr->NextQtdTerminate = TRUE;
QtdHwPtr->AltNextQtdPointer = 0;
QtdHwPtr->AltNextQtdTerminate = TRUE;
QtdHwPtr->DataToggle = Toggle;
QtdHwPtr->TotalBytes = (UINT32) DataLen;
QtdHwPtr->CurrentPage = 0;
QtdHwPtr->ErrorCount = QTD_ERROR_COUNTER;
QtdHwPtr->Status = QtdStatus;
QtdHwPtr->Rsvd1 = 0;
QtdHwPtr->Rsvd2 = 0;
QtdHwPtr->Rsvd3 = 0;
QtdHwPtr->Rsvd4 = 0;
QtdHwPtr->Rsvd5 = 0;
QtdHwPtr->Rsvd6 = 0;
//
// Set PacketID [Setup/Data/Status]
//
switch (PktId) {
case SETUP_PACKET_ID:
QtdHwPtr->PidCode = SETUP_PACKET_PID_CODE;
break;
case INPUT_PACKET_ID:
QtdHwPtr->PidCode = INPUT_PACKET_PID_CODE;
break;
case OUTPUT_PACKET_ID:
QtdHwPtr->PidCode = OUTPUT_PACKET_PID_CODE;
break;
default:
Status = EFI_INVALID_PARAMETER;
goto exit;
}
//
// Set Data Buffer Pointers
//
if (NULL != DataPtr) {
SetQtdBufferPointer (
QtdHwPtr,
DataPtr,
DataLen
);
(*QtdPtrPtr)->StaticCurrentOffset = QtdHwPtr->CurrentOffset;
}
exit:
return Status;
}
EFI_STATUS
CreateSetupQtd (
IN USB2_HC_DEV *HcDev,
IN UINT8 *DevReqPtr,
OUT EHCI_QTD_ENTITY **QtdPtrPtr
)
/*++
Routine Description:
Create Qtd Structure for Setup
Arguments:
HcDev - USB2_HC_DEV
DevReqPtr - A pointer to Device Request Data
QtdPtrPtr - A pointer of pointer to Qtd for return
Returns:
EFI_SUCCESS Success
EFI_OUT_OF_RESOURCES Cannot allocate resources
--*/
{
return CreateQtd (
HcDev,
DevReqPtr,
sizeof (EFI_USB_DEVICE_REQUEST),
SETUP_PACKET_ID,
DATA0,
QTD_STATUS_ACTIVE,
QtdPtrPtr
);
}
EFI_STATUS
CreateDataQtd (
IN USB2_HC_DEV *HcDev,
IN UINT8 *DataPtr,
IN UINTN DataLen,
IN UINT8 PktId,
IN UINT8 Toggle,
OUT EHCI_QTD_ENTITY **QtdPtrPtr
)
/*++
Routine Description:
Create Qtd Structure for data
Arguments:
HcDev - USB2_HC_DEV
DataPtr - A pointer to user data buffer to transfer
DataLen - Length of user data to transfer
PktId - Packet Identification of this Qtd
Toggle - Data Toggle of this Qtd
QtdPtrPtr - A pointer of pointer to Qtd for return
Returns:
EFI_SUCCESS Success
EFI_OUT_OF_RESOURCES Cannot allocate resources
--*/
{
return CreateQtd (
HcDev,
DataPtr,
DataLen,
PktId,
Toggle,
QTD_STATUS_ACTIVE,
QtdPtrPtr
);
}
EFI_STATUS
CreateAltQtd (
IN USB2_HC_DEV *HcDev,
IN UINT8 PktId,
OUT EHCI_QTD_ENTITY **QtdPtrPtr
)
/*++
Routine Description:
Create Qtd Structure for Alternative
Arguments:
HcDev - USB2_HC_DEV
PktId - Packet Identification of this Qtd
QtdPtrPtr - A pointer of pointer to Qtd for return
Returns:
EFI_SUCCESS Success
EFI_OUT_OF_RESOURCES Cannot allocate resources
--*/
{
return CreateQtd (
HcDev,
NULL,
0,
PktId,
0,
QTD_STATUS_ACTIVE,
QtdPtrPtr
);
}
EFI_STATUS
CreateStatusQtd (
IN USB2_HC_DEV *HcDev,
IN UINT8 PktId,
OUT EHCI_QTD_ENTITY **QtdPtrPtr
)
/*++
Routine Description:
Create Qtd Structure for status
Arguments:
HcDev - USB2_HC_DEV
PktId - Packet Identification of this Qtd
QtdPtrPtr - A pointer of pointer to Qtd for return
Returns:
EFI_SUCCESS Success
EFI_OUT_OF_RESOURCES Cannot allocate resources
--*/
{
return CreateQtd (
HcDev,
NULL,
0,
PktId,
DATA1,
QTD_STATUS_ACTIVE,
QtdPtrPtr
);
}
EFI_STATUS
CreateControlQtds (
IN USB2_HC_DEV *HcDev,
IN UINT8 DataPktId,
IN UINT8 *RequestCursor,
IN UINT8 *DataCursor,
IN UINTN DataLen,
IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
OUT EHCI_QTD_ENTITY **ControlQtdsHead
)
/*++
Routine Description:
Create Qtds list for Control Transfer
Arguments:
HcDev - USB2_HC_DEV
DataPktId - Packet Identification of Data Qtds
RequestCursor - A pointer to request structure buffer to transfer
DataCursor - A pointer to user data buffer to transfer
DataLen - Length of user data to transfer
ControlQtdsHead - A pointer of pointer to first Qtd for control tranfer for return
Returns:
EFI_SUCCESS Success
EFI_OUT_OF_RESOURCES Cannot allocate resources
--*/
{
EFI_STATUS Status;
EHCI_QTD_ENTITY *QtdPtr;
EHCI_QTD_ENTITY *PreQtdPtr;
EHCI_QTD_ENTITY *SetupQtdPtr;
EHCI_QTD_ENTITY *FirstDataQtdPtr;
EHCI_QTD_ENTITY *LastDataQtdPtr;
EHCI_QTD_ENTITY *StatusQtdPtr;
UINT8 DataToggle;
UINT8 StatusPktId;
UINTN CapacityOfQtd;
UINTN SizePerQtd;
UINTN DataCount;
QtdPtr = NULL;
PreQtdPtr = NULL;
SetupQtdPtr = NULL;
FirstDataQtdPtr = NULL;
LastDataQtdPtr = NULL;
StatusQtdPtr = NULL;
CapacityOfQtd = 0;
//
// Setup Stage of Control Transfer
//
Status = CreateSetupQtd (
HcDev,
RequestCursor,
&SetupQtdPtr
);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
goto exit;
}
//
// Data Stage of Control Transfer
//
DataToggle = 1;
DataCount = DataLen;
//
// Create Qtd structure and link together
//
while (DataCount > 0) {
//
// PktSize is the data load size that each Qtd.
//
CapacityOfQtd = GetCapacityOfQtd (DataCursor);
SizePerQtd = DataCount;
if (DataCount > CapacityOfQtd) {
SizePerQtd = CapacityOfQtd;
}
Status = CreateDataQtd (
HcDev,
DataCursor,
SizePerQtd,
DataPktId,
DataToggle,
&QtdPtr
);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
if (NULL == FirstDataQtdPtr) {
goto destory_setup_qtd;
} else {
goto destory_qtds;
}
}
if (NULL == FirstDataQtdPtr) {
FirstDataQtdPtr = QtdPtr;
} else {
LinkQtdToQtd (PreQtdPtr, QtdPtr);
}
DataToggle ^= 1;
PreQtdPtr = QtdPtr;
DataCursor += SizePerQtd;
DataCount -= SizePerQtd;
}
LastDataQtdPtr = QtdPtr;
//
// Status Stage of Control Transfer
//
if (OUTPUT_PACKET_ID == DataPktId) {
StatusPktId = INPUT_PACKET_ID;
} else {
StatusPktId = OUTPUT_PACKET_ID;
}
Status = CreateStatusQtd (
HcDev,
StatusPktId,
&StatusQtdPtr
);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
goto destory_qtds;
}
//
// Link setup Qtd -> data Qtds -> status Qtd
//
if (FirstDataQtdPtr != NULL) {
LinkQtdToQtd (SetupQtdPtr, FirstDataQtdPtr);
LinkQtdToQtd (LastDataQtdPtr, StatusQtdPtr);
} else {
LinkQtdToQtd (SetupQtdPtr, StatusQtdPtr);
}
*ControlQtdsHead = SetupQtdPtr;
goto exit;
destory_qtds:
DestoryQtds (HcDev, FirstDataQtdPtr);
destory_setup_qtd:
DestoryQtds (HcDev, SetupQtdPtr);
exit:
return Status;
}
EFI_STATUS
CreateBulkOrInterruptQtds (
IN USB2_HC_DEV *HcDev,
IN UINT8 PktId,
IN UINT8 *DataCursor,
IN UINTN DataLen,
IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
OUT EHCI_QTD_ENTITY **QtdsHead
)
/*++
Routine Description:
Create Qtds list for Bulk or Interrupt Transfer
Arguments:
HcDev - USB2_HC_DEV
PktId - Packet Identification of Qtds
DataCursor - A pointer to user data buffer to transfer
DataLen - Length of user data to transfer
DataToggle - Data Toggle to start
Translator - Translator Transaction for SplitX
QtdsHead - A pointer of pointer to first Qtd for control tranfer for return
Returns:
EFI_SUCCESS Success
EFI_OUT_OF_RESOURCES Cannot allocate resources
--*/
{
EFI_STATUS Status;
EHCI_QTD_ENTITY *QtdPtr;
EHCI_QTD_ENTITY *PreQtdPtr;
EHCI_QTD_ENTITY *FirstQtdPtr;
EHCI_QTD_ENTITY *AltQtdPtr;
UINTN DataCount;
UINTN CapacityOfQtd;
UINTN SizePerQtd;
Status = EFI_SUCCESS;
QtdPtr = NULL;
PreQtdPtr = NULL;
FirstQtdPtr = NULL;
AltQtdPtr = NULL;
CapacityOfQtd = 0;
DataCount = DataLen;
while (DataCount > 0) {
CapacityOfQtd = GetCapacityOfQtd (DataCursor);
SizePerQtd = DataCount;
if (DataCount > CapacityOfQtd) {
SizePerQtd = CapacityOfQtd;
}
Status = CreateDataQtd (
HcDev,
DataCursor,
SizePerQtd,
PktId,
0,
&QtdPtr
);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
if (NULL == FirstQtdPtr) {
goto exit;
} else {
goto destory_qtds;
}
}
if (NULL == FirstQtdPtr) {
FirstQtdPtr = QtdPtr;
} else {
LinkQtdToQtd (PreQtdPtr, QtdPtr);
}
PreQtdPtr = QtdPtr;
DataCursor += SizePerQtd;
DataCount -= SizePerQtd;
}
//
// Set Alternate Qtd
//
if (INPUT_PACKET_ID == PktId && 0 < GetNumberOfQtd (FirstQtdPtr)) {
Status = CreateAltQtd (
HcDev,
PktId,
&AltQtdPtr
);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
goto destory_qtds;
}
LinkQtdsToAltQtd (FirstQtdPtr, AltQtdPtr);
}
*QtdsHead = FirstQtdPtr;
goto exit;
destory_qtds:
DestoryQtds (HcDev, FirstQtdPtr);
exit:
return Status;
}
VOID
DestoryQtds (
IN USB2_HC_DEV *HcDev,
IN EHCI_QTD_ENTITY *FirstQtdPtr
)
/*++
Routine Description:
Destory all Qtds in the list
Arguments:
HcDev - USB2_HC_DEV
FirstQtdPtr - A pointer to first Qtd in the list
Returns:
VOID
--*/
{
EHCI_QTD_ENTITY *PrevQtd;
EHCI_QTD_ENTITY *NextQtd;
if (!FirstQtdPtr) {
goto exit;
}
PrevQtd = FirstQtdPtr;
//
// Delete all the Qtds.
//
do {
NextQtd = PrevQtd->Next;
EhciFreePool (HcDev, (UINT8 *) PrevQtd, sizeof (EHCI_QTD_ENTITY));
PrevQtd = NextQtd;
} while (NULL != PrevQtd);
exit:
return ;
}
UINTN
GetNumberOfQtd (
IN EHCI_QTD_ENTITY *FirstQtdPtr
)
/*++
Routine Description:
Number of Qtds in the list
Arguments:
FirstQtdPtr - A pointer to first Qtd in the list
Returns:
Number of Qtds in the list
--*/
{
UINTN Count;
EHCI_QTD_ENTITY *QtdPtr;
Count = 0;
QtdPtr = FirstQtdPtr;
while (NULL != QtdPtr) {
Count++;
QtdPtr = QtdPtr->Next;
}
return Count;
}
UINTN
GetCapacityOfQtd (
IN UINT8 *BufferCursor
)
/*++
Routine Description:
Get Size of First Qtd
Arguments:
BufferCursor - BufferCursor of the Qtd
Returns:
Size of First Qtd
--*/
{
if (EFI_PAGE_MASK & GET_0B_TO_31B (BufferCursor)) {
return EFI_PAGE_SIZE * 4;
} else {
return EFI_PAGE_SIZE * 5;
}
}
UINTN
GetApproxiOfInterval (
IN UINTN Interval
)
/*++
Routine Description:
Get the approximate value in the 2 index sequence
Arguments:
Interval - the value of interval
Returns:
approximate value of interval in the 2 index sequence
--*/
{
UINTN Orignate;
UINTN Approxi;
Orignate = Interval;
Approxi = 1;
while (Orignate != 1 && Orignate != 0) {
Orignate = Orignate >> 1;
Approxi = Approxi << 1;
}
if (Interval & (Approxi >> 1)) {
Approxi = Approxi << 1;
}
return Approxi;
}
EHCI_QTD_HW *
GetQtdAlternateNextPointer (
IN EHCI_QTD_HW *HwQtdPtr
)
/*++
Routine Description:
Get Qtd alternate next pointer field
Arguments:
HwQtdPtr - A pointer to hardware Qtd structure
Returns:
A pointer to hardware alternate Qtd
--*/
{
EHCI_QTD_HW *Value;
Value = NULL;
if (!HwQtdPtr->AltNextQtdTerminate) {
Value = (EHCI_QTD_HW *) GET_0B_TO_31B (HwQtdPtr->AltNextQtdPointer << 5);
}
return Value;
}
EHCI_QTD_HW *
GetQtdNextPointer (
IN EHCI_QTD_HW *HwQtdPtr
)
/*++
Routine Description:
Get Qtd next pointer field
Arguments:
HwQtdPtr - A pointer to hardware Qtd structure
Returns:
A pointer to next hardware Qtd structure
--*/
{
EHCI_QTD_HW *Value;
Value = NULL;
if (!HwQtdPtr->NextQtdTerminate) {
Value = (EHCI_QTD_HW *) GET_0B_TO_31B (HwQtdPtr->NextQtdPointer << 5);
}
return Value;
}
VOID
LinkQtdToQtd (
IN EHCI_QTD_ENTITY * PreQtdPtr,
IN EHCI_QTD_ENTITY * QtdPtr
)
/*++
Routine Description:
Link Qtds together
Arguments:
PreQtdPtr - A pointer to pre Qtd
QtdPtr - A pointer to next Qtd
Returns:
VOID
--*/
{
EHCI_QTD_HW *QtdHwPtr;
ASSERT(PreQtdPtr);
ASSERT(QtdPtr);
//
// Software link
//
PreQtdPtr->Next = QtdPtr;
QtdPtr->Prev = PreQtdPtr;
//
// Hardware link
//
QtdHwPtr = &(QtdPtr->Qtd);
PreQtdPtr->Qtd.NextQtdPointer = (UINT32) (GET_0B_TO_31B(QtdHwPtr) >> 5);
PreQtdPtr->Qtd.NextQtdTerminate = FALSE;
return ;
}
VOID
LinkQtdsToAltQtd (
IN EHCI_QTD_ENTITY * FirstQtdPtr,
IN EHCI_QTD_ENTITY * AltQtdPtr
)
/*++
Routine Description:
Link AlterQtds together
Arguments:
FirstQtdPtr - A pointer to first Qtd in the list
AltQtdPtr - A pointer to alternative Qtd
Returns:
VOID
--*/
{
EHCI_QTD_ENTITY *QtdPtr;
EHCI_QTD_HW *AltQtdHwPtr;
ASSERT(FirstQtdPtr);
ASSERT(AltQtdPtr);
AltQtdHwPtr = &(AltQtdPtr->Qtd);
QtdPtr = FirstQtdPtr;
while (NULL != QtdPtr) {
//
// Software link
//
QtdPtr->AltNext = AltQtdPtr;
//
// Hardware link
//
QtdPtr->Qtd.AltNextQtdPointer = (UINT32) (GET_0B_TO_31B(AltQtdHwPtr) >> 5);
QtdPtr->Qtd.AltNextQtdTerminate = FALSE;
QtdPtr = QtdPtr->Next;
}
return ;
}
VOID
LinkQtdToQh (
IN EHCI_QH_ENTITY *QhPtr,
IN EHCI_QTD_ENTITY *QtdPtr
)
/*++
Routine Description:
Link Qtds list to Qh
Arguments:
QhPtr - A pointer to Qh
QtdPtr - A pointer to first Qtd in the list
Returns:
VOID
--*/
{
EHCI_QTD_ENTITY *Cursor;
EHCI_QTD_HW *QtdHwPtr;
ASSERT (QhPtr);
ASSERT (QtdPtr);
QhPtr->FirstQtdPtr = QtdPtr;
if (NULL != QtdPtr->AltNext) {
QhPtr->AltQtdPtr = QtdPtr->AltNext;
}
Cursor = QtdPtr;
while (NULL != Cursor) {
Cursor->SelfQh = QhPtr;
if (NULL == Cursor->Next) {
QhPtr->LastQtdPtr = Cursor;
}
Cursor = Cursor->Next;
}
QtdHwPtr = &(QtdPtr->Qtd);
QhPtr->Qh.NextQtdPointer = (UINT32) (GET_0B_TO_31B (QtdHwPtr) >> 5);
QhPtr->Qh.NextQtdTerminate = FALSE;
QhPtr->Qh.AltNextQtdPointer = 0;
QhPtr->Qh.AltNextQtdTerminate = TRUE;
if ((QtdPtr->Qtd.PidCode == OUTPUT_PACKET_PID_CODE) &&
(QhPtr->TransferType == BULK_TRANSFER)) {
//
//Start PING first
//
QhPtr->Qh.Status |= QTD_STATUS_DO_PING;
}
return ;
}
EFI_STATUS
LinkQhToAsyncList (
IN USB2_HC_DEV *HcDev,
IN EHCI_QH_ENTITY *QhPtr
)
/*++
Routine Description:
Link Qh to Async Schedule List
Arguments:
HcDev - USB2_HC_DEV
QhPtr - A pointer to Qh
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
EFI_STATUS Status;
ASSERT (HcDev);
ASSERT (QhPtr);
//
// NULL QH created before
//
HcDev->NULLQH->Next = QhPtr;
HcDev->NULLQH->Prev = QhPtr;
QhPtr->Next = HcDev->NULLQH;
QhPtr->Prev = HcDev->NULLQH;
HcDev->NULLQH->Qh.QhHorizontalPointer = (UINT32) (GET_0B_TO_31B (&(QhPtr->Qh) >> 5));
QhPtr->Qh.QhHorizontalPointer = (UINT32) (GET_0B_TO_31B (&(HcDev->NULLQH->Qh) >> 5));
Status = SetAsyncListAddr (HcDev, HcDev->NULLQH);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto exit;
}
if (!IsAsyncScheduleEnabled (HcDev)) {
Status = EnableAsynchronousSchedule (HcDev);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto exit;
}
Status = WaitForAsyncScheduleEnable (HcDev, EHCI_GENERIC_TIMEOUT);
if (EFI_ERROR (Status)) {
DEBUG ((gEHCDebugLevel, "EHCI: WaitForAsyncScheduleEnable TimeOut"));
Status = EFI_TIMEOUT;
goto exit;
}
if (IsEhcHalted (HcDev)) {
Status = StartScheduleExecution (HcDev);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
}
}
}
exit:
return Status;
}
EFI_STATUS
UnlinkQhFromAsyncList (
IN USB2_HC_DEV *HcDev,
IN EHCI_QH_ENTITY *QhPtr
)
/*++
Routine Description:
Unlink Qh from Async Schedule List
Arguments:
HcDev - USB2_HC_DEV
QhPtr - A pointer to Qh
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
EFI_STATUS Status;
Status = EFI_SUCCESS;
ASSERT (HcDev);
ASSERT (QhPtr);
HcDev->NULLQH->Next = HcDev->NULLQH;
HcDev->NULLQH->Prev = HcDev->NULLQH;
QhPtr->Next = QhPtr;
QhPtr->Prev = QhPtr;
HcDev->NULLQH->Qh.QhHorizontalPointer = (UINT32) (GET_0B_TO_31B (&(HcDev->NULLQH->Qh) >> 5));
SetAndWaitDoorBell (HcDev, 2 * EHCI_GENERIC_TIMEOUT);
QhPtr->Qh.QhTerminate = 1;
QhPtr->Qh.QhHorizontalPointer = 0;
Status = DisableAsynchronousSchedule (HcDev);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto exit;
}
Status = WaitForAsyncScheduleDisable (HcDev, EHCI_GENERIC_TIMEOUT);
if (EFI_ERROR (Status)) {
DEBUG ((gEHCErrorLevel, "EHCI: WaitForAsyncScheduleDisable TimeOut\n"));
Status = EFI_TIMEOUT;
goto exit;
}
exit:
return Status;
}
VOID
LinkQhToPeriodicList (
IN USB2_HC_DEV *HcDev,
IN EHCI_QH_ENTITY *QhPtr
)
/*++
Routine Description:
Link Qh to Periodic Schedule List
Arguments:
HcDev - USB2_HC_DEV
QhPtr - A pointer to Qh
Returns:
VOID
--*/
{
FRAME_LIST_ENTRY *FrameEntryPtr;
EHCI_QH_ENTITY *FindQhPtr;
EHCI_QH_HW *FindQhHwPtr;
UINTN FrameIndex;
ASSERT (HcDev);
ASSERT (QhPtr);
FindQhPtr = NULL;
FindQhHwPtr = NULL;
FrameIndex = 0;
FrameEntryPtr = (FRAME_LIST_ENTRY *) HcDev->PeriodicFrameListBuffer;
QhPtr->Qh.HeadReclamationFlag = FALSE;
if (QhPtr->TransferType == ASYNC_INTERRUPT_TRANSFER) {
//
// AsyncInterruptTransfer Qh
//
//
// Link to Frame[0] List
//
if (!FrameEntryPtr->LinkTerminate) {
//
// Not Null FrameList
//
FindQhHwPtr = (EHCI_QH_HW *) GET_0B_TO_31B (FrameEntryPtr->LinkPointer << 5);
FindQhPtr = (EHCI_QH_ENTITY *) GET_QH_ENTITY_ADDR (FindQhHwPtr);
//
// FindQh is Left/Right to Qh
//
while ((NULL != FindQhPtr->Next) && (FindQhPtr->Interval > QhPtr->Interval)) {
FindQhPtr = FindQhPtr->Next;
}
if (FindQhPtr->Interval == QhPtr->Interval) {
//
// Link Qh after FindQh
//
if (NULL != FindQhPtr->Next) {
FindQhPtr->Next->Prev = QhPtr;
QhPtr->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (&(FindQhPtr->Next->Qh) >> 5);
QhPtr->Qh.SelectType = QH_SELECT_TYPE;
QhPtr->Qh.QhTerminate = FALSE;
}
FindQhPtr->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
FindQhPtr->Qh.SelectType = QH_SELECT_TYPE;
FindQhPtr->Qh.QhTerminate = FALSE;
QhPtr->Prev = FindQhPtr;
QhPtr->Next = FindQhPtr->Next;
FindQhPtr->Next = QhPtr;
} else if (FindQhPtr->Interval < QhPtr->Interval) {
//
// Link Qh before FindQh
//
if (NULL == FindQhPtr->Prev) {
//
// Qh is the First one in Frame[0] List
//
FrameEntryPtr->LinkPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
FrameEntryPtr->SelectType = QH_SELECT_TYPE;
FrameEntryPtr->LinkTerminate = FALSE;
} else {
//
// Qh is not the First one in Frame[0] List
//
FindQhPtr->Prev->Next = QhPtr;
FindQhPtr->Prev->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
FindQhPtr->Prev->Qh.SelectType = QH_SELECT_TYPE;
FindQhPtr->Prev->Qh.QhTerminate = FALSE;
}
QhPtr->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (&(FindQhPtr->Qh) >> 5);
QhPtr->Qh.SelectType = QH_SELECT_TYPE;
QhPtr->Qh.QhTerminate = FALSE;
QhPtr->Next = FindQhPtr;
QhPtr->Prev = FindQhPtr->Prev;
FindQhPtr->Prev = QhPtr;
} else {
//
// Link Qh after FindQh, Qh is the Last one
//
FindQhPtr->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
FindQhPtr->Prev->Qh.SelectType = QH_SELECT_TYPE;
FindQhPtr->Qh.QhTerminate = FALSE;
QhPtr->Prev = FindQhPtr;
QhPtr->Next = NULL;
FindQhPtr->Next = QhPtr;
}
} else {
//
// Null FrameList
//
FrameEntryPtr->LinkPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
FrameEntryPtr->SelectType = QH_SELECT_TYPE;
FrameEntryPtr->LinkTerminate = FALSE;
}
//
// Other Frame[X]
//
if (NULL == QhPtr->Prev) {
//
// Qh is the First one in Frame[0] List
//
FrameIndex += QhPtr->Interval;
while (FrameIndex < HcDev->PeriodicFrameListLength) {
FrameEntryPtr = (FRAME_LIST_ENTRY *) (FrameEntryPtr + QhPtr->Interval);
FrameEntryPtr->LinkPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
FrameEntryPtr->SelectType = QH_SELECT_TYPE;
FrameEntryPtr->LinkTerminate = FALSE;
FrameIndex += QhPtr->Interval;
}
} else if (QhPtr->Interval < QhPtr->Prev->Interval) {
//
// Qh is not the First one in Frame[0] List, and Prev.interval > Qh.interval
//
FrameIndex += QhPtr->Interval;
while (FrameIndex < HcDev->PeriodicFrameListLength) {
FrameEntryPtr = (FRAME_LIST_ENTRY *) (FrameEntryPtr + QhPtr->Interval);
if ((FrameIndex % QhPtr->Prev->Interval) != 0) {
FrameEntryPtr->LinkPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
FrameEntryPtr->SelectType = QH_SELECT_TYPE;
FrameEntryPtr->LinkTerminate = FALSE;
}
FrameIndex += QhPtr->Interval;
}
}
} else {
//
// SyncInterruptTransfer Qh
//
if (!FrameEntryPtr->LinkTerminate) {
//
// Not Null FrameList
//
FindQhHwPtr = (EHCI_QH_HW *) GET_0B_TO_31B (FrameEntryPtr->LinkPointer << 5);
FindQhPtr = (EHCI_QH_ENTITY *) GET_QH_ENTITY_ADDR (FindQhHwPtr);
//
// FindQh is Last Qh in the Asynchronous List, Link Qh after FindQh
//
while (NULL != FindQhPtr->Next) {
FindQhPtr = FindQhPtr->Next;
}
FindQhPtr->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
FindQhPtr->Qh.SelectType = QH_SELECT_TYPE;
FindQhPtr->Qh.QhTerminate = FALSE;
FindQhPtr->Next = QhPtr;
QhPtr->Prev = FindQhPtr;
} else {
//
// Null FrameList
//
FrameEntryPtr->LinkPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh) >> 5);
FrameEntryPtr->SelectType = QH_SELECT_TYPE;
FrameEntryPtr->LinkTerminate = FALSE;
}
}
return ;
}
VOID
UnlinkQhFromPeriodicList (
IN USB2_HC_DEV *HcDev,
IN EHCI_QH_ENTITY *QhPtr,
IN UINTN Interval
)
/*++
Routine Description:
Unlink Qh from Periodic Schedule List
Arguments:
HcDev - USB2_HC_DEV
QhPtr - A pointer to Qh
Interval - Interval of this periodic transfer
Returns:
VOID
--*/
{
FRAME_LIST_ENTRY *FrameEntryPtr;
UINTN FrameIndex;
FrameIndex = 0;
ASSERT (HcDev);
ASSERT (QhPtr);
FrameIndex = 0;
FrameEntryPtr = (FRAME_LIST_ENTRY *) HcDev->PeriodicFrameListBuffer;
if (QhPtr->TransferType == ASYNC_INTERRUPT_TRANSFER) {
//
// AsyncInterruptTransfer Qh
//
if (NULL == QhPtr->Prev) {
//
// Qh is the First one on Frame[0] List
//
if (NULL == QhPtr->Next) {
//
// Only one on Frame[0] List
//
while (FrameIndex < HcDev->PeriodicFrameListLength) {
FrameEntryPtr->LinkPointer = 0;
FrameEntryPtr->SelectType = 0;
FrameEntryPtr->LinkTerminate = TRUE;
FrameEntryPtr += Interval;
FrameIndex += Interval;
}
} else {
while (FrameIndex < HcDev->PeriodicFrameListLength) {
FrameEntryPtr->LinkPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Next->Qh) >> 5);
FrameEntryPtr->SelectType = QH_SELECT_TYPE;
FrameEntryPtr->LinkTerminate = FALSE;
FrameEntryPtr += Interval;
FrameIndex += Interval;
}
}
} else {
//
// Not First one on Frame[0] List
//
if (NULL == QhPtr->Next) {
//
// Qh is the Last one on Frame[0] List
//
QhPtr->Prev->Qh.QhHorizontalPointer = 0;
QhPtr->Prev->Qh.SelectType = 0;
QhPtr->Prev->Qh.QhTerminate = TRUE;
} else {
QhPtr->Prev->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (&(QhPtr->Next->Qh) >> 5);
QhPtr->Prev->Qh.SelectType = QH_SELECT_TYPE;
QhPtr->Prev->Qh.QhTerminate = FALSE;
}
if (Interval == QhPtr->Prev->Interval) {
//
// Interval is the same as Prev
// Not involed Frame[X]
//
} else {
//
// Other Frame[X]
//
while (FrameIndex < HcDev->PeriodicFrameListLength) {
if ((FrameIndex % QhPtr->Prev->Interval) != 0) {
FrameEntryPtr->LinkPointer = QhPtr->Prev->Qh.QhHorizontalPointer;
FrameEntryPtr->SelectType = QhPtr->Prev->Qh.SelectType;
FrameEntryPtr->LinkTerminate = QhPtr->Prev->Qh.QhTerminate;
}
FrameEntryPtr += Interval;
FrameIndex += Interval;
}
}
}
if (NULL != QhPtr->Next) {
QhPtr->Next->Prev = QhPtr->Prev;
}
if (NULL != QhPtr->Prev) {
QhPtr->Prev->Next = QhPtr->Next;
}
} else {
//
// SyncInterruptTransfer Qh
//
if (NULL == QhPtr->Prev) {
//
// Qh is the only one Qh on Frame[0] List
//
FrameEntryPtr->LinkPointer = 0;
FrameEntryPtr->SelectType = 0;
FrameEntryPtr->LinkTerminate = TRUE;
} else {
QhPtr->Prev->Qh.QhHorizontalPointer = 0;
QhPtr->Prev->Qh.SelectType = 0;
QhPtr->Prev->Qh.QhTerminate = TRUE;
}
if (NULL != QhPtr->Prev) {
QhPtr->Prev->Next = NULL;
}
}
return ;
}
VOID
LinkToAsyncReqeust (
IN USB2_HC_DEV *HcDev,
IN EHCI_ASYNC_REQUEST *AsyncRequestPtr
)
/*++
Routine Description:
Llink AsyncRequest Entry to Async Request List
Arguments:
HcDev - USB2_HC_DEV
AsyncRequestPtr - A pointer to Async Request Entry
Returns:
VOID
--*/
{
EHCI_ASYNC_REQUEST *CurrentPtr;
CurrentPtr = HcDev->AsyncRequestList;
HcDev->AsyncRequestList = AsyncRequestPtr;
AsyncRequestPtr->Prev = NULL;
AsyncRequestPtr->Next = CurrentPtr;
if (NULL != CurrentPtr) {
CurrentPtr->Prev = AsyncRequestPtr;
}
return ;
}
VOID
UnlinkFromAsyncReqeust (
IN USB2_HC_DEV *HcDev,
IN EHCI_ASYNC_REQUEST *AsyncRequestPtr
)
/*++
Routine Description:
Unlink AsyncRequest Entry from Async Request List
Arguments:
HcDev - USB2_HC_DEV
AsyncRequestPtr - A pointer to Async Request Entry
Returns:
VOID
--*/
{
if (NULL == AsyncRequestPtr->Prev) {
HcDev->AsyncRequestList = AsyncRequestPtr->Next;
if (NULL != AsyncRequestPtr->Next) {
AsyncRequestPtr->Next->Prev = NULL;
}
} else {
AsyncRequestPtr->Prev->Next = AsyncRequestPtr->Next;
if (NULL != AsyncRequestPtr->Next) {
AsyncRequestPtr->Next->Prev = AsyncRequestPtr->Prev;
}
}
return ;
}
VOID
SetQtdBufferPointer (
IN EHCI_QTD_HW *QtdHwPtr,
IN VOID *DataPtr,
IN UINTN DataLen
)
/*++
Routine Description:
Set data buffer pointers in Qtd
Arguments:
QtdHwPtr - A pointer to Qtd hardware structure
DataPtr - A pointer to user data buffer
DataLen - Length of the user data buffer
Returns:
VOID
--*/
{
UINTN RemainLen;
ASSERT (QtdHwPtr);
ASSERT (DataLen <= 5 * EFI_PAGE_SIZE);
RemainLen = DataLen;
//
// Allow buffer address range across 4G.
// But EFI_USB_MAX_BULK_BUFFER_NUM = 1, so don't allow
// seperate buffer array.
//
//
// Set BufferPointer0, ExtBufferPointer0 and Offset
//
QtdHwPtr->BufferPointer0 = (UINT32) (GET_0B_TO_31B (DataPtr) >> EFI_PAGE_SHIFT);
QtdHwPtr->CurrentOffset = (UINT32) (GET_0B_TO_31B (DataPtr) & EFI_PAGE_MASK);
//
// Set BufferPointer1 and ExtBufferPointer1
//
RemainLen = RemainLen > (EFI_PAGE_SIZE - QtdHwPtr->CurrentOffset) ? (RemainLen - (EFI_PAGE_SIZE - QtdHwPtr->CurrentOffset)) : 0;
if (RemainLen == 0) {
goto exit;
}
QtdHwPtr->BufferPointer1 = QtdHwPtr->BufferPointer0 + 1;
//
// Set BufferPointer2 and ExtBufferPointer2
//
RemainLen = RemainLen > EFI_PAGE_SIZE ? (RemainLen - EFI_PAGE_SIZE) : 0;
if (RemainLen == 0) {
goto exit;
}
QtdHwPtr->BufferPointer2 = QtdHwPtr->BufferPointer1 + 1;
//
// Set BufferPointer3 and ExtBufferPointer3
//
RemainLen = RemainLen > EFI_PAGE_SIZE ? (RemainLen - EFI_PAGE_SIZE) : 0;
if (RemainLen == 0) {
goto exit;
}
QtdHwPtr->BufferPointer3 = QtdHwPtr->BufferPointer2 + 1;
//
// Set BufferPointer4 and ExtBufferPointer4
//
RemainLen = RemainLen > EFI_PAGE_SIZE ? (RemainLen - EFI_PAGE_SIZE) : 0;
if (RemainLen == 0) {
goto exit;
}
QtdHwPtr->BufferPointer4 = QtdHwPtr->BufferPointer3 + 1;
exit:
return ;
}
BOOLEAN
IsQtdStatusActive (
IN EHCI_QTD_HW *HwQtdPtr
)
/*++
Routine Description:
Whether Qtd status is active or not
Arguments:
HwQtdPtr - A pointer to hardware Qtd structure
Returns:
TRUE Active
FALSE Inactive
--*/
{
UINT8 QtdStatus;
BOOLEAN Value;
QtdStatus = (UINT8) (HwQtdPtr->Status);
Value = (BOOLEAN) (QtdStatus & QTD_STATUS_ACTIVE);
//DEBUG ((gEHCErrorLevel, "EHCI: IsQtdStatusActive 0x%X, Address = %x\r\n",HwQtdPtr->Status, HwQtdPtr));
return Value;
}
BOOLEAN
IsQtdStatusHalted (
IN EHCI_QTD_HW *HwQtdPtr
)
/*++
Routine Description:
Whether Qtd status is halted or not
Arguments:
HwQtdPtr - A pointer to hardware Qtd structure
Returns:
TRUE Halted
FALSE Not halted
--*/
{
UINT8 QtdStatus;
BOOLEAN Value;
QtdStatus = (UINT8) (HwQtdPtr->Status);
Value = (BOOLEAN) (QtdStatus & QTD_STATUS_HALTED);
return Value;
}
BOOLEAN
IsQtdStatusBufferError (
IN EHCI_QTD_HW *HwQtdPtr
)
/*++
Routine Description:
Whether Qtd status is buffer error or not
Arguments:
HwQtdPtr - A pointer to hardware Qtd structure
Returns:
TRUE Buffer error
FALSE No buffer error
--*/
{
UINT8 QtdStatus;
BOOLEAN Value;
QtdStatus = (UINT8) (HwQtdPtr->Status);
Value = (BOOLEAN) (QtdStatus & QTD_STATUS_BUFFER_ERR);
return Value;
}
BOOLEAN
IsQtdStatusBabbleError (
IN EHCI_QTD_HW *HwQtdPtr
)
/*++
Routine Description:
Whether Qtd status is babble error or not
Arguments:
HwQtdPtr - A pointer to hardware Qtd structure
Returns:
TRUE Babble error
FALSE No babble error
--*/
{
UINT8 QtdStatus;
BOOLEAN Value;
QtdStatus = (UINT8) (HwQtdPtr->Status);
Value = (BOOLEAN) (QtdStatus & QTD_STATUS_BABBLE_ERR);
return Value;
}
BOOLEAN
IsQtdStatusTransactionError (
IN EHCI_QTD_HW *HwQtdPtr
)
/*++
Routine Description:
Whether Qtd status is transaction error or not
Arguments:
HwQtdPtr - A pointer to hardware Qtd structure
Returns:
TRUE Transaction error
FALSE No transaction error
--*/
{
UINT8 QtdStatus;
BOOLEAN Value;
QtdStatus = (UINT8) (HwQtdPtr->Status);
Value = (BOOLEAN) (QtdStatus & QTD_STATUS_TRANSACTION_ERR);
return Value;
}
BOOLEAN
IsDataInTransfer (
IN UINT8 EndPointAddress
)
/*++
Routine Description:
Whether is a DataIn direction transfer
Arguments:
EndPointAddress - address of the endpoint
Returns:
TRUE DataIn
FALSE DataOut
--*/
{
BOOLEAN Value;
if (EndPointAddress & 0x80) {
Value = TRUE;
} else {
Value = FALSE;
}
return Value;
}
EFI_STATUS
MapDataBuffer (
IN USB2_HC_DEV *HcDev,
IN EFI_USB_DATA_DIRECTION TransferDirection,
IN VOID *Data,
IN OUT UINTN *DataLength,
OUT UINT8 *PktId,
OUT UINT8 **DataCursor,
OUT VOID **DataMap
)
/*++
Routine Description:
Map address of user data buffer
Arguments:
HcDev - USB2_HC_DEV
TransferDirection - direction of transfer
Data - A pointer to user data buffer
DataLength - length of user data
PktId - Packte Identificaion
DataCursor - mapped address to return
DataMap - identificaion of this mapping to return
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS TempPhysicalAddr;
Status = EFI_SUCCESS;
switch (TransferDirection) {
case EfiUsbDataIn:
*PktId = INPUT_PACKET_ID;
//
// BusMasterWrite means cpu read
//
Status = HcDev->PciIo->Map (
HcDev->PciIo,
EfiPciIoOperationBusMasterWrite,
Data,
DataLength,
&TempPhysicalAddr,
DataMap
);
if (EFI_ERROR (Status)) {
DEBUG ((gEHCDebugLevel, "EHCI: MapDataBuffer Failed\n"));
Status = EFI_DEVICE_ERROR;
goto exit;
}
*DataCursor = (UINT8 *) ((UINTN) TempPhysicalAddr);
break;
case EfiUsbDataOut:
*PktId = OUTPUT_PACKET_ID;
//
// BusMasterRead means cpu write
//
Status = HcDev->PciIo->Map (
HcDev->PciIo,
EfiPciIoOperationBusMasterRead,
Data,
DataLength,
&TempPhysicalAddr,
DataMap
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto exit;
}
*DataCursor = (UINT8 *) ((UINTN) TempPhysicalAddr);
break;
case EfiUsbNoData:
*PktId = OUTPUT_PACKET_ID;
Data = NULL;
*DataLength = 0;
*DataCursor = NULL;
*DataMap = NULL;
break;
default:
Status = EFI_INVALID_PARAMETER;
}
exit:
return Status;
}
EFI_STATUS
MapRequestBuffer (
IN USB2_HC_DEV *HcDev,
IN OUT VOID *Request,
OUT UINT8 **RequestCursor,
OUT VOID **RequestMap
)
/*++
Routine Description:
Map address of request structure buffer
Arguments:
HcDev - USB2_HC_DEV
Request - A pointer to request structure
RequestCursor - Mapped address of request structure to return
RequestMap - Identificaion of this mapping to return
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
EFI_STATUS Status;
UINTN RequestLen;
EFI_PHYSICAL_ADDRESS TempPhysicalAddr;
RequestLen = sizeof (EFI_USB_DEVICE_REQUEST);
Status = HcDev->PciIo->Map (
HcDev->PciIo,
EfiPciIoOperationBusMasterRead,
(UINT8 *) Request,
(UINTN *) &RequestLen,
&TempPhysicalAddr,
RequestMap
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto exit;
}
*RequestCursor = (UINT8 *) ((UINTN) TempPhysicalAddr);
exit:
return Status;
}
EFI_STATUS
DeleteAsyncRequestTransfer (
IN USB2_HC_DEV *HcDev,
IN UINT8 DeviceAddress,
IN UINT8 EndPointAddress,
OUT UINT8 *DataToggle
)
/*++
Routine Description:
Delete all asynchronous request transfer
Arguments:
HcDev - USB2_HC_DEV
DeviceAddress - address of usb device
EndPointAddress - address of endpoint
DataToggle - stored data toggle
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
EFI_STATUS Status;
EHCI_ASYNC_REQUEST *AsyncRequestPtr;
EHCI_ASYNC_REQUEST *MatchPtr;
EHCI_QH_HW *QhHwPtr;
UINT8 EndPointNum;
if (NULL == HcDev->AsyncRequestList) {
Status = EFI_INVALID_PARAMETER;
goto exit;
}
MatchPtr = NULL;
QhHwPtr = NULL;
EndPointNum = (UINT8) (EndPointAddress & 0x0f);
AsyncRequestPtr = HcDev->AsyncRequestList;
//
// Find QH of AsyncRequest by DeviceAddress and EndPointNum
//
do {
QhHwPtr = &(AsyncRequestPtr->QhPtr->Qh);
if (QhHwPtr->DeviceAddr == DeviceAddress && QhHwPtr->EndpointNum == EndPointNum) {
MatchPtr = AsyncRequestPtr;
break;
}
AsyncRequestPtr = AsyncRequestPtr->Next;
} while (NULL != AsyncRequestPtr);
if (NULL == MatchPtr) {
Status = EFI_INVALID_PARAMETER;
goto exit;
}
Status = DisablePeriodicSchedule (HcDev);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto exit;
}
Status = WaitForPeriodicScheduleDisable (HcDev, EHCI_GENERIC_TIMEOUT);
if (EFI_ERROR (Status)) {
DEBUG ((gEHCErrorLevel, "EHCI: WaitForPeriodicScheduleDisable TimeOut\n"));
Status = EFI_TIMEOUT;
goto exit;
}
*DataToggle = (UINT8) MatchPtr->QhPtr->Qh.DataToggle;
UnlinkQhFromPeriodicList (HcDev, MatchPtr->QhPtr, MatchPtr->QhPtr->Interval);
UnlinkFromAsyncReqeust (HcDev, MatchPtr);
if (NULL == HcDev->AsyncRequestList) {
Status = StopPollingTimer (HcDev);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto exit;
}
} else {
Status = EnablePeriodicSchedule (HcDev);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto exit;
}
Status = WaitForPeriodicScheduleEnable (HcDev, EHCI_GENERIC_TIMEOUT);
if (EFI_ERROR (Status)) {
DEBUG ((gEHCErrorLevel, "EHCI: WaitForPeriodicScheduleEnable TimeOut\n"));
Status = EFI_TIMEOUT;
goto exit;
}
if (IsEhcHalted (HcDev)) {
Status = StartScheduleExecution (HcDev);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto exit;
}
}
}
DestoryQtds (HcDev, MatchPtr->QhPtr->FirstQtdPtr);
DestoryQh (HcDev, MatchPtr->QhPtr);
EhciFreePool (HcDev, (UINT8 *) MatchPtr, sizeof (EHCI_ASYNC_REQUEST));
exit:
return Status;
}
VOID
CleanUpAllAsyncRequestTransfer (
IN USB2_HC_DEV *HcDev
)
/*++
Routine Description:
Clean up all asynchronous request transfer
Arguments:
HcDev - USB2_HC_DEV
Returns:
VOID
--*/
{
EHCI_ASYNC_REQUEST *AsyncRequestPtr;
EHCI_ASYNC_REQUEST *FreePtr;
AsyncRequestPtr = NULL;
FreePtr = NULL;
StopPollingTimer (HcDev);
AsyncRequestPtr = HcDev->AsyncRequestList;
while (NULL != AsyncRequestPtr) {
FreePtr = AsyncRequestPtr;
AsyncRequestPtr = AsyncRequestPtr->Next;
UnlinkFromAsyncReqeust (HcDev, FreePtr);
UnlinkQhFromPeriodicList (HcDev, FreePtr->QhPtr, FreePtr->QhPtr->Interval);
DestoryQtds (HcDev, FreePtr->QhPtr->FirstQtdPtr);
DestoryQh (HcDev, FreePtr->QhPtr);
EhciFreePool (HcDev, (UINT8 *) FreePtr, sizeof (EHCI_ASYNC_REQUEST));
}
return ;
}
VOID
ZeroOutQhOverlay (
IN EHCI_QH_ENTITY *QhPtr
)
/*++
Routine Description:
Zero out the fields in Qh structure
Arguments:
QhPtr - A pointer to Qh structure
Returns:
VOID
--*/
{
QhPtr->Qh.CurrentQtdPointer = 0;
QhPtr->Qh.AltNextQtdPointer = 0;
QhPtr->Qh.NakCount = 0;
QhPtr->Qh.AltNextQtdTerminate = 0;
QhPtr->Qh.TotalBytes = 0;
QhPtr->Qh.InterruptOnComplete = 0;
QhPtr->Qh.CurrentPage = 0;
QhPtr->Qh.ErrorCount = 0;
QhPtr->Qh.PidCode = 0;
QhPtr->Qh.Status = 0;
QhPtr->Qh.BufferPointer0 = 0;
QhPtr->Qh.CurrentOffset = 0;
QhPtr->Qh.BufferPointer1 = 0;
QhPtr->Qh.CompleteSplitMask = 0;
QhPtr->Qh.BufferPointer2 = 0;
QhPtr->Qh.SplitBytes = 0;
QhPtr->Qh.FrameTag = 0;
QhPtr->Qh.BufferPointer3 = 0;
QhPtr->Qh.BufferPointer4 = 0;
}
VOID
UpdateAsyncRequestTransfer (
IN EHCI_ASYNC_REQUEST *AsyncRequestPtr,
IN UINT32 TransferResult,
IN UINTN ErrQtdPos
)
/*++
Routine Description:
Update asynchronous request transfer
Arguments:
AsyncRequestPtr - A pointer to async request
TransferResult - transfer result
ErrQtdPos - postion of error Qtd
Returns:
VOID
--*/
{
EHCI_QTD_ENTITY *QtdPtr;
QtdPtr = NULL;
if (EFI_USB_NOERROR == TransferResult) {
//
// Update Qh for next trigger
//
QtdPtr = AsyncRequestPtr->QhPtr->FirstQtdPtr;
//
// Update fields in Qh
//
//
// Get DataToggle from Overlay in Qh
//
// ZeroOut Overlay in Qh except DataToggle, HostController will update this field
//
ZeroOutQhOverlay (AsyncRequestPtr->QhPtr);
AsyncRequestPtr->QhPtr->Qh.NextQtdPointer = (UINT32) (GET_0B_TO_31B (&(QtdPtr->Qtd)) >> 5);
AsyncRequestPtr->QhPtr->Qh.NextQtdTerminate = FALSE;
//
// Update fields in Qtd
//
while (NULL != QtdPtr) {
QtdPtr->Qtd.TotalBytes = QtdPtr->StaticTotalBytes;
QtdPtr->Qtd.CurrentOffset = QtdPtr->StaticCurrentOffset;
QtdPtr->Qtd.CurrentPage = 0;
QtdPtr->Qtd.ErrorCount = QTD_ERROR_COUNTER;
QtdPtr->Qtd.Status = QTD_STATUS_ACTIVE;
QtdPtr->TotalBytes = QtdPtr->StaticTotalBytes;
QtdPtr = QtdPtr->Next;
}
}
return ;
}
BOOLEAN
CheckQtdsTransferResult (
IN BOOLEAN IsControl,
IN EHCI_QH_ENTITY *QhPtr,
OUT UINT32 *Result,
OUT UINTN *ErrQtdPos,
OUT UINTN *ActualLen
)
/*++
Routine Description:
Check transfer result of Qtds
Arguments:
IsControl - Is control transfer or not
QhPtr - A pointer to Qh
Result - Transfer result
ErrQtdPos - Error TD Position
ActualLen - Actual Transfer Size
Returns:
TRUE Qtds finished
FALSE Not finish
--*/
{
UINTN ActualLenPerQtd;
EHCI_QTD_ENTITY *QtdPtr;
EHCI_QTD_HW *QtdHwPtr;
BOOLEAN Value;
ASSERT (QhPtr);
ASSERT (Result);
ASSERT (ErrQtdPos);
ASSERT (ActualLen);
Value = TRUE;
QtdPtr = QhPtr->FirstQtdPtr;
QtdHwPtr = &(QtdPtr->Qtd);
while (NULL != QtdHwPtr) {
if (IsQtdStatusActive (QtdHwPtr)) {
*Result |= EFI_USB_ERR_NOTEXECUTE;
}
if (IsQtdStatusHalted (QtdHwPtr)) {
DEBUG ((gEHCErrorLevel, "EHCI: QTD_STATUS_HALTED 0x%X\n", QtdHwPtr->Status));
*Result |= EFI_USB_ERR_STALL;
}
if (IsQtdStatusBufferError (QtdHwPtr)) {
DEBUG ((gEHCErrorLevel, "EHCI: QTD_STATUS_BUFFER_ERR 0x%X\n", QtdHwPtr->Status));
*Result |= EFI_USB_ERR_BUFFER;
}
if (IsQtdStatusBabbleError (QtdHwPtr)) {
DEBUG ((gEHCErrorLevel, "EHCI: StatusBufferError 0x%X\n", QtdHwPtr->Status));
*Result |= EFI_USB_ERR_BABBLE;
}
if (IsQtdStatusTransactionError (QtdHwPtr)) {
//
//Exclude Special Case
//
if (((QtdHwPtr->Status & QTD_STATUS_HALTED) == QTD_STATUS_HALTED) ||
((QtdHwPtr->Status & QTD_STATUS_ACTIVE) == QTD_STATUS_ACTIVE) ||
((QtdHwPtr->ErrorCount != QTD_ERROR_COUNTER))) {
*Result |= EFI_USB_ERR_TIMEOUT;
DEBUG ((gEHCErrorLevel, "EHCI: QTD_STATUS_TRANSACTION_ERR: 0x%X\n", QtdHwPtr->Status));
}
}
ActualLenPerQtd = QtdPtr->TotalBytes - QtdHwPtr->TotalBytes;
QtdPtr->TotalBytes = QtdHwPtr->TotalBytes;
//
// Accumulate actual transferred data length in each DataQtd.
//
if (SETUP_PACKET_PID_CODE != QtdHwPtr->PidCode) {
*ActualLen += ActualLenPerQtd;
}
if (*Result) {
Value = FALSE;
break;
}
if ((INPUT_PACKET_PID_CODE == QtdHwPtr->PidCode)&& (QtdPtr->TotalBytes > 0)) {
//
// Short Packet: IN, Short
//
DEBUG ((gEHCDebugLevel, "EHCI: Short Packet Status: 0x%x\n", QtdHwPtr->Status));
break;
}
if (QtdPtr->Next != NULL) {
(*ErrQtdPos)++;
QtdPtr = QtdPtr->Next;
QtdHwPtr = &(QtdPtr->Qtd);
} else {
QtdHwPtr = NULL;
}
}
return Value;
}
EFI_STATUS
ExecuteTransfer (
IN USB2_HC_DEV *HcDev,
IN BOOLEAN IsControl,
IN EHCI_QH_ENTITY *QhPtr,
IN OUT UINTN *ActualLen,
OUT UINT8 *DataToggle,
IN UINTN TimeOut,
OUT UINT32 *TransferResult
)
/*++
Routine Description:
Execute Bulk or SyncInterrupt Transfer
Arguments:
HcDev - USB2_HC_DEV
IsControl - Is control transfer or not
QhPtr - A pointer to Qh
ActualLen - Actual transfered Len
DataToggle - Data Toggle
TimeOut - TimeOut threshold
TransferResult - Transfer result
Returns:
EFI_SUCCESS Sucess
EFI_DEVICE_ERROR Fail
--*/
{
EFI_STATUS Status;
UINTN ErrQtdPos;
UINTN Delay;
BOOLEAN Finished;
Status = EFI_SUCCESS;
ErrQtdPos = 0;
*TransferResult = EFI_USB_NOERROR;
*ActualLen = 0;
Finished = FALSE;
Delay = (TimeOut * STALL_1_MILLI_SECOND / 50);
do {
*TransferResult = 0;
Finished = CheckQtdsTransferResult (
IsControl,
QhPtr,
TransferResult,
&ErrQtdPos,
ActualLen
);
if (Finished) {
break;
}
//
// Qtd is inactive, which means bulk or interrupt transfer's end.
//
if (!(*TransferResult & EFI_USB_ERR_NOTEXECUTE)) {
break;
}
gBS->Stall (EHCI_SYNC_REQUEST_POLLING_TIME);
} while (--Delay);
if (EFI_USB_NOERROR != *TransferResult) {
if (0 == Delay) {
DEBUG((gEHCErrorLevel, "EHCI: QTDS TimeOut\n"));
Status = EFI_TIMEOUT;
} else {
Status = EFI_DEVICE_ERROR;
}
}
//
// Special for Bulk and Interrupt Transfer
//
*DataToggle = (UINT8) QhPtr->Qh.DataToggle;
return Status;
}
EFI_STATUS
AsyncRequestMoniter (
IN EFI_EVENT Event,
IN VOID *Context
)
/*++
Routine Description:
Interrupt transfer periodic check handler
Arguments:
Event - Interrupt event
Context - Pointer to USB2_HC_DEV
Returns:
EFI_SUCCESS Success
EFI_DEVICE_ERROR Fail
--*/
{
EFI_STATUS Status;
USB2_HC_DEV *HcDev;
EHCI_ASYNC_REQUEST *AsyncRequestPtr;
EHCI_ASYNC_REQUEST *NextPtr;
EHCI_QTD_HW *QtdHwPtr;
UINTN ErrQtdPos;
UINTN ActualLen;
UINT32 TransferResult;
UINT8 *ReceiveBuffer;
UINT8 *ProcessBuffer;
Status = EFI_SUCCESS;
QtdHwPtr = NULL;
ReceiveBuffer = NULL;
ProcessBuffer = NULL;
HcDev = (USB2_HC_DEV *) Context;
AsyncRequestPtr = HcDev->AsyncRequestList;
while (NULL != AsyncRequestPtr) {
TransferResult = 0;
ErrQtdPos = 0;
ActualLen = 0;
CheckQtdsTransferResult (
FALSE,
AsyncRequestPtr->QhPtr,
&TransferResult,
&ErrQtdPos,
&ActualLen
);
if ((TransferResult & EFI_USB_ERR_NAK) || (TransferResult & EFI_USB_ERR_NOTEXECUTE)) {
AsyncRequestPtr = AsyncRequestPtr->Next;
continue;
}
//
// Allocate memory for EHC private data structure
//
ProcessBuffer = AllocateZeroPool (ActualLen);
if (NULL == ProcessBuffer) {
Status = EFI_OUT_OF_RESOURCES;
goto exit;
}
QtdHwPtr = &(AsyncRequestPtr->QhPtr->FirstQtdPtr->Qtd);
ReceiveBuffer = (UINT8 *) GET_0B_TO_31B ((QtdHwPtr->BufferPointer0 << EFI_PAGE_SHIFT) | AsyncRequestPtr->QhPtr->FirstQtdPtr->StaticCurrentOffset);
CopyMem (
ProcessBuffer,
ReceiveBuffer,
ActualLen
);
UpdateAsyncRequestTransfer (AsyncRequestPtr, TransferResult, ErrQtdPos);
NextPtr = AsyncRequestPtr->Next;
if (EFI_USB_NOERROR == TransferResult) {
if (AsyncRequestPtr->CallBackFunc != NULL) {
(AsyncRequestPtr->CallBackFunc) (ProcessBuffer, ActualLen, AsyncRequestPtr->Context, TransferResult);
}
} else {
//
// leave error recovery to its related device driver. A common case of
// the error recovery is to re-submit the interrupt transfer.
// When an interrupt transfer is re-submitted, its position in the linked
// list is changed. It is inserted to the head of the linked list, while
// this function scans the whole list from head to tail. Thus, the
// re-submitted interrupt transfer's callback function will not be called
// again in this round.
//
if (AsyncRequestPtr->CallBackFunc != NULL) {
(AsyncRequestPtr->CallBackFunc) (NULL, 0, AsyncRequestPtr->Context, TransferResult);
}
}
if (NULL != ProcessBuffer) {
gBS->FreePool (ProcessBuffer);
}
AsyncRequestPtr = NextPtr;
}
exit:
return Status;
}