1 /** @file
2 OHCI transfer scheduling routines.
3
4 Copyright (c) 2013-2015 Intel Corporation.
5
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16
17 #include "Ohci.h"
18
19 /**
20
21 Add an item of interrupt context
22
23 @param Ohc UHC private data
24 @param NewEntry New entry to add
25
26 @retval EFI_SUCCESS Item successfully added
27
28 **/
29 EFI_STATUS
OhciAddInterruptContextEntry(IN USB_OHCI_HC_DEV * Ohc,IN INTERRUPT_CONTEXT_ENTRY * NewEntry)30 OhciAddInterruptContextEntry (
31 IN USB_OHCI_HC_DEV *Ohc,
32 IN INTERRUPT_CONTEXT_ENTRY *NewEntry
33 )
34 {
35 INTERRUPT_CONTEXT_ENTRY *Entry;
36 EFI_TPL OriginalTPL;
37
38 OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
39
40 if (Ohc->InterruptContextList == NULL) {
41 Ohc->InterruptContextList = NewEntry;
42 } else {
43 Entry = Ohc->InterruptContextList;
44 while (Entry->NextEntry != NULL) {
45 Entry = Entry->NextEntry;
46 }
47 Entry->NextEntry = NewEntry;
48 }
49
50 gBS->RestoreTPL (OriginalTPL);
51
52 return EFI_SUCCESS;
53 }
54
55
56 /**
57
58 Free a interrupt context entry
59
60 @param Ohc UHC private data
61 @param Entry Pointer to an interrupt context entry
62
63 @retval EFI_SUCCESS Entry freed
64 @retval EFI_INVALID_PARAMETER Entry is NULL
65
66 **/
67 EFI_STATUS
OhciFreeInterruptContextEntry(IN USB_OHCI_HC_DEV * Ohc,IN INTERRUPT_CONTEXT_ENTRY * Entry)68 OhciFreeInterruptContextEntry (
69 IN USB_OHCI_HC_DEV *Ohc,
70 IN INTERRUPT_CONTEXT_ENTRY *Entry
71 )
72 {
73 TD_DESCRIPTOR *Td;
74 if (Entry == NULL) {
75 return EFI_INVALID_PARAMETER;
76 }
77 if (Entry->UCBufferMapping != NULL) {
78 Ohc->PciIo->Unmap(Ohc->PciIo, Entry->UCBufferMapping);
79 }
80 if (Entry->UCBuffer != NULL) {
81 FreePool(Entry->UCBuffer);
82 }
83 while (Entry->DataTd) {
84 Td = Entry->DataTd;
85 Entry->DataTd = (TD_DESCRIPTOR *)(UINTN)(Entry->DataTd->NextTDPointer);
86 UsbHcFreeMem(Ohc->MemPool, Td, sizeof(TD_DESCRIPTOR));
87 }
88 FreePool(Entry);
89 return EFI_SUCCESS;
90 }
91
92
93 /**
94
95 Free entries match the device address and endpoint address
96
97 @Param Ohc UHC private date
98 @Param DeviceAddress Item to free must match this device address
99 @Param EndPointAddress Item to free must match this end point address
100 @Param DataToggle DataToggle for output
101
102 @retval EFI_SUCCESS Items match the requirement removed
103
104 **/
105 EFI_STATUS
OhciFreeInterruptContext(IN USB_OHCI_HC_DEV * Ohc,IN UINT8 DeviceAddress,IN UINT8 EndPointAddress,OUT UINT8 * DataToggle)106 OhciFreeInterruptContext(
107 IN USB_OHCI_HC_DEV *Ohc,
108 IN UINT8 DeviceAddress,
109 IN UINT8 EndPointAddress,
110 OUT UINT8 *DataToggle
111 )
112 {
113 INTERRUPT_CONTEXT_ENTRY *Entry;
114 INTERRUPT_CONTEXT_ENTRY *TempEntry;
115 EFI_TPL OriginalTPL;
116
117
118 OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
119
120 while (Ohc->InterruptContextList != NULL &&
121 Ohc->InterruptContextList->DeviceAddress == DeviceAddress &&
122 Ohc->InterruptContextList->EndPointAddress == EndPointAddress) {
123 TempEntry = Ohc->InterruptContextList;
124 Ohc->InterruptContextList = Ohc->InterruptContextList->NextEntry;
125 if (DataToggle != NULL) {
126 *DataToggle = (UINT8) (TempEntry->DataTd->Word0.DataToggle & 0x1);
127 }
128 OhciFreeInterruptContextEntry (Ohc, TempEntry);
129 }
130
131 Entry = Ohc->InterruptContextList;
132 if (Entry == NULL) {
133 gBS->RestoreTPL (OriginalTPL);
134 return EFI_SUCCESS;
135 }
136 while (Entry->NextEntry != NULL) {
137 if (Entry->NextEntry->DeviceAddress == DeviceAddress &&
138 Entry->NextEntry->EndPointAddress == EndPointAddress) {
139 TempEntry = Entry->NextEntry;
140 Entry->NextEntry = Entry->NextEntry->NextEntry;
141 if (DataToggle != NULL) {
142 *DataToggle = (UINT8) (TempEntry->DataTd->Word0.DataToggle & 0x1);
143 }
144 OhciFreeInterruptContextEntry (Ohc, TempEntry);
145 } else {
146 Entry = Entry->NextEntry;
147 }
148 }
149
150 gBS->RestoreTPL (OriginalTPL);
151
152 return EFI_SUCCESS;
153 }
154
155
156 /**
157
158 Convert Error code from OHCI format to EFI format
159
160 @Param ErrorCode ErrorCode in OHCI format
161
162 @retval ErrorCode in EFI format
163
164 **/
165 UINT32
ConvertErrorCode(IN UINT32 ErrorCode)166 ConvertErrorCode (
167 IN UINT32 ErrorCode
168 )
169 {
170 UINT32 TransferResult;
171
172 switch (ErrorCode) {
173 case TD_NO_ERROR:
174 TransferResult = EFI_USB_NOERROR;
175 break;
176
177 case TD_TOBE_PROCESSED:
178 case TD_TOBE_PROCESSED_2:
179 TransferResult = EFI_USB_ERR_NOTEXECUTE;
180 break;
181
182 case TD_DEVICE_STALL:
183 TransferResult = EFI_USB_ERR_STALL;
184 break;
185
186 case TD_BUFFER_OVERRUN:
187 case TD_BUFFER_UNDERRUN:
188 TransferResult = EFI_USB_ERR_BUFFER;
189 break;
190
191 case TD_CRC_ERROR:
192 TransferResult = EFI_USB_ERR_CRC;
193 break;
194
195 case TD_NO_RESPONSE:
196 TransferResult = EFI_USB_ERR_TIMEOUT;
197 break;
198
199 case TD_BITSTUFFING_ERROR:
200 TransferResult = EFI_USB_ERR_BITSTUFF;
201 break;
202
203 default:
204 TransferResult = EFI_USB_ERR_SYSTEM;
205 }
206
207 return TransferResult;
208 }
209
210
211 /**
212
213 Check TDs Results
214
215 @Param Ohc UHC private data
216 @Param Td TD_DESCRIPTOR
217 @Param Result Result to return
218
219 @retval TRUE means OK
220 @retval FLASE means Error or Short packet
221
222 **/
223 BOOLEAN
OhciCheckTDsResults(IN USB_OHCI_HC_DEV * Ohc,IN TD_DESCRIPTOR * Td,OUT UINT32 * Result)224 OhciCheckTDsResults (
225 IN USB_OHCI_HC_DEV *Ohc,
226 IN TD_DESCRIPTOR *Td,
227 OUT UINT32 *Result
228 )
229 {
230 UINT32 TdCompletionCode;
231
232 *Result = EFI_USB_NOERROR;
233
234 while (Td) {
235 TdCompletionCode = Td->Word0.ConditionCode;
236
237 *Result |= ConvertErrorCode(TdCompletionCode);
238 //
239 // if any error encountered, stop processing the left TDs.
240 //
241 if (*Result) {
242 return FALSE;
243 }
244
245 Td = (TD_DESCRIPTOR *)(UINTN)(Td->NextTDPointer);
246 }
247 return TRUE;
248
249 }
250
251
252 /**
253
254 Check the task status on an ED
255
256 @Param Ed Pointer to the ED task that TD hooked on
257 @Param HeadTd TD header for current transaction
258
259 @retval Task Status Code
260
261 **/
262
263 UINT32
CheckEDStatus(IN ED_DESCRIPTOR * Ed,IN TD_DESCRIPTOR * HeadTd,OUT OHCI_ED_RESULT * EdResult)264 CheckEDStatus (
265 IN ED_DESCRIPTOR *Ed,
266 IN TD_DESCRIPTOR *HeadTd,
267 OUT OHCI_ED_RESULT *EdResult
268 )
269 {
270 while(HeadTd != NULL) {
271 if (HeadTd->NextTDPointer == 0) {
272 return TD_NO_ERROR;
273 }
274 if (HeadTd->Word0.ConditionCode != 0) {
275 return HeadTd->Word0.ConditionCode;
276 }
277 EdResult->NextToggle = ((UINT8)(HeadTd->Word0.DataToggle) & BIT0) ^ BIT0;
278 HeadTd = (TD_DESCRIPTOR *)(UINTN)(HeadTd->NextTDPointer);
279 }
280 if (OhciGetEDField (Ed, ED_TDHEAD_PTR) != OhciGetEDField (Ed, ED_TDTAIL_PTR)) {
281 return TD_TOBE_PROCESSED;
282 }
283 return TD_NO_ERROR;
284 }
285
286 /**
287
288 Check the task status
289
290 @Param Ohc UHC private data
291 @Param ListType Pipe type
292 @Param Ed Pointer to the ED task hooked on
293 @Param HeadTd Head of TD corresponding to the task
294 @Param ErrorCode return the ErrorCode
295
296 @retval EFI_SUCCESS Task done
297 @retval EFI_NOT_READY Task on processing
298 @retval EFI_DEVICE_ERROR Some error occured
299
300 **/
301 EFI_STATUS
CheckIfDone(IN USB_OHCI_HC_DEV * Ohc,IN DESCRIPTOR_LIST_TYPE ListType,IN ED_DESCRIPTOR * Ed,IN TD_DESCRIPTOR * HeadTd,OUT OHCI_ED_RESULT * EdResult)302 CheckIfDone (
303 IN USB_OHCI_HC_DEV *Ohc,
304 IN DESCRIPTOR_LIST_TYPE ListType,
305 IN ED_DESCRIPTOR *Ed,
306 IN TD_DESCRIPTOR *HeadTd,
307 OUT OHCI_ED_RESULT *EdResult
308 )
309 {
310 EdResult->ErrorCode = TD_TOBE_PROCESSED;
311
312 switch (ListType) {
313 case CONTROL_LIST:
314 if (OhciGetHcCommandStatus (Ohc, CONTROL_LIST_FILLED) != 0) {
315 return EFI_NOT_READY;
316 }
317 break;
318 case BULK_LIST:
319 if (OhciGetHcCommandStatus (Ohc, BULK_LIST_FILLED) != 0) {
320 return EFI_NOT_READY;
321 }
322 break;
323 default:
324 break;
325 }
326
327 EdResult->ErrorCode = CheckEDStatus (Ed, HeadTd, EdResult);
328
329 if (EdResult->ErrorCode == TD_NO_ERROR) {
330 return EFI_SUCCESS;
331 } else if (EdResult->ErrorCode == TD_TOBE_PROCESSED) {
332 return EFI_NOT_READY;
333 } else {
334 return EFI_DEVICE_ERROR;
335 }
336 }
337
338
339 /**
340
341 Convert TD condition code to Efi Status
342
343 @Param ConditionCode Condition code to convert
344
345 @retval EFI_SUCCESS No error occured
346 @retval EFI_NOT_READY TD still on processing
347 @retval EFI_DEVICE_ERROR Error occured in processing TD
348
349 **/
350
351 EFI_STATUS
OhciTDConditionCodeToStatus(IN UINT32 ConditionCode)352 OhciTDConditionCodeToStatus (
353 IN UINT32 ConditionCode
354 )
355 {
356 if (ConditionCode == TD_NO_ERROR) {
357 return EFI_SUCCESS;
358 }
359
360 if (ConditionCode == TD_TOBE_PROCESSED) {
361 return EFI_NOT_READY;
362 }
363
364 return EFI_DEVICE_ERROR;
365 }
366
367 /**
368
369 Invoke callbacks hooked on done TDs
370
371 @Param Entry Interrupt transfer transaction information data structure
372 @Param Context Ohc private data
373
374 **/
375
376 VOID
OhciInvokeInterruptCallBack(IN INTERRUPT_CONTEXT_ENTRY * Entry,IN UINT32 Result)377 OhciInvokeInterruptCallBack(
378 IN INTERRUPT_CONTEXT_ENTRY *Entry,
379 IN UINT32 Result
380 )
381 {
382 //Generally speaking, Keyboard driver should not
383 //check the Keyboard buffer if an error happens, it will be robust
384 //if we NULLed the buffer once error happens
385 if (Result) {
386 Entry->CallBackFunction (
387 NULL,
388 0,
389 Entry->Context,
390 Result
391 );
392 }else{
393 Entry->CallBackFunction (
394 (VOID *)(UINTN)(Entry->DataTd->DataBuffer),
395 Entry->DataTd->ActualSendLength,
396 Entry->Context,
397 Result
398 );
399 }
400 }
401
402
403 /**
404
405 Timer to submit periodic interrupt transfer, and invoke callbacks hooked on done TDs
406
407 @param Event Event handle
408 @param Context Device private data
409
410 **/
411
412 VOID
413 EFIAPI
OhciHouseKeeper(IN EFI_EVENT Event,IN VOID * Context)414 OhciHouseKeeper (
415 IN EFI_EVENT Event,
416 IN VOID *Context
417 )
418 {
419
420 USB_OHCI_HC_DEV *Ohc;
421 INTERRUPT_CONTEXT_ENTRY *Entry;
422 INTERRUPT_CONTEXT_ENTRY *PreEntry;
423 ED_DESCRIPTOR *Ed;
424 TD_DESCRIPTOR *DataTd;
425 TD_DESCRIPTOR *HeadTd;
426
427 UINT8 Toggle;
428 EFI_TPL OriginalTPL;
429 UINT32 Result;
430
431 Ohc = (USB_OHCI_HC_DEV *) Context;
432 OriginalTPL = gBS->RaiseTPL(TPL_NOTIFY);
433
434 Entry = Ohc->InterruptContextList;
435 PreEntry = NULL;
436
437 while(Entry != NULL) {
438
439 OhciCheckTDsResults(Ohc, Entry->DataTd, &Result );
440 if (((Result & EFI_USB_ERR_STALL) == EFI_USB_ERR_STALL) ||
441 ((Result & EFI_USB_ERR_NOTEXECUTE) == EFI_USB_ERR_NOTEXECUTE)) {
442 PreEntry = Entry;
443 Entry = Entry->NextEntry;
444 continue;
445 }
446
447 if (Entry->CallBackFunction != NULL) {
448 OhciInvokeInterruptCallBack (Entry, Result);
449 if (Ohc->InterruptContextList == NULL) {
450 gBS->RestoreTPL (OriginalTPL);
451 return;
452 }
453 }
454 if (Entry->IsPeriodic) {
455
456 Ed = Entry->Ed;
457 HeadTd = Entry->DataTd;
458 DataTd = HeadTd;
459 Toggle = 0;
460 if (Result == EFI_USB_NOERROR) {
461 //
462 // Update toggle if there is no error, and re-submit the interrupt Ed&Tds
463 //
464 if ((Ed != NULL) && (DataTd != NULL)) {
465 Ed->Word0.Skip = 1;
466 }
467 //
468 // From hcir1_0a.pdf 4.2.2
469 // ToggleCarry:This bit is the data toggle carry bit,
470 // Whenever a TD is retired, this bit is written to
471 // contain the last data toggle value(LSb of data Toggel
472 // file) from the retired TD.
473 // This field is not used for Isochronous Endpoints
474 //
475 if (Ed == NULL) {
476 return;
477 }
478 Toggle = (UINT8) OhciGetEDField (Ed, ED_DTTOGGLE);
479 while(DataTd != NULL) {
480 if (DataTd->NextTDPointer == 0) {
481 DataTd->Word0.DataToggle = 0;
482 break;
483 } else {
484 OhciSetTDField (DataTd, TD_DT_TOGGLE, Toggle);
485 }
486 DataTd = (TD_DESCRIPTOR *)(UINTN)(DataTd->NextTDPointer);
487 Toggle ^= 1;
488 }
489 //
490 // HC will only update DataToggle, ErrorCount, ConditionCode
491 // CurrentBufferPointer & NextTD, so we only need to update
492 // them once we want to active them again
493 //
494 DataTd = HeadTd;
495 while (DataTd != NULL) {
496 if (DataTd->NextTDPointer == 0) {
497 OhciSetTDField (DataTd, TD_ERROR_CNT | TD_COND_CODE | TD_CURR_BUFFER_PTR | TD_NEXT_PTR, 0);
498 break;
499 }
500 OhciSetTDField (DataTd, TD_ERROR_CNT, 0);
501 OhciSetTDField (DataTd, TD_COND_CODE, TD_TOBE_PROCESSED);
502 DataTd->NextTD = DataTd->NextTDPointer;
503 DataTd->CurrBufferPointer = DataTd->DataBuffer;
504 DataTd = (TD_DESCRIPTOR *)(UINTN)(DataTd->NextTDPointer);
505 }
506 //
507 // Active current Ed,Td
508 //
509 // HC will only update Halted, ToggleCarry & TDQueueHeadPointer,
510 // So we only need to update them once we want to active them again.
511 //
512 if ((Ed != NULL) && (DataTd != NULL)) {
513 Ed->Word2.TdHeadPointer = (UINT32)((UINTN)HeadTd>>4);
514 OhciSetEDField (Ed, ED_HALTED | ED_DTTOGGLE, 0);
515 Ed->Word0.Skip = 0;
516 }
517 }
518 } else {
519 if (PreEntry == NULL) {
520 Ohc->InterruptContextList = Entry->NextEntry;
521 } else {
522 PreEntry = Entry;
523 PreEntry->NextEntry = Entry->NextEntry;
524 }
525 OhciFreeInterruptContextEntry (Ohc, PreEntry);
526 gBS->RestoreTPL (OriginalTPL);
527 return;
528 }
529 PreEntry = Entry;
530 Entry = Entry->NextEntry;
531 }
532 gBS->RestoreTPL (OriginalTPL);
533 }
534
535