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