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

3073 lines
68 KiB
C
Raw Normal View History

/*++
Copyright (c) 2006, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
Module Name:
EhciSched.c
Abstract:
Revision History
--*/
#include "Ehci.h"
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, "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, "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, "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 (
EFI_EVENT_TIMER | EFI_EVENT_NOTIFY_SIGNAL,
EFI_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;
}
//
// Init fields in Qh
//
gBS->SetMem (*QhPtrPtr, sizeof (EHCI_QH_ENTITY), 0);
//
// 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;
(*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;
}
//
// Init fields in Qtd
//
gBS->SetMem (*QtdPtrPtr, sizeof (EHCI_QTD_ENTITY), 0);
//
// 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;
UINTN Xnum;
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);
}
//
// Reverse Data Toggle or not determined by parity of transactions of one qtd
//
Xnum = Translator ? GetNumberOfTransaction (SizePerQtd, EHCI_BLOCK_SIZE_WITH_TT) : GetNumberOfTransaction (SizePerQtd, EHCI_BLOCK_SIZE);
if (Xnum % 2 != 0) {
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 && 1 < 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
GetNumberOfTransaction (
IN UINTN SizeOfData,
IN UINTN SizeOfTransaction
)
/*++
Routine Description:
Number of Transactions in one Qtd
Arguments:
SizeOfData - Size of one Qtd
SizeOfTransaction - Size of one Transaction
Returns:
Number of Transactions in this Qtd
--*/
{
return ((SizeOfData & (SizeOfTransaction - 1)) ? SizeOfData / SizeOfTransaction + 1 : SizeOfData / SizeOfTransaction);
}
UINTN
GetCapacityOfQtd (
IN UINT8 *BufferCursor
)
/*++
Routine Description:
Get Size of First Qtd
Arguments:
BufferCursor - BufferCursor of the Qtd
Returns:
Size of First Qtd
--*/
{
return (EHCI_MAX_QTD_CAPACITY - (EHCI_BLOCK_SIZE * GetNumberOfTransaction (EFI_PAGE_MASK & GET_0B_TO_31B (BufferCursor), EHCI_BLOCK_SIZE)));
}
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 *) (UINTN) 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 *) (UINTN) 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;
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);
QhPtr->Qh.HeadReclamationFlag = TRUE;
Status = SetAsyncListAddr (HcDev, QhPtr);
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, "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);
if (QhPtr == QhPtr->Next) {
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, "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 *) (UINTN) 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 (((UINTN) &(FindQhPtr->Next->Qh)) >> 5);
QhPtr->Qh.SelectType = QH_SELECT_TYPE;
QhPtr->Qh.QhTerminate = FALSE;
}
FindQhPtr->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (((UINTN)&(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 (((UINTN) &(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 (((UINTN) &(QhPtr->Qh)) >> 5);
FindQhPtr->Prev->Qh.SelectType = QH_SELECT_TYPE;
FindQhPtr->Prev->Qh.QhTerminate = FALSE;
}
QhPtr->Qh.QhHorizontalPointer = (UINT32) GET_0B_TO_31B (((UINTN) &(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 (((UINTN) &(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 (((UINTN) &(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 (((UINTN) &(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 (((UINTN) &(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 *) (UINTN) 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 (((UINTN) &(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 (((UINTN) &(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 (((UINTN) &(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 (((UINTN) &(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;
RemainLen = DataLen;
ASSERT (QtdHwPtr);
//
// Set BufferPointer0, ExtBufferPointer0 and Offset
//
QtdHwPtr->BufferPointer0 = (UINT32) (GET_0B_TO_31B (DataPtr) >> 12);
QtdHwPtr->CurrentOffset = (UINT32) (GET_0B_TO_31B (DataPtr) & EFI_PAGE_MASK);
QtdHwPtr->ExtBufferPointer0 = (UINT32) GET_32B_TO_63B (DataPtr);
//
// 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;
QtdHwPtr->ExtBufferPointer1 = QtdHwPtr->ExtBufferPointer0;
//
// Set BufferPointer2 and ExtBufferPointer2
//
RemainLen = RemainLen > EFI_PAGE_SIZE ? (RemainLen - EFI_PAGE_SIZE) : 0;
if (RemainLen == 0) {
goto exit;
}
QtdHwPtr->BufferPointer2 = QtdHwPtr->BufferPointer1 + 1;
QtdHwPtr->ExtBufferPointer2 = QtdHwPtr->ExtBufferPointer0;
//
// Set BufferPointer3 and ExtBufferPointer3
//
RemainLen = RemainLen > EFI_PAGE_SIZE ? (RemainLen - EFI_PAGE_SIZE) : 0;
if (RemainLen == 0) {
goto exit;
}
QtdHwPtr->BufferPointer3 = QtdHwPtr->BufferPointer2 + 1;
QtdHwPtr->ExtBufferPointer3 = QtdHwPtr->ExtBufferPointer0;
//
// Set BufferPointer4 and ExtBufferPointer4
//
RemainLen = RemainLen > EFI_PAGE_SIZE ? (RemainLen - EFI_PAGE_SIZE) : 0;
if (RemainLen == 0) {
goto exit;
}
QtdHwPtr->BufferPointer4 = QtdHwPtr->BufferPointer3 + 1;
QtdHwPtr->ExtBufferPointer4 = QtdHwPtr->ExtBufferPointer0;
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);
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, "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 = 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, "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, "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;
QhPtr->Qh.ExtBufferPointer0 = 0;
QhPtr->Qh.ExtBufferPointer1 = 0;
QhPtr->Qh.ExtBufferPointer2 = 0;
QhPtr->Qh.ExtBufferPointer3 = 0;
QhPtr->Qh.ExtBufferPointer4 = 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)) {
*Result |= EFI_USB_ERR_STALL;
}
if (IsQtdStatusBufferError (QtdHwPtr)) {
*Result |= EFI_USB_ERR_BUFFER;
}
if (IsQtdStatusBabbleError (QtdHwPtr)) {
*Result |= EFI_USB_ERR_BABBLE;
}
if (IsQtdStatusTransactionError (QtdHwPtr)) {
*Result |= EFI_USB_ERR_TIMEOUT;
}
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 ((!IsControl) && (QtdPtr->TotalBytes > 0)) {
//
// Did something, but isn't full workload
//
break;
}
(*ErrQtdPos)++;
QtdHwPtr = GetQtdNextPointer (QtdHwPtr);
QtdPtr = (EHCI_QTD_ENTITY *) GET_QTD_ENTITY_ADDR (QtdHwPtr);
}
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;
UINTN RequireLen;
BOOLEAN Finished;
Status = EFI_SUCCESS;
ErrQtdPos = 0;
*TransferResult = EFI_USB_NOERROR;
RequireLen = *ActualLen;
*ActualLen = 0;
Finished = FALSE;
Delay = (TimeOut * STALL_1_MILLI_SECOND / 50) + 1;
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) {
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_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 *) (UINTN) GET_0B_TO_31B ((QtdHwPtr->BufferPointer0 << 12) | AsyncRequestPtr->QhPtr->FirstQtdPtr->StaticCurrentOffset);
CopyMem (
ProcessBuffer,
ReceiveBuffer,
ActualLen
);
UpdateAsyncRequestTransfer (AsyncRequestPtr, TransferResult, ErrQtdPos);
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 = AsyncRequestPtr->Next;
}
exit:
return Status;
}