| /*++ | |
| 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; | |
| } | |