2 OHCI transfer scheduling routines.
4 Copyright (c) 2013-2015 Intel Corporation.
6 SPDX-License-Identifier: BSD-2-Clause-Patent
15 Add an item of interrupt context
17 @param Ohc UHC private data
18 @param NewEntry New entry to add
20 @retval EFI_SUCCESS Item successfully added
24 OhciAddInterruptContextEntry (
25 IN USB_OHCI_HC_DEV
*Ohc
,
26 IN INTERRUPT_CONTEXT_ENTRY
*NewEntry
29 INTERRUPT_CONTEXT_ENTRY
*Entry
;
32 OriginalTPL
= gBS
->RaiseTPL (TPL_NOTIFY
);
34 if (Ohc
->InterruptContextList
== NULL
) {
35 Ohc
->InterruptContextList
= NewEntry
;
37 Entry
= Ohc
->InterruptContextList
;
38 while (Entry
->NextEntry
!= NULL
) {
39 Entry
= Entry
->NextEntry
;
41 Entry
->NextEntry
= NewEntry
;
44 gBS
->RestoreTPL (OriginalTPL
);
52 Free a interrupt context entry
54 @param Ohc UHC private data
55 @param Entry Pointer to an interrupt context entry
57 @retval EFI_SUCCESS Entry freed
58 @retval EFI_INVALID_PARAMETER Entry is NULL
62 OhciFreeInterruptContextEntry (
63 IN USB_OHCI_HC_DEV
*Ohc
,
64 IN INTERRUPT_CONTEXT_ENTRY
*Entry
69 return EFI_INVALID_PARAMETER
;
71 if (Entry
->UCBufferMapping
!= NULL
) {
72 Ohc
->PciIo
->Unmap(Ohc
->PciIo
, Entry
->UCBufferMapping
);
74 if (Entry
->UCBuffer
!= NULL
) {
75 FreePool(Entry
->UCBuffer
);
77 while (Entry
->DataTd
) {
79 Entry
->DataTd
= (TD_DESCRIPTOR
*)(UINTN
)(Entry
->DataTd
->NextTDPointer
);
80 UsbHcFreeMem(Ohc
->MemPool
, Td
, sizeof(TD_DESCRIPTOR
));
89 Free entries match the device address and endpoint address
91 @Param Ohc UHC private date
92 @Param DeviceAddress Item to free must match this device address
93 @Param EndPointAddress Item to free must match this end point address
94 @Param DataToggle DataToggle for output
96 @retval EFI_SUCCESS Items match the requirement removed
100 OhciFreeInterruptContext(
101 IN USB_OHCI_HC_DEV
*Ohc
,
102 IN UINT8 DeviceAddress
,
103 IN UINT8 EndPointAddress
,
104 OUT UINT8
*DataToggle
107 INTERRUPT_CONTEXT_ENTRY
*Entry
;
108 INTERRUPT_CONTEXT_ENTRY
*TempEntry
;
112 OriginalTPL
= gBS
->RaiseTPL (TPL_NOTIFY
);
114 while (Ohc
->InterruptContextList
!= NULL
&&
115 Ohc
->InterruptContextList
->DeviceAddress
== DeviceAddress
&&
116 Ohc
->InterruptContextList
->EndPointAddress
== EndPointAddress
) {
117 TempEntry
= Ohc
->InterruptContextList
;
118 Ohc
->InterruptContextList
= Ohc
->InterruptContextList
->NextEntry
;
119 if (DataToggle
!= NULL
) {
120 *DataToggle
= (UINT8
) (TempEntry
->DataTd
->Word0
.DataToggle
& 0x1);
122 OhciFreeInterruptContextEntry (Ohc
, TempEntry
);
125 Entry
= Ohc
->InterruptContextList
;
127 gBS
->RestoreTPL (OriginalTPL
);
130 while (Entry
->NextEntry
!= NULL
) {
131 if (Entry
->NextEntry
->DeviceAddress
== DeviceAddress
&&
132 Entry
->NextEntry
->EndPointAddress
== EndPointAddress
) {
133 TempEntry
= Entry
->NextEntry
;
134 Entry
->NextEntry
= Entry
->NextEntry
->NextEntry
;
135 if (DataToggle
!= NULL
) {
136 *DataToggle
= (UINT8
) (TempEntry
->DataTd
->Word0
.DataToggle
& 0x1);
138 OhciFreeInterruptContextEntry (Ohc
, TempEntry
);
140 Entry
= Entry
->NextEntry
;
144 gBS
->RestoreTPL (OriginalTPL
);
152 Convert Error code from OHCI format to EFI format
154 @Param ErrorCode ErrorCode in OHCI format
156 @retval ErrorCode in EFI format
164 UINT32 TransferResult
;
168 TransferResult
= EFI_USB_NOERROR
;
171 case TD_TOBE_PROCESSED
:
172 case TD_TOBE_PROCESSED_2
:
173 TransferResult
= EFI_USB_ERR_NOTEXECUTE
;
176 case TD_DEVICE_STALL
:
177 TransferResult
= EFI_USB_ERR_STALL
;
180 case TD_BUFFER_OVERRUN
:
181 case TD_BUFFER_UNDERRUN
:
182 TransferResult
= EFI_USB_ERR_BUFFER
;
186 TransferResult
= EFI_USB_ERR_CRC
;
190 TransferResult
= EFI_USB_ERR_TIMEOUT
;
193 case TD_BITSTUFFING_ERROR
:
194 TransferResult
= EFI_USB_ERR_BITSTUFF
;
198 TransferResult
= EFI_USB_ERR_SYSTEM
;
201 return TransferResult
;
209 @Param Ohc UHC private data
210 @Param Td TD_DESCRIPTOR
211 @Param Result Result to return
213 @retval TRUE means OK
214 @retval FLASE means Error or Short packet
218 OhciCheckTDsResults (
219 IN USB_OHCI_HC_DEV
*Ohc
,
220 IN TD_DESCRIPTOR
*Td
,
224 UINT32 TdCompletionCode
;
226 *Result
= EFI_USB_NOERROR
;
229 TdCompletionCode
= Td
->Word0
.ConditionCode
;
231 *Result
|= ConvertErrorCode(TdCompletionCode
);
233 // if any error encountered, stop processing the left TDs.
239 Td
= (TD_DESCRIPTOR
*)(UINTN
)(Td
->NextTDPointer
);
248 Check the task status on an ED
250 @Param Ed Pointer to the ED task that TD hooked on
251 @Param HeadTd TD header for current transaction
253 @retval Task Status Code
259 IN ED_DESCRIPTOR
*Ed
,
260 IN TD_DESCRIPTOR
*HeadTd
,
261 OUT OHCI_ED_RESULT
*EdResult
264 while(HeadTd
!= NULL
) {
265 if (HeadTd
->NextTDPointer
== 0) {
268 if (HeadTd
->Word0
.ConditionCode
!= 0) {
269 return HeadTd
->Word0
.ConditionCode
;
271 EdResult
->NextToggle
= ((UINT8
)(HeadTd
->Word0
.DataToggle
) & BIT0
) ^ BIT0
;
272 HeadTd
= (TD_DESCRIPTOR
*)(UINTN
)(HeadTd
->NextTDPointer
);
274 if (OhciGetEDField (Ed
, ED_TDHEAD_PTR
) != OhciGetEDField (Ed
, ED_TDTAIL_PTR
)) {
275 return TD_TOBE_PROCESSED
;
282 Check the task status
284 @Param Ohc UHC private data
285 @Param ListType Pipe type
286 @Param Ed Pointer to the ED task hooked on
287 @Param HeadTd Head of TD corresponding to the task
288 @Param ErrorCode return the ErrorCode
290 @retval EFI_SUCCESS Task done
291 @retval EFI_NOT_READY Task on processing
292 @retval EFI_DEVICE_ERROR Some error occured
297 IN USB_OHCI_HC_DEV
*Ohc
,
298 IN DESCRIPTOR_LIST_TYPE ListType
,
299 IN ED_DESCRIPTOR
*Ed
,
300 IN TD_DESCRIPTOR
*HeadTd
,
301 OUT OHCI_ED_RESULT
*EdResult
304 EdResult
->ErrorCode
= TD_TOBE_PROCESSED
;
308 if (OhciGetHcCommandStatus (Ohc
, CONTROL_LIST_FILLED
) != 0) {
309 return EFI_NOT_READY
;
313 if (OhciGetHcCommandStatus (Ohc
, BULK_LIST_FILLED
) != 0) {
314 return EFI_NOT_READY
;
321 EdResult
->ErrorCode
= CheckEDStatus (Ed
, HeadTd
, EdResult
);
323 if (EdResult
->ErrorCode
== TD_NO_ERROR
) {
325 } else if (EdResult
->ErrorCode
== TD_TOBE_PROCESSED
) {
326 return EFI_NOT_READY
;
328 return EFI_DEVICE_ERROR
;
335 Convert TD condition code to Efi Status
337 @Param ConditionCode Condition code to convert
339 @retval EFI_SUCCESS No error occured
340 @retval EFI_NOT_READY TD still on processing
341 @retval EFI_DEVICE_ERROR Error occured in processing TD
346 OhciTDConditionCodeToStatus (
347 IN UINT32 ConditionCode
350 if (ConditionCode
== TD_NO_ERROR
) {
354 if (ConditionCode
== TD_TOBE_PROCESSED
) {
355 return EFI_NOT_READY
;
358 return EFI_DEVICE_ERROR
;
363 Invoke callbacks hooked on done TDs
365 @Param Entry Interrupt transfer transaction information data structure
366 @Param Context Ohc private data
371 OhciInvokeInterruptCallBack(
372 IN INTERRUPT_CONTEXT_ENTRY
*Entry
,
376 //Generally speaking, Keyboard driver should not
377 //check the Keyboard buffer if an error happens, it will be robust
378 //if we NULLed the buffer once error happens
380 Entry
->CallBackFunction (
387 Entry
->CallBackFunction (
388 (VOID
*)(UINTN
)(Entry
->DataTd
->DataBuffer
),
389 Entry
->DataTd
->ActualSendLength
,
399 Timer to submit periodic interrupt transfer, and invoke callbacks hooked on done TDs
401 @param Event Event handle
402 @param Context Device private data
414 USB_OHCI_HC_DEV
*Ohc
;
415 INTERRUPT_CONTEXT_ENTRY
*Entry
;
416 INTERRUPT_CONTEXT_ENTRY
*PreEntry
;
418 TD_DESCRIPTOR
*DataTd
;
419 TD_DESCRIPTOR
*HeadTd
;
425 Ohc
= (USB_OHCI_HC_DEV
*) Context
;
426 OriginalTPL
= gBS
->RaiseTPL(TPL_NOTIFY
);
428 Entry
= Ohc
->InterruptContextList
;
431 while(Entry
!= NULL
) {
433 OhciCheckTDsResults(Ohc
, Entry
->DataTd
, &Result
);
434 if (((Result
& EFI_USB_ERR_STALL
) == EFI_USB_ERR_STALL
) ||
435 ((Result
& EFI_USB_ERR_NOTEXECUTE
) == EFI_USB_ERR_NOTEXECUTE
)) {
437 Entry
= Entry
->NextEntry
;
441 if (Entry
->CallBackFunction
!= NULL
) {
442 OhciInvokeInterruptCallBack (Entry
, Result
);
443 if (Ohc
->InterruptContextList
== NULL
) {
444 gBS
->RestoreTPL (OriginalTPL
);
448 if (Entry
->IsPeriodic
) {
451 HeadTd
= Entry
->DataTd
;
454 if (Result
== EFI_USB_NOERROR
) {
456 // Update toggle if there is no error, and re-submit the interrupt Ed&Tds
458 if ((Ed
!= NULL
) && (DataTd
!= NULL
)) {
462 // From hcir1_0a.pdf 4.2.2
463 // ToggleCarry:This bit is the data toggle carry bit,
464 // Whenever a TD is retired, this bit is written to
465 // contain the last data toggle value(LSb of data Toggel
466 // file) from the retired TD.
467 // This field is not used for Isochronous Endpoints
472 Toggle
= (UINT8
) OhciGetEDField (Ed
, ED_DTTOGGLE
);
473 while(DataTd
!= NULL
) {
474 if (DataTd
->NextTDPointer
== 0) {
475 DataTd
->Word0
.DataToggle
= 0;
478 OhciSetTDField (DataTd
, TD_DT_TOGGLE
, Toggle
);
480 DataTd
= (TD_DESCRIPTOR
*)(UINTN
)(DataTd
->NextTDPointer
);
484 // HC will only update DataToggle, ErrorCount, ConditionCode
485 // CurrentBufferPointer & NextTD, so we only need to update
486 // them once we want to active them again
489 while (DataTd
!= NULL
) {
490 if (DataTd
->NextTDPointer
== 0) {
491 OhciSetTDField (DataTd
, TD_ERROR_CNT
| TD_COND_CODE
| TD_CURR_BUFFER_PTR
| TD_NEXT_PTR
, 0);
494 OhciSetTDField (DataTd
, TD_ERROR_CNT
, 0);
495 OhciSetTDField (DataTd
, TD_COND_CODE
, TD_TOBE_PROCESSED
);
496 DataTd
->NextTD
= DataTd
->NextTDPointer
;
497 DataTd
->CurrBufferPointer
= DataTd
->DataBuffer
;
498 DataTd
= (TD_DESCRIPTOR
*)(UINTN
)(DataTd
->NextTDPointer
);
501 // Active current Ed,Td
503 // HC will only update Halted, ToggleCarry & TDQueueHeadPointer,
504 // So we only need to update them once we want to active them again.
506 if ((Ed
!= NULL
) && (DataTd
!= NULL
)) {
507 Ed
->Word2
.TdHeadPointer
= (UINT32
)((UINTN
)HeadTd
>>4);
508 OhciSetEDField (Ed
, ED_HALTED
| ED_DTTOGGLE
, 0);
513 if (PreEntry
== NULL
) {
514 Ohc
->InterruptContextList
= Entry
->NextEntry
;
517 PreEntry
->NextEntry
= Entry
->NextEntry
;
519 OhciFreeInterruptContextEntry (Ohc
, PreEntry
);
520 gBS
->RestoreTPL (OriginalTPL
);
524 Entry
= Entry
->NextEntry
;
526 gBS
->RestoreTPL (OriginalTPL
);