]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.c
1. Added EFI_MEDIA_CHANGED and EFI_INVALID_PARAMETER returns in UsbMassReadBlocks().
[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
913cb9dc 124 Uhc->BulkQh->QhHw.HorizonLink = QH_HLINK (Uhc->BulkQh, FALSE);\r
913cb9dc 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
139ON_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
165VOID\r
166UhciDestoryFrameList (\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
211UINTN\r
212UhciConvertPollRate (\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
244VOID\r
245UhciLinkQhToFrameList (\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
349VOID\r
350UhciUnlinkQhFromFrameList (\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
411STATIC\r
412BOOLEAN\r
413UhciCheckTdStatus (\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
c52fa98c 497 QhResult->NextToggle = (UINT8) (1 - (UINT8)TdHw->DataToggle);\r
913cb9dc 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
1c619535 514 DEBUG ((EFI_D_INFO, "UhciCheckTdStatus: short packet read occured\n"));\r
913cb9dc 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
524ON_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
557EFI_STATUS\r
558UhciExecuteTransfer (\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
41e8ff27 574 Delay = (TimeOut * UHC_1_MILLISECOND / UHC_SYNC_POLL_INTERVAL) + 1;\r
1c619535 575\r
913cb9dc 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
41e8ff27 586 gBS->Stall (UHC_SYNC_POLL_INTERVAL);\r
913cb9dc 587 }\r
588\r
589 if (!Finished) {\r
1c619535 590 DEBUG ((EFI_D_ERROR, "UhciExecuteTransfer: execution not finished for %dms\n", TimeOut));\r
591 UhciDumpQh (Qh);\r
592 UhciDumpTds (Td);\r
913cb9dc 593\r
594 Status = EFI_TIMEOUT;\r
595\r
596 } else if (QhResult->Result != EFI_USB_NOERROR) {\r
1c619535 597 DEBUG ((EFI_D_ERROR, "UhciExecuteTransfer: execution failed with result %x\n", QhResult->Result));\r
598 UhciDumpQh (Qh);\r
599 UhciDumpTds (Td);\r
913cb9dc 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
618STATIC\r
619VOID\r
620UhciUpdateAsyncReq (\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
676EFI_STATUS\r
677UhciCreateAsyncReq (\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
738STATIC\r
739VOID\r
740UhciFreeAsyncReq (\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
777STATIC\r
778VOID\r
779UhciUnlinkAsyncReq (\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
817EFI_STATUS\r
818UhciRemoveAsyncReq (\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
894STATIC\r
895VOID\r
896UhciRecycleAsyncReq (\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
925VOID\r
926UhciFreeAllAsyncReq (\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
963VOID\r
964UhciMonitorAsyncReqList (\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