]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / UhciDxe / UhciQueue.c
... / ...
CommitLineData
1/** @file\r
2\r
3 The UHCI register operation routines.\r
4\r
5Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
6SPDX-License-Identifier: BSD-2-Clause-Patent\r
7\r
8**/\r
9\r
10#include "Uhci.h"\r
11\r
12\r
13/**\r
14 Map address of request structure buffer.\r
15\r
16 @param Uhc The UHCI device.\r
17 @param Request The user request buffer.\r
18 @param MappedAddr Mapped address of request.\r
19 @param Map Identificaion of this mapping to return.\r
20\r
21 @return EFI_SUCCESS Success.\r
22 @return EFI_DEVICE_ERROR Fail to map the user request.\r
23\r
24**/\r
25EFI_STATUS\r
26UhciMapUserRequest (\r
27 IN USB_HC_DEV *Uhc,\r
28 IN OUT VOID *Request,\r
29 OUT UINT8 **MappedAddr,\r
30 OUT VOID **Map\r
31 )\r
32{\r
33 EFI_STATUS Status;\r
34 UINTN Len;\r
35 EFI_PHYSICAL_ADDRESS PhyAddr;\r
36\r
37 Len = sizeof (EFI_USB_DEVICE_REQUEST);\r
38 Status = Uhc->PciIo->Map (\r
39 Uhc->PciIo,\r
40 EfiPciIoOperationBusMasterRead,\r
41 Request,\r
42 &Len,\r
43 &PhyAddr,\r
44 Map\r
45 );\r
46\r
47 if (!EFI_ERROR (Status)) {\r
48 *MappedAddr = (UINT8 *) (UINTN) PhyAddr;\r
49 }\r
50\r
51 return Status;\r
52}\r
53\r
54\r
55/**\r
56 Map address of user data buffer.\r
57\r
58 @param Uhc The UHCI device.\r
59 @param Direction Direction of the data transfer.\r
60 @param Data The user data buffer.\r
61 @param Len Length of the user data.\r
62 @param PktId Packet identificaion.\r
63 @param MappedAddr Mapped address to return.\r
64 @param Map Identificaion of this mapping to return.\r
65\r
66 @return EFI_SUCCESS Success.\r
67 @return EFI_DEVICE_ERROR Fail to map the user data.\r
68\r
69**/\r
70EFI_STATUS\r
71UhciMapUserData (\r
72 IN USB_HC_DEV *Uhc,\r
73 IN EFI_USB_DATA_DIRECTION Direction,\r
74 IN VOID *Data,\r
75 IN OUT UINTN *Len,\r
76 OUT UINT8 *PktId,\r
77 OUT UINT8 **MappedAddr,\r
78 OUT VOID **Map\r
79 )\r
80{\r
81 EFI_STATUS Status;\r
82 EFI_PHYSICAL_ADDRESS PhyAddr;\r
83\r
84 Status = EFI_SUCCESS;\r
85\r
86 switch (Direction) {\r
87 case EfiUsbDataIn:\r
88 //\r
89 // BusMasterWrite means cpu read\r
90 //\r
91 *PktId = INPUT_PACKET_ID;\r
92 Status = Uhc->PciIo->Map (\r
93 Uhc->PciIo,\r
94 EfiPciIoOperationBusMasterWrite,\r
95 Data,\r
96 Len,\r
97 &PhyAddr,\r
98 Map\r
99 );\r
100\r
101 if (EFI_ERROR (Status)) {\r
102 goto EXIT;\r
103 }\r
104\r
105 *MappedAddr = (UINT8 *) (UINTN) PhyAddr;\r
106 break;\r
107\r
108 case EfiUsbDataOut:\r
109 *PktId = OUTPUT_PACKET_ID;\r
110 Status = Uhc->PciIo->Map (\r
111 Uhc->PciIo,\r
112 EfiPciIoOperationBusMasterRead,\r
113 Data,\r
114 Len,\r
115 &PhyAddr,\r
116 Map\r
117 );\r
118\r
119 if (EFI_ERROR (Status)) {\r
120 goto EXIT;\r
121 }\r
122\r
123 *MappedAddr = (UINT8 *) (UINTN) PhyAddr;\r
124 break;\r
125\r
126 case EfiUsbNoData:\r
127 if ((Len != NULL) && (*Len != 0)) {\r
128 Status = EFI_INVALID_PARAMETER;\r
129 goto EXIT;\r
130 }\r
131\r
132 *PktId = OUTPUT_PACKET_ID;\r
133 *MappedAddr = NULL;\r
134 *Map = NULL;\r
135 break;\r
136\r
137 default:\r
138 Status = EFI_INVALID_PARAMETER;\r
139 }\r
140\r
141EXIT:\r
142 return Status;\r
143}\r
144\r
145\r
146/**\r
147 Link the TD To QH.\r
148\r
149 @param Uhc The UHCI device.\r
150 @param Qh The queue head for the TD to link to.\r
151 @param Td The TD to link.\r
152\r
153**/\r
154VOID\r
155UhciLinkTdToQh (\r
156 IN USB_HC_DEV *Uhc,\r
157 IN UHCI_QH_SW *Qh,\r
158 IN UHCI_TD_SW *Td\r
159 )\r
160{\r
161 EFI_PHYSICAL_ADDRESS PhyAddr;\r
162\r
163 PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Td, sizeof (UHCI_TD_HW));\r
164\r
165 ASSERT ((Qh != NULL) && (Td != NULL));\r
166\r
167 Qh->QhHw.VerticalLink = QH_VLINK (PhyAddr, FALSE);\r
168 Qh->TDs = (VOID *) Td;\r
169}\r
170\r
171\r
172/**\r
173 Unlink TD from the QH.\r
174\r
175 @param Qh The queue head to unlink from.\r
176 @param Td The TD to unlink.\r
177\r
178**/\r
179VOID\r
180UhciUnlinkTdFromQh (\r
181 IN UHCI_QH_SW *Qh,\r
182 IN UHCI_TD_SW *Td\r
183 )\r
184{\r
185 ASSERT ((Qh != NULL) && (Td != NULL));\r
186\r
187 Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);\r
188 Qh->TDs = NULL;\r
189}\r
190\r
191\r
192/**\r
193 Append a new TD To the previous TD.\r
194\r
195 @param Uhc The UHCI device.\r
196 @param PrevTd Previous UHCI_TD_SW to be linked to.\r
197 @param ThisTd TD to link.\r
198\r
199**/\r
200VOID\r
201UhciAppendTd (\r
202 IN USB_HC_DEV *Uhc,\r
203 IN UHCI_TD_SW *PrevTd,\r
204 IN UHCI_TD_SW *ThisTd\r
205 )\r
206{\r
207 EFI_PHYSICAL_ADDRESS PhyAddr;\r
208\r
209 PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, ThisTd, sizeof (UHCI_TD_HW));\r
210\r
211 ASSERT ((PrevTd != NULL) && (ThisTd != NULL));\r
212\r
213 PrevTd->TdHw.NextLink = TD_LINK (PhyAddr, TRUE, FALSE);\r
214 PrevTd->NextTd = (VOID *) ThisTd;\r
215}\r
216\r
217\r
218/**\r
219 Delete a list of TDs.\r
220\r
221 @param Uhc The UHCI device.\r
222 @param FirstTd TD link list head.\r
223\r
224 @return None.\r
225\r
226**/\r
227VOID\r
228UhciDestoryTds (\r
229 IN USB_HC_DEV *Uhc,\r
230 IN UHCI_TD_SW *FirstTd\r
231 )\r
232{\r
233 UHCI_TD_SW *NextTd;\r
234 UHCI_TD_SW *ThisTd;\r
235\r
236 NextTd = FirstTd;\r
237\r
238 while (NextTd != NULL) {\r
239 ThisTd = NextTd;\r
240 NextTd = ThisTd->NextTd;\r
241 UsbHcFreeMem (Uhc->MemPool, ThisTd, sizeof (UHCI_TD_SW));\r
242 }\r
243}\r
244\r
245\r
246/**\r
247 Create an initialize a new queue head.\r
248\r
249 @param Uhc The UHCI device.\r
250 @param Interval The polling interval for the queue.\r
251\r
252 @return The newly created queue header.\r
253\r
254**/\r
255UHCI_QH_SW *\r
256UhciCreateQh (\r
257 IN USB_HC_DEV *Uhc,\r
258 IN UINTN Interval\r
259 )\r
260{\r
261 UHCI_QH_SW *Qh;\r
262\r
263 Qh = UsbHcAllocateMem (Uhc->MemPool, sizeof (UHCI_QH_SW));\r
264\r
265 if (Qh == NULL) {\r
266 return NULL;\r
267 }\r
268\r
269 Qh->QhHw.HorizonLink = QH_HLINK (NULL, TRUE);\r
270 Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);\r
271 Qh->Interval = UhciConvertPollRate(Interval);\r
272 Qh->TDs = NULL;\r
273 Qh->NextQh = NULL;\r
274\r
275 return Qh;\r
276}\r
277\r
278\r
279/**\r
280 Create and intialize a TD.\r
281\r
282 @param Uhc The UHCI device.\r
283\r
284 @return The newly allocated and initialized TD.\r
285\r
286**/\r
287UHCI_TD_SW *\r
288UhciCreateTd (\r
289 IN USB_HC_DEV *Uhc\r
290 )\r
291{\r
292 UHCI_TD_SW *Td;\r
293\r
294 Td = UsbHcAllocateMem (Uhc->MemPool, sizeof (UHCI_TD_SW));\r
295 if (Td == NULL) {\r
296 return NULL;\r
297 }\r
298\r
299 Td->TdHw.NextLink = TD_LINK (NULL, FALSE, TRUE);\r
300 Td->NextTd = NULL;\r
301 Td->Data = NULL;\r
302 Td->DataLen = 0;\r
303\r
304 return Td;\r
305}\r
306\r
307\r
308/**\r
309 Create and initialize a TD for Setup Stage of a control transfer.\r
310\r
311 @param Uhc The UHCI device.\r
312 @param DevAddr Device address.\r
313 @param Request A pointer to cpu memory address of Device request.\r
314 @param RequestPhy A pointer to pci memory address of Device request.\r
315 @param IsLow Full speed or low speed.\r
316\r
317 @return The created setup Td Pointer.\r
318\r
319**/\r
320UHCI_TD_SW *\r
321UhciCreateSetupTd (\r
322 IN USB_HC_DEV *Uhc,\r
323 IN UINT8 DevAddr,\r
324 IN UINT8 *Request,\r
325 IN UINT8 *RequestPhy,\r
326 IN BOOLEAN IsLow\r
327 )\r
328{\r
329 UHCI_TD_SW *Td;\r
330\r
331 Td = UhciCreateTd (Uhc);\r
332\r
333 if (Td == NULL) {\r
334 return NULL;\r
335 }\r
336\r
337 Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);\r
338 Td->TdHw.ShortPacket = FALSE;\r
339 Td->TdHw.IsIsoch = FALSE;\r
340 Td->TdHw.IntOnCpl = FALSE;\r
341 Td->TdHw.ErrorCount = 0x03;\r
342 Td->TdHw.Status |= USBTD_ACTIVE;\r
343 Td->TdHw.DataToggle = 0;\r
344 Td->TdHw.EndPoint = 0;\r
345 Td->TdHw.LowSpeed = IsLow ? 1 : 0;\r
346 Td->TdHw.DeviceAddr = DevAddr & 0x7F;\r
347 Td->TdHw.MaxPacketLen = (UINT32) (sizeof (EFI_USB_DEVICE_REQUEST) - 1);\r
348 Td->TdHw.PidCode = SETUP_PACKET_ID;\r
349 Td->TdHw.DataBuffer = (UINT32) (UINTN) RequestPhy;\r
350\r
351 Td->Data = Request;\r
352 Td->DataLen = (UINT16) sizeof (EFI_USB_DEVICE_REQUEST);\r
353\r
354 return Td;\r
355}\r
356\r
357\r
358/**\r
359 Create a TD for data.\r
360\r
361 @param Uhc The UHCI device.\r
362 @param DevAddr Device address.\r
363 @param Endpoint Endpoint number.\r
364 @param DataPtr A pointer to cpu memory address of Data buffer.\r
365 @param DataPhyPtr A pointer to pci memory address of Data buffer.\r
366 @param Len Data length.\r
367 @param PktId Packet ID.\r
368 @param Toggle Data toggle value.\r
369 @param IsLow Full speed or low speed.\r
370\r
371 @return Data Td pointer if success, otherwise NULL.\r
372\r
373**/\r
374UHCI_TD_SW *\r
375UhciCreateDataTd (\r
376 IN USB_HC_DEV *Uhc,\r
377 IN UINT8 DevAddr,\r
378 IN UINT8 Endpoint,\r
379 IN UINT8 *DataPtr,\r
380 IN UINT8 *DataPhyPtr,\r
381 IN UINTN Len,\r
382 IN UINT8 PktId,\r
383 IN UINT8 Toggle,\r
384 IN BOOLEAN IsLow\r
385 )\r
386{\r
387 UHCI_TD_SW *Td;\r
388\r
389 //\r
390 // Code as length - 1, and the max valid length is 0x500\r
391 //\r
392 ASSERT (Len <= 0x500);\r
393\r
394 Td = UhciCreateTd (Uhc);\r
395\r
396 if (Td == NULL) {\r
397 return NULL;\r
398 }\r
399\r
400 Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);\r
401 Td->TdHw.ShortPacket = FALSE;\r
402 Td->TdHw.IsIsoch = FALSE;\r
403 Td->TdHw.IntOnCpl = FALSE;\r
404 Td->TdHw.ErrorCount = 0x03;\r
405 Td->TdHw.Status = USBTD_ACTIVE;\r
406 Td->TdHw.LowSpeed = IsLow ? 1 : 0;\r
407 Td->TdHw.DataToggle = Toggle & 0x01;\r
408 Td->TdHw.EndPoint = Endpoint & 0x0F;\r
409 Td->TdHw.DeviceAddr = DevAddr & 0x7F;\r
410 Td->TdHw.MaxPacketLen = (UINT32) (Len - 1);\r
411 Td->TdHw.PidCode = (UINT8) PktId;\r
412 Td->TdHw.DataBuffer = (UINT32) (UINTN) DataPhyPtr;\r
413\r
414 Td->Data = DataPtr;\r
415 Td->DataLen = (UINT16) Len;\r
416\r
417 return Td;\r
418}\r
419\r
420\r
421/**\r
422 Create TD for the Status Stage of control transfer.\r
423\r
424 @param Uhc The UHCI device.\r
425 @param DevAddr Device address.\r
426 @param PktId Packet ID.\r
427 @param IsLow Full speed or low speed.\r
428\r
429 @return Status Td Pointer.\r
430\r
431**/\r
432UHCI_TD_SW *\r
433UhciCreateStatusTd (\r
434 IN USB_HC_DEV *Uhc,\r
435 IN UINT8 DevAddr,\r
436 IN UINT8 PktId,\r
437 IN BOOLEAN IsLow\r
438 )\r
439{\r
440 UHCI_TD_SW *Td;\r
441\r
442 Td = UhciCreateTd (Uhc);\r
443\r
444 if (Td == NULL) {\r
445 return NULL;\r
446 }\r
447\r
448 Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);\r
449 Td->TdHw.ShortPacket = FALSE;\r
450 Td->TdHw.IsIsoch = FALSE;\r
451 Td->TdHw.IntOnCpl = FALSE;\r
452 Td->TdHw.ErrorCount = 0x03;\r
453 Td->TdHw.Status |= USBTD_ACTIVE;\r
454 Td->TdHw.MaxPacketLen = 0x7FF; //0x7FF: there is no data (refer to UHCI spec)\r
455 Td->TdHw.DataToggle = 1;\r
456 Td->TdHw.EndPoint = 0;\r
457 Td->TdHw.LowSpeed = IsLow ? 1 : 0;\r
458 Td->TdHw.DeviceAddr = DevAddr & 0x7F;\r
459 Td->TdHw.PidCode = (UINT8) PktId;\r
460 Td->TdHw.DataBuffer = (UINT32) (UINTN) NULL;\r
461\r
462 Td->Data = NULL;\r
463 Td->DataLen = 0;\r
464\r
465 return Td;\r
466}\r
467\r
468\r
469/**\r
470 Create Tds list for Control Transfer.\r
471\r
472 @param Uhc The UHCI device.\r
473 @param DeviceAddr The device address.\r
474 @param DataPktId Packet Identification of Data Tds.\r
475 @param Request A pointer to cpu memory address of request structure buffer to transfer.\r
476 @param RequestPhy A pointer to pci memory address of request structure buffer to transfer.\r
477 @param Data A pointer to cpu memory address of user data buffer to transfer.\r
478 @param DataPhy A pointer to pci memory address of user data buffer to transfer.\r
479 @param DataLen Length of user data to transfer.\r
480 @param MaxPacket Maximum packet size for control transfer.\r
481 @param IsLow Full speed or low speed.\r
482\r
483 @return The Td list head for the control transfer.\r
484\r
485**/\r
486UHCI_TD_SW *\r
487UhciCreateCtrlTds (\r
488 IN USB_HC_DEV *Uhc,\r
489 IN UINT8 DeviceAddr,\r
490 IN UINT8 DataPktId,\r
491 IN UINT8 *Request,\r
492 IN UINT8 *RequestPhy,\r
493 IN UINT8 *Data,\r
494 IN UINT8 *DataPhy,\r
495 IN UINTN DataLen,\r
496 IN UINT8 MaxPacket,\r
497 IN BOOLEAN IsLow\r
498 )\r
499{\r
500 UHCI_TD_SW *SetupTd;\r
501 UHCI_TD_SW *FirstDataTd;\r
502 UHCI_TD_SW *DataTd;\r
503 UHCI_TD_SW *PrevDataTd;\r
504 UHCI_TD_SW *StatusTd;\r
505 UINT8 DataToggle;\r
506 UINT8 StatusPktId;\r
507 UINTN ThisTdLen;\r
508\r
509\r
510 DataTd = NULL;\r
511 SetupTd = NULL;\r
512 FirstDataTd = NULL;\r
513 PrevDataTd = NULL;\r
514 StatusTd = NULL;\r
515\r
516 //\r
517 // Create setup packets for the transfer\r
518 //\r
519 SetupTd = UhciCreateSetupTd (Uhc, DeviceAddr, Request, RequestPhy, IsLow);\r
520\r
521 if (SetupTd == NULL) {\r
522 return NULL;\r
523 }\r
524\r
525 //\r
526 // Create data packets for the transfer\r
527 //\r
528 DataToggle = 1;\r
529\r
530 while (DataLen > 0) {\r
531 //\r
532 // PktSize is the data load size in each Td.\r
533 //\r
534 ThisTdLen = (DataLen > MaxPacket ? MaxPacket : DataLen);\r
535\r
536 DataTd = UhciCreateDataTd (\r
537 Uhc,\r
538 DeviceAddr,\r
539 0,\r
540 Data, //cpu memory address\r
541 DataPhy, //Pci memory address\r
542 ThisTdLen,\r
543 DataPktId,\r
544 DataToggle,\r
545 IsLow\r
546 );\r
547\r
548 if (DataTd == NULL) {\r
549 goto FREE_TD;\r
550 }\r
551\r
552 if (FirstDataTd == NULL) {\r
553 FirstDataTd = DataTd;\r
554 FirstDataTd->NextTd = NULL;\r
555 } else {\r
556 UhciAppendTd (Uhc, PrevDataTd, DataTd);\r
557 }\r
558\r
559 DataToggle ^= 1;\r
560 PrevDataTd = DataTd;\r
561 Data += ThisTdLen;\r
562 DataPhy += ThisTdLen;\r
563 DataLen -= ThisTdLen;\r
564 }\r
565\r
566 //\r
567 // Status packet is on the opposite direction to data packets\r
568 //\r
569 if (OUTPUT_PACKET_ID == DataPktId) {\r
570 StatusPktId = INPUT_PACKET_ID;\r
571 } else {\r
572 StatusPktId = OUTPUT_PACKET_ID;\r
573 }\r
574\r
575 StatusTd = UhciCreateStatusTd (Uhc, DeviceAddr, StatusPktId, IsLow);\r
576\r
577 if (StatusTd == NULL) {\r
578 goto FREE_TD;\r
579 }\r
580\r
581 //\r
582 // Link setup Td -> data Tds -> status Td together\r
583 //\r
584 if (FirstDataTd != NULL) {\r
585 UhciAppendTd (Uhc, SetupTd, FirstDataTd);\r
586 UhciAppendTd (Uhc, PrevDataTd, StatusTd);\r
587 } else {\r
588 UhciAppendTd (Uhc, SetupTd, StatusTd);\r
589 }\r
590\r
591 return SetupTd;\r
592\r
593FREE_TD:\r
594 if (SetupTd != NULL) {\r
595 UhciDestoryTds (Uhc, SetupTd);\r
596 }\r
597\r
598 if (FirstDataTd != NULL) {\r
599 UhciDestoryTds (Uhc, FirstDataTd);\r
600 }\r
601\r
602 return NULL;\r
603}\r
604\r
605\r
606/**\r
607 Create Tds list for Bulk/Interrupt Transfer.\r
608\r
609 @param Uhc USB_HC_DEV.\r
610 @param DevAddr Address of Device.\r
611 @param EndPoint Endpoint Number.\r
612 @param PktId Packet Identification of Data Tds.\r
613 @param Data A pointer to cpu memory address of user data buffer to transfer.\r
614 @param DataPhy A pointer to pci memory address of user data buffer to transfer.\r
615 @param DataLen Length of user data to transfer.\r
616 @param DataToggle Data Toggle Pointer.\r
617 @param MaxPacket Maximum packet size for Bulk/Interrupt transfer.\r
618 @param IsLow Is Low Speed Device.\r
619\r
620 @return The Tds list head for the bulk transfer.\r
621\r
622**/\r
623UHCI_TD_SW *\r
624UhciCreateBulkOrIntTds (\r
625 IN USB_HC_DEV *Uhc,\r
626 IN UINT8 DevAddr,\r
627 IN UINT8 EndPoint,\r
628 IN UINT8 PktId,\r
629 IN UINT8 *Data,\r
630 IN UINT8 *DataPhy,\r
631 IN UINTN DataLen,\r
632 IN OUT UINT8 *DataToggle,\r
633 IN UINT8 MaxPacket,\r
634 IN BOOLEAN IsLow\r
635 )\r
636{\r
637 UHCI_TD_SW *DataTd;\r
638 UHCI_TD_SW *FirstDataTd;\r
639 UHCI_TD_SW *PrevDataTd;\r
640 UINTN ThisTdLen;\r
641\r
642 DataTd = NULL;\r
643 FirstDataTd = NULL;\r
644 PrevDataTd = NULL;\r
645\r
646 //\r
647 // Create data packets for the transfer\r
648 //\r
649 while (DataLen > 0) {\r
650 //\r
651 // PktSize is the data load size that each Td.\r
652 //\r
653 ThisTdLen = DataLen;\r
654\r
655 if (DataLen > MaxPacket) {\r
656 ThisTdLen = MaxPacket;\r
657 }\r
658\r
659 DataTd = UhciCreateDataTd (\r
660 Uhc,\r
661 DevAddr,\r
662 EndPoint,\r
663 Data,\r
664 DataPhy,\r
665 ThisTdLen,\r
666 PktId,\r
667 *DataToggle,\r
668 IsLow\r
669 );\r
670\r
671 if (DataTd == NULL) {\r
672 goto FREE_TD;\r
673 }\r
674\r
675 if (PktId == INPUT_PACKET_ID) {\r
676 DataTd->TdHw.ShortPacket = TRUE;\r
677 }\r
678\r
679 if (FirstDataTd == NULL) {\r
680 FirstDataTd = DataTd;\r
681 FirstDataTd->NextTd = NULL;\r
682 } else {\r
683 UhciAppendTd (Uhc, PrevDataTd, DataTd);\r
684 }\r
685\r
686 *DataToggle ^= 1;\r
687 PrevDataTd = DataTd;\r
688 Data += ThisTdLen;\r
689 DataPhy += ThisTdLen;\r
690 DataLen -= ThisTdLen;\r
691 }\r
692\r
693 return FirstDataTd;\r
694\r
695FREE_TD:\r
696 if (FirstDataTd != NULL) {\r
697 UhciDestoryTds (Uhc, FirstDataTd);\r
698 }\r
699\r
700 return NULL;\r
701}\r