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