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