]> git.proxmox.com Git - mirror_edk2.git/blob - QuarkSocPkg/QuarkSouthCluster/Usb/Ohci/Dxe/OhciSched.c
QuarkSocPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / QuarkSocPkg / QuarkSouthCluster / Usb / Ohci / Dxe / OhciSched.c
1 /** @file
2 OHCI transfer scheduling routines.
3
4 Copyright (c) 2013-2015 Intel Corporation.
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10
11 #include "Ohci.h"
12
13 /**
14
15 Add an item of interrupt context
16
17 @param Ohc UHC private data
18 @param NewEntry New entry to add
19
20 @retval EFI_SUCCESS Item successfully added
21
22 **/
23 EFI_STATUS
24 OhciAddInterruptContextEntry (
25 IN USB_OHCI_HC_DEV *Ohc,
26 IN INTERRUPT_CONTEXT_ENTRY *NewEntry
27 )
28 {
29 INTERRUPT_CONTEXT_ENTRY *Entry;
30 EFI_TPL OriginalTPL;
31
32 OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
33
34 if (Ohc->InterruptContextList == NULL) {
35 Ohc->InterruptContextList = NewEntry;
36 } else {
37 Entry = Ohc->InterruptContextList;
38 while (Entry->NextEntry != NULL) {
39 Entry = Entry->NextEntry;
40 }
41 Entry->NextEntry = NewEntry;
42 }
43
44 gBS->RestoreTPL (OriginalTPL);
45
46 return EFI_SUCCESS;
47 }
48
49
50 /**
51
52 Free a interrupt context entry
53
54 @param Ohc UHC private data
55 @param Entry Pointer to an interrupt context entry
56
57 @retval EFI_SUCCESS Entry freed
58 @retval EFI_INVALID_PARAMETER Entry is NULL
59
60 **/
61 EFI_STATUS
62 OhciFreeInterruptContextEntry (
63 IN USB_OHCI_HC_DEV *Ohc,
64 IN INTERRUPT_CONTEXT_ENTRY *Entry
65 )
66 {
67 TD_DESCRIPTOR *Td;
68 if (Entry == NULL) {
69 return EFI_INVALID_PARAMETER;
70 }
71 if (Entry->UCBufferMapping != NULL) {
72 Ohc->PciIo->Unmap(Ohc->PciIo, Entry->UCBufferMapping);
73 }
74 if (Entry->UCBuffer != NULL) {
75 FreePool(Entry->UCBuffer);
76 }
77 while (Entry->DataTd) {
78 Td = Entry->DataTd;
79 Entry->DataTd = (TD_DESCRIPTOR *)(UINTN)(Entry->DataTd->NextTDPointer);
80 UsbHcFreeMem(Ohc->MemPool, Td, sizeof(TD_DESCRIPTOR));
81 }
82 FreePool(Entry);
83 return EFI_SUCCESS;
84 }
85
86
87 /**
88
89 Free entries match the device address and endpoint address
90
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
95
96 @retval EFI_SUCCESS Items match the requirement removed
97
98 **/
99 EFI_STATUS
100 OhciFreeInterruptContext(
101 IN USB_OHCI_HC_DEV *Ohc,
102 IN UINT8 DeviceAddress,
103 IN UINT8 EndPointAddress,
104 OUT UINT8 *DataToggle
105 )
106 {
107 INTERRUPT_CONTEXT_ENTRY *Entry;
108 INTERRUPT_CONTEXT_ENTRY *TempEntry;
109 EFI_TPL OriginalTPL;
110
111
112 OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
113
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);
121 }
122 OhciFreeInterruptContextEntry (Ohc, TempEntry);
123 }
124
125 Entry = Ohc->InterruptContextList;
126 if (Entry == NULL) {
127 gBS->RestoreTPL (OriginalTPL);
128 return EFI_SUCCESS;
129 }
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);
137 }
138 OhciFreeInterruptContextEntry (Ohc, TempEntry);
139 } else {
140 Entry = Entry->NextEntry;
141 }
142 }
143
144 gBS->RestoreTPL (OriginalTPL);
145
146 return EFI_SUCCESS;
147 }
148
149
150 /**
151
152 Convert Error code from OHCI format to EFI format
153
154 @Param ErrorCode ErrorCode in OHCI format
155
156 @retval ErrorCode in EFI format
157
158 **/
159 UINT32
160 ConvertErrorCode (
161 IN UINT32 ErrorCode
162 )
163 {
164 UINT32 TransferResult;
165
166 switch (ErrorCode) {
167 case TD_NO_ERROR:
168 TransferResult = EFI_USB_NOERROR;
169 break;
170
171 case TD_TOBE_PROCESSED:
172 case TD_TOBE_PROCESSED_2:
173 TransferResult = EFI_USB_ERR_NOTEXECUTE;
174 break;
175
176 case TD_DEVICE_STALL:
177 TransferResult = EFI_USB_ERR_STALL;
178 break;
179
180 case TD_BUFFER_OVERRUN:
181 case TD_BUFFER_UNDERRUN:
182 TransferResult = EFI_USB_ERR_BUFFER;
183 break;
184
185 case TD_CRC_ERROR:
186 TransferResult = EFI_USB_ERR_CRC;
187 break;
188
189 case TD_NO_RESPONSE:
190 TransferResult = EFI_USB_ERR_TIMEOUT;
191 break;
192
193 case TD_BITSTUFFING_ERROR:
194 TransferResult = EFI_USB_ERR_BITSTUFF;
195 break;
196
197 default:
198 TransferResult = EFI_USB_ERR_SYSTEM;
199 }
200
201 return TransferResult;
202 }
203
204
205 /**
206
207 Check TDs Results
208
209 @Param Ohc UHC private data
210 @Param Td TD_DESCRIPTOR
211 @Param Result Result to return
212
213 @retval TRUE means OK
214 @retval FLASE means Error or Short packet
215
216 **/
217 BOOLEAN
218 OhciCheckTDsResults (
219 IN USB_OHCI_HC_DEV *Ohc,
220 IN TD_DESCRIPTOR *Td,
221 OUT UINT32 *Result
222 )
223 {
224 UINT32 TdCompletionCode;
225
226 *Result = EFI_USB_NOERROR;
227
228 while (Td) {
229 TdCompletionCode = Td->Word0.ConditionCode;
230
231 *Result |= ConvertErrorCode(TdCompletionCode);
232 //
233 // if any error encountered, stop processing the left TDs.
234 //
235 if (*Result) {
236 return FALSE;
237 }
238
239 Td = (TD_DESCRIPTOR *)(UINTN)(Td->NextTDPointer);
240 }
241 return TRUE;
242
243 }
244
245
246 /**
247
248 Check the task status on an ED
249
250 @Param Ed Pointer to the ED task that TD hooked on
251 @Param HeadTd TD header for current transaction
252
253 @retval Task Status Code
254
255 **/
256
257 UINT32
258 CheckEDStatus (
259 IN ED_DESCRIPTOR *Ed,
260 IN TD_DESCRIPTOR *HeadTd,
261 OUT OHCI_ED_RESULT *EdResult
262 )
263 {
264 while(HeadTd != NULL) {
265 if (HeadTd->NextTDPointer == 0) {
266 return TD_NO_ERROR;
267 }
268 if (HeadTd->Word0.ConditionCode != 0) {
269 return HeadTd->Word0.ConditionCode;
270 }
271 EdResult->NextToggle = ((UINT8)(HeadTd->Word0.DataToggle) & BIT0) ^ BIT0;
272 HeadTd = (TD_DESCRIPTOR *)(UINTN)(HeadTd->NextTDPointer);
273 }
274 if (OhciGetEDField (Ed, ED_TDHEAD_PTR) != OhciGetEDField (Ed, ED_TDTAIL_PTR)) {
275 return TD_TOBE_PROCESSED;
276 }
277 return TD_NO_ERROR;
278 }
279
280 /**
281
282 Check the task status
283
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
289
290 @retval EFI_SUCCESS Task done
291 @retval EFI_NOT_READY Task on processing
292 @retval EFI_DEVICE_ERROR Some error occured
293
294 **/
295 EFI_STATUS
296 CheckIfDone (
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
302 )
303 {
304 EdResult->ErrorCode = TD_TOBE_PROCESSED;
305
306 switch (ListType) {
307 case CONTROL_LIST:
308 if (OhciGetHcCommandStatus (Ohc, CONTROL_LIST_FILLED) != 0) {
309 return EFI_NOT_READY;
310 }
311 break;
312 case BULK_LIST:
313 if (OhciGetHcCommandStatus (Ohc, BULK_LIST_FILLED) != 0) {
314 return EFI_NOT_READY;
315 }
316 break;
317 default:
318 break;
319 }
320
321 EdResult->ErrorCode = CheckEDStatus (Ed, HeadTd, EdResult);
322
323 if (EdResult->ErrorCode == TD_NO_ERROR) {
324 return EFI_SUCCESS;
325 } else if (EdResult->ErrorCode == TD_TOBE_PROCESSED) {
326 return EFI_NOT_READY;
327 } else {
328 return EFI_DEVICE_ERROR;
329 }
330 }
331
332
333 /**
334
335 Convert TD condition code to Efi Status
336
337 @Param ConditionCode Condition code to convert
338
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
342
343 **/
344
345 EFI_STATUS
346 OhciTDConditionCodeToStatus (
347 IN UINT32 ConditionCode
348 )
349 {
350 if (ConditionCode == TD_NO_ERROR) {
351 return EFI_SUCCESS;
352 }
353
354 if (ConditionCode == TD_TOBE_PROCESSED) {
355 return EFI_NOT_READY;
356 }
357
358 return EFI_DEVICE_ERROR;
359 }
360
361 /**
362
363 Invoke callbacks hooked on done TDs
364
365 @Param Entry Interrupt transfer transaction information data structure
366 @Param Context Ohc private data
367
368 **/
369
370 VOID
371 OhciInvokeInterruptCallBack(
372 IN INTERRUPT_CONTEXT_ENTRY *Entry,
373 IN UINT32 Result
374 )
375 {
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
379 if (Result) {
380 Entry->CallBackFunction (
381 NULL,
382 0,
383 Entry->Context,
384 Result
385 );
386 }else{
387 Entry->CallBackFunction (
388 (VOID *)(UINTN)(Entry->DataTd->DataBuffer),
389 Entry->DataTd->ActualSendLength,
390 Entry->Context,
391 Result
392 );
393 }
394 }
395
396
397 /**
398
399 Timer to submit periodic interrupt transfer, and invoke callbacks hooked on done TDs
400
401 @param Event Event handle
402 @param Context Device private data
403
404 **/
405
406 VOID
407 EFIAPI
408 OhciHouseKeeper (
409 IN EFI_EVENT Event,
410 IN VOID *Context
411 )
412 {
413
414 USB_OHCI_HC_DEV *Ohc;
415 INTERRUPT_CONTEXT_ENTRY *Entry;
416 INTERRUPT_CONTEXT_ENTRY *PreEntry;
417 ED_DESCRIPTOR *Ed;
418 TD_DESCRIPTOR *DataTd;
419 TD_DESCRIPTOR *HeadTd;
420
421 UINT8 Toggle;
422 EFI_TPL OriginalTPL;
423 UINT32 Result;
424
425 Ohc = (USB_OHCI_HC_DEV *) Context;
426 OriginalTPL = gBS->RaiseTPL(TPL_NOTIFY);
427
428 Entry = Ohc->InterruptContextList;
429 PreEntry = NULL;
430
431 while(Entry != NULL) {
432
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)) {
436 PreEntry = Entry;
437 Entry = Entry->NextEntry;
438 continue;
439 }
440
441 if (Entry->CallBackFunction != NULL) {
442 OhciInvokeInterruptCallBack (Entry, Result);
443 if (Ohc->InterruptContextList == NULL) {
444 gBS->RestoreTPL (OriginalTPL);
445 return;
446 }
447 }
448 if (Entry->IsPeriodic) {
449
450 Ed = Entry->Ed;
451 HeadTd = Entry->DataTd;
452 DataTd = HeadTd;
453 Toggle = 0;
454 if (Result == EFI_USB_NOERROR) {
455 //
456 // Update toggle if there is no error, and re-submit the interrupt Ed&Tds
457 //
458 if ((Ed != NULL) && (DataTd != NULL)) {
459 Ed->Word0.Skip = 1;
460 }
461 //
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
468 //
469 if (Ed == NULL) {
470 return;
471 }
472 Toggle = (UINT8) OhciGetEDField (Ed, ED_DTTOGGLE);
473 while(DataTd != NULL) {
474 if (DataTd->NextTDPointer == 0) {
475 DataTd->Word0.DataToggle = 0;
476 break;
477 } else {
478 OhciSetTDField (DataTd, TD_DT_TOGGLE, Toggle);
479 }
480 DataTd = (TD_DESCRIPTOR *)(UINTN)(DataTd->NextTDPointer);
481 Toggle ^= 1;
482 }
483 //
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
487 //
488 DataTd = HeadTd;
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);
492 break;
493 }
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);
499 }
500 //
501 // Active current Ed,Td
502 //
503 // HC will only update Halted, ToggleCarry & TDQueueHeadPointer,
504 // So we only need to update them once we want to active them again.
505 //
506 if ((Ed != NULL) && (DataTd != NULL)) {
507 Ed->Word2.TdHeadPointer = (UINT32)((UINTN)HeadTd>>4);
508 OhciSetEDField (Ed, ED_HALTED | ED_DTTOGGLE, 0);
509 Ed->Word0.Skip = 0;
510 }
511 }
512 } else {
513 if (PreEntry == NULL) {
514 Ohc->InterruptContextList = Entry->NextEntry;
515 } else {
516 PreEntry = Entry;
517 PreEntry->NextEntry = Entry->NextEntry;
518 }
519 OhciFreeInterruptContextEntry (Ohc, PreEntry);
520 gBS->RestoreTPL (OriginalTPL);
521 return;
522 }
523 PreEntry = Entry;
524 Entry = Entry->NextEntry;
525 }
526 gBS->RestoreTPL (OriginalTPL);
527 }
528