mirror of https://github.com/acidanthera/audk.git
3241 lines
71 KiB
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;
|
|
}
|
|
|