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