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