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