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