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