--- /dev/null
+/** @file\r
+OHCI transfer scheduling routines.\r
+\r
+Copyright (c) 2013-2015 Intel Corporation.\r
+\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+\r
+#include "Ohci.h"\r
+\r
+/**\r
+\r
+ Add an item of interrupt context\r
+\r
+ @param Ohc UHC private data\r
+ @param NewEntry New entry to add\r
+\r
+ @retval EFI_SUCCESS Item successfully added\r
+\r
+**/\r
+EFI_STATUS\r
+OhciAddInterruptContextEntry (\r
+ IN USB_OHCI_HC_DEV *Ohc,\r
+ IN INTERRUPT_CONTEXT_ENTRY *NewEntry\r
+ )\r
+{\r
+ INTERRUPT_CONTEXT_ENTRY *Entry;\r
+ EFI_TPL OriginalTPL;\r
+\r
+ OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);\r
+\r
+ if (Ohc->InterruptContextList == NULL) {\r
+ Ohc->InterruptContextList = NewEntry;\r
+ } else {\r
+ Entry = Ohc->InterruptContextList;\r
+ while (Entry->NextEntry != NULL) {\r
+ Entry = Entry->NextEntry;\r
+ }\r
+ Entry->NextEntry = NewEntry;\r
+ }\r
+\r
+ gBS->RestoreTPL (OriginalTPL);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Free a interrupt context entry\r
+\r
+ @param Ohc UHC private data\r
+ @param Entry Pointer to an interrupt context entry\r
+\r
+ @retval EFI_SUCCESS Entry freed\r
+ @retval EFI_INVALID_PARAMETER Entry is NULL\r
+\r
+**/\r
+EFI_STATUS\r
+OhciFreeInterruptContextEntry (\r
+ IN USB_OHCI_HC_DEV *Ohc,\r
+ IN INTERRUPT_CONTEXT_ENTRY *Entry\r
+ )\r
+{\r
+ TD_DESCRIPTOR *Td;\r
+ if (Entry == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if (Entry->UCBufferMapping != NULL) {\r
+ Ohc->PciIo->Unmap(Ohc->PciIo, Entry->UCBufferMapping);\r
+ }\r
+ if (Entry->UCBuffer != NULL) {\r
+ FreePool(Entry->UCBuffer);\r
+ }\r
+ while (Entry->DataTd) {\r
+ Td = Entry->DataTd;\r
+ Entry->DataTd = (TD_DESCRIPTOR *)(UINTN)(Entry->DataTd->NextTDPointer);\r
+ UsbHcFreeMem(Ohc->MemPool, Td, sizeof(TD_DESCRIPTOR));\r
+ }\r
+ FreePool(Entry);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Free entries match the device address and endpoint address\r
+\r
+ @Param Ohc UHC private date\r
+ @Param DeviceAddress Item to free must match this device address\r
+ @Param EndPointAddress Item to free must match this end point address\r
+ @Param DataToggle DataToggle for output\r
+\r
+ @retval EFI_SUCCESS Items match the requirement removed\r
+\r
+**/\r
+EFI_STATUS\r
+OhciFreeInterruptContext(\r
+ IN USB_OHCI_HC_DEV *Ohc,\r
+ IN UINT8 DeviceAddress,\r
+ IN UINT8 EndPointAddress,\r
+ OUT UINT8 *DataToggle\r
+ )\r
+{\r
+ INTERRUPT_CONTEXT_ENTRY *Entry;\r
+ INTERRUPT_CONTEXT_ENTRY *TempEntry;\r
+ EFI_TPL OriginalTPL;\r
+\r
+\r
+ OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);\r
+\r
+ while (Ohc->InterruptContextList != NULL &&\r
+ Ohc->InterruptContextList->DeviceAddress == DeviceAddress &&\r
+ Ohc->InterruptContextList->EndPointAddress == EndPointAddress) {\r
+ TempEntry = Ohc->InterruptContextList;\r
+ Ohc->InterruptContextList = Ohc->InterruptContextList->NextEntry;\r
+ if (DataToggle != NULL) {\r
+ *DataToggle = (UINT8) (TempEntry->DataTd->Word0.DataToggle & 0x1);\r
+ }\r
+ OhciFreeInterruptContextEntry (Ohc, TempEntry);\r
+ }\r
+\r
+ Entry = Ohc->InterruptContextList;\r
+ if (Entry == NULL) {\r
+ gBS->RestoreTPL (OriginalTPL);\r
+ return EFI_SUCCESS;\r
+ }\r
+ while (Entry->NextEntry != NULL) {\r
+ if (Entry->NextEntry->DeviceAddress == DeviceAddress &&\r
+ Entry->NextEntry->EndPointAddress == EndPointAddress) {\r
+ TempEntry = Entry->NextEntry;\r
+ Entry->NextEntry = Entry->NextEntry->NextEntry;\r
+ if (DataToggle != NULL) {\r
+ *DataToggle = (UINT8) (TempEntry->DataTd->Word0.DataToggle & 0x1);\r
+ }\r
+ OhciFreeInterruptContextEntry (Ohc, TempEntry);\r
+ } else {\r
+ Entry = Entry->NextEntry;\r
+ }\r
+ }\r
+\r
+ gBS->RestoreTPL (OriginalTPL);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Convert Error code from OHCI format to EFI format\r
+\r
+ @Param ErrorCode ErrorCode in OHCI format\r
+\r
+ @retval ErrorCode in EFI format\r
+\r
+**/\r
+UINT32\r
+ConvertErrorCode (\r
+ IN UINT32 ErrorCode\r
+ )\r
+{\r
+ UINT32 TransferResult;\r
+\r
+ switch (ErrorCode) {\r
+ case TD_NO_ERROR:\r
+ TransferResult = EFI_USB_NOERROR;\r
+ break;\r
+\r
+ case TD_TOBE_PROCESSED:\r
+ case TD_TOBE_PROCESSED_2:\r
+ TransferResult = EFI_USB_ERR_NOTEXECUTE;\r
+ break;\r
+\r
+ case TD_DEVICE_STALL:\r
+ TransferResult = EFI_USB_ERR_STALL;\r
+ break;\r
+\r
+ case TD_BUFFER_OVERRUN:\r
+ case TD_BUFFER_UNDERRUN:\r
+ TransferResult = EFI_USB_ERR_BUFFER;\r
+ break;\r
+\r
+ case TD_CRC_ERROR:\r
+ TransferResult = EFI_USB_ERR_CRC;\r
+ break;\r
+\r
+ case TD_NO_RESPONSE:\r
+ TransferResult = EFI_USB_ERR_TIMEOUT;\r
+ break;\r
+\r
+ case TD_BITSTUFFING_ERROR:\r
+ TransferResult = EFI_USB_ERR_BITSTUFF;\r
+ break;\r
+\r
+ default:\r
+ TransferResult = EFI_USB_ERR_SYSTEM;\r
+ }\r
+\r
+ return TransferResult;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Check TDs Results\r
+\r
+ @Param Ohc UHC private data\r
+ @Param Td TD_DESCRIPTOR\r
+ @Param Result Result to return\r
+\r
+ @retval TRUE means OK\r
+ @retval FLASE means Error or Short packet\r
+\r
+**/\r
+BOOLEAN\r
+OhciCheckTDsResults (\r
+ IN USB_OHCI_HC_DEV *Ohc,\r
+ IN TD_DESCRIPTOR *Td,\r
+ OUT UINT32 *Result\r
+ )\r
+{\r
+ UINT32 TdCompletionCode;\r
+\r
+ *Result = EFI_USB_NOERROR;\r
+\r
+ while (Td) {\r
+ TdCompletionCode = Td->Word0.ConditionCode;\r
+\r
+ *Result |= ConvertErrorCode(TdCompletionCode);\r
+ //\r
+ // if any error encountered, stop processing the left TDs.\r
+ //\r
+ if (*Result) {\r
+ return FALSE;\r
+ }\r
+\r
+ Td = (TD_DESCRIPTOR *)(UINTN)(Td->NextTDPointer);\r
+ }\r
+ return TRUE;\r
+\r
+}\r
+\r
+\r
+/**\r
+\r
+ Check the task status on an ED\r
+\r
+ @Param Ed Pointer to the ED task that TD hooked on\r
+ @Param HeadTd TD header for current transaction\r
+\r
+ @retval Task Status Code\r
+\r
+**/\r
+\r
+UINT32\r
+CheckEDStatus (\r
+ IN ED_DESCRIPTOR *Ed,\r
+ IN TD_DESCRIPTOR *HeadTd,\r
+ OUT OHCI_ED_RESULT *EdResult\r
+ )\r
+{\r
+ while(HeadTd != NULL) {\r
+ if (HeadTd->NextTDPointer == 0) {\r
+ return TD_NO_ERROR;\r
+ }\r
+ if (HeadTd->Word0.ConditionCode != 0) {\r
+ return HeadTd->Word0.ConditionCode;\r
+ }\r
+ EdResult->NextToggle = ((UINT8)(HeadTd->Word0.DataToggle) & BIT0) ^ BIT0;\r
+ HeadTd = (TD_DESCRIPTOR *)(UINTN)(HeadTd->NextTDPointer);\r
+ }\r
+ if (OhciGetEDField (Ed, ED_TDHEAD_PTR) != OhciGetEDField (Ed, ED_TDTAIL_PTR)) {\r
+ return TD_TOBE_PROCESSED;\r
+ }\r
+ return TD_NO_ERROR;\r
+}\r
+\r
+/**\r
+\r
+ Check the task status\r
+\r
+ @Param Ohc UHC private data\r
+ @Param ListType Pipe type\r
+ @Param Ed Pointer to the ED task hooked on\r
+ @Param HeadTd Head of TD corresponding to the task\r
+ @Param ErrorCode return the ErrorCode\r
+\r
+ @retval EFI_SUCCESS Task done\r
+ @retval EFI_NOT_READY Task on processing\r
+ @retval EFI_DEVICE_ERROR Some error occured\r
+\r
+**/\r
+EFI_STATUS\r
+CheckIfDone (\r
+ IN USB_OHCI_HC_DEV *Ohc,\r
+ IN DESCRIPTOR_LIST_TYPE ListType,\r
+ IN ED_DESCRIPTOR *Ed,\r
+ IN TD_DESCRIPTOR *HeadTd,\r
+ OUT OHCI_ED_RESULT *EdResult\r
+ )\r
+{\r
+ EdResult->ErrorCode = TD_TOBE_PROCESSED;\r
+\r
+ switch (ListType) {\r
+ case CONTROL_LIST:\r
+ if (OhciGetHcCommandStatus (Ohc, CONTROL_LIST_FILLED) != 0) {\r
+ return EFI_NOT_READY;\r
+ }\r
+ break;\r
+ case BULK_LIST:\r
+ if (OhciGetHcCommandStatus (Ohc, BULK_LIST_FILLED) != 0) {\r
+ return EFI_NOT_READY;\r
+ }\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+\r
+ EdResult->ErrorCode = CheckEDStatus (Ed, HeadTd, EdResult);\r
+\r
+ if (EdResult->ErrorCode == TD_NO_ERROR) {\r
+ return EFI_SUCCESS;\r
+ } else if (EdResult->ErrorCode == TD_TOBE_PROCESSED) {\r
+ return EFI_NOT_READY;\r
+ } else {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+}\r
+\r
+\r
+/**\r
+\r
+ Convert TD condition code to Efi Status\r
+\r
+ @Param ConditionCode Condition code to convert\r
+\r
+ @retval EFI_SUCCESS No error occured\r
+ @retval EFI_NOT_READY TD still on processing\r
+ @retval EFI_DEVICE_ERROR Error occured in processing TD\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+OhciTDConditionCodeToStatus (\r
+ IN UINT32 ConditionCode\r
+ )\r
+{\r
+ if (ConditionCode == TD_NO_ERROR) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (ConditionCode == TD_TOBE_PROCESSED) {\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ return EFI_DEVICE_ERROR;\r
+}\r
+\r
+/**\r
+\r
+ Invoke callbacks hooked on done TDs\r
+\r
+ @Param Entry Interrupt transfer transaction information data structure\r
+ @Param Context Ohc private data\r
+\r
+**/\r
+\r
+VOID\r
+OhciInvokeInterruptCallBack(\r
+ IN INTERRUPT_CONTEXT_ENTRY *Entry,\r
+ IN UINT32 Result\r
+)\r
+{\r
+ //Generally speaking, Keyboard driver should not\r
+ //check the Keyboard buffer if an error happens, it will be robust\r
+ //if we NULLed the buffer once error happens\r
+ if (Result) {\r
+ Entry->CallBackFunction (\r
+ NULL,\r
+ 0,\r
+ Entry->Context,\r
+ Result\r
+ );\r
+ }else{\r
+ Entry->CallBackFunction (\r
+ (VOID *)(UINTN)(Entry->DataTd->DataBuffer),\r
+ Entry->DataTd->ActualSendLength,\r
+ Entry->Context,\r
+ Result\r
+ );\r
+ }\r
+}\r
+\r
+\r
+/**\r
+\r
+ Timer to submit periodic interrupt transfer, and invoke callbacks hooked on done TDs\r
+\r
+ @param Event Event handle\r
+ @param Context Device private data\r
+\r
+**/\r
+\r
+VOID\r
+EFIAPI\r
+OhciHouseKeeper (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+\r
+ USB_OHCI_HC_DEV *Ohc;\r
+ INTERRUPT_CONTEXT_ENTRY *Entry;\r
+ INTERRUPT_CONTEXT_ENTRY *PreEntry;\r
+ ED_DESCRIPTOR *Ed;\r
+ TD_DESCRIPTOR *DataTd;\r
+ TD_DESCRIPTOR *HeadTd;\r
+\r
+ UINT8 Toggle;\r
+ EFI_TPL OriginalTPL;\r
+ UINT32 Result;\r
+\r
+ Ohc = (USB_OHCI_HC_DEV *) Context;\r
+ OriginalTPL = gBS->RaiseTPL(TPL_NOTIFY);\r
+\r
+ Entry = Ohc->InterruptContextList;\r
+ PreEntry = NULL;\r
+\r
+ while(Entry != NULL) {\r
+\r
+ OhciCheckTDsResults(Ohc, Entry->DataTd, &Result );\r
+ if (((Result & EFI_USB_ERR_STALL) == EFI_USB_ERR_STALL) ||\r
+ ((Result & EFI_USB_ERR_NOTEXECUTE) == EFI_USB_ERR_NOTEXECUTE)) {\r
+ PreEntry = Entry;\r
+ Entry = Entry->NextEntry;\r
+ continue;\r
+ }\r
+\r
+ if (Entry->CallBackFunction != NULL) {\r
+ OhciInvokeInterruptCallBack (Entry, Result);\r
+ if (Ohc->InterruptContextList == NULL) {\r
+ gBS->RestoreTPL (OriginalTPL);\r
+ return;\r
+ }\r
+ }\r
+ if (Entry->IsPeriodic) {\r
+\r
+ Ed = Entry->Ed;\r
+ HeadTd = Entry->DataTd;\r
+ DataTd = HeadTd;\r
+ Toggle = 0;\r
+ if (Result == EFI_USB_NOERROR) {\r
+ //\r
+ // Update toggle if there is no error, and re-submit the interrupt Ed&Tds\r
+ //\r
+ if ((Ed != NULL) && (DataTd != NULL)) {\r
+ Ed->Word0.Skip = 1;\r
+ }\r
+ //\r
+ // From hcir1_0a.pdf 4.2.2\r
+ // ToggleCarry:This bit is the data toggle carry bit,\r
+ // Whenever a TD is retired, this bit is written to\r
+ // contain the last data toggle value(LSb of data Toggel\r
+ // file) from the retired TD.\r
+ // This field is not used for Isochronous Endpoints\r
+ //\r
+ if (Ed == NULL) {\r
+ return;\r
+ }\r
+ Toggle = (UINT8) OhciGetEDField (Ed, ED_DTTOGGLE);\r
+ while(DataTd != NULL) {\r
+ if (DataTd->NextTDPointer == 0) {\r
+ DataTd->Word0.DataToggle = 0;\r
+ break;\r
+ } else {\r
+ OhciSetTDField (DataTd, TD_DT_TOGGLE, Toggle);\r
+ }\r
+ DataTd = (TD_DESCRIPTOR *)(UINTN)(DataTd->NextTDPointer);\r
+ Toggle ^= 1;\r
+ }\r
+ //\r
+ // HC will only update DataToggle, ErrorCount, ConditionCode\r
+ // CurrentBufferPointer & NextTD, so we only need to update\r
+ // them once we want to active them again\r
+ //\r
+ DataTd = HeadTd;\r
+ while (DataTd != NULL) {\r
+ if (DataTd->NextTDPointer == 0) {\r
+ OhciSetTDField (DataTd, TD_ERROR_CNT | TD_COND_CODE | TD_CURR_BUFFER_PTR | TD_NEXT_PTR, 0);\r
+ break;\r
+ }\r
+ OhciSetTDField (DataTd, TD_ERROR_CNT, 0);\r
+ OhciSetTDField (DataTd, TD_COND_CODE, TD_TOBE_PROCESSED);\r
+ DataTd->NextTD = DataTd->NextTDPointer;\r
+ DataTd->CurrBufferPointer = DataTd->DataBuffer;\r
+ DataTd = (TD_DESCRIPTOR *)(UINTN)(DataTd->NextTDPointer);\r
+ }\r
+ //\r
+ // Active current Ed,Td\r
+ //\r
+ // HC will only update Halted, ToggleCarry & TDQueueHeadPointer,\r
+ // So we only need to update them once we want to active them again.\r
+ //\r
+ if ((Ed != NULL) && (DataTd != NULL)) {\r
+ Ed->Word2.TdHeadPointer = (UINT32)((UINTN)HeadTd>>4);\r
+ OhciSetEDField (Ed, ED_HALTED | ED_DTTOGGLE, 0);\r
+ Ed->Word0.Skip = 0;\r
+ }\r
+ }\r
+ } else {\r
+ if (PreEntry == NULL) {\r
+ Ohc->InterruptContextList = Entry->NextEntry;\r
+ } else {\r
+ PreEntry = Entry;\r
+ PreEntry->NextEntry = Entry->NextEntry;\r
+ }\r
+ OhciFreeInterruptContextEntry (Ohc, PreEntry);\r
+ gBS->RestoreTPL (OriginalTPL);\r
+ return;\r
+ }\r
+ PreEntry = Entry;\r
+ Entry = Entry->NextEntry;\r
+ }\r
+ gBS->RestoreTPL (OriginalTPL);\r
+}\r
+\r