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