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