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