]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.c
MdeModulePkg/EhciPei: Use BaseLib linked list iteration macros
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / EhciPei / EhciUrb.c
CommitLineData
4b1bf81c 1/** @file\r
2PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid\r
3which is used to enable recovery function from USB Drivers.\r
4\r
d1102dba 5Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>\r
30980945 6Copyright (c) Microsoft Corporation.<BR>\r
d1102dba 7\r
9d510e61 8SPDX-License-Identifier: BSD-2-Clause-Patent\r
4b1bf81c 9\r
10**/\r
11\r
12#include "EhcPeim.h"\r
13\r
14/**\r
15 Delete a single asynchronous interrupt transfer for\r
16 the device and endpoint.\r
d1102dba 17\r
4b1bf81c 18 @param Ehc The EHCI device.\r
19 @param Data Current data not associated with a QTD.\r
20 @param DataLen The length of the data.\r
21 @param PktId Packet ID to use in the QTD.\r
22 @param Toggle Data toggle to use in the QTD.\r
23 @param MaxPacket Maximu packet length of the endpoint.\r
24\r
25 @retval the pointer to the created QTD or NULL if failed to create one.\r
26\r
27**/\r
28PEI_EHC_QTD *\r
29EhcCreateQtd (\r
30 IN PEI_USB2_HC_DEV *Ehc,\r
31 IN UINT8 *Data,\r
32 IN UINTN DataLen,\r
33 IN UINT8 PktId,\r
34 IN UINT8 Toggle,\r
35 IN UINTN MaxPacket\r
36 )\r
37{\r
38 PEI_EHC_QTD *Qtd;\r
39 QTD_HW *QtdHw;\r
40 UINTN Index;\r
41 UINTN Len;\r
42 UINTN ThisBufLen;\r
43\r
44 ASSERT (Ehc != NULL);\r
45\r
46 Qtd = UsbHcAllocateMem (Ehc, Ehc->MemPool, sizeof (PEI_EHC_QTD));\r
47\r
48 if (Qtd == NULL) {\r
49 return NULL;\r
50 }\r
51\r
52 Qtd->Signature = EHC_QTD_SIG;\r
53 Qtd->Data = Data;\r
54 Qtd->DataLen = 0;\r
55\r
56 InitializeListHead (&Qtd->QtdList);\r
57\r
58 QtdHw = &Qtd->QtdHw;\r
59 QtdHw->NextQtd = QTD_LINK (NULL, TRUE);\r
60 QtdHw->AltNext = QTD_LINK (NULL, TRUE);\r
61 QtdHw->Status = QTD_STAT_ACTIVE;\r
62 QtdHw->Pid = PktId;\r
63 QtdHw->ErrCnt = QTD_MAX_ERR;\r
64 QtdHw->Ioc = 0;\r
65 QtdHw->TotalBytes = 0;\r
66 QtdHw->DataToggle = Toggle;\r
67\r
68 //\r
69 // Fill in the buffer points\r
70 //\r
71 if (Data != NULL) {\r
72 Len = 0;\r
73\r
74 for (Index = 0; Index <= QTD_MAX_BUFFER; Index++) {\r
75 //\r
76 // Set the buffer point (Check page 41 EHCI Spec 1.0). No need to\r
77 // compute the offset and clear Reserved fields. This is already\r
78 // done in the data point.\r
79 //\r
80 QtdHw->Page[Index] = EHC_LOW_32BIT (Data);\r
81 QtdHw->PageHigh[Index] = EHC_HIGH_32BIT (Data);\r
82\r
83 ThisBufLen = QTD_BUF_LEN - (EHC_LOW_32BIT (Data) & QTD_BUF_MASK);\r
84\r
85 if (Len + ThisBufLen >= DataLen) {\r
86 Len = DataLen;\r
87 break;\r
88 }\r
89\r
90 Len += ThisBufLen;\r
91 Data += ThisBufLen;\r
92 }\r
d1102dba 93\r
4b1bf81c 94 //\r
95 // Need to fix the last pointer if the Qtd can't hold all the\r
96 // user's data to make sure that the length is in the unit of\r
97 // max packets. If it can hold all the data, there is no such\r
98 // need.\r
99 //\r
100 if (Len < DataLen) {\r
101 Len = Len - Len % MaxPacket;\r
102 }\r
103\r
104 QtdHw->TotalBytes = (UINT32) Len;\r
105 Qtd->DataLen = Len;\r
106 }\r
107\r
108 return Qtd;\r
109}\r
110\r
111/**\r
112 Initialize the queue head for interrupt transfer,\r
113 that is, initialize the following three fields:\r
114 1. SplitXState in the Status field.\r
115 2. Microframe S-mask.\r
116 3. Microframe C-mask.\r
d1102dba 117\r
4b1bf81c 118 @param Ep The queue head's related endpoint.\r
119 @param QhHw The queue head to initialize.\r
120\r
121**/\r
122VOID\r
123EhcInitIntQh (\r
124 IN USB_ENDPOINT *Ep,\r
125 IN QH_HW *QhHw\r
126 )\r
127{\r
128 //\r
129 // Because UEFI interface can't utilitize an endpoint with\r
130 // poll rate faster than 1ms, only need to set one bit in\r
131 // the queue head. simple. But it may be changed later. If\r
132 // sub-1ms interrupt is supported, need to update the S-Mask\r
133 // here\r
134 //\r
135 if (Ep->DevSpeed == EFI_USB_SPEED_HIGH) {\r
136 QhHw->SMask = QH_MICROFRAME_0;\r
137 return ;\r
138 }\r
d1102dba 139\r
4b1bf81c 140 //\r
141 // For low/full speed device, the transfer must go through\r
142 // the split transaction. Need to update three fields\r
143 // 1. SplitXState in the status\r
144 // 2. Microframe S-Mask\r
145 // 3. Microframe C-Mask\r
146 // UEFI USB doesn't exercise admission control. It simplely\r
147 // schedule the high speed transactions in microframe 0, and\r
148 // full/low speed transactions at microframe 1. This also\r
149 // avoid the use of FSTN.\r
150 //\r
151 QhHw->SMask = QH_MICROFRAME_1;\r
152 QhHw->CMask = QH_MICROFRAME_3 | QH_MICROFRAME_4 | QH_MICROFRAME_5;\r
153}\r
154\r
155/**\r
156 Allocate and initialize a EHCI queue head.\r
d1102dba 157\r
4b1bf81c 158 @param Ehci The EHCI device.\r
159 @param Ep The endpoint to create queue head for.\r
160\r
161 @retval the pointer to the created queue head or NULL if failed to create one.\r
162\r
163**/\r
164PEI_EHC_QH *\r
165EhcCreateQh (\r
166 IN PEI_USB2_HC_DEV *Ehci,\r
167 IN USB_ENDPOINT *Ep\r
168 )\r
169{\r
170 PEI_EHC_QH *Qh;\r
171 QH_HW *QhHw;\r
172\r
173 Qh = UsbHcAllocateMem (Ehci, Ehci->MemPool, sizeof (PEI_EHC_QH));\r
174\r
175 if (Qh == NULL) {\r
176 return NULL;\r
177 }\r
178\r
179 Qh->Signature = EHC_QH_SIG;\r
180 Qh->NextQh = NULL;\r
181 Qh->Interval = Ep->PollRate;\r
d1102dba 182\r
4b1bf81c 183 InitializeListHead (&Qh->Qtds);\r
184\r
185 QhHw = &Qh->QhHw;\r
186 QhHw->HorizonLink = QH_LINK (NULL, 0, TRUE);\r
187 QhHw->DeviceAddr = Ep->DevAddr;\r
188 QhHw->Inactive = 0;\r
189 QhHw->EpNum = Ep->EpAddr;\r
190 QhHw->EpSpeed = Ep->DevSpeed;\r
191 QhHw->DtCtrl = 0;\r
192 QhHw->ReclaimHead = 0;\r
193 QhHw->MaxPacketLen = (UINT32) Ep->MaxPacket;\r
194 QhHw->CtrlEp = 0;\r
195 QhHw->NakReload = QH_NAK_RELOAD;\r
196 QhHw->HubAddr = Ep->HubAddr;\r
197 QhHw->PortNum = Ep->HubPort;\r
198 QhHw->Multiplier = 1;\r
199 QhHw->DataToggle = Ep->Toggle;\r
200\r
201 if (Ep->DevSpeed != EFI_USB_SPEED_HIGH) {\r
202 QhHw->Status |= QTD_STAT_DO_SS;\r
203 }\r
204\r
205 switch (Ep->Type) {\r
206 case EHC_CTRL_TRANSFER:\r
207 //\r
208 // Special initialization for the control transfer:\r
209 // 1. Control transfer initialize data toggle from each QTD\r
210 // 2. Set the Control Endpoint Flag (C) for low/full speed endpoint.\r
211 //\r
212 QhHw->DtCtrl = 1;\r
213\r
214 if (Ep->DevSpeed != EFI_USB_SPEED_HIGH) {\r
215 QhHw->CtrlEp = 1;\r
216 }\r
217 break;\r
218\r
219 case EHC_INT_TRANSFER_ASYNC:\r
220 case EHC_INT_TRANSFER_SYNC:\r
221 //\r
222 // Special initialization for the interrupt transfer\r
223 // to set the S-Mask and C-Mask\r
224 //\r
225 QhHw->NakReload = 0;\r
226 EhcInitIntQh (Ep, QhHw);\r
227 break;\r
228\r
229 case EHC_BULK_TRANSFER:\r
230 if ((Ep->DevSpeed == EFI_USB_SPEED_HIGH) && (Ep->Direction == EfiUsbDataOut)) {\r
231 QhHw->Status |= QTD_STAT_DO_PING;\r
232 }\r
233\r
234 break;\r
235 }\r
236\r
237 return Qh;\r
238}\r
239\r
240/**\r
241 Convert the poll interval from application to that\r
242 be used by EHCI interface data structure. Only need\r
243 to get the max 2^n that is less than interval. UEFI\r
244 can't support high speed endpoint with a interval less\r
245 than 8 microframe because interval is specified in\r
246 the unit of ms (millisecond).\r
d1102dba 247\r
4b1bf81c 248 @param Interval The interval to convert.\r
249\r
250 @retval The converted interval.\r
251\r
252**/\r
253UINTN\r
254EhcConvertPollRate (\r
255 IN UINTN Interval\r
256 )\r
257{\r
258 UINTN BitCount;\r
259\r
260 if (Interval == 0) {\r
261 return 1;\r
262 }\r
d1102dba 263\r
4b1bf81c 264 //\r
265 // Find the index (1 based) of the highest non-zero bit\r
266 //\r
267 BitCount = 0;\r
268\r
269 while (Interval != 0) {\r
270 Interval >>= 1;\r
271 BitCount++;\r
272 }\r
273\r
274 return (UINTN)1 << (BitCount - 1);\r
275}\r
276\r
277/**\r
278 Free a list of QTDs.\r
d1102dba 279\r
4b1bf81c 280 @param Ehc The EHCI device.\r
281 @param Qtds The list head of the QTD.\r
282\r
283**/\r
284VOID\r
285EhcFreeQtds (\r
286 IN PEI_USB2_HC_DEV *Ehc,\r
287 IN EFI_LIST_ENTRY *Qtds\r
288 )\r
289{\r
290 EFI_LIST_ENTRY *Entry;\r
291 EFI_LIST_ENTRY *Next;\r
292 PEI_EHC_QTD *Qtd;\r
293\r
30980945 294 BASE_LIST_FOR_EACH_SAFE (Entry, Next, Qtds) {\r
4b1bf81c 295 Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);\r
296\r
297 RemoveEntryList (&Qtd->QtdList);\r
2c656af0 298 UsbHcFreeMem (Ehc, Ehc->MemPool, Qtd, sizeof (PEI_EHC_QTD));\r
4b1bf81c 299 }\r
300}\r
301\r
302/**\r
303 Free an allocated URB. It is possible for it to be partially inited.\r
d1102dba 304\r
4b1bf81c 305 @param Ehc The EHCI device.\r
306 @param Urb The URB to free.\r
307\r
308**/\r
309VOID\r
310EhcFreeUrb (\r
311 IN PEI_USB2_HC_DEV *Ehc,\r
312 IN PEI_URB *Urb\r
313 )\r
314{\r
2c656af0
SZ
315 if (Urb->RequestPhy != NULL) {\r
316 IoMmuUnmap (Ehc->IoMmu, Urb->RequestMap);\r
317 }\r
318\r
319 if (Urb->DataMap != NULL) {\r
320 IoMmuUnmap (Ehc->IoMmu, Urb->DataMap);\r
321 }\r
322\r
4b1bf81c 323 if (Urb->Qh != NULL) {\r
324 //\r
325 // Ensure that this queue head has been unlinked from the\r
326 // schedule data structures. Free all the associated QTDs\r
327 //\r
328 EhcFreeQtds (Ehc, &Urb->Qh->Qtds);\r
2c656af0 329 UsbHcFreeMem (Ehc, Ehc->MemPool, Urb->Qh, sizeof (PEI_EHC_QH));\r
4b1bf81c 330 }\r
331}\r
332\r
333/**\r
334 Create a list of QTDs for the URB.\r
d1102dba 335\r
4b1bf81c 336 @param Ehc The EHCI device.\r
337 @param Urb The URB to create QTDs for.\r
338\r
339 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for QTD.\r
340 @retval EFI_SUCCESS The QTDs are allocated for the URB.\r
341\r
342**/\r
343EFI_STATUS\r
344EhcCreateQtds (\r
345 IN PEI_USB2_HC_DEV *Ehc,\r
346 IN PEI_URB *Urb\r
347 )\r
348{\r
349 USB_ENDPOINT *Ep;\r
350 PEI_EHC_QH *Qh;\r
351 PEI_EHC_QTD *Qtd;\r
352 PEI_EHC_QTD *StatusQtd;\r
353 PEI_EHC_QTD *NextQtd;\r
354 EFI_LIST_ENTRY *Entry;\r
355 UINT32 AlterNext;\r
356 UINT8 Toggle;\r
357 UINTN Len;\r
358 UINT8 Pid;\r
359\r
360 ASSERT ((Urb != NULL) && (Urb->Qh != NULL));\r
361\r
362 //\r
363 // EHCI follows the alternet next QTD pointer if it meets\r
364 // a short read and the AlterNext pointer is valid. UEFI\r
365 // EHCI driver should terminate the transfer except the\r
366 // control transfer.\r
367 //\r
368 Toggle = 0;\r
369 Qh = Urb->Qh;\r
370 Ep = &Urb->Ep;\r
371 StatusQtd = NULL;\r
372 AlterNext = QTD_LINK (NULL, TRUE);\r
373\r
374 if (Ep->Direction == EfiUsbDataIn) {\r
375 AlterNext = QTD_LINK (Ehc->ShortReadStop, FALSE);\r
376 }\r
d1102dba 377\r
4b1bf81c 378 //\r
379 // Build the Setup and status packets for control transfer\r
380 //\r
381 if (Urb->Ep.Type == EHC_CTRL_TRANSFER) {\r
382 Len = sizeof (EFI_USB_DEVICE_REQUEST);\r
383 Qtd = EhcCreateQtd (Ehc, Urb->RequestPhy, Len, QTD_PID_SETUP, 0, Ep->MaxPacket);\r
384\r
385 if (Qtd == NULL) {\r
386 return EFI_OUT_OF_RESOURCES;\r
387 }\r
388\r
389 InsertTailList (&Qh->Qtds, &Qtd->QtdList);\r
390\r
391 //\r
392 // Create the status packet now. Set the AlterNext to it. So, when\r
393 // EHCI meets a short control read, it can resume at the status stage.\r
394 // Use the opposite direction of the data stage, or IN if there is\r
395 // no data stage.\r
396 //\r
397 if (Ep->Direction == EfiUsbDataIn) {\r
398 Pid = QTD_PID_OUTPUT;\r
399 } else {\r
400 Pid = QTD_PID_INPUT;\r
401 }\r
402\r
403 StatusQtd = EhcCreateQtd (Ehc, NULL, 0, Pid, 1, Ep->MaxPacket);\r
404\r
405 if (StatusQtd == NULL) {\r
406 goto ON_ERROR;\r
407 }\r
408\r
409 if (Ep->Direction == EfiUsbDataIn) {\r
410 AlterNext = QTD_LINK (StatusQtd, FALSE);\r
411 }\r
412\r
413 Toggle = 1;\r
414 }\r
415\r
416 //\r
417 // Build the data packets for all the transfers\r
418 //\r
419 if (Ep->Direction == EfiUsbDataIn) {\r
420 Pid = QTD_PID_INPUT;\r
421 } else {\r
422 Pid = QTD_PID_OUTPUT;\r
423 }\r
424\r
425 Qtd = NULL;\r
426 Len = 0;\r
427\r
428 while (Len < Urb->DataLen) {\r
429 Qtd = EhcCreateQtd (\r
430 Ehc,\r
431 (UINT8 *) Urb->DataPhy + Len,\r
432 Urb->DataLen - Len,\r
433 Pid,\r
434 Toggle,\r
435 Ep->MaxPacket\r
436 );\r
437\r
438 if (Qtd == NULL) {\r
439 goto ON_ERROR;\r
440 }\r
441\r
442 Qtd->QtdHw.AltNext = AlterNext;\r
443 InsertTailList (&Qh->Qtds, &Qtd->QtdList);\r
444\r
445 //\r
446 // Switch the Toggle bit if odd number of packets are included in the QTD.\r
447 //\r
448 if (((Qtd->DataLen + Ep->MaxPacket - 1) / Ep->MaxPacket) % 2) {\r
449 Toggle = (UINT8) (1 - Toggle);\r
450 }\r
451\r
452 Len += Qtd->DataLen;\r
453 }\r
d1102dba 454\r
4b1bf81c 455 //\r
456 // Insert the status packet for control transfer\r
457 //\r
458 if (Ep->Type == EHC_CTRL_TRANSFER) {\r
459 InsertTailList (&Qh->Qtds, &StatusQtd->QtdList);\r
460 }\r
461\r
462 //\r
463 // OK, all the QTDs needed are created. Now, fix the NextQtd point\r
464 //\r
30980945 465 BASE_LIST_FOR_EACH (Entry, &Qh->Qtds) {\r
4b1bf81c 466 Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);\r
467\r
468 //\r
469 // break if it is the last entry on the list\r
470 //\r
471 if (Entry->ForwardLink == &Qh->Qtds) {\r
472 break;\r
473 }\r
474\r
475 NextQtd = EFI_LIST_CONTAINER (Entry->ForwardLink, PEI_EHC_QTD, QtdList);\r
476 Qtd->QtdHw.NextQtd = QTD_LINK (NextQtd, FALSE);\r
477 }\r
478\r
479 //\r
480 // Link the QTDs to the queue head\r
481 //\r
482 NextQtd = EFI_LIST_CONTAINER (Qh->Qtds.ForwardLink, PEI_EHC_QTD, QtdList);\r
483 Qh->QhHw.NextQtd = QTD_LINK (NextQtd, FALSE);\r
484 return EFI_SUCCESS;\r
485\r
486ON_ERROR:\r
487 EhcFreeQtds (Ehc, &Qh->Qtds);\r
488 return EFI_OUT_OF_RESOURCES;\r
489}\r
490\r
491/**\r
492 Create a new URB and its associated QTD.\r
d1102dba 493\r
4b1bf81c 494 @param Ehc The EHCI device.\r
495 @param DevAddr The device address.\r
496 @param EpAddr Endpoint addrress & its direction.\r
497 @param DevSpeed The device speed.\r
498 @param Toggle Initial data toggle to use.\r
499 @param MaxPacket The max packet length of the endpoint.\r
500 @param Hub The transaction translator to use.\r
501 @param Type The transaction type.\r
502 @param Request The standard USB request for control transfer.\r
503 @param Data The user data to transfer.\r
504 @param DataLen The length of data buffer.\r
505 @param Callback The function to call when data is transferred.\r
506 @param Context The context to the callback.\r
507 @param Interval The interval for interrupt transfer.\r
508\r
509 @retval the pointer to the created URB or NULL.\r
510\r
511**/\r
512PEI_URB *\r
513EhcCreateUrb (\r
514 IN PEI_USB2_HC_DEV *Ehc,\r
515 IN UINT8 DevAddr,\r
d1102dba 516 IN UINT8 EpAddr,\r
4b1bf81c 517 IN UINT8 DevSpeed,\r
518 IN UINT8 Toggle,\r
519 IN UINTN MaxPacket,\r
520 IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Hub,\r
521 IN UINTN Type,\r
522 IN EFI_USB_DEVICE_REQUEST *Request,\r
523 IN VOID *Data,\r
524 IN UINTN DataLen,\r
525 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,\r
526 IN VOID *Context,\r
527 IN UINTN Interval\r
528 )\r
529{\r
530 USB_ENDPOINT *Ep;\r
531 EFI_PHYSICAL_ADDRESS PhyAddr;\r
2c656af0 532 EDKII_IOMMU_OPERATION MapOp;\r
4b1bf81c 533 EFI_STATUS Status;\r
534 UINTN Len;\r
535 PEI_URB *Urb;\r
536 VOID *Map;\r
d1102dba 537\r
7a2505bb
SZ
538 Map = NULL;\r
539\r
4b1bf81c 540 Urb = Ehc->Urb;\r
541 Urb->Signature = EHC_URB_SIG;\r
542 InitializeListHead (&Urb->UrbList);\r
543\r
544 Ep = &Urb->Ep;\r
545 Ep->DevAddr = DevAddr;\r
546 Ep->EpAddr = (UINT8) (EpAddr & 0x0F);\r
547 Ep->Direction = (((EpAddr & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut);\r
548 Ep->DevSpeed = DevSpeed;\r
549 Ep->MaxPacket = MaxPacket;\r
550\r
551 Ep->HubAddr = 0;\r
552 Ep->HubPort = 0;\r
553\r
554 if (DevSpeed != EFI_USB_SPEED_HIGH) {\r
555 ASSERT (Hub != NULL);\r
556\r
557 Ep->HubAddr = Hub->TranslatorHubAddress;\r
558 Ep->HubPort = Hub->TranslatorPortNumber;\r
559 }\r
560\r
561 Ep->Toggle = Toggle;\r
562 Ep->Type = Type;\r
563 Ep->PollRate = EhcConvertPollRate (Interval);\r
564\r
565 Urb->Request = Request;\r
566 Urb->Data = Data;\r
567 Urb->DataLen = DataLen;\r
568 Urb->Callback = Callback;\r
569 Urb->Context = Context;\r
570 Urb->Qh = EhcCreateQh (Ehc, &Urb->Ep);\r
571\r
572 if (Urb->Qh == NULL) {\r
573 goto ON_ERROR;\r
574 }\r
c34a5aab
SZ
575\r
576 Urb->RequestPhy = NULL;\r
577 Urb->RequestMap = NULL;\r
578 Urb->DataPhy = NULL;\r
579 Urb->DataMap = NULL;\r
580\r
4b1bf81c 581 //\r
582 // Map the request and user data\r
583 //\r
584 if (Request != NULL) {\r
585 Len = sizeof (EFI_USB_DEVICE_REQUEST);\r
2c656af0
SZ
586 MapOp = EdkiiIoMmuOperationBusMasterRead;\r
587 Status = IoMmuMap (Ehc->IoMmu, MapOp, Request, &Len, &PhyAddr, &Map);\r
588\r
589 if (EFI_ERROR (Status) || (Len != sizeof (EFI_USB_DEVICE_REQUEST))) {\r
4b1bf81c 590 goto ON_ERROR;\r
591 }\r
592\r
593 Urb->RequestPhy = (VOID *) ((UINTN) PhyAddr);\r
594 Urb->RequestMap = Map;\r
595 }\r
596\r
597 if (Data != NULL) {\r
598 Len = DataLen;\r
2c656af0
SZ
599\r
600 if (Ep->Direction == EfiUsbDataIn) {\r
601 MapOp = EdkiiIoMmuOperationBusMasterWrite;\r
602 } else {\r
603 MapOp = EdkiiIoMmuOperationBusMasterRead;\r
604 }\r
605\r
606 Status = IoMmuMap (Ehc->IoMmu, MapOp, Data, &Len, &PhyAddr, &Map);\r
607\r
608 if (EFI_ERROR (Status) || (Len != DataLen)) {\r
4b1bf81c 609 goto ON_ERROR;\r
610 }\r
611\r
612 Urb->DataPhy = (VOID *) ((UINTN) PhyAddr);\r
613 Urb->DataMap = Map;\r
614 }\r
615\r
616 Status = EhcCreateQtds (Ehc, Urb);\r
617\r
618 if (EFI_ERROR (Status)) {\r
619 goto ON_ERROR;\r
620 }\r
621\r
622 return Urb;\r
623\r
624ON_ERROR:\r
625 EhcFreeUrb (Ehc, Urb);\r
626 return NULL;\r
627}\r