]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c
add parameter check logic to clean up Klocwork warning.
[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
ab6495ea 158 @return None.\r
913cb9dc 159\r
160**/\r
161VOID\r
162UhciLinkTdToQh (\r
163 IN UHCI_QH_SW *Qh,\r
164 IN UHCI_TD_SW *Td\r
165 )\r
166{\r
167 ASSERT ((Qh != NULL) && (Td != NULL));\r
168\r
169 Qh->QhHw.VerticalLink = QH_VLINK (Td, FALSE);\r
170 Qh->TDs = (VOID *) Td;\r
171}\r
172\r
173\r
174/**\r
ab6495ea 175 Unlink TD from the QH.\r
913cb9dc 176\r
ab6495ea 177 @param Qh The queue head to unlink from.\r
178 @param Td The TD to unlink.\r
913cb9dc 179\r
ab6495ea 180 @return None.\r
913cb9dc 181\r
182**/\r
183VOID\r
184UhciUnlinkTdFromQh (\r
185 IN UHCI_QH_SW *Qh,\r
186 IN UHCI_TD_SW *Td\r
187 )\r
188{\r
189 ASSERT ((Qh != NULL) && (Td != NULL));\r
190\r
191 Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);\r
192 Qh->TDs = NULL;\r
193}\r
194\r
195\r
196/**\r
ab6495ea 197 Append a new TD To the previous TD.\r
913cb9dc 198\r
ab6495ea 199 @param PrevTd Previous UHCI_TD_SW to be linked to.\r
200 @param ThisTd TD to link.\r
913cb9dc 201\r
ab6495ea 202 @return None.\r
913cb9dc 203\r
204**/\r
913cb9dc 205VOID\r
206UhciAppendTd (\r
207 IN UHCI_TD_SW *PrevTd,\r
208 IN UHCI_TD_SW *ThisTd\r
209 )\r
210{\r
211 ASSERT ((PrevTd != NULL) && (ThisTd != NULL));\r
212\r
213 PrevTd->TdHw.NextLink = TD_LINK (ThisTd, TRUE, FALSE);\r
214 PrevTd->NextTd = (VOID *) ThisTd;\r
215}\r
216\r
217\r
218/**\r
ab6495ea 219 Delete a list of TDs.\r
913cb9dc 220\r
ab6495ea 221 @param Uhc The UHCI device.\r
222 @param FirstTd TD link list head.\r
913cb9dc 223\r
ab6495ea 224 @return None.\r
913cb9dc 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
ab6495ea 247 Create an initialize a new queue head.\r
913cb9dc 248\r
ab6495ea 249 @param Uhc The UHCI device.\r
250 @param Interval The polling interval for the queue.\r
913cb9dc 251\r
ab6495ea 252 @return The newly created queue header.\r
913cb9dc 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
b4c24e2d 271 Qh->Interval = UhciConvertPollRate(Interval);\r
913cb9dc 272 Qh->TDs = NULL;\r
273 Qh->NextQh = NULL;\r
274\r
275 return Qh;\r
276}\r
277\r
278\r
279/**\r
ab6495ea 280 Create and intialize a TD.\r
913cb9dc 281\r
ab6495ea 282 @param Uhc The UHCI device.\r
913cb9dc 283\r
ab6495ea 284 @return The newly allocated and initialized TD.\r
913cb9dc 285\r
286**/\r
913cb9dc 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
ab6495ea 309 Create and initialize a TD for Setup Stage of a control transfer.\r
913cb9dc 310\r
ab6495ea 311 @param Uhc The UHCI device.\r
312 @param DevAddr Device address.\r
313 @param Request Device request.\r
314 @param IsLow Full speed or low speed.\r
913cb9dc 315\r
ab6495ea 316 @return The created setup Td Pointer.\r
913cb9dc 317\r
318**/\r
913cb9dc 319UHCI_TD_SW *\r
320UhciCreateSetupTd (\r
321 IN USB_HC_DEV *Uhc,\r
322 IN UINT8 DevAddr,\r
323 IN UINT8 *Request,\r
324 IN BOOLEAN IsLow\r
325 )\r
326{\r
327 UHCI_TD_SW *Td;\r
328\r
329 Td = UhciCreateTd (Uhc);\r
330\r
331 if (Td == NULL) {\r
332 return NULL;\r
333 }\r
334\r
335 Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);\r
336 Td->TdHw.ShortPacket = FALSE;\r
337 Td->TdHw.IsIsoch = FALSE;\r
338 Td->TdHw.IntOnCpl = FALSE;\r
339 Td->TdHw.ErrorCount = 0x03;\r
340 Td->TdHw.Status |= USBTD_ACTIVE;\r
341 Td->TdHw.DataToggle = 0;\r
342 Td->TdHw.EndPoint = 0;\r
343 Td->TdHw.LowSpeed = IsLow ? 1 : 0;\r
344 Td->TdHw.DeviceAddr = DevAddr & 0x7F;\r
345 Td->TdHw.MaxPacketLen = (UINT32) (sizeof (EFI_USB_DEVICE_REQUEST) - 1);\r
346 Td->TdHw.PidCode = SETUP_PACKET_ID;\r
347 Td->TdHw.DataBuffer = (UINT32) (UINTN) Request;\r
348\r
349 Td->Data = Request;\r
350 Td->DataLen = sizeof (EFI_USB_DEVICE_REQUEST);\r
351\r
352 return Td;\r
353}\r
354\r
355\r
356/**\r
ab6495ea 357 Create a TD for data.\r
913cb9dc 358\r
ab6495ea 359 @param Uhc The UHCI device.\r
360 @param DevAddr Device address.\r
361 @param Endpoint Endpoint number.\r
362 @param DataPtr Data buffer.\r
363 @param Len Data length.\r
364 @param PktId Packet ID.\r
365 @param Toggle Data toggle value.\r
366 @param IsLow Full speed or low speed.\r
913cb9dc 367\r
ab6495ea 368 @return Data Td pointer if success, otherwise NULL.\r
913cb9dc 369\r
370**/\r
913cb9dc 371UHCI_TD_SW *\r
372UhciCreateDataTd (\r
373 IN USB_HC_DEV *Uhc,\r
374 IN UINT8 DevAddr,\r
375 IN UINT8 Endpoint,\r
376 IN UINT8 *DataPtr,\r
377 IN UINTN Len,\r
378 IN UINT8 PktId,\r
379 IN UINT8 Toggle,\r
380 IN BOOLEAN IsLow\r
381 )\r
382{\r
383 UHCI_TD_SW *Td;\r
384\r
385 //\r
386 // Code as length - 1, and the max valid length is 0x500\r
387 //\r
388 ASSERT (Len <= 0x500);\r
389\r
390 Td = UhciCreateTd (Uhc);\r
391\r
392 if (Td == NULL) {\r
393 return NULL;\r
394 }\r
395\r
396 Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);\r
397 Td->TdHw.ShortPacket = FALSE;\r
398 Td->TdHw.IsIsoch = FALSE;\r
399 Td->TdHw.IntOnCpl = FALSE;\r
400 Td->TdHw.ErrorCount = 0X03;\r
401 Td->TdHw.Status = USBTD_ACTIVE;\r
402 Td->TdHw.LowSpeed = IsLow ? 1 : 0;\r
403 Td->TdHw.DataToggle = Toggle & 0x01;\r
404 Td->TdHw.EndPoint = Endpoint & 0x0F;\r
405 Td->TdHw.DeviceAddr = DevAddr & 0x7F;\r
406 Td->TdHw.MaxPacketLen = (UINT32) (Len - 1);\r
407 Td->TdHw.PidCode = (UINT8) PktId;\r
408 Td->TdHw.DataBuffer = (UINT32) (UINTN) DataPtr;\r
409\r
410 Td->Data = DataPtr;\r
411 Td->DataLen = (UINT16) Len;\r
412\r
413 return Td;\r
414}\r
415\r
416\r
417/**\r
ab6495ea 418 Create TD for the Status Stage of control transfer.\r
913cb9dc 419\r
ab6495ea 420 @param Uhc The UHCI device.\r
421 @param DevAddr Device address.\r
422 @param PktId Packet ID.\r
423 @param IsLow Full speed or low speed.\r
913cb9dc 424\r
ab6495ea 425 @return Status Td Pointer.\r
913cb9dc 426\r
427**/\r
913cb9dc 428UHCI_TD_SW *\r
429UhciCreateStatusTd (\r
430 IN USB_HC_DEV *Uhc,\r
431 IN UINT8 DevAddr,\r
432 IN UINT8 PktId,\r
433 IN BOOLEAN IsLow\r
434 )\r
435{\r
436 UHCI_TD_SW *Td;\r
437\r
438 Td = UhciCreateTd (Uhc);\r
439\r
440 if (Td == NULL) {\r
441 return NULL;\r
442 }\r
443\r
444 Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);\r
445 Td->TdHw.ShortPacket = FALSE;\r
446 Td->TdHw.IsIsoch = FALSE;\r
447 Td->TdHw.IntOnCpl = FALSE;\r
448 Td->TdHw.ErrorCount = 0x03;\r
449 Td->TdHw.Status |= USBTD_ACTIVE;\r
450 Td->TdHw.MaxPacketLen = 0x7FF; //0x7FF: there is no data (refer to UHCI spec)\r
451 Td->TdHw.DataToggle = 1;\r
452 Td->TdHw.EndPoint = 0;\r
453 Td->TdHw.LowSpeed = IsLow ? 1 : 0;\r
454 Td->TdHw.DeviceAddr = DevAddr & 0x7F;\r
455 Td->TdHw.PidCode = (UINT8) PktId;\r
456 Td->TdHw.DataBuffer = (UINT32) (UINTN) NULL;\r
457\r
458 Td->Data = NULL;\r
459 Td->DataLen = 0;\r
460\r
461 return Td;\r
462}\r
463\r
464\r
465/**\r
ab6495ea 466 Create Tds list for Control Transfer.\r
913cb9dc 467\r
ab6495ea 468 @param Uhc The UHCI device.\r
469 @param DeviceAddr The device address.\r
470 @param DataPktId Packet Identification of Data Tds.\r
471 @param Request A pointer to request structure buffer to transfer.\r
472 @param Data A pointer to user data buffer to transfer.\r
473 @param DataLen Length of user data to transfer.\r
474 @param MaxPacket Maximum packet size for control transfer.\r
475 @param IsLow Full speed or low speed.\r
913cb9dc 476\r
ab6495ea 477 @return The Td list head for the control transfer.\r
913cb9dc 478\r
479**/\r
480UHCI_TD_SW *\r
481UhciCreateCtrlTds (\r
482 IN USB_HC_DEV *Uhc,\r
483 IN UINT8 DeviceAddr,\r
484 IN UINT8 DataPktId,\r
485 IN UINT8 *Request,\r
486 IN UINT8 *Data,\r
487 IN UINTN DataLen,\r
488 IN UINT8 MaxPacket,\r
489 IN BOOLEAN IsLow\r
490 )\r
491{\r
492 UHCI_TD_SW *SetupTd;\r
493 UHCI_TD_SW *FirstDataTd;\r
494 UHCI_TD_SW *DataTd;\r
495 UHCI_TD_SW *PrevDataTd;\r
496 UHCI_TD_SW *StatusTd;\r
497 UINT8 DataToggle;\r
498 UINT8 StatusPktId;\r
499 UINTN ThisTdLen;\r
500\r
501\r
502 DataTd = NULL;\r
503 SetupTd = NULL;\r
504 FirstDataTd = NULL;\r
505 PrevDataTd = NULL;\r
506 StatusTd = NULL;\r
507\r
508 //\r
509 // Create setup packets for the transfer\r
510 //\r
511 SetupTd = UhciCreateSetupTd (Uhc, DeviceAddr, Request, IsLow);\r
512\r
513 if (SetupTd == NULL) {\r
514 return NULL;\r
515 }\r
516\r
517 //\r
518 // Create data packets for the transfer\r
519 //\r
520 DataToggle = 1;\r
521\r
522 while (DataLen > 0) {\r
523 //\r
524 // PktSize is the data load size in each Td.\r
525 //\r
526 ThisTdLen = (DataLen > MaxPacket ? MaxPacket : DataLen);\r
527\r
528 DataTd = UhciCreateDataTd (\r
529 Uhc,\r
530 DeviceAddr,\r
531 0,\r
532 Data,\r
533 ThisTdLen,\r
534 DataPktId,\r
535 DataToggle,\r
536 IsLow\r
537 );\r
538\r
539 if (DataTd == NULL) {\r
540 goto FREE_TD;\r
541 }\r
542\r
543 if (FirstDataTd == NULL) {\r
544 FirstDataTd = DataTd;\r
545 FirstDataTd->NextTd = NULL;\r
546 } else {\r
547 UhciAppendTd (PrevDataTd, DataTd);\r
548 }\r
549\r
550 DataToggle ^= 1;\r
551 PrevDataTd = DataTd;\r
552 Data += ThisTdLen;\r
553 DataLen -= ThisTdLen;\r
554 }\r
555\r
556 //\r
557 // Status packet is on the opposite direction to data packets\r
558 //\r
559 if (OUTPUT_PACKET_ID == DataPktId) {\r
560 StatusPktId = INPUT_PACKET_ID;\r
561 } else {\r
562 StatusPktId = OUTPUT_PACKET_ID;\r
563 }\r
564\r
565 StatusTd = UhciCreateStatusTd (Uhc, DeviceAddr, StatusPktId, IsLow);\r
566\r
567 if (StatusTd == NULL) {\r
568 goto FREE_TD;\r
569 }\r
570\r
571 //\r
572 // Link setup Td -> data Tds -> status Td together\r
573 //\r
574 if (FirstDataTd != NULL) {\r
575 UhciAppendTd (SetupTd, FirstDataTd);\r
576 UhciAppendTd (PrevDataTd, StatusTd);\r
577 } else {\r
578 UhciAppendTd (SetupTd, StatusTd);\r
579 }\r
580\r
581 return SetupTd;\r
582\r
583FREE_TD:\r
584 if (SetupTd != NULL) {\r
585 UhciDestoryTds (Uhc, SetupTd);\r
586 }\r
587\r
588 if (FirstDataTd != NULL) {\r
589 UhciDestoryTds (Uhc, FirstDataTd);\r
590 }\r
591\r
592 return NULL;\r
593}\r
594\r
595\r
596/**\r
ab6495ea 597 Create Tds list for Bulk/Interrupt Transfer.\r
598\r
599 @param Uhc USB_HC_DEV.\r
600 @param DevAddr Address of Device.\r
601 @param EndPoint Endpoint Number.\r
602 @param PktId Packet Identification of Data Tds.\r
603 @param Data A pointer to user data buffer to transfer.\r
604 @param DataLen Length of user data to transfer.\r
605 @param DataToggle Data Toggle Pointer.\r
606 @param MaxPacket Maximum packet size for Bulk/Interrupt transfer.\r
607 @param IsLow Is Low Speed Device.\r
608\r
609 @return The Tds list head for the bulk transfer.\r
913cb9dc 610\r
611**/\r
612UHCI_TD_SW *\r
613UhciCreateBulkOrIntTds (\r
614 IN USB_HC_DEV *Uhc,\r
615 IN UINT8 DevAddr,\r
616 IN UINT8 EndPoint,\r
617 IN UINT8 PktId,\r
618 IN UINT8 *Data,\r
619 IN UINTN DataLen,\r
620 IN OUT UINT8 *DataToggle,\r
621 IN UINT8 MaxPacket,\r
622 IN BOOLEAN IsLow\r
623 )\r
624{\r
625 UHCI_TD_SW *DataTd;\r
626 UHCI_TD_SW *FirstDataTd;\r
627 UHCI_TD_SW *PrevDataTd;\r
628 UINTN ThisTdLen;\r
629\r
630 DataTd = NULL;\r
631 FirstDataTd = NULL;\r
632 PrevDataTd = NULL;\r
633\r
634 //\r
635 // Create data packets for the transfer\r
636 //\r
637 while (DataLen > 0) {\r
638 //\r
639 // PktSize is the data load size that each Td.\r
640 //\r
641 ThisTdLen = DataLen;\r
642\r
643 if (DataLen > MaxPacket) {\r
644 ThisTdLen = MaxPacket;\r
645 }\r
646\r
647 DataTd = UhciCreateDataTd (\r
648 Uhc,\r
649 DevAddr,\r
650 EndPoint,\r
651 Data,\r
652 ThisTdLen,\r
653 PktId,\r
654 *DataToggle,\r
655 IsLow\r
656 );\r
657\r
658 if (DataTd == NULL) {\r
659 goto FREE_TD;\r
660 }\r
661\r
662 if (PktId == INPUT_PACKET_ID) {\r
663 DataTd->TdHw.ShortPacket = TRUE;\r
664 }\r
665\r
666 if (FirstDataTd == NULL) {\r
667 FirstDataTd = DataTd;\r
668 FirstDataTd->NextTd = NULL;\r
669 } else {\r
670 UhciAppendTd (PrevDataTd, DataTd);\r
671 }\r
672\r
673 *DataToggle ^= 1;\r
674 PrevDataTd = DataTd;\r
675 Data += ThisTdLen;\r
676 DataLen -= ThisTdLen;\r
677 }\r
678\r
679 return FirstDataTd;\r
680\r
681FREE_TD:\r
682 if (FirstDataTd != NULL) {\r
683 UhciDestoryTds (Uhc, FirstDataTd);\r
684 }\r
685\r
686 return NULL;\r
687}\r