]> git.proxmox.com Git - mirror_edk2.git/blame - SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Transfer.c
SecurityPkg: Cleanup gEfiSmmAccess2ProtocolGuid reference.
[mirror_edk2.git] / SourceLevelDebugPkg / Library / DebugCommunicationLibUsb3 / DebugCommunicationLibUsb3Transfer.c
CommitLineData
2cb6eabe
EL
1/** @file\r
2 Debug Port Library implementation based on usb3 debug port.\r
3\r
4 Copyright (c) 2014, Intel Corporation. All rights reserved.<BR>\r
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php.\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14#include "DebugCommunicationLibUsb3Internal.h"\r
15\r
16/**\r
17 Synchronize the specified transfer ring to update the enqueue and dequeue pointer.\r
18\r
19 @param Handle Debug port handle.\r
20 @param TrsRing The transfer ring to sync.\r
21\r
22 @retval EFI_SUCCESS The transfer ring is synchronized successfully.\r
23\r
24**/\r
25EFI_STATUS\r
26EFIAPI\r
27XhcSyncTrsRing (\r
28 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
29 IN TRANSFER_RING *TrsRing\r
30 )\r
31{\r
32 UINTN Index;\r
33 TRB_TEMPLATE *TrsTrb;\r
34 UINT32 CycleBit;\r
35\r
36 ASSERT (TrsRing != NULL);\r
37\r
38 //\r
39 // Calculate the latest RingEnqueue and RingPCS\r
40 //\r
41 TrsTrb = (TRB_TEMPLATE *)(UINTN) TrsRing->RingEnqueue;\r
42\r
43 ASSERT (TrsTrb != NULL);\r
44 \r
45 for (Index = 0; Index < TrsRing->TrbNumber; Index++) {\r
46 if (TrsTrb->CycleBit != (TrsRing->RingPCS & BIT0)) {\r
47 break;\r
48 }\r
49 TrsTrb++;\r
50 if ((UINT8) TrsTrb->Type == TRB_TYPE_LINK) {\r
51 ASSERT (((LINK_TRB*)TrsTrb)->TC != 0);\r
52 //\r
53 // set cycle bit in Link TRB as normal\r
54 //\r
55 ((LINK_TRB*)TrsTrb)->CycleBit = TrsRing->RingPCS & BIT0;\r
56 //\r
57 // Toggle PCS maintained by software\r
58 //\r
59 TrsRing->RingPCS = (TrsRing->RingPCS & BIT0) ? 0 : 1;\r
60 TrsTrb = (TRB_TEMPLATE *)(UINTN)((TrsTrb->Parameter1 | LShiftU64 ((UINT64)TrsTrb->Parameter2, 32)) & ~0x0F);\r
61 }\r
62 }\r
63 ASSERT (Index != TrsRing->TrbNumber);\r
64\r
65 if ((EFI_PHYSICAL_ADDRESS)(UINTN) TrsTrb != TrsRing->RingEnqueue) {\r
66 TrsRing->RingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN) TrsTrb;\r
67 }\r
68\r
69 //\r
70 // Clear the Trb context for enqueue, but reserve the PCS bit which indicates free Trb.\r
71 //\r
72 CycleBit = TrsTrb->CycleBit;\r
73 ZeroMem (TrsTrb, sizeof (TRB_TEMPLATE));\r
74 TrsTrb->CycleBit = CycleBit;\r
75\r
76 return EFI_SUCCESS;\r
77}\r
78\r
79/**\r
80 Synchronize the specified event ring to update the enqueue and dequeue pointer.\r
81\r
82 @param Handle Debug port handle.\r
83 @param EvtRing The event ring to sync.\r
84\r
85 @retval EFI_SUCCESS The event ring is synchronized successfully.\r
86\r
87**/\r
88EFI_STATUS\r
89EFIAPI\r
90XhcSyncEventRing (\r
91 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
92 IN EVENT_RING *EvtRing\r
93 )\r
94{\r
95 UINTN Index;\r
96 TRB_TEMPLATE *EvtTrb1;\r
97\r
98 ASSERT (EvtRing != NULL);\r
99\r
100 //\r
101 // Calculate the EventRingEnqueue and EventRingCCS.\r
102 // Note: only support single Segment\r
103 //\r
104 EvtTrb1 = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue;\r
105\r
106 for (Index = 0; Index < EvtRing->TrbNumber; Index++) {\r
107 if (EvtTrb1->CycleBit != EvtRing->EventRingCCS) {\r
108 break;\r
109 }\r
110\r
111 EvtTrb1++;\r
112\r
113 if ((UINTN)EvtTrb1 >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) {\r
114 EvtTrb1 = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingSeg0;\r
115 EvtRing->EventRingCCS = (EvtRing->EventRingCCS) ? 0 : 1;\r
116 }\r
117 }\r
118\r
119 if (Index < EvtRing->TrbNumber) {\r
120 EvtRing->EventRingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN)EvtTrb1;\r
121 } else {\r
122 ASSERT (FALSE);\r
123 }\r
124\r
125 return EFI_SUCCESS;\r
126}\r
127\r
128/**\r
129 Check if there is a new generated event.\r
130\r
131 @param Handle Debug port handle.\r
132 @param EvtRing The event ring to check.\r
133 @param NewEvtTrb The new event TRB found.\r
134\r
135 @retval EFI_SUCCESS Found a new event TRB at the event ring.\r
136 @retval EFI_NOT_READY The event ring has no new event.\r
137\r
138**/\r
139EFI_STATUS\r
140EFIAPI\r
141XhcCheckNewEvent (\r
142 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
143 IN EVENT_RING *EvtRing,\r
144 OUT TRB_TEMPLATE **NewEvtTrb\r
145 )\r
146{\r
147 EFI_STATUS Status;\r
148 TRB_TEMPLATE *EvtTrb;\r
149\r
150 ASSERT (EvtRing != NULL);\r
151\r
152 EvtTrb = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue;\r
153 *NewEvtTrb = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue;\r
154\r
155 if (EvtRing->EventRingDequeue == EvtRing->EventRingEnqueue) {\r
156 return EFI_NOT_READY;\r
157 }\r
158\r
159 Status = EFI_SUCCESS;\r
160\r
161 EvtRing->EventRingDequeue += sizeof (TRB_TEMPLATE);\r
162 //\r
163 // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring.\r
164 //\r
165 if ((UINTN)EvtRing->EventRingDequeue >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) {\r
166 EvtRing->EventRingDequeue = EvtRing->EventRingSeg0;\r
167 }\r
168\r
169 return Status;\r
170}\r
171\r
172/**\r
173 Check if the Trb is a transaction of the URB.\r
174\r
175 @param Ring The transfer ring to be checked.\r
176 @param Trb The TRB to be checked.\r
177\r
178 @retval TRUE It is a transaction of the URB.\r
179 @retval FALSE It is not any transaction of the URB.\r
180\r
181**/\r
182BOOLEAN\r
183IsTrbInTrsRing (\r
184 IN TRANSFER_RING *Ring,\r
185 IN TRB_TEMPLATE *Trb\r
186 )\r
187{\r
188 TRB_TEMPLATE *CheckedTrb;\r
189 UINTN Index;\r
190 \r
191 CheckedTrb = (TRB_TEMPLATE *)(UINTN) Ring->RingSeg0;\r
192 \r
193 ASSERT (Ring->TrbNumber == TR_RING_TRB_NUMBER);\r
194\r
195 for (Index = 0; Index < Ring->TrbNumber; Index++) {\r
196 if (Trb == CheckedTrb) {\r
197 return TRUE;\r
198 }\r
199 CheckedTrb++;\r
200 }\r
201\r
202 return FALSE;\r
203}\r
204\r
205/**\r
206 Check the URB's execution result and update the URB's\r
207 result accordingly.\r
208\r
209 @param Handle Debug port handle.\r
210 @param Urb The URB to check result.\r
211\r
212**/\r
213VOID\r
214XhcCheckUrbResult (\r
215 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
216 IN URB *Urb\r
217 )\r
218{\r
219 EVT_TRB_TRANSFER *EvtTrb;\r
220 TRB_TEMPLATE *TRBPtr;\r
221 UINTN Index;\r
222 EFI_STATUS Status;\r
223 URB *CheckedUrb;\r
224 UINT64 XhcDequeue;\r
225 UINT32 High;\r
226 UINT32 Low;\r
227 \r
228 ASSERT ((Handle != NULL) && (Urb != NULL));\r
229\r
230 if (Urb->Finished) {\r
231 goto EXIT;\r
232 }\r
233\r
234 EvtTrb = NULL;\r
235 \r
236 //\r
237 // Traverse the event ring to find out all new events from the previous check.\r
238 //\r
239 XhcSyncEventRing (Handle, &Handle->EventRing);\r
240 \r
241 for (Index = 0; Index < Handle->EventRing.TrbNumber; Index++) {\r
242\r
243 Status = XhcCheckNewEvent (Handle, &Handle->EventRing, ((TRB_TEMPLATE **)&EvtTrb));\r
244 if (Status == EFI_NOT_READY) {\r
245 //\r
246 // All new events are handled, return directly.\r
247 //\r
248 goto EXIT;\r
249 }\r
250 \r
251 if ((EvtTrb->Type != TRB_TYPE_COMMAND_COMPLT_EVENT) && (EvtTrb->Type != TRB_TYPE_TRANS_EVENT)) {\r
252 continue;\r
253 }\r
254 \r
255 TRBPtr = (TRB_TEMPLATE *)(UINTN)(EvtTrb->TRBPtrLo | LShiftU64 ((UINT64) EvtTrb->TRBPtrHi, 32));\r
256 \r
257 if (IsTrbInTrsRing ((TRANSFER_RING *)(UINTN)(Urb->Ring), TRBPtr)) {\r
258 CheckedUrb = Urb;\r
259 } else if (IsTrbInTrsRing ((TRANSFER_RING *)(UINTN)(Handle->UrbIn.Ring), TRBPtr)) {\r
260 //\r
261 // If it is read event and it should be generated by poll, and current operation is write, we need save data into internal buffer.\r
262 // Internal buffer is used by next read.\r
263 //\r
264 Handle->DataCount = (UINT8) (Handle->UrbIn.DataLen - EvtTrb->Length);\r
265 CopyMem (Handle->Data, (VOID *)(UINTN)Handle->UrbIn.Data, Handle->DataCount);\r
266 //\r
267 // Fill this TRB complete with CycleBit, otherwise next read will fail with old TRB.\r
268 //\r
269 TRBPtr->CycleBit = (TRBPtr->CycleBit & BIT0) ? 0 : 1;\r
270 continue;\r
271 } else {\r
272 continue;\r
273 }\r
274 \r
275 if ((EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) ||\r
276 (EvtTrb->Completecode == TRB_COMPLETION_SUCCESS)) {\r
277 //\r
278 // The length of data which were transferred.\r
279 //\r
280 CheckedUrb->Completed += (CheckedUrb->DataLen - EvtTrb->Length);\r
281 } else {\r
282 CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT;\r
283 }\r
284 //\r
285 // This Urb has been processed\r
286 //\r
287 CheckedUrb->Finished = TRUE;\r
288 }\r
289\r
290EXIT:\r
291 //\r
292 // Advance event ring to last available entry\r
293 //\r
294 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,\r
295 // So divide it to two 32-bytes width register access.\r
296 //\r
297 Low = XhcReadDebugReg (Handle, XHC_DC_DCERDP);\r
298 High = XhcReadDebugReg (Handle, XHC_DC_DCERDP + 4);\r
299 XhcDequeue = (UINT64)(LShiftU64((UINT64)High, 32) | Low);\r
300\r
301 if ((XhcDequeue & (~0x0F)) != ((UINT64)(UINTN)Handle->EventRing.EventRingDequeue & (~0x0F))) {\r
302 //\r
303 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,\r
304 // So divide it to two 32-bytes width register access.\r
305 //\r
306 XhcWriteDebugReg (Handle, XHC_DC_DCERDP, XHC_LOW_32BIT (Handle->EventRing.EventRingDequeue));\r
307 XhcWriteDebugReg (Handle, XHC_DC_DCERDP + 4, XHC_HIGH_32BIT (Handle->EventRing.EventRingDequeue));\r
308 }\r
309}\r
310\r
311/**\r
312 Ring the door bell to notify XHCI there is a transaction to be executed.\r
313\r
314 @param Handle Debug port handle.\r
315 @param Urb The pointer to URB.\r
316\r
317 @retval EFI_SUCCESS Successfully ring the door bell.\r
318\r
319**/\r
320EFI_STATUS\r
321EFIAPI\r
322XhcRingDoorBell (\r
323 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
324 IN URB *Urb\r
325 )\r
326{\r
327 UINT32 Dcdb;\r
328\r
329 //\r
330 // 7.6.8.2 DCDB Register\r
331 // \r
332 Dcdb = (Urb->Direction == EfiUsbDataIn) ? 0x100 : 0x0;\r
333 \r
334 XhcWriteDebugReg (\r
335 Handle,\r
336 XHC_DC_DCDB,\r
337 Dcdb\r
338 );\r
339\r
340 return EFI_SUCCESS;\r
341}\r
342\r
343/**\r
344 Execute the transfer by polling the URB. This is a synchronous operation.\r
345\r
346 @param Handle Debug port handle.\r
347 @param Urb The URB to execute.\r
348 @param Timeout The time to wait before abort, in microsecond.\r
349\r
350**/\r
351VOID\r
352XhcExecTransfer (\r
353 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
354 IN URB *Urb,\r
355 IN UINTN Timeout\r
356 )\r
357{\r
358 TRANSFER_RING *Ring;\r
359 UINT64 Begin;\r
360 UINT64 TimeoutTicker;\r
361 UINT64 TimerRound;\r
362 TRB_TEMPLATE *Trb;\r
363\r
364 Begin = 0;\r
365 TimeoutTicker = 0; \r
366 TimerRound = 0;\r
367\r
368 XhcRingDoorBell (Handle, Urb);\r
369\r
370 if (Timeout != 0) {\r
371 Begin = GetPerformanceCounter ();\r
372 TimeoutTicker = DivU64x32 (\r
373 MultU64x64 (\r
374 Handle->TimerFrequency,\r
375 Timeout\r
376 ),\r
377 1000000u\r
378 );\r
379 TimerRound = DivU64x64Remainder (\r
380 TimeoutTicker,\r
381 DivU64x32 (Handle->TimerCycle, 2),\r
382 &TimeoutTicker\r
383 );\r
384 }\r
385\r
386 //\r
387 // Event Ring Not Empty bit can only be set to 1 by XHC after ringing door bell with some delay.\r
388 //\r
389 while (TRUE) {\r
390 if (Timeout != 0) {\r
391 if (TimerRound == 0) {\r
392 if (IsTimerTimeout (Handle, Begin, TimeoutTicker)) {\r
393 //\r
394 // If time out occurs.\r
395 //\r
396 Urb->Result |= EFI_USB_ERR_TIMEOUT;\r
397 break;\r
398 }\r
399 } else {\r
400 if (IsTimerTimeout (Handle, Begin, DivU64x32 (Handle->TimerCycle, 2))) {\r
401 TimerRound --;\r
402 }\r
403 }\r
404 }\r
405 XhcCheckUrbResult (Handle, Urb);\r
406 if (Urb->Finished) {\r
407 break;\r
408 }\r
409 }\r
410 \r
411 //\r
412 // If URB transfer is error, restore transfer ring to original value before URB transfer\r
413 // This will make the current transfer TRB is always at the latest unused one in transfer ring.\r
414 //\r
415 Ring = (TRANSFER_RING *)(UINTN) Urb->Ring;\r
416 if ((Urb->Result != EFI_USB_NOERROR) && (Urb->Direction == EfiUsbDataIn)) {\r
417 //\r
418 // Adjust Enqueue pointer\r
419 //\r
420 Ring->RingEnqueue = Urb->Trb;\r
421 //\r
422 // Clear CCS flag for next use\r
423 //\r
424 Trb = (TRB_TEMPLATE *)(UINTN) Urb->Trb;\r
425 Trb->CycleBit = ((~Ring->RingPCS) & BIT0);\r
426 } else {\r
427 //\r
428 // Update transfer ring for next transfer.\r
429 //\r
430 XhcSyncTrsRing (Handle, Ring);\r
431 }\r
432}\r
433\r
434/**\r
435 Create a transfer TRB.\r
436\r
437 @param Handle Debug port handle.\r
438 @param Urb The urb used to construct the transfer TRB.\r
439\r
440 @return Created TRB or NULL\r
441\r
442**/\r
443EFI_STATUS\r
444XhcCreateTransferTrb (\r
445 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
446 IN URB *Urb\r
447 )\r
448{\r
449 TRANSFER_RING *EPRing;\r
450 TRB *Trb;\r
451\r
452 if (Urb->Direction == EfiUsbDataIn) {\r
453 EPRing = &Handle->TransferRingIn;\r
454 } else {\r
455 EPRing = &Handle->TransferRingOut;\r
456 }\r
457 \r
458 Urb->Ring = (EFI_PHYSICAL_ADDRESS)(UINTN) EPRing;\r
459 XhcSyncTrsRing (Handle, EPRing);\r
460\r
461 Urb->Trb = EPRing->RingEnqueue;\r
462 Trb = (TRB *)(UINTN)EPRing->RingEnqueue;\r
463 Trb = (TRB *)(UINTN)EPRing->RingEnqueue;\r
464 Trb->TrbNormal.TRBPtrLo = XHC_LOW_32BIT (Urb->Data);\r
465 Trb->TrbNormal.TRBPtrHi = XHC_HIGH_32BIT (Urb->Data);\r
466 Trb->TrbNormal.Length = Urb->DataLen;\r
467 Trb->TrbNormal.TDSize = 0;\r
468 Trb->TrbNormal.IntTarget = 0;\r
469 Trb->TrbNormal.ISP = 1;\r
470 Trb->TrbNormal.IOC = 1;\r
471 Trb->TrbNormal.Type = TRB_TYPE_NORMAL;\r
472 \r
473 //\r
474 // Update the cycle bit to indicate this TRB has been consumed.\r
475 //\r
476 Trb->TrbNormal.CycleBit = EPRing->RingPCS & BIT0;\r
477 \r
478 return EFI_SUCCESS;\r
479}\r
480\r
481/**\r
482 Create a new URB for a new transaction.\r
483\r
484 @param Handle Debug port handle.\r
485 @param Direction The direction of data flow.\r
486 @param Data The user data to transfer\r
487 @param DataLen The length of data buffer\r
488\r
489 @return Created URB or NULL\r
490\r
491**/\r
492URB*\r
493XhcCreateUrb (\r
494 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
495 IN EFI_USB_DATA_DIRECTION Direction,\r
496 IN VOID *Data,\r
497 IN UINTN DataLen\r
498 )\r
499{\r
500 EFI_STATUS Status;\r
501 URB *Urb;\r
502 EFI_PHYSICAL_ADDRESS UrbData;\r
503 \r
504 if (Direction == EfiUsbDataIn) {\r
505 Urb = &Handle->UrbIn;\r
506 } else {\r
507 Urb = &Handle->UrbOut;\r
508 }\r
509\r
510 UrbData = Urb->Data;\r
511 \r
512 ZeroMem (Urb, sizeof (URB));\r
513 Urb->Direction = Direction;\r
514 \r
515 //\r
516 // Allocate memory to move data from CAR or SMRAM to normal memory\r
517 // to make XHCI DMA successfully\r
518 // re-use the pre-allocate buffer in PEI to avoid DXE memory service or gBS are not ready\r
519 //\r
520 Urb->Data = UrbData;\r
521 \r
522 if (Direction == EfiUsbDataIn) {\r
523 //\r
524 // Do not break URB data in buffer as it may contain the data which were just put in via DMA by XHC\r
525 //\r
526 Urb->DataLen = (UINT32) DataLen;\r
527 } else {\r
528 //\r
529 // Put data into URB data out buffer which will create TRBs\r
530 //\r
531 ZeroMem ((VOID*)(UINTN) Urb->Data, DataLen);\r
532 CopyMem ((VOID*)(UINTN) Urb->Data, Data, DataLen);\r
533 Urb->DataLen = (UINT32) DataLen;\r
534 }\r
535 \r
536 Status = XhcCreateTransferTrb (Handle, Urb);\r
537 ASSERT_EFI_ERROR (Status);\r
538\r
539 return Urb;\r
540}\r
541\r
542/**\r
543 Submits bulk transfer to a bulk endpoint of a USB device.\r
544\r
545 @param Handle Debug port handle.\r
546 @param Direction The direction of data transfer.\r
547 @param Data Array of pointers to the buffers of data to transmit\r
548 from or receive into.\r
549 @param DataLength The lenght of the data buffer.\r
550 @param Timeout Indicates the maximum time, in microsecond, which\r
551 the transfer is allowed to complete.\r
552\r
553 @retval EFI_SUCCESS The transfer was completed successfully.\r
554 @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource.\r
555 @retval EFI_INVALID_PARAMETER Some parameters are invalid.\r
556 @retval EFI_TIMEOUT The transfer failed due to timeout.\r
557 @retval EFI_DEVICE_ERROR The transfer failed due to host controller error.\r
558\r
559**/\r
560EFI_STATUS\r
561EFIAPI\r
562XhcDataTransfer (\r
563 IN USB3_DEBUG_PORT_HANDLE *Handle,\r
564 IN EFI_USB_DATA_DIRECTION Direction,\r
565 IN OUT VOID *Data,\r
566 IN OUT UINTN *DataLength,\r
567 IN UINTN Timeout\r
568 )\r
569{\r
570 URB *Urb;\r
571 EFI_STATUS Status;\r
572 \r
573 //\r
574 // Validate the parameters\r
575 //\r
576 if ((DataLength == NULL) || (*DataLength == 0) || (Data == NULL)) {\r
577 return EFI_INVALID_PARAMETER;\r
578 }\r
579\r
580 //\r
581 // Create a new URB, insert it into the asynchronous\r
582 // schedule list, then poll the execution status.\r
583 //\r
584 Urb = XhcCreateUrb (Handle, Direction, Data, *DataLength);\r
585 ASSERT (Urb != NULL);\r
586\r
587 XhcExecTransfer (Handle, Urb, Timeout);\r
588\r
589 *DataLength = Urb->Completed;\r
590\r
591 Status = EFI_TIMEOUT;\r
592 if (Urb->Result == EFI_USB_NOERROR) {\r
593 Status = EFI_SUCCESS;\r
594 }\r
595 \r
596 if (Direction == EfiUsbDataIn) {\r
597 //\r
598 // Move data from internal buffer to outside buffer (outside buffer may be in SMRAM...)\r
599 // SMRAM does not allow to do DMA, so we create an internal buffer.\r
600 //\r
601 CopyMem (Data, (VOID *)(UINTN)Urb->Data, *DataLength);\r
602 }\r
603\r
604 return Status;\r
605}\r
606\r