]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Pci/EhciDxe/EhciSched.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / EhciDxe / EhciSched.c
CommitLineData
913cb9dc 1/** @file\r
2\r
78c2ffb5 3 EHCI transfer scheduling routines.\r
4\r
d1102dba 5Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
e33d3e7f 6Copyright (c) Microsoft Corporation.<BR>\r
9d510e61 7SPDX-License-Identifier: BSD-2-Clause-Patent\r
913cb9dc 8\r
913cb9dc 9**/\r
10\r
11#include "Ehci.h"\r
12\r
913cb9dc 13/**\r
78c2ffb5 14 Create helper QTD/QH for the EHCI device.\r
913cb9dc 15\r
78c2ffb5 16 @param Ehc The EHCI device.\r
913cb9dc 17\r
78c2ffb5 18 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH.\r
19 @retval EFI_SUCCESS Helper QH/QTD are created.\r
913cb9dc 20\r
21**/\r
913cb9dc 22EFI_STATUS\r
23EhcCreateHelpQ (\r
1436aea4 24 IN USB2_HC_DEV *Ehc\r
913cb9dc 25 )\r
26{\r
1436aea4
MK
27 USB_ENDPOINT Ep;\r
28 EHC_QH *Qh;\r
29 QH_HW *QhHw;\r
30 EHC_QTD *Qtd;\r
31 EFI_PHYSICAL_ADDRESS PciAddr;\r
913cb9dc 32\r
33 //\r
34 // Create an inactive Qtd to terminate the short packet read.\r
35 //\r
739802e4 36 Qtd = EhcCreateQtd (Ehc, NULL, NULL, 0, QTD_PID_INPUT, 0, 64);\r
913cb9dc 37\r
38 if (Qtd == NULL) {\r
39 return EFI_OUT_OF_RESOURCES;\r
40 }\r
41\r
1436aea4
MK
42 Qtd->QtdHw.Status = QTD_STAT_HALTED;\r
43 Ehc->ShortReadStop = Qtd;\r
913cb9dc 44\r
45 //\r
46 // Create a QH to act as the EHC reclamation header.\r
47 // Set the header to loopback to itself.\r
48 //\r
1436aea4
MK
49 Ep.DevAddr = 0;\r
50 Ep.EpAddr = 1;\r
51 Ep.Direction = EfiUsbDataIn;\r
52 Ep.DevSpeed = EFI_USB_SPEED_HIGH;\r
53 Ep.MaxPacket = 64;\r
54 Ep.HubAddr = 0;\r
55 Ep.HubPort = 0;\r
56 Ep.Toggle = 0;\r
57 Ep.Type = EHC_BULK_TRANSFER;\r
58 Ep.PollRate = 1;\r
59\r
60 Qh = EhcCreateQh (Ehc, &Ep);\r
913cb9dc 61\r
62 if (Qh == NULL) {\r
63 return EFI_OUT_OF_RESOURCES;\r
64 }\r
65\r
739802e4 66 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));\r
913cb9dc 67 QhHw = &Qh->QhHw;\r
1436aea4 68 QhHw->HorizonLink = QH_LINK (PciAddr + OFFSET_OF (EHC_QH, QhHw), EHC_TYPE_QH, FALSE);\r
913cb9dc 69 QhHw->Status = QTD_STAT_HALTED;\r
70 QhHw->ReclaimHead = 1;\r
3a2f9cce 71 Qh->NextQh = Qh;\r
913cb9dc 72 Ehc->ReclaimHead = Qh;\r
73\r
74 //\r
75 // Create a dummy QH to act as the terminator for periodical schedule\r
76 //\r
1436aea4
MK
77 Ep.EpAddr = 2;\r
78 Ep.Type = EHC_INT_TRANSFER_SYNC;\r
913cb9dc 79\r
1436aea4 80 Qh = EhcCreateQh (Ehc, &Ep);\r
913cb9dc 81\r
82 if (Qh == NULL) {\r
83 return EFI_OUT_OF_RESOURCES;\r
84 }\r
85\r
86 Qh->QhHw.Status = QTD_STAT_HALTED;\r
87 Ehc->PeriodOne = Qh;\r
88\r
89 return EFI_SUCCESS;\r
90}\r
91\r
913cb9dc 92/**\r
78c2ffb5 93 Initialize the schedule data structure such as frame list.\r
913cb9dc 94\r
78c2ffb5 95 @param Ehc The EHCI device to init schedule data.\r
913cb9dc 96\r
78c2ffb5 97 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.\r
98 @retval EFI_SUCCESS The schedule data is initialized.\r
913cb9dc 99\r
100**/\r
101EFI_STATUS\r
102EhcInitSched (\r
1436aea4 103 IN USB2_HC_DEV *Ehc\r
913cb9dc 104 )\r
105{\r
106 EFI_PCI_IO_PROTOCOL *PciIo;\r
107 VOID *Buf;\r
108 EFI_PHYSICAL_ADDRESS PhyAddr;\r
109 VOID *Map;\r
110 UINTN Pages;\r
111 UINTN Bytes;\r
112 UINTN Index;\r
913cb9dc 113 EFI_STATUS Status;\r
739802e4 114 EFI_PHYSICAL_ADDRESS PciAddr;\r
913cb9dc 115\r
116 //\r
117 // First initialize the periodical schedule data:\r
118 // 1. Allocate and map the memory for the frame list\r
119 // 2. Create the help QTD/QH\r
120 // 3. Initialize the frame entries\r
121 // 4. Set the frame list register\r
122 //\r
123 PciIo = Ehc->PciIo;\r
124\r
125 Bytes = 4096;\r
126 Pages = EFI_SIZE_TO_PAGES (Bytes);\r
127\r
128 Status = PciIo->AllocateBuffer (\r
129 PciIo,\r
130 AllocateAnyPages,\r
131 EfiBootServicesData,\r
132 Pages,\r
133 &Buf,\r
134 0\r
135 );\r
136\r
137 if (EFI_ERROR (Status)) {\r
138 return EFI_OUT_OF_RESOURCES;\r
139 }\r
140\r
141 Status = PciIo->Map (\r
142 PciIo,\r
143 EfiPciIoOperationBusMasterCommonBuffer,\r
144 Buf,\r
145 &Bytes,\r
146 &PhyAddr,\r
147 &Map\r
148 );\r
149\r
150 if (EFI_ERROR (Status) || (Bytes != 4096)) {\r
151 PciIo->FreeBuffer (PciIo, Pages, Buf);\r
152 return EFI_OUT_OF_RESOURCES;\r
153 }\r
154\r
1436aea4
MK
155 Ehc->PeriodFrame = Buf;\r
156 Ehc->PeriodFrameMap = Map;\r
592b87a4 157\r
158 //\r
159 // Program the FRAMELISTBASE register with the low 32 bit addr\r
160 //\r
161 EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (PhyAddr));\r
162 //\r
163 // Program the CTRLDSSEGMENT register with the high 32 bit addr\r
164 //\r
165 EhcWriteOpReg (Ehc, EHC_CTRLDSSEG_OFFSET, EHC_HIGH_32BIT (PhyAddr));\r
913cb9dc 166\r
167 //\r
168 // Init memory pool management then create the helper\r
169 // QTD/QH. If failed, previously allocated resources\r
170 // will be freed by EhcFreeSched\r
171 //\r
172 Ehc->MemPool = UsbHcInitMemPool (\r
173 PciIo,\r
167c3fb4 174 Ehc->Support64BitDma,\r
592b87a4 175 EHC_HIGH_32BIT (PhyAddr)\r
913cb9dc 176 );\r
177\r
178 if (Ehc->MemPool == NULL) {\r
60cf9cfc 179 Status = EFI_OUT_OF_RESOURCES;\r
180 goto ErrorExit1;\r
913cb9dc 181 }\r
182\r
183 Status = EhcCreateHelpQ (Ehc);\r
184\r
185 if (EFI_ERROR (Status)) {\r
592b87a4 186 goto ErrorExit;\r
913cb9dc 187 }\r
188\r
189 //\r
190 // Initialize the frame list entries then set the registers\r
191 //\r
1436aea4 192 Ehc->PeriodFrameHost = AllocateZeroPool (EHC_FRAME_LEN * sizeof (UINTN));\r
592b87a4 193 if (Ehc->PeriodFrameHost == NULL) {\r
194 Status = EFI_OUT_OF_RESOURCES;\r
195 goto ErrorExit;\r
196 }\r
197\r
1436aea4 198 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (EHC_QH));\r
913cb9dc 199\r
200 for (Index = 0; Index < EHC_FRAME_LEN; Index++) {\r
592b87a4 201 //\r
202 // Store the pci bus address of the QH in period frame list which will be accessed by pci bus master.\r
203 //\r
204 ((UINT32 *)(Ehc->PeriodFrame))[Index] = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
205 //\r
206 // Store the host address of the QH in period frame list which will be accessed by host.\r
207 //\r
208 ((UINTN *)(Ehc->PeriodFrameHost))[Index] = (UINTN)Ehc->PeriodOne;\r
913cb9dc 209 }\r
210\r
913cb9dc 211 //\r
212 // Second initialize the asynchronous schedule:\r
213 // Only need to set the AsynListAddr register to\r
214 // the reclamation header\r
215 //\r
739802e4 216 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (EHC_QH));\r
217 EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (PciAddr));\r
913cb9dc 218 return EFI_SUCCESS;\r
592b87a4 219\r
220ErrorExit:\r
592b87a4 221 if (Ehc->PeriodOne != NULL) {\r
222 UsbHcFreeMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (EHC_QH));\r
223 Ehc->PeriodOne = NULL;\r
224 }\r
225\r
226 if (Ehc->ReclaimHead != NULL) {\r
227 UsbHcFreeMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (EHC_QH));\r
228 Ehc->ReclaimHead = NULL;\r
229 }\r
230\r
231 if (Ehc->ShortReadStop != NULL) {\r
232 UsbHcFreeMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (EHC_QTD));\r
233 Ehc->ShortReadStop = NULL;\r
234 }\r
60cf9cfc 235\r
236ErrorExit1:\r
237 PciIo->FreeBuffer (PciIo, Pages, Buf);\r
238 PciIo->Unmap (PciIo, Map);\r
239\r
592b87a4 240 return Status;\r
913cb9dc 241}\r
242\r
913cb9dc 243/**\r
244 Free the schedule data. It may be partially initialized.\r
245\r
78c2ffb5 246 @param Ehc The EHCI device.\r
913cb9dc 247\r
913cb9dc 248**/\r
249VOID\r
250EhcFreeSched (\r
1436aea4 251 IN USB2_HC_DEV *Ehc\r
913cb9dc 252 )\r
253{\r
1436aea4 254 EFI_PCI_IO_PROTOCOL *PciIo;\r
913cb9dc 255\r
256 EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, 0);\r
257 EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, 0);\r
258\r
259 if (Ehc->PeriodOne != NULL) {\r
260 UsbHcFreeMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (EHC_QH));\r
261 Ehc->PeriodOne = NULL;\r
262 }\r
263\r
264 if (Ehc->ReclaimHead != NULL) {\r
265 UsbHcFreeMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (EHC_QH));\r
266 Ehc->ReclaimHead = NULL;\r
267 }\r
268\r
269 if (Ehc->ShortReadStop != NULL) {\r
270 UsbHcFreeMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (EHC_QTD));\r
271 Ehc->ShortReadStop = NULL;\r
272 }\r
273\r
274 if (Ehc->MemPool != NULL) {\r
275 UsbHcFreeMemPool (Ehc->MemPool);\r
276 Ehc->MemPool = NULL;\r
277 }\r
278\r
592b87a4 279 if (Ehc->PeriodFrame != NULL) {\r
913cb9dc 280 PciIo = Ehc->PciIo;\r
281 ASSERT (PciIo != NULL);\r
282\r
283 PciIo->Unmap (PciIo, Ehc->PeriodFrameMap);\r
284\r
285 PciIo->FreeBuffer (\r
286 PciIo,\r
287 EFI_SIZE_TO_PAGES (EFI_PAGE_SIZE),\r
592b87a4 288 Ehc->PeriodFrame\r
913cb9dc 289 );\r
290\r
592b87a4 291 Ehc->PeriodFrame = NULL;\r
292 }\r
293\r
294 if (Ehc->PeriodFrameHost != NULL) {\r
295 FreePool (Ehc->PeriodFrameHost);\r
739802e4 296 Ehc->PeriodFrameHost = NULL;\r
913cb9dc 297 }\r
298}\r
299\r
913cb9dc 300/**\r
301 Link the queue head to the asynchronous schedule list.\r
302 UEFI only supports one CTRL/BULK transfer at a time\r
303 due to its interfaces. This simplifies the AsynList\r
304 management: A reclamation header is always linked to\r
305 the AsyncListAddr, the only active QH is appended to it.\r
306\r
78c2ffb5 307 @param Ehc The EHCI device.\r
308 @param Qh The queue head to link.\r
913cb9dc 309\r
913cb9dc 310**/\r
311VOID\r
312EhcLinkQhToAsync (\r
1436aea4
MK
313 IN USB2_HC_DEV *Ehc,\r
314 IN EHC_QH *Qh\r
913cb9dc 315 )\r
316{\r
1436aea4
MK
317 EHC_QH *Head;\r
318 EFI_PHYSICAL_ADDRESS PciAddr;\r
913cb9dc 319\r
320 //\r
321 // Append the queue head after the reclaim header, then\r
322 // fix the hardware visiable parts (EHCI R1.0 page 72).\r
323 // ReclaimHead is always linked to the EHCI's AsynListAddr.\r
324 //\r
1436aea4 325 Head = Ehc->ReclaimHead;\r
913cb9dc 326\r
1436aea4
MK
327 Qh->NextQh = Head->NextQh;\r
328 Head->NextQh = Qh;\r
913cb9dc 329\r
1436aea4
MK
330 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh->NextQh, sizeof (EHC_QH));\r
331 Qh->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
332 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Head->NextQh, sizeof (EHC_QH));\r
333 Head->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
913cb9dc 334}\r
335\r
913cb9dc 336/**\r
337 Unlink a queue head from the asynchronous schedule list.\r
78c2ffb5 338 Need to synchronize with hardware.\r
913cb9dc 339\r
78c2ffb5 340 @param Ehc The EHCI device.\r
341 @param Qh The queue head to unlink.\r
913cb9dc 342\r
913cb9dc 343**/\r
344VOID\r
345EhcUnlinkQhFromAsync (\r
1436aea4
MK
346 IN USB2_HC_DEV *Ehc,\r
347 IN EHC_QH *Qh\r
913cb9dc 348 )\r
349{\r
1436aea4
MK
350 EHC_QH *Head;\r
351 EFI_STATUS Status;\r
352 EFI_PHYSICAL_ADDRESS PciAddr;\r
913cb9dc 353\r
354 ASSERT (Ehc->ReclaimHead->NextQh == Qh);\r
355\r
356 //\r
357 // Remove the QH from reclamation head, then update the hardware\r
358 // visiable part: Only need to loopback the ReclaimHead. The Qh\r
359 // is pointing to ReclaimHead (which is staill in the list).\r
360 //\r
1436aea4 361 Head = Ehc->ReclaimHead;\r
913cb9dc 362\r
1436aea4
MK
363 Head->NextQh = Qh->NextQh;\r
364 Qh->NextQh = NULL;\r
913cb9dc 365\r
1436aea4
MK
366 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Head->NextQh, sizeof (EHC_QH));\r
367 Head->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
913cb9dc 368\r
369 //\r
370 // Set and wait the door bell to synchronize with the hardware\r
371 //\r
41e8ff27 372 Status = EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIMEOUT);\r
913cb9dc 373\r
374 if (EFI_ERROR (Status)) {\r
87000d77 375 DEBUG ((DEBUG_ERROR, "EhcUnlinkQhFromAsync: Failed to synchronize with doorbell\n"));\r
913cb9dc 376 }\r
377}\r
378\r
913cb9dc 379/**\r
380 Link a queue head for interrupt transfer to the periodic\r
381 schedule frame list. This code is very much the same as\r
382 that in UHCI.\r
383\r
78c2ffb5 384 @param Ehc The EHCI device.\r
385 @param Qh The queue head to link.\r
913cb9dc 386\r
913cb9dc 387**/\r
388VOID\r
389EhcLinkQhToPeriod (\r
1436aea4
MK
390 IN USB2_HC_DEV *Ehc,\r
391 IN EHC_QH *Qh\r
913cb9dc 392 )\r
393{\r
1436aea4
MK
394 UINTN Index;\r
395 EHC_QH *Prev;\r
396 EHC_QH *Next;\r
397 EFI_PHYSICAL_ADDRESS PciAddr;\r
913cb9dc 398\r
913cb9dc 399 for (Index = 0; Index < EHC_FRAME_LEN; Index += Qh->Interval) {\r
400 //\r
401 // First QH can't be NULL because we always keep PeriodOne\r
402 // heads on the frame list\r
403 //\r
1436aea4
MK
404 ASSERT (!EHC_LINK_TERMINATED (((UINT32 *)Ehc->PeriodFrame)[Index]));\r
405 Next = (EHC_QH *)((UINTN *)Ehc->PeriodFrameHost)[Index];\r
406 Prev = NULL;\r
913cb9dc 407\r
408 //\r
409 // Now, insert the queue head (Qh) into this frame:\r
410 // 1. Find a queue head with the same poll interval, just insert\r
411 // Qh after this queue head, then we are done.\r
412 //\r
413 // 2. Find the position to insert the queue head into:\r
414 // Previous head's interval is bigger than Qh's\r
415 // Next head's interval is less than Qh's\r
416 // Then, insert the Qh between then\r
417 //\r
418 while (Next->Interval > Qh->Interval) {\r
1436aea4
MK
419 Prev = Next;\r
420 Next = Next->NextQh;\r
913cb9dc 421 }\r
422\r
423 ASSERT (Next != NULL);\r
424\r
425 //\r
426 // The entry may have been linked into the frame by early insertation.\r
427 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh\r
428 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.\r
429 // It isn't necessary to compare all the QH with the same interval to\r
430 // Qh. This is because if there is other QH with the same interval, Qh\r
431 // should has been inserted after that at Frames[0] and at Frames[0] it is\r
432 // impossible for (Next == Qh)\r
433 //\r
434 if (Next == Qh) {\r
435 continue;\r
436 }\r
437\r
438 if (Next->Interval == Qh->Interval) {\r
439 //\r
440 // If there is a QH with the same interval, it locates at\r
441 // Frames[0], and we can simply insert it after this QH. We\r
442 // are all done.\r
443 //\r
444 ASSERT ((Index == 0) && (Qh->NextQh == NULL));\r
445\r
1436aea4
MK
446 Prev = Next;\r
447 Next = Next->NextQh;\r
913cb9dc 448\r
1436aea4
MK
449 Qh->NextQh = Next;\r
450 Prev->NextQh = Qh;\r
913cb9dc 451\r
1436aea4
MK
452 Qh->QhHw.HorizonLink = Prev->QhHw.HorizonLink;\r
453 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));\r
454 Prev->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
913cb9dc 455 break;\r
456 }\r
457\r
458 //\r
459 // OK, find the right position, insert it in. If Qh's next\r
460 // link has already been set, it is in position. This is\r
461 // guarranted by 2^n polling interval.\r
462 //\r
463 if (Qh->NextQh == NULL) {\r
1436aea4
MK
464 Qh->NextQh = Next;\r
465 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Next, sizeof (EHC_QH));\r
466 Qh->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
913cb9dc 467 }\r
468\r
739802e4 469 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));\r
470\r
913cb9dc 471 if (Prev == NULL) {\r
1436aea4
MK
472 ((UINT32 *)Ehc->PeriodFrame)[Index] = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
473 ((UINTN *)Ehc->PeriodFrameHost)[Index] = (UINTN)Qh;\r
913cb9dc 474 } else {\r
1436aea4
MK
475 Prev->NextQh = Qh;\r
476 Prev->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
913cb9dc 477 }\r
478 }\r
479}\r
480\r
913cb9dc 481/**\r
482 Unlink an interrupt queue head from the periodic\r
78c2ffb5 483 schedule frame list.\r
913cb9dc 484\r
78c2ffb5 485 @param Ehc The EHCI device.\r
486 @param Qh The queue head to unlink.\r
913cb9dc 487\r
913cb9dc 488**/\r
489VOID\r
490EhcUnlinkQhFromPeriod (\r
1436aea4
MK
491 IN USB2_HC_DEV *Ehc,\r
492 IN EHC_QH *Qh\r
913cb9dc 493 )\r
494{\r
1436aea4
MK
495 UINTN Index;\r
496 EHC_QH *Prev;\r
497 EHC_QH *This;\r
913cb9dc 498\r
913cb9dc 499 for (Index = 0; Index < EHC_FRAME_LEN; Index += Qh->Interval) {\r
500 //\r
501 // Frame link can't be NULL because we always keep PeroidOne\r
502 // on the frame list\r
503 //\r
1436aea4
MK
504 ASSERT (!EHC_LINK_TERMINATED (((UINT32 *)Ehc->PeriodFrame)[Index]));\r
505 This = (EHC_QH *)((UINTN *)Ehc->PeriodFrameHost)[Index];\r
506 Prev = NULL;\r
913cb9dc 507\r
508 //\r
509 // Walk through the frame's QH list to find the\r
510 // queue head to remove\r
511 //\r
512 while ((This != NULL) && (This != Qh)) {\r
1436aea4
MK
513 Prev = This;\r
514 This = This->NextQh;\r
913cb9dc 515 }\r
516\r
517 //\r
518 // Qh may have already been unlinked from this frame\r
519 // by early action. See the comments in EhcLinkQhToPeriod.\r
520 //\r
521 if (This == NULL) {\r
522 continue;\r
523 }\r
524\r
525 if (Prev == NULL) {\r
526 //\r
527 // Qh is the first entry in the frame\r
528 //\r
1436aea4
MK
529 ((UINT32 *)Ehc->PeriodFrame)[Index] = Qh->QhHw.HorizonLink;\r
530 ((UINTN *)Ehc->PeriodFrameHost)[Index] = (UINTN)Qh->NextQh;\r
913cb9dc 531 } else {\r
1436aea4
MK
532 Prev->NextQh = Qh->NextQh;\r
533 Prev->QhHw.HorizonLink = Qh->QhHw.HorizonLink;\r
913cb9dc 534 }\r
535 }\r
536}\r
537\r
913cb9dc 538/**\r
539 Check the URB's execution result and update the URB's\r
540 result accordingly.\r
541\r
78c2ffb5 542 @param Ehc The EHCI device.\r
543 @param Urb The URB to check result.\r
913cb9dc 544\r
545 @return Whether the result of URB transfer is finialized.\r
546\r
547**/\r
913cb9dc 548BOOLEAN\r
549EhcCheckUrbResult (\r
1436aea4
MK
550 IN USB2_HC_DEV *Ehc,\r
551 IN URB *Urb\r
913cb9dc 552 )\r
553{\r
1436aea4
MK
554 LIST_ENTRY *Entry;\r
555 EHC_QTD *Qtd;\r
556 QTD_HW *QtdHw;\r
557 UINT8 State;\r
558 BOOLEAN Finished;\r
559 EFI_PHYSICAL_ADDRESS PciAddr;\r
913cb9dc 560\r
561 ASSERT ((Ehc != NULL) && (Urb != NULL) && (Urb->Qh != NULL));\r
562\r
1436aea4
MK
563 Finished = TRUE;\r
564 Urb->Completed = 0;\r
913cb9dc 565\r
1436aea4 566 Urb->Result = EFI_USB_NOERROR;\r
913cb9dc 567\r
568 if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {\r
569 Urb->Result |= EFI_USB_ERR_SYSTEM;\r
570 goto ON_EXIT;\r
571 }\r
572\r
e33d3e7f 573 BASE_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {\r
913cb9dc 574 Qtd = EFI_LIST_CONTAINER (Entry, EHC_QTD, QtdList);\r
575 QtdHw = &Qtd->QtdHw;\r
1436aea4 576 State = (UINT8)QtdHw->Status;\r
913cb9dc 577\r
578 if (EHC_BIT_IS_SET (State, QTD_STAT_HALTED)) {\r
579 //\r
580 // EHCI will halt the queue head when met some error.\r
581 // If it is halted, the result of URB is finialized.\r
582 //\r
583 if ((State & QTD_STAT_ERR_MASK) == 0) {\r
584 Urb->Result |= EFI_USB_ERR_STALL;\r
585 }\r
586\r
587 if (EHC_BIT_IS_SET (State, QTD_STAT_BABBLE_ERR)) {\r
588 Urb->Result |= EFI_USB_ERR_BABBLE;\r
589 }\r
590\r
591 if (EHC_BIT_IS_SET (State, QTD_STAT_BUFF_ERR)) {\r
592 Urb->Result |= EFI_USB_ERR_BUFFER;\r
593 }\r
594\r
595 if (EHC_BIT_IS_SET (State, QTD_STAT_TRANS_ERR) && (QtdHw->ErrCnt == 0)) {\r
596 Urb->Result |= EFI_USB_ERR_TIMEOUT;\r
597 }\r
598\r
599 Finished = TRUE;\r
600 goto ON_EXIT;\r
913cb9dc 601 } else if (EHC_BIT_IS_SET (State, QTD_STAT_ACTIVE)) {\r
602 //\r
603 // The QTD is still active, no need to check furthur.\r
604 //\r
605 Urb->Result |= EFI_USB_ERR_NOTEXECUTE;\r
606\r
607 Finished = FALSE;\r
608 goto ON_EXIT;\r
913cb9dc 609 } else {\r
610 //\r
611 // This QTD is finished OK or met short packet read. Update the\r
612 // transfer length if it isn't a setup.\r
613 //\r
614 if (QtdHw->Pid != QTD_PID_SETUP) {\r
615 Urb->Completed += Qtd->DataLen - QtdHw->TotalBytes;\r
616 }\r
617\r
618 if ((QtdHw->TotalBytes != 0) && (QtdHw->Pid == QTD_PID_INPUT)) {\r
1c619535 619 EhcDumpQh (Urb->Qh, "Short packet read", FALSE);\r
913cb9dc 620\r
621 //\r
622 // Short packet read condition. If it isn't a setup transfer,\r
623 // no need to check furthur: the queue head will halt at the\r
624 // ShortReadStop. If it is a setup transfer, need to check the\r
625 // Status Stage of the setup transfer to get the finial result\r
626 //\r
739802e4 627 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (EHC_QTD));\r
628 if (QtdHw->AltNext == QTD_LINK (PciAddr, FALSE)) {\r
87000d77 629 DEBUG ((DEBUG_VERBOSE, "EhcCheckUrbResult: Short packet read, break\n"));\r
913cb9dc 630\r
631 Finished = TRUE;\r
632 goto ON_EXIT;\r
633 }\r
634\r
87000d77 635 DEBUG ((DEBUG_VERBOSE, "EhcCheckUrbResult: Short packet read, continue\n"));\r
913cb9dc 636 }\r
637 }\r
638 }\r
639\r
640ON_EXIT:\r
641 //\r
642 // Return the data toggle set by EHCI hardware, bulk and interrupt\r
643 // transfer will use this to initialize the next transaction. For\r
644 // Control transfer, it always start a new data toggle sequence for\r
645 // new transfer.\r
646 //\r
647 // NOTICE: don't move DT update before the loop, otherwise there is\r
648 // a race condition that DT is wrong.\r
649 //\r
1436aea4 650 Urb->DataToggle = (UINT8)Urb->Qh->QhHw.DataToggle;\r
913cb9dc 651\r
652 return Finished;\r
653}\r
654\r
913cb9dc 655/**\r
656 Execute the transfer by polling the URB. This is a synchronous operation.\r
657\r
78c2ffb5 658 @param Ehc The EHCI device.\r
659 @param Urb The URB to execute.\r
660 @param TimeOut The time to wait before abort, in millisecond.\r
913cb9dc 661\r
78c2ffb5 662 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.\r
663 @return EFI_TIMEOUT The transfer failed due to time out.\r
664 @return EFI_SUCCESS The transfer finished OK.\r
913cb9dc 665\r
666**/\r
667EFI_STATUS\r
668EhcExecTransfer (\r
1436aea4
MK
669 IN USB2_HC_DEV *Ehc,\r
670 IN URB *Urb,\r
671 IN UINTN TimeOut\r
913cb9dc 672 )\r
673{\r
1436aea4
MK
674 EFI_STATUS Status;\r
675 UINTN Index;\r
676 UINTN Loop;\r
677 BOOLEAN Finished;\r
678 BOOLEAN InfiniteLoop;\r
913cb9dc 679\r
17a6c337 680 Status = EFI_SUCCESS;\r
ca243131 681 Loop = TimeOut * EHC_1_MILLISECOND;\r
17a6c337 682 Finished = FALSE;\r
683 InfiniteLoop = FALSE;\r
913cb9dc 684\r
17a6c337 685 //\r
686 // According to UEFI spec section 16.2.4, If Timeout is 0, then the caller\r
687 // must wait for the function to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR\r
688 // is returned.\r
689 //\r
690 if (TimeOut == 0) {\r
691 InfiniteLoop = TRUE;\r
692 }\r
693\r
694 for (Index = 0; InfiniteLoop || (Index < Loop); Index++) {\r
913cb9dc 695 Finished = EhcCheckUrbResult (Ehc, Urb);\r
696\r
697 if (Finished) {\r
698 break;\r
699 }\r
700\r
ca243131 701 gBS->Stall (EHC_1_MICROSECOND);\r
913cb9dc 702 }\r
703\r
704 if (!Finished) {\r
d9e7f6fe 705 DEBUG ((DEBUG_VERBOSE, "EhcExecTransfer: transfer not finished in %dms\n", (UINT32)TimeOut));\r
1c619535 706 EhcDumpQh (Urb->Qh, NULL, FALSE);\r
913cb9dc 707\r
708 Status = EFI_TIMEOUT;\r
913cb9dc 709 } else if (Urb->Result != EFI_USB_NOERROR) {\r
87000d77 710 DEBUG ((DEBUG_ERROR, "EhcExecTransfer: transfer failed with %x\n", Urb->Result));\r
1c619535 711 EhcDumpQh (Urb->Qh, NULL, FALSE);\r
913cb9dc 712\r
713 Status = EFI_DEVICE_ERROR;\r
714 }\r
715\r
716 return Status;\r
717}\r
718\r
913cb9dc 719/**\r
720 Delete a single asynchronous interrupt transfer for\r
78c2ffb5 721 the device and endpoint.\r
913cb9dc 722\r
78c2ffb5 723 @param Ehc The EHCI device.\r
724 @param DevAddr The address of the target device.\r
725 @param EpNum The endpoint of the target.\r
726 @param DataToggle Return the next data toggle to use.\r
913cb9dc 727\r
78c2ffb5 728 @retval EFI_SUCCESS An asynchronous transfer is removed.\r
729 @retval EFI_NOT_FOUND No transfer for the device is found.\r
913cb9dc 730\r
731**/\r
732EFI_STATUS\r
733EhciDelAsyncIntTransfer (\r
1436aea4
MK
734 IN USB2_HC_DEV *Ehc,\r
735 IN UINT8 DevAddr,\r
736 IN UINT8 EpNum,\r
737 OUT UINT8 *DataToggle\r
913cb9dc 738 )\r
739{\r
740 LIST_ENTRY *Entry;\r
741 LIST_ENTRY *Next;\r
742 URB *Urb;\r
743 EFI_USB_DATA_DIRECTION Direction;\r
744\r
78c2ffb5 745 Direction = (((EpNum & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut);\r
913cb9dc 746 EpNum &= 0x0F;\r
747\r
e33d3e7f 748 BASE_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {\r
913cb9dc 749 Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);\r
750\r
751 if ((Urb->Ep.DevAddr == DevAddr) && (Urb->Ep.EpAddr == EpNum) &&\r
1436aea4
MK
752 (Urb->Ep.Direction == Direction))\r
753 {\r
913cb9dc 754 //\r
755 // Check the URB status to retrieve the next data toggle\r
756 // from the associated queue head.\r
757 //\r
758 EhcCheckUrbResult (Ehc, Urb);\r
759 *DataToggle = Urb->DataToggle;\r
760\r
761 EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);\r
762 RemoveEntryList (&Urb->UrbList);\r
763\r
f87db256 764 gBS->FreePool (Urb->Data);\r
913cb9dc 765 EhcFreeUrb (Ehc, Urb);\r
766 return EFI_SUCCESS;\r
767 }\r
768 }\r
769\r
770 return EFI_NOT_FOUND;\r
771}\r
772\r
913cb9dc 773/**\r
78c2ffb5 774 Remove all the asynchronous interrutp transfers.\r
913cb9dc 775\r
78c2ffb5 776 @param Ehc The EHCI device.\r
913cb9dc 777\r
913cb9dc 778**/\r
779VOID\r
780EhciDelAllAsyncIntTransfers (\r
1436aea4 781 IN USB2_HC_DEV *Ehc\r
913cb9dc 782 )\r
783{\r
1436aea4
MK
784 LIST_ENTRY *Entry;\r
785 LIST_ENTRY *Next;\r
786 URB *Urb;\r
913cb9dc 787\r
e33d3e7f 788 BASE_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {\r
913cb9dc 789 Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);\r
790\r
791 EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);\r
792 RemoveEntryList (&Urb->UrbList);\r
793\r
f87db256 794 gBS->FreePool (Urb->Data);\r
913cb9dc 795 EhcFreeUrb (Ehc, Urb);\r
796 }\r
797}\r
798\r
4f792685
SZ
799/**\r
800 Insert a single asynchronous interrupt transfer for\r
801 the device and endpoint.\r
802\r
803 @param Ehc The EHCI device.\r
804 @param DevAddr The device address.\r
805 @param EpAddr Endpoint addrress & its direction.\r
806 @param DevSpeed The device speed.\r
807 @param Toggle Initial data toggle to use.\r
808 @param MaxPacket The max packet length of the endpoint.\r
809 @param Hub The transaction translator to use.\r
810 @param DataLen The length of data buffer.\r
811 @param Callback The function to call when data is transferred.\r
812 @param Context The context to the callback.\r
813 @param Interval The interval for interrupt transfer.\r
814\r
815 @return Created URB or NULL.\r
816\r
817**/\r
818URB *\r
819EhciInsertAsyncIntTransfer (\r
1436aea4
MK
820 IN USB2_HC_DEV *Ehc,\r
821 IN UINT8 DevAddr,\r
822 IN UINT8 EpAddr,\r
823 IN UINT8 DevSpeed,\r
824 IN UINT8 Toggle,\r
825 IN UINTN MaxPacket,\r
826 IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Hub,\r
827 IN UINTN DataLen,\r
828 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,\r
829 IN VOID *Context,\r
830 IN UINTN Interval\r
4f792685
SZ
831 )\r
832{\r
1436aea4
MK
833 VOID *Data;\r
834 URB *Urb;\r
4f792685 835\r
f87db256
SZ
836 Data = AllocatePool (DataLen);\r
837\r
838 if (Data == NULL) {\r
839 DEBUG ((DEBUG_ERROR, "%a: failed to allocate buffer\n", __FUNCTION__));\r
840 return NULL;\r
841 }\r
842\r
4f792685
SZ
843 Urb = EhcCreateUrb (\r
844 Ehc,\r
845 DevAddr,\r
846 EpAddr,\r
847 DevSpeed,\r
848 Toggle,\r
849 MaxPacket,\r
850 Hub,\r
851 EHC_INT_TRANSFER_ASYNC,\r
852 NULL,\r
f87db256 853 Data,\r
4f792685
SZ
854 DataLen,\r
855 Callback,\r
856 Context,\r
857 Interval\r
858 );\r
859\r
860 if (Urb == NULL) {\r
861 DEBUG ((DEBUG_ERROR, "%a: failed to create URB\n", __FUNCTION__));\r
f87db256 862 gBS->FreePool (Data);\r
4f792685
SZ
863 return NULL;\r
864 }\r
865\r
866 //\r
867 // New asynchronous transfer must inserted to the head.\r
868 // Check the comments in EhcMoniteAsyncRequests\r
869 //\r
870 EhcLinkQhToPeriod (Ehc, Urb->Qh);\r
871 InsertHeadList (&Ehc->AsyncIntTransfers, &Urb->UrbList);\r
872\r
873 return Urb;\r
874}\r
50fa1b3a 875\r
f87db256
SZ
876/**\r
877 Flush data from PCI controller specific address to mapped system\r
878 memory address.\r
879\r
880 @param Ehc The EHCI device.\r
881 @param Urb The URB to unmap.\r
882\r
883 @retval EFI_SUCCESS Success to flush data to mapped system memory.\r
884 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.\r
885\r
886**/\r
887EFI_STATUS\r
888EhcFlushAsyncIntMap (\r
1436aea4
MK
889 IN USB2_HC_DEV *Ehc,\r
890 IN URB *Urb\r
f87db256
SZ
891 )\r
892{\r
1436aea4
MK
893 EFI_STATUS Status;\r
894 EFI_PHYSICAL_ADDRESS PhyAddr;\r
895 EFI_PCI_IO_PROTOCOL_OPERATION MapOp;\r
896 EFI_PCI_IO_PROTOCOL *PciIo;\r
897 UINTN Len;\r
898 VOID *Map;\r
f87db256
SZ
899\r
900 PciIo = Ehc->PciIo;\r
901 Len = Urb->DataLen;\r
902\r
903 if (Urb->Ep.Direction == EfiUsbDataIn) {\r
904 MapOp = EfiPciIoOperationBusMasterWrite;\r
905 } else {\r
906 MapOp = EfiPciIoOperationBusMasterRead;\r
907 }\r
908\r
909 Status = PciIo->Unmap (PciIo, Urb->DataMap);\r
910 if (EFI_ERROR (Status)) {\r
911 goto ON_ERROR;\r
912 }\r
913\r
914 Urb->DataMap = NULL;\r
915\r
916 Status = PciIo->Map (PciIo, MapOp, Urb->Data, &Len, &PhyAddr, &Map);\r
917 if (EFI_ERROR (Status) || (Len != Urb->DataLen)) {\r
918 goto ON_ERROR;\r
919 }\r
920\r
1436aea4
MK
921 Urb->DataPhy = (VOID *)((UINTN)PhyAddr);\r
922 Urb->DataMap = Map;\r
f87db256
SZ
923 return EFI_SUCCESS;\r
924\r
925ON_ERROR:\r
926 return EFI_DEVICE_ERROR;\r
927}\r
928\r
913cb9dc 929/**\r
78c2ffb5 930 Update the queue head for next round of asynchronous transfer.\r
913cb9dc 931\r
739802e4 932 @param Ehc The EHCI device.\r
78c2ffb5 933 @param Urb The URB to update.\r
913cb9dc 934\r
913cb9dc 935**/\r
913cb9dc 936VOID\r
937EhcUpdateAsyncRequest (\r
1436aea4
MK
938 IN USB2_HC_DEV *Ehc,\r
939 IN URB *Urb\r
913cb9dc 940 )\r
941{\r
1436aea4
MK
942 LIST_ENTRY *Entry;\r
943 EHC_QTD *FirstQtd;\r
944 QH_HW *QhHw;\r
945 EHC_QTD *Qtd;\r
946 QTD_HW *QtdHw;\r
947 UINTN Index;\r
948 EFI_PHYSICAL_ADDRESS PciAddr;\r
913cb9dc 949\r
950 Qtd = NULL;\r
951\r
952 if (Urb->Result == EFI_USB_NOERROR) {\r
953 FirstQtd = NULL;\r
954\r
e33d3e7f 955 BASE_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {\r
913cb9dc 956 Qtd = EFI_LIST_CONTAINER (Entry, EHC_QTD, QtdList);\r
957\r
958 if (FirstQtd == NULL) {\r
959 FirstQtd = Qtd;\r
960 }\r
961\r
962 //\r
963 // Update the QTD for next round of transfer. Host control\r
964 // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/\r
965 // Current Offset. These fields need to be updated. DT isn't\r
966 // used by interrupt transfer. It uses DT in queue head.\r
967 // Current Offset is in Page[0], only need to reset Page[0]\r
968 // to initial data buffer.\r
969 //\r
970 QtdHw = &Qtd->QtdHw;\r
971 QtdHw->Status = QTD_STAT_ACTIVE;\r
972 QtdHw->ErrCnt = QTD_MAX_ERR;\r
973 QtdHw->CurPage = 0;\r
1436aea4 974 QtdHw->TotalBytes = (UINT32)Qtd->DataLen;\r
aa91de05 975 //\r
976 // calculate physical address by offset.\r
977 //\r
1436aea4
MK
978 PciAddr = (UINTN)Urb->DataPhy + ((UINTN)Qtd->Data - (UINTN)Urb->Data);\r
979 QtdHw->Page[0] = EHC_LOW_32BIT (PciAddr);\r
980 QtdHw->PageHigh[0] = EHC_HIGH_32BIT (PciAddr);\r
913cb9dc 981 }\r
982\r
983 //\r
984 // Update QH for next round of transfer. Host control only\r
985 // touch the fields in transfer overlay area. Only need to\r
986 // zero out the overlay area and set NextQtd to the first\r
987 // QTD. DateToggle bit is left untouched.\r
988 //\r
1436aea4
MK
989 QhHw = &Urb->Qh->QhHw;\r
990 QhHw->CurQtd = QTD_LINK (0, TRUE);\r
991 QhHw->AltQtd = 0;\r
913cb9dc 992\r
1436aea4
MK
993 QhHw->Status = 0;\r
994 QhHw->Pid = 0;\r
995 QhHw->ErrCnt = 0;\r
996 QhHw->CurPage = 0;\r
997 QhHw->Ioc = 0;\r
998 QhHw->TotalBytes = 0;\r
913cb9dc 999\r
1000 for (Index = 0; Index < 5; Index++) {\r
1001 QhHw->Page[Index] = 0;\r
1002 QhHw->PageHigh[Index] = 0;\r
1003 }\r
1004\r
1436aea4 1005 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, FirstQtd, sizeof (EHC_QTD));\r
739802e4 1006 QhHw->NextQtd = QTD_LINK (PciAddr, FALSE);\r
913cb9dc 1007 }\r
1008\r
1436aea4 1009 return;\r
913cb9dc 1010}\r
1011\r
913cb9dc 1012/**\r
78c2ffb5 1013 Interrupt transfer periodic check handler.\r
913cb9dc 1014\r
78c2ffb5 1015 @param Event Interrupt event.\r
1016 @param Context Pointer to USB2_HC_DEV.\r
913cb9dc 1017\r
913cb9dc 1018**/\r
1019VOID\r
6d3ea23f 1020EFIAPI\r
597f4ee2 1021EhcMonitorAsyncRequests (\r
1436aea4
MK
1022 IN EFI_EVENT Event,\r
1023 IN VOID *Context\r
913cb9dc 1024 )\r
1025{\r
1436aea4
MK
1026 USB2_HC_DEV *Ehc;\r
1027 EFI_TPL OldTpl;\r
1028 LIST_ENTRY *Entry;\r
1029 LIST_ENTRY *Next;\r
1030 BOOLEAN Finished;\r
1031 UINT8 *ProcBuf;\r
1032 URB *Urb;\r
1033 EFI_STATUS Status;\r
1034\r
1035 OldTpl = gBS->RaiseTPL (EHC_TPL);\r
1036 Ehc = (USB2_HC_DEV *)Context;\r
913cb9dc 1037\r
e33d3e7f 1038 BASE_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {\r
913cb9dc 1039 Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);\r
1040\r
1041 //\r
1042 // Check the result of URB execution. If it is still\r
1043 // active, check the next one.\r
1044 //\r
1045 Finished = EhcCheckUrbResult (Ehc, Urb);\r
1046\r
1047 if (!Finished) {\r
1048 continue;\r
1049 }\r
1050\r
f87db256
SZ
1051 //\r
1052 // Flush any PCI posted write transactions from a PCI host\r
1053 // bridge to system memory.\r
1054 //\r
1055 Status = EhcFlushAsyncIntMap (Ehc, Urb);\r
1056 if (EFI_ERROR (Status)) {\r
87000d77 1057 DEBUG ((DEBUG_ERROR, "EhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));\r
f87db256
SZ
1058 }\r
1059\r
913cb9dc 1060 //\r
1061 // Allocate a buffer then copy the transferred data for user.\r
1062 // If failed to allocate the buffer, update the URB for next\r
1063 // round of transfer. Ignore the data of this round.\r
1064 //\r
1065 ProcBuf = NULL;\r
1066\r
1067 if (Urb->Result == EFI_USB_NOERROR) {\r
7fb7259f
RN
1068 //\r
1069 // Make sure the data received from HW is no more than expected.\r
1070 //\r
1071 if (Urb->Completed <= Urb->DataLen) {\r
1072 ProcBuf = AllocatePool (Urb->Completed);\r
1073 }\r
913cb9dc 1074\r
1075 if (ProcBuf == NULL) {\r
739802e4 1076 EhcUpdateAsyncRequest (Ehc, Urb);\r
913cb9dc 1077 continue;\r
1078 }\r
1079\r
1080 CopyMem (ProcBuf, Urb->Data, Urb->Completed);\r
1081 }\r
1082\r
739802e4 1083 EhcUpdateAsyncRequest (Ehc, Urb);\r
913cb9dc 1084\r
1085 //\r
1086 // Leave error recovery to its related device driver. A\r
1087 // common case of the error recovery is to re-submit the\r
1088 // interrupt transfer which is linked to the head of the\r
1089 // list. This function scans from head to tail. So the\r
1090 // re-submitted interrupt transfer's callback function\r
1091 // will not be called again in this round. Don't touch this\r
1092 // URB after the callback, it may have been removed by the\r
1093 // callback.\r
1094 //\r
1095 if (Urb->Callback != NULL) {\r
1096 //\r
1097 // Restore the old TPL, USB bus maybe connect device in\r
1098 // his callback. Some drivers may has a lower TPL restriction.\r
1099 //\r
1100 gBS->RestoreTPL (OldTpl);\r
1436aea4 1101 (Urb->Callback)(ProcBuf, Urb->Completed, Urb->Context, Urb->Result);\r
913cb9dc 1102 OldTpl = gBS->RaiseTPL (EHC_TPL);\r
1103 }\r
1104\r
1105 if (ProcBuf != NULL) {\r
7351070b 1106 FreePool (ProcBuf);\r
913cb9dc 1107 }\r
1108 }\r
1109\r
1110 gBS->RestoreTPL (OldTpl);\r
1111}\r