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