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