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