• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2 
3   The EHCI register operation routines.
4 
5 Copyright (c) 2007 - 2013, Intel Corporation. All rights reserved.<BR>
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 #include "Uhci.h"
17 
18 
19 /**
20   Create Frame List Structure.
21 
22   @param  Uhc                    UHCI device.
23 
24   @retval EFI_OUT_OF_RESOURCES   Can't allocate memory resources.
25   @retval EFI_UNSUPPORTED        Map memory fail.
26   @retval EFI_SUCCESS            Success.
27 
28 **/
29 EFI_STATUS
UhciInitFrameList(IN USB_HC_DEV * Uhc)30 UhciInitFrameList (
31   IN USB_HC_DEV         *Uhc
32   )
33 {
34   EFI_PHYSICAL_ADDRESS  MappedAddr;
35   EFI_STATUS            Status;
36   VOID                  *Buffer;
37   VOID                  *Mapping;
38   UINTN                 Pages;
39   UINTN                 Bytes;
40   UINTN                 Index;
41   EFI_PHYSICAL_ADDRESS  PhyAddr;
42 
43   //
44   // The Frame List is a common buffer that will be
45   // accessed by both the cpu and the usb bus master
46   // at the same time. The Frame List ocupies 4K bytes,
47   // and must be aligned on 4-Kbyte boundaries.
48   //
49   Bytes = 4096;
50   Pages = EFI_SIZE_TO_PAGES (Bytes);
51 
52   Status = Uhc->PciIo->AllocateBuffer (
53                          Uhc->PciIo,
54                          AllocateAnyPages,
55                          EfiBootServicesData,
56                          Pages,
57                          &Buffer,
58                          0
59                          );
60 
61   if (EFI_ERROR (Status)) {
62     return EFI_OUT_OF_RESOURCES;
63   }
64 
65   Status = Uhc->PciIo->Map (
66                          Uhc->PciIo,
67                          EfiPciIoOperationBusMasterCommonBuffer,
68                          Buffer,
69                          &Bytes,
70                          &MappedAddr,
71                          &Mapping
72                          );
73 
74   if (EFI_ERROR (Status) || (Bytes != 4096)) {
75     Status = EFI_UNSUPPORTED;
76     goto ON_ERROR;
77   }
78 
79   Uhc->FrameBase           = (UINT32 *) (UINTN) Buffer;
80   Uhc->FrameMapping        = Mapping;
81 
82   //
83   // Tell the Host Controller where the Frame List lies,
84   // by set the Frame List Base Address Register.
85   //
86   UhciSetFrameListBaseAddr (Uhc->PciIo, (VOID *) (UINTN) MappedAddr);
87 
88   //
89   // Allocate the QH used by sync interrupt/control/bulk transfer.
90   // FS ctrl/bulk queue head is set to loopback so additional BW
91   // can be reclaimed. Notice, LS don't support bulk transfer and
92   // also doesn't support BW reclamation.
93   //
94   Uhc->SyncIntQh  = UhciCreateQh (Uhc, 1);
95   Uhc->CtrlQh     = UhciCreateQh (Uhc, 1);
96   Uhc->BulkQh     = UhciCreateQh (Uhc, 1);
97 
98   if ((Uhc->SyncIntQh == NULL) || (Uhc->CtrlQh == NULL) || (Uhc->BulkQh == NULL)) {
99     Uhc->PciIo->Unmap (Uhc->PciIo, Mapping);
100     Status = EFI_OUT_OF_RESOURCES;
101     goto ON_ERROR;
102   }
103 
104   //
105   //                                                +-------------+
106   //                                                |             |
107   // Link the three together: SyncIntQh->CtrlQh->BulkQh <---------+
108   // Each frame entry is linked to this sequence of QH. These QH
109   // will remain on the schedul, never got removed
110   //
111   PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_HW));
112   Uhc->SyncIntQh->QhHw.HorizonLink  = QH_HLINK (PhyAddr, FALSE);
113   Uhc->SyncIntQh->NextQh            = Uhc->CtrlQh;
114 
115   PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_HW));
116   Uhc->CtrlQh->QhHw.HorizonLink     = QH_HLINK (PhyAddr, FALSE);
117   Uhc->CtrlQh->NextQh               = Uhc->BulkQh;
118 
119   //
120   // Some old platform such as Intel's Tiger 4 has a difficult time
121   // in supporting the full speed bandwidth reclamation in the previous
122   // mentioned form. Most new platforms don't suffer it.
123   //
124   Uhc->BulkQh->QhHw.HorizonLink     = QH_HLINK (PhyAddr, FALSE);
125 
126   Uhc->BulkQh->NextQh               = NULL;
127 
128   Uhc->FrameBaseHostAddr = AllocateZeroPool (4096);
129   if (Uhc->FrameBaseHostAddr == NULL) {
130     Status = EFI_OUT_OF_RESOURCES;
131     goto ON_ERROR;
132   }
133 
134   PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_HW));
135   for (Index = 0; Index < UHCI_FRAME_NUM; Index++) {
136     Uhc->FrameBase[Index] = QH_HLINK (PhyAddr, FALSE);
137     Uhc->FrameBaseHostAddr[Index] = (UINT32)(UINTN)Uhc->SyncIntQh;
138   }
139 
140   return EFI_SUCCESS;
141 
142 ON_ERROR:
143   if (Uhc->SyncIntQh != NULL) {
144     UsbHcFreeMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_SW));
145   }
146 
147   if (Uhc->CtrlQh != NULL) {
148     UsbHcFreeMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_SW));
149   }
150 
151   if (Uhc->BulkQh != NULL) {
152     UsbHcFreeMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_SW));
153   }
154 
155   Uhc->PciIo->FreeBuffer (Uhc->PciIo, Pages, Buffer);
156   return Status;
157 }
158 
159 
160 /**
161   Destory FrameList buffer.
162 
163   @param  Uhc                    The UHCI device.
164 
165 **/
166 VOID
UhciDestoryFrameList(IN USB_HC_DEV * Uhc)167 UhciDestoryFrameList (
168   IN USB_HC_DEV           *Uhc
169   )
170 {
171   //
172   // Unmap the common buffer for framelist entry,
173   // and free the common buffer.
174   // Uhci's frame list occupy 4k memory.
175   //
176   Uhc->PciIo->Unmap (Uhc->PciIo, Uhc->FrameMapping);
177 
178   Uhc->PciIo->FreeBuffer (
179                 Uhc->PciIo,
180                 EFI_SIZE_TO_PAGES (4096),
181                 (VOID *) Uhc->FrameBase
182                 );
183 
184   if (Uhc->FrameBaseHostAddr != NULL) {
185     FreePool (Uhc->FrameBaseHostAddr);
186   }
187 
188   if (Uhc->SyncIntQh != NULL) {
189     UsbHcFreeMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_SW));
190   }
191 
192   if (Uhc->CtrlQh != NULL) {
193     UsbHcFreeMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_SW));
194   }
195 
196   if (Uhc->BulkQh != NULL) {
197     UsbHcFreeMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_SW));
198   }
199 
200   Uhc->FrameBase           = NULL;
201   Uhc->FrameBaseHostAddr   = NULL;
202   Uhc->SyncIntQh           = NULL;
203   Uhc->CtrlQh              = NULL;
204   Uhc->BulkQh              = NULL;
205 }
206 
207 
208 /**
209   Convert the poll rate to the maxium 2^n that is smaller
210   than Interval.
211 
212   @param  Interval               The poll rate to convert.
213 
214   @return The converted poll rate.
215 
216 **/
217 UINTN
UhciConvertPollRate(IN UINTN Interval)218 UhciConvertPollRate (
219   IN  UINTN               Interval
220   )
221 {
222   UINTN                   BitCount;
223 
224   ASSERT (Interval != 0);
225 
226   //
227   // Find the index (1 based) of the highest non-zero bit
228   //
229   BitCount = 0;
230 
231   while (Interval != 0) {
232     Interval >>= 1;
233     BitCount++;
234   }
235 
236   return (UINTN)1 << (BitCount - 1);
237 }
238 
239 
240 /**
241   Link a queue head (for asynchronous interrupt transfer) to
242   the frame list.
243 
244   @param  Uhc                    The UHCI device.
245   @param  Qh                     The queue head to link into.
246 
247 **/
248 VOID
UhciLinkQhToFrameList(USB_HC_DEV * Uhc,UHCI_QH_SW * Qh)249 UhciLinkQhToFrameList (
250   USB_HC_DEV              *Uhc,
251   UHCI_QH_SW              *Qh
252   )
253 {
254   UINTN                   Index;
255   UHCI_QH_SW              *Prev;
256   UHCI_QH_SW              *Next;
257   EFI_PHYSICAL_ADDRESS    PhyAddr;
258   EFI_PHYSICAL_ADDRESS    QhPciAddr;
259 
260   ASSERT ((Uhc->FrameBase != NULL) && (Qh != NULL));
261 
262   QhPciAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Qh, sizeof (UHCI_QH_HW));
263 
264   for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) {
265     //
266     // First QH can't be NULL because we always keep static queue
267     // heads on the frame list
268     //
269     ASSERT (!LINK_TERMINATED (Uhc->FrameBase[Index]));
270     Next  = (UHCI_QH_SW*)(UINTN)Uhc->FrameBaseHostAddr[Index];
271     Prev  = NULL;
272 
273     //
274     // Now, insert the queue head (Qh) into this frame:
275     // 1. Find a queue head with the same poll interval, just insert
276     //    Qh after this queue head, then we are done.
277     //
278     // 2. Find the position to insert the queue head into:
279     //      Previous head's interval is bigger than Qh's
280     //      Next head's interval is less than Qh's
281     //    Then, insert the Qh between then
282     //
283     // This method is very much the same as that used by EHCI.
284     // Because each QH's interval is round down to 2^n, poll
285     // rate is correct.
286     //
287     while (Next->Interval > Qh->Interval) {
288       Prev  = Next;
289       Next  = Next->NextQh;
290       ASSERT (Next != NULL);
291     }
292 
293     //
294     // The entry may have been linked into the frame by early insertation.
295     // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
296     // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
297     // It isn't necessary to compare all the QH with the same interval to
298     // Qh. This is because if there is other QH with the same interval, Qh
299     // should has been inserted after that at FrameBase[0] and at FrameBase[0] it is
300     // impossible (Next == Qh)
301     //
302     if (Next == Qh) {
303       continue;
304     }
305 
306     if (Next->Interval == Qh->Interval) {
307       //
308       // If there is a QH with the same interval, it locates at
309       // FrameBase[0], and we can simply insert it after this QH. We
310       // are all done.
311       //
312       ASSERT ((Index == 0) && (Qh->NextQh == NULL));
313 
314       Prev                    = Next;
315       Next                    = Next->NextQh;
316 
317       Qh->NextQh              = Next;
318       Prev->NextQh            = Qh;
319 
320       Qh->QhHw.HorizonLink    = Prev->QhHw.HorizonLink;
321 
322       Prev->QhHw.HorizonLink  = QH_HLINK (QhPciAddr, FALSE);
323       break;
324     }
325 
326     //
327     // OK, find the right position, insert it in. If Qh's next
328     // link has already been set, it is in position. This is
329     // guarranted by 2^n polling interval.
330     //
331     if (Qh->NextQh == NULL) {
332       Qh->NextQh            = Next;
333       PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Next, sizeof (UHCI_QH_HW));
334       Qh->QhHw.HorizonLink  = QH_HLINK (PhyAddr, FALSE);
335     }
336 
337     if (Prev == NULL) {
338       Uhc->FrameBase[Index]           = QH_HLINK (QhPciAddr, FALSE);
339       Uhc->FrameBaseHostAddr[Index]   = (UINT32)(UINTN)Qh;
340     } else {
341       Prev->NextQh            = Qh;
342       Prev->QhHw.HorizonLink  = QH_HLINK (QhPciAddr, FALSE);
343     }
344   }
345 }
346 
347 
348 /**
349   Unlink QH from the frame list is easier: find all
350   the precedence node, and pointer there next to QhSw's
351   next.
352 
353   @param  Uhc                    The UHCI device.
354   @param  Qh                     The queue head to unlink.
355 
356 **/
357 VOID
UhciUnlinkQhFromFrameList(USB_HC_DEV * Uhc,UHCI_QH_SW * Qh)358 UhciUnlinkQhFromFrameList (
359   USB_HC_DEV              *Uhc,
360   UHCI_QH_SW              *Qh
361   )
362 {
363   UINTN                   Index;
364   UHCI_QH_SW              *Prev;
365   UHCI_QH_SW              *This;
366 
367   ASSERT ((Uhc->FrameBase != NULL) && (Qh != NULL));
368 
369   for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) {
370     //
371     // Frame link can't be NULL because we always keep static
372     // queue heads on the frame list
373     //
374     ASSERT (!LINK_TERMINATED (Uhc->FrameBase[Index]));
375     This  = (UHCI_QH_SW*)(UINTN)Uhc->FrameBaseHostAddr[Index];
376     Prev  = NULL;
377 
378     //
379     // Walk through the frame's QH list to find the
380     // queue head to remove
381     //
382     while ((This != NULL) && (This != Qh)) {
383       Prev  = This;
384       This  = This->NextQh;
385     }
386 
387     //
388     // Qh may have already been unlinked from this frame
389     // by early action.
390     //
391     if (This == NULL) {
392       continue;
393     }
394 
395     if (Prev == NULL) {
396       //
397       // Qh is the first entry in the frame
398       //
399       Uhc->FrameBase[Index]           = Qh->QhHw.HorizonLink;
400       Uhc->FrameBaseHostAddr[Index]   = (UINT32)(UINTN)Qh->NextQh;
401     } else {
402       Prev->NextQh            = Qh->NextQh;
403       Prev->QhHw.HorizonLink  = Qh->QhHw.HorizonLink;
404     }
405   }
406 }
407 
408 
409 /**
410   Check TDs Results.
411 
412   @param  Uhc                    This UHCI device.
413   @param  Td                     UHCI_TD_SW to check.
414   @param  IsLow                  Is Low Speed Device.
415   @param  QhResult               Return the result of this TD list.
416 
417   @return Whether the TD's result is finialized.
418 
419 **/
420 BOOLEAN
UhciCheckTdStatus(IN USB_HC_DEV * Uhc,IN UHCI_TD_SW * Td,IN BOOLEAN IsLow,OUT UHCI_QH_RESULT * QhResult)421 UhciCheckTdStatus (
422   IN  USB_HC_DEV          *Uhc,
423   IN  UHCI_TD_SW          *Td,
424   IN  BOOLEAN             IsLow,
425   OUT UHCI_QH_RESULT      *QhResult
426   )
427 {
428   UINTN                   Len;
429   UINT8                   State;
430   UHCI_TD_HW              *TdHw;
431   BOOLEAN                 Finished;
432 
433   Finished             = TRUE;
434 
435   //
436   // Initialize the data toggle to that of the first
437   // TD. The next toggle to use is either:
438   // 1. first TD's toggle if no TD is executed OK
439   // 2. the next toggle of last executed-OK TD
440   //
441   QhResult->Result     = EFI_USB_NOERROR;
442   QhResult->NextToggle = (UINT8)Td->TdHw.DataToggle;
443   QhResult->Complete   = 0;
444 
445   while (Td != NULL) {
446     TdHw  = &Td->TdHw;
447     State = (UINT8)TdHw->Status;
448 
449     //
450     // UHCI will set STALLED bit when it abort the execution
451     // of TD list. There are several reasons:
452     //   1. BABBLE error happened
453     //   2. Received a STALL response
454     //   3. Error count decreased to zero.
455     //
456     // It also set CRC/Timeout/NAK/Buffer Error/BitStuff Error
457     // bits when corresponding conditions happen. But these
458     // conditions are not deadly, that is a TD can successfully
459     // completes even these bits are set. But it is likely that
460     // upper layer won't distinguish these condtions. So, only
461     // set these bits when TD is actually halted.
462     //
463     if ((State & USBTD_STALLED) != 0) {
464       if ((State & USBTD_BABBLE) != 0) {
465         QhResult->Result |= EFI_USB_ERR_BABBLE;
466 
467       } else if (TdHw->ErrorCount != 0) {
468         QhResult->Result |= EFI_USB_ERR_STALL;
469       }
470 
471       if ((State & USBTD_CRC) != 0) {
472         QhResult->Result |= EFI_USB_ERR_CRC;
473       }
474 
475       if ((State & USBTD_BUFFERR) != 0) {
476         QhResult->Result |= EFI_USB_ERR_BUFFER;
477       }
478 
479       if ((Td->TdHw.Status & USBTD_BITSTUFF) != 0) {
480         QhResult->Result |= EFI_USB_ERR_BITSTUFF;
481       }
482 
483       if (TdHw->ErrorCount == 0) {
484         QhResult->Result |= EFI_USB_ERR_TIMEOUT;
485       }
486 
487       Finished = TRUE;
488       goto ON_EXIT;
489 
490     } else if ((State & USBTD_ACTIVE) != 0) {
491       //
492       // The TD is still active, no need to check further.
493       //
494       QhResult->Result |= EFI_USB_ERR_NOTEXECUTE;
495 
496       Finished = FALSE;
497       goto ON_EXIT;
498 
499     } else {
500       //
501       // Update the next data toggle, it is always the
502       // next to the last known-good TD's data toggle if
503       // any TD is executed OK
504       //
505       QhResult->NextToggle = (UINT8) (1 - (UINT8)TdHw->DataToggle);
506 
507       //
508       // This TD is finished OK or met short packet read. Update the
509       // transfer length if it isn't a SETUP.
510       //
511       Len = (TdHw->ActualLen + 1) & 0x7FF;
512 
513       if (TdHw->PidCode != SETUP_PACKET_ID) {
514         QhResult->Complete += Len;
515       }
516 
517       //
518       // Short packet condition for full speed input TD, also
519       // terminate the transfer
520       //
521       if (!IsLow && (TdHw->ShortPacket == 1) && (Len < Td->DataLen)) {
522         DEBUG ((EFI_D_VERBOSE, "UhciCheckTdStatus: short packet read occured\n"));
523 
524         Finished = TRUE;
525         goto ON_EXIT;
526       }
527     }
528 
529     Td = Td->NextTd;
530   }
531 
532 ON_EXIT:
533   //
534   // Check whether HC is halted. Don't move this up. It must be
535   // called after data toggle is successfully updated.
536   //
537   if (!UhciIsHcWorking (Uhc->PciIo)) {
538     QhResult->Result |= EFI_USB_ERR_SYSTEM;
539     Finished  = TRUE;
540   }
541 
542   if (Finished) {
543     Uhc->PciIo->Flush (Uhc->PciIo);
544   }
545 
546   UhciAckAllInterrupt (Uhc);
547   return Finished;
548 }
549 
550 
551 /**
552   Check the result of the transfer.
553 
554   @param  Uhc                    The UHCI device.
555   @param  Qh                     The queue head of the transfer.
556   @param  Td                     The first TDs of the transfer.
557   @param  TimeOut                TimeOut value in milliseconds.
558   @param  IsLow                  Is Low Speed Device.
559   @param  QhResult               The variable to return result.
560 
561   @retval EFI_SUCCESS            The transfer finished with success.
562   @retval EFI_DEVICE_ERROR       Transfer failed.
563 
564 **/
565 EFI_STATUS
UhciExecuteTransfer(IN USB_HC_DEV * Uhc,IN UHCI_QH_SW * Qh,IN UHCI_TD_SW * Td,IN UINTN TimeOut,IN BOOLEAN IsLow,OUT UHCI_QH_RESULT * QhResult)566 UhciExecuteTransfer (
567   IN  USB_HC_DEV          *Uhc,
568   IN  UHCI_QH_SW          *Qh,
569   IN  UHCI_TD_SW          *Td,
570   IN  UINTN               TimeOut,
571   IN  BOOLEAN             IsLow,
572   OUT UHCI_QH_RESULT      *QhResult
573   )
574 {
575   UINTN                   Index;
576   UINTN                   Delay;
577   BOOLEAN                 Finished;
578   EFI_STATUS              Status;
579   BOOLEAN                 InfiniteLoop;
580 
581   Finished     = FALSE;
582   Status       = EFI_SUCCESS;
583   Delay        = TimeOut * UHC_1_MILLISECOND;
584   InfiniteLoop = FALSE;
585 
586   //
587   // According to UEFI spec section 16.2.4, If Timeout is 0, then the caller
588   // must wait for the function to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR
589   // is returned.
590   //
591   if (TimeOut == 0) {
592     InfiniteLoop = TRUE;
593   }
594 
595   for (Index = 0; InfiniteLoop || (Index < Delay); Index++) {
596     Finished = UhciCheckTdStatus (Uhc, Td, IsLow, QhResult);
597 
598     //
599     // Transfer is OK or some error occured (TD inactive)
600     //
601     if (Finished) {
602       break;
603     }
604 
605     gBS->Stall (UHC_1_MICROSECOND);
606   }
607 
608   if (!Finished) {
609     DEBUG ((EFI_D_ERROR, "UhciExecuteTransfer: execution not finished for %dms\n", (UINT32)TimeOut));
610     UhciDumpQh (Qh);
611     UhciDumpTds (Td);
612 
613     Status = EFI_TIMEOUT;
614 
615   } else if (QhResult->Result != EFI_USB_NOERROR) {
616     DEBUG ((EFI_D_ERROR, "UhciExecuteTransfer: execution failed with result %x\n", QhResult->Result));
617     UhciDumpQh (Qh);
618     UhciDumpTds (Td);
619 
620     Status = EFI_DEVICE_ERROR;
621   }
622 
623   return Status;
624 }
625 
626 
627 /**
628   Update Async Request, QH and TDs.
629 
630   @param  Uhc                    The UHCI device.
631   @param  AsyncReq               The UHCI asynchronous transfer to update.
632   @param  Result                 Transfer reslut.
633   @param  NextToggle             The toggle of next data.
634 
635 **/
636 VOID
UhciUpdateAsyncReq(IN USB_HC_DEV * Uhc,IN UHCI_ASYNC_REQUEST * AsyncReq,IN UINT32 Result,IN UINT32 NextToggle)637 UhciUpdateAsyncReq (
638   IN USB_HC_DEV          *Uhc,
639   IN UHCI_ASYNC_REQUEST  *AsyncReq,
640   IN UINT32              Result,
641   IN UINT32              NextToggle
642   )
643 {
644   UHCI_QH_SW              *Qh;
645   UHCI_TD_SW              *FirstTd;
646   UHCI_TD_SW              *Td;
647 
648   Qh          = AsyncReq->QhSw;
649   FirstTd     = AsyncReq->FirstTd;
650 
651   if (Result == EFI_USB_NOERROR) {
652     //
653     // The last transfer succeeds. Then we need to update
654     // the Qh and Td for next round of transfer.
655     // 1. Update the TD's data toggle
656     // 2. Activate all the TDs
657     // 3. Link the TD to the queue head again since during
658     //    execution, queue head's TD pointer is changed by
659     //    hardware.
660     //
661     for (Td = FirstTd; Td != NULL; Td = Td->NextTd) {
662       Td->TdHw.DataToggle = NextToggle;
663       NextToggle         ^= 1;
664       Td->TdHw.Status    |= USBTD_ACTIVE;
665     }
666 
667     UhciLinkTdToQh (Uhc, Qh, FirstTd);
668     return ;
669   }
670 }
671 
672 
673 /**
674   Create Async Request node, and Link to List.
675 
676   @param  Uhc                    The UHCI device.
677   @param  Qh                     The queue head of the transfer.
678   @param  FirstTd                First TD of the transfer.
679   @param  DevAddr                Device Address.
680   @param  EndPoint               EndPoint Address.
681   @param  DataLen                Data length.
682   @param  Interval               Polling Interval when inserted to frame list.
683   @param  Data                   Data buffer, unmapped.
684   @param  Callback               Callback after interrupt transfeer.
685   @param  Context                Callback Context passed as function parameter.
686   @param  IsLow                  Is Low Speed.
687 
688   @retval EFI_SUCCESS            An asynchronous transfer is created.
689   @retval EFI_INVALID_PARAMETER  Paremeter is error.
690   @retval EFI_OUT_OF_RESOURCES   Failed because of resource shortage.
691 
692 **/
693 EFI_STATUS
UhciCreateAsyncReq(IN USB_HC_DEV * Uhc,IN UHCI_QH_SW * Qh,IN UHCI_TD_SW * FirstTd,IN UINT8 DevAddr,IN UINT8 EndPoint,IN UINTN DataLen,IN UINTN Interval,IN UINT8 * Data,IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,IN VOID * Context,IN BOOLEAN IsLow)694 UhciCreateAsyncReq (
695   IN USB_HC_DEV                       *Uhc,
696   IN UHCI_QH_SW                       *Qh,
697   IN UHCI_TD_SW                       *FirstTd,
698   IN UINT8                            DevAddr,
699   IN UINT8                            EndPoint,
700   IN UINTN                            DataLen,
701   IN UINTN                            Interval,
702   IN UINT8                            *Data,
703   IN EFI_ASYNC_USB_TRANSFER_CALLBACK  Callback,
704   IN VOID                             *Context,
705   IN BOOLEAN                          IsLow
706   )
707 {
708   UHCI_ASYNC_REQUEST      *AsyncReq;
709 
710   AsyncReq = AllocatePool (sizeof (UHCI_ASYNC_REQUEST));
711 
712   if (AsyncReq == NULL) {
713     return EFI_OUT_OF_RESOURCES;
714   }
715 
716   //
717   // Fill Request field. Data is allocated host memory, not mapped
718   //
719   AsyncReq->Signature   = UHCI_ASYNC_INT_SIGNATURE;
720   AsyncReq->DevAddr     = DevAddr;
721   AsyncReq->EndPoint    = EndPoint;
722   AsyncReq->DataLen     = DataLen;
723   AsyncReq->Interval    = UhciConvertPollRate(Interval);
724   AsyncReq->Data        = Data;
725   AsyncReq->Callback    = Callback;
726   AsyncReq->Context     = Context;
727   AsyncReq->QhSw        = Qh;
728   AsyncReq->FirstTd     = FirstTd;
729   AsyncReq->IsLow       = IsLow;
730 
731   //
732   // Insert the new interrupt transfer to the head of the list.
733   // The interrupt transfer's monitor function scans the whole
734   // list from head to tail. The new interrupt transfer MUST be
735   // added to the head of the list.
736   //
737   InsertHeadList (&(Uhc->AsyncIntList), &(AsyncReq->Link));
738 
739   return EFI_SUCCESS;
740 }
741 
742 
743 /**
744   Free an asynchronous request's resource such as memory.
745 
746   @param  Uhc                    The UHCI device.
747   @param  AsyncReq               The asynchronous request to free.
748 
749 **/
750 VOID
UhciFreeAsyncReq(IN USB_HC_DEV * Uhc,IN UHCI_ASYNC_REQUEST * AsyncReq)751 UhciFreeAsyncReq (
752   IN USB_HC_DEV           *Uhc,
753   IN UHCI_ASYNC_REQUEST   *AsyncReq
754   )
755 {
756   ASSERT ((Uhc != NULL) && (AsyncReq != NULL));
757 
758   UhciDestoryTds (Uhc, AsyncReq->FirstTd);
759   UsbHcFreeMem (Uhc->MemPool, AsyncReq->QhSw, sizeof (UHCI_QH_SW));
760 
761   if (AsyncReq->Data != NULL) {
762     UsbHcFreeMem (Uhc->MemPool, AsyncReq->Data, AsyncReq->DataLen);
763   }
764 
765   gBS->FreePool (AsyncReq);
766 }
767 
768 
769 /**
770   Unlink an asynchronous request's from UHC's asynchronus list.
771   also remove the queue head from the frame list. If FreeNow,
772   release its resource also. Otherwise, add the request to the
773   UHC's recycle list to wait for a while before release the memory.
774   Until then, hardware won't hold point to the request.
775 
776   @param  Uhc                    The UHCI device.
777   @param  AsyncReq               The asynchronous request to free.
778   @param  FreeNow                If TRUE, free the resource immediately, otherwise
779                                  add the request to recycle wait list.
780 
781 **/
782 VOID
UhciUnlinkAsyncReq(IN USB_HC_DEV * Uhc,IN UHCI_ASYNC_REQUEST * AsyncReq,IN BOOLEAN FreeNow)783 UhciUnlinkAsyncReq (
784   IN USB_HC_DEV           *Uhc,
785   IN UHCI_ASYNC_REQUEST   *AsyncReq,
786   IN BOOLEAN              FreeNow
787   )
788 {
789   ASSERT ((Uhc != NULL) && (AsyncReq != NULL));
790 
791   RemoveEntryList (&(AsyncReq->Link));
792   UhciUnlinkQhFromFrameList (Uhc, AsyncReq->QhSw);
793 
794   if (FreeNow) {
795     UhciFreeAsyncReq (Uhc, AsyncReq);
796   } else {
797     //
798     // To sychronize with hardware, mark the queue head as inactive
799     // then add AsyncReq to UHC's recycle list
800     //
801     AsyncReq->QhSw->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);
802     AsyncReq->Recycle = Uhc->RecycleWait;
803     Uhc->RecycleWait  = AsyncReq;
804   }
805 }
806 
807 
808 /**
809   Delete Async Interrupt QH and TDs.
810 
811   @param  Uhc                    The UHCI device.
812   @param  DevAddr                Device Address.
813   @param  EndPoint               EndPoint Address.
814   @param  Toggle                 The next data toggle to use.
815 
816   @retval EFI_SUCCESS            The request is deleted.
817   @retval EFI_INVALID_PARAMETER  Paremeter is error.
818   @retval EFI_NOT_FOUND          The asynchronous isn't found.
819 
820 **/
821 EFI_STATUS
UhciRemoveAsyncReq(IN USB_HC_DEV * Uhc,IN UINT8 DevAddr,IN UINT8 EndPoint,OUT UINT8 * Toggle)822 UhciRemoveAsyncReq (
823   IN  USB_HC_DEV          *Uhc,
824   IN  UINT8               DevAddr,
825   IN  UINT8               EndPoint,
826   OUT UINT8               *Toggle
827   )
828 {
829   EFI_STATUS          Status;
830   UHCI_ASYNC_REQUEST  *AsyncReq;
831   UHCI_QH_RESULT      QhResult;
832   LIST_ENTRY          *Link;
833   BOOLEAN             Found;
834 
835   Status = EFI_SUCCESS;
836 
837   //
838   // If no asynchronous interrupt transaction exists
839   //
840   if (IsListEmpty (&(Uhc->AsyncIntList))) {
841     return EFI_SUCCESS;
842   }
843 
844   //
845   // Find the asynchronous transfer to this device/endpoint pair
846   //
847   Found = FALSE;
848   Link  = Uhc->AsyncIntList.ForwardLink;
849 
850   do {
851     AsyncReq  = UHCI_ASYNC_INT_FROM_LINK (Link);
852     Link      = Link->ForwardLink;
853 
854     if ((AsyncReq->DevAddr == DevAddr) && (AsyncReq->EndPoint == EndPoint)) {
855       Found = TRUE;
856       break;
857     }
858 
859   } while (Link != &(Uhc->AsyncIntList));
860 
861   if (!Found) {
862     return EFI_NOT_FOUND;
863   }
864 
865   //
866   // Check the result of the async transfer then update it
867   // to get the next data toggle to use.
868   //
869   UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult);
870   *Toggle = QhResult.NextToggle;
871 
872   //
873   // Don't release the request now, keep it to synchronize with hardware.
874   //
875   UhciUnlinkAsyncReq (Uhc, AsyncReq, FALSE);
876   return Status;
877 }
878 
879 
880 /**
881   Recycle the asynchronouse request. When a queue head
882   is unlinked from frame list, host controller hardware
883   may still hold a cached pointer to it. To synchronize
884   with hardware, the request is released in two steps:
885   first it is linked to the UHC's RecycleWait list. At
886   the next time UhciMonitorAsyncReqList is fired, it is
887   moved to UHC's Recylelist. Then, at another timer
888   activation, all the requests on Recycle list is freed.
889   This guarrantes that each unlink queue head keeps
890   existing for at least 50ms, far enough for the hardware
891   to clear its cache.
892 
893   @param  Uhc                    The UHCI device.
894 
895 **/
896 VOID
UhciRecycleAsyncReq(IN USB_HC_DEV * Uhc)897 UhciRecycleAsyncReq (
898   IN USB_HC_DEV           *Uhc
899   )
900 {
901   UHCI_ASYNC_REQUEST      *Req;
902   UHCI_ASYNC_REQUEST      *Next;
903 
904   Req = Uhc->Recycle;
905 
906   while (Req != NULL) {
907     Next = Req->Recycle;
908     UhciFreeAsyncReq (Uhc, Req);
909     Req  = Next;
910   }
911 
912   Uhc->Recycle     = Uhc->RecycleWait;
913   Uhc->RecycleWait = NULL;
914 }
915 
916 
917 
918 /**
919   Release all the asynchronous transfers on the lsit.
920 
921   @param  Uhc                    The UHCI device.
922 
923 **/
924 VOID
UhciFreeAllAsyncReq(IN USB_HC_DEV * Uhc)925 UhciFreeAllAsyncReq (
926   IN USB_HC_DEV           *Uhc
927   )
928 {
929   LIST_ENTRY              *Head;
930   UHCI_ASYNC_REQUEST      *AsyncReq;
931 
932   //
933   // Call UhciRecycleAsyncReq twice. The requests on Recycle
934   // will be released at the first call; The requests on
935   // RecycleWait will be released at the second call.
936   //
937   UhciRecycleAsyncReq (Uhc);
938   UhciRecycleAsyncReq (Uhc);
939 
940   Head = &(Uhc->AsyncIntList);
941 
942   if (IsListEmpty (Head)) {
943     return;
944   }
945 
946   while (!IsListEmpty (Head)) {
947     AsyncReq  = UHCI_ASYNC_INT_FROM_LINK (Head->ForwardLink);
948     UhciUnlinkAsyncReq (Uhc, AsyncReq, TRUE);
949   }
950 }
951 
952 
953 /**
954   Interrupt transfer periodic check handler.
955 
956   @param  Event                  The event of the time.
957   @param  Context                Context of the event, pointer to USB_HC_DEV.
958 
959 **/
960 VOID
961 EFIAPI
UhciMonitorAsyncReqList(IN EFI_EVENT Event,IN VOID * Context)962 UhciMonitorAsyncReqList (
963   IN EFI_EVENT            Event,
964   IN VOID                 *Context
965   )
966 {
967   UHCI_ASYNC_REQUEST      *AsyncReq;
968   LIST_ENTRY              *Link;
969   USB_HC_DEV              *Uhc;
970   VOID                    *Data;
971   BOOLEAN                 Finished;
972   UHCI_QH_RESULT          QhResult;
973 
974   Uhc = (USB_HC_DEV *) Context;
975 
976   //
977   // Recycle the asynchronous requests expired, and promote
978   // requests waiting to be recycled the next time when this
979   // timer expires
980   //
981   UhciRecycleAsyncReq (Uhc);
982 
983   if (IsListEmpty (&(Uhc->AsyncIntList))) {
984     return ;
985   }
986 
987   //
988   // This loop must be delete safe
989   //
990   Link = Uhc->AsyncIntList.ForwardLink;
991 
992   do {
993     AsyncReq  = UHCI_ASYNC_INT_FROM_LINK (Link);
994     Link      = Link->ForwardLink;
995 
996     Finished = UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult);
997 
998     if (!Finished) {
999       continue;
1000     }
1001 
1002     //
1003     // Copy the data to temporary buffer if there are some
1004     // data transferred. We may have zero-length packet
1005     //
1006     Data = NULL;
1007 
1008     if (QhResult.Complete != 0) {
1009       Data = AllocatePool (QhResult.Complete);
1010 
1011       if (Data == NULL) {
1012         return ;
1013       }
1014 
1015       CopyMem (Data, AsyncReq->FirstTd->Data, QhResult.Complete);
1016     }
1017 
1018     UhciUpdateAsyncReq (Uhc, AsyncReq, QhResult.Result, QhResult.NextToggle);
1019 
1020     //
1021     // Now, either transfer is SUCCESS or met errors since
1022     // we have skipped to next transfer earlier if current
1023     // transfer is still active.
1024     //
1025     if (QhResult.Result == EFI_USB_NOERROR) {
1026       AsyncReq->Callback (Data, QhResult.Complete, AsyncReq->Context, QhResult.Result);
1027     } else {
1028       //
1029       // Leave error recovery to its related device driver.
1030       // A common case of the error recovery is to re-submit
1031       // the interrupt transfer. When an interrupt transfer
1032       // is re-submitted, its position in the linked list is
1033       // changed. It is inserted to the head of the linked
1034       // list, while this function scans the whole list from
1035       // head to tail. Thus, the re-submitted interrupt transfer's
1036       // callback function will not be called again in this round.
1037       //
1038       AsyncReq->Callback (NULL, 0, AsyncReq->Context, QhResult.Result);
1039     }
1040 
1041     if (Data != NULL) {
1042       gBS->FreePool (Data);
1043     }
1044   } while (Link != &(Uhc->AsyncIntList));
1045 }
1046