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