3 Copyright (c) 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 The EHCI register operation routines.
29 Create Frame List Structure
31 @param Uhc UHCI device
33 @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources
34 @retval EFI_UNSUPPORTED Map memory fail
35 @retval EFI_SUCCESS Success
43 EFI_PHYSICAL_ADDRESS MappedAddr
;
52 // The Frame List is a common buffer that will be
53 // accessed by both the cpu and the usb bus master
54 // at the same time. The Frame List ocupies 4K bytes,
55 // and must be aligned on 4-Kbyte boundaries.
58 Pages
= EFI_SIZE_TO_PAGES (Bytes
);
60 Status
= Uhc
->PciIo
->AllocateBuffer (
69 if (EFI_ERROR (Status
)) {
70 return EFI_OUT_OF_RESOURCES
;
73 Status
= Uhc
->PciIo
->Map (
75 EfiPciIoOperationBusMasterCommonBuffer
,
82 if (EFI_ERROR (Status
) || (Bytes
!= 4096)) {
83 Status
= EFI_UNSUPPORTED
;
87 Uhc
->FrameBase
= (UINT32
*) (UINTN
) MappedAddr
;
88 Uhc
->FrameMapping
= Mapping
;
91 // Allocate the QH used by sync interrupt/control/bulk transfer.
92 // FS ctrl/bulk queue head is set to loopback so additional BW
93 // can be reclaimed. Notice, LS don't support bulk transfer and
94 // also doesn't support BW reclamation.
96 Uhc
->SyncIntQh
= UhciCreateQh (Uhc
, 1);
97 Uhc
->CtrlQh
= UhciCreateQh (Uhc
, 1);
98 Uhc
->BulkQh
= UhciCreateQh (Uhc
, 1);
100 if ((Uhc
->SyncIntQh
== NULL
) || (Uhc
->CtrlQh
== NULL
) || (Uhc
->BulkQh
== NULL
)) {
101 Uhc
->PciIo
->Unmap (Uhc
->PciIo
, Mapping
);
102 Status
= EFI_OUT_OF_RESOURCES
;
109 // Link the three together: SyncIntQh->CtrlQh->BulkQh <---------+
110 // Each frame entry is linked to this sequence of QH. These QH
111 // will remain on the schedul, never got removed
113 Uhc
->SyncIntQh
->QhHw
.HorizonLink
= QH_HLINK (Uhc
->CtrlQh
, FALSE
);
114 Uhc
->SyncIntQh
->NextQh
= Uhc
->CtrlQh
;
116 Uhc
->CtrlQh
->QhHw
.HorizonLink
= QH_HLINK (Uhc
->BulkQh
, FALSE
);
117 Uhc
->CtrlQh
->NextQh
= Uhc
->BulkQh
;
120 // Some old platform such as Intel's Tiger 4 has a difficult time
121 // in supporting the full speed bandwidth reclamation in the previous
122 // mentioned form. Most new platforms don't suffer it.
124 #ifdef UHCI_NO_BW_RECLAMATION
125 Uhc
->BulkQh
->QhHw
.HorizonLink
= QH_HLINK (NULL
, TRUE
);
127 Uhc
->BulkQh
->QhHw
.HorizonLink
= QH_HLINK (Uhc
->BulkQh
, FALSE
);
130 Uhc
->BulkQh
->NextQh
= NULL
;
132 for (Index
= 0; Index
< UHCI_FRAME_NUM
; Index
++) {
133 Uhc
->FrameBase
[Index
] = QH_HLINK (Uhc
->SyncIntQh
, FALSE
);
137 // Tell the Host Controller where the Frame List lies,
138 // by set the Frame List Base Address Register.
140 UhciSetFrameListBaseAddr (Uhc
->PciIo
, (VOID
*) (Uhc
->FrameBase
));
144 if (Uhc
->SyncIntQh
!= NULL
) {
145 UsbHcFreeMem (Uhc
->MemPool
, Uhc
->SyncIntQh
, sizeof (UHCI_QH_SW
));
148 if (Uhc
->CtrlQh
!= NULL
) {
149 UsbHcFreeMem (Uhc
->MemPool
, Uhc
->CtrlQh
, sizeof (UHCI_QH_SW
));
152 if (Uhc
->BulkQh
!= NULL
) {
153 UsbHcFreeMem (Uhc
->MemPool
, Uhc
->BulkQh
, sizeof (UHCI_QH_SW
));
156 Uhc
->PciIo
->FreeBuffer (Uhc
->PciIo
, Pages
, Buffer
);
162 Destory FrameList buffer
164 @param Uhc The UHCI device
170 UhciDestoryFrameList (
175 // Unmap the common buffer for framelist entry,
176 // and free the common buffer.
177 // Uhci's frame list occupy 4k memory.
179 Uhc
->PciIo
->Unmap (Uhc
->PciIo
, Uhc
->FrameMapping
);
181 Uhc
->PciIo
->FreeBuffer (
183 EFI_SIZE_TO_PAGES (4096),
184 (VOID
*) Uhc
->FrameBase
187 if (Uhc
->SyncIntQh
!= NULL
) {
188 UsbHcFreeMem (Uhc
->MemPool
, Uhc
->SyncIntQh
, sizeof (UHCI_QH_SW
));
191 if (Uhc
->CtrlQh
!= NULL
) {
192 UsbHcFreeMem (Uhc
->MemPool
, Uhc
->CtrlQh
, sizeof (UHCI_QH_SW
));
195 if (Uhc
->BulkQh
!= NULL
) {
196 UsbHcFreeMem (Uhc
->MemPool
, Uhc
->BulkQh
, sizeof (UHCI_QH_SW
));
199 Uhc
->FrameBase
= NULL
;
200 Uhc
->SyncIntQh
= NULL
;
207 Convert the poll rate to the maxium 2^n that is smaller
210 @param Interval The poll rate to convert
212 @return The converted poll rate
216 UhciConvertPollRate (
222 ASSERT (Interval
!= 0);
225 // Find the index (1 based) of the highest non-zero bit
229 while (Interval
!= 0) {
234 return (UINTN
)1 << (BitCount
- 1);
239 Link a queue head (for asynchronous interrupt transfer) to
242 @param FrameBase The base of the frame list
243 @param Qh The queue head to link into
249 UhciLinkQhToFrameList (
258 ASSERT ((FrameBase
!= NULL
) && (Qh
!= NULL
));
260 for (Index
= 0; Index
< UHCI_FRAME_NUM
; Index
+= Qh
->Interval
) {
262 // First QH can't be NULL because we always keep static queue
263 // heads on the frame list
265 ASSERT (!LINK_TERMINATED (FrameBase
[Index
]));
266 Next
= UHCI_ADDR (FrameBase
[Index
]);
270 // Now, insert the queue head (Qh) into this frame:
271 // 1. Find a queue head with the same poll interval, just insert
272 // Qh after this queue head, then we are done.
274 // 2. Find the position to insert the queue head into:
275 // Previous head's interval is bigger than Qh's
276 // Next head's interval is less than Qh's
277 // Then, insert the Qh between then
279 // This method is very much the same as that used by EHCI.
280 // Because each QH's interval is round down to 2^n, poll
283 while (Next
->Interval
> Qh
->Interval
) {
288 ASSERT (Next
!= NULL
);
291 // The entry may have been linked into the frame by early insertation.
292 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
293 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
294 // It isn't necessary to compare all the QH with the same interval to
295 // Qh. This is because if there is other QH with the same interval, Qh
296 // should has been inserted after that at FrameBase[0] and at FrameBase[0] it is
297 // impossible (Next == Qh)
303 if (Next
->Interval
== Qh
->Interval
) {
305 // If there is a QH with the same interval, it locates at
306 // FrameBase[0], and we can simply insert it after this QH. We
309 ASSERT ((Index
== 0) && (Qh
->NextQh
== NULL
));
317 Qh
->QhHw
.HorizonLink
= Prev
->QhHw
.HorizonLink
;
318 Prev
->QhHw
.HorizonLink
= QH_HLINK (Qh
, FALSE
);
323 // OK, find the right position, insert it in. If Qh's next
324 // link has already been set, it is in position. This is
325 // guarranted by 2^n polling interval.
327 if (Qh
->NextQh
== NULL
) {
329 Qh
->QhHw
.HorizonLink
= QH_HLINK (Next
, FALSE
);
333 FrameBase
[Index
] = QH_HLINK (Qh
, FALSE
);
336 Prev
->QhHw
.HorizonLink
= QH_HLINK (Qh
, FALSE
);
343 Unlink QH from the frame list is easier: find all
344 the precedence node, and pointer there next to QhSw's
347 @param FrameBase The base address of the frame list
348 @param Qh The queue head to unlink
354 UhciUnlinkQhFromFrameList (
363 ASSERT ((FrameBase
!= NULL
) && (Qh
!= NULL
));
365 for (Index
= 0; Index
< UHCI_FRAME_NUM
; Index
+= Qh
->Interval
) {
367 // Frame link can't be NULL because we always keep static
368 // queue heads on the frame list
370 ASSERT (!LINK_TERMINATED (FrameBase
[Index
]));
371 This
= UHCI_ADDR (FrameBase
[Index
]);
375 // Walk through the frame's QH list to find the
376 // queue head to remove
378 while ((This
!= NULL
) && (This
!= Qh
)) {
384 // Qh may have already been unlinked from this frame
393 // Qh is the first entry in the frame
395 FrameBase
[Index
] = Qh
->QhHw
.HorizonLink
;
397 Prev
->NextQh
= Qh
->NextQh
;
398 Prev
->QhHw
.HorizonLink
= Qh
->QhHw
.HorizonLink
;
407 @param Uhc This UHCI device
408 @param Td UHCI_TD_SW to check
409 @param IsLow Is Low Speed Device
410 @param QhResult Return the result of this TD list
412 @return Whether the TD's result is finialized.
421 OUT UHCI_QH_RESULT
*QhResult
432 // Initialize the data toggle to that of the first
433 // TD. The next toggle to use is either:
434 // 1. first TD's toggle if no TD is executed OK
435 // 2. the next toggle of last executed-OK TD
437 QhResult
->Result
= EFI_USB_NOERROR
;
438 QhResult
->NextToggle
= (UINT8
)Td
->TdHw
.DataToggle
;
439 QhResult
->Complete
= 0;
443 State
= (UINT8
)TdHw
->Status
;
446 // UHCI will set STALLED bit when it abort the execution
447 // of TD list. There are several reasons:
448 // 1. BABBLE error happened
449 // 2. Received a STALL response
450 // 3. Error count decreased to zero.
452 // It also set CRC/Timeout/NAK/Buffer Error/BitStuff Error
453 // bits when corresponding conditions happen. But these
454 // conditions are not deadly, that is a TD can successfully
455 // completes even these bits are set. But it is likely that
456 // upper layer won't distinguish these condtions. So, only
457 // set these bits when TD is actually halted.
459 if (State
& USBTD_STALLED
) {
460 if (State
& USBTD_BABBLE
) {
461 QhResult
->Result
|= EFI_USB_ERR_BABBLE
;
463 } else if (TdHw
->ErrorCount
!= 0) {
464 QhResult
->Result
|= EFI_USB_ERR_STALL
;
467 if (State
& USBTD_CRC
) {
468 QhResult
->Result
|= EFI_USB_ERR_CRC
;
471 if (State
& USBTD_BUFFERR
) {
472 QhResult
->Result
|= EFI_USB_ERR_BUFFER
;
475 if (Td
->TdHw
.Status
& USBTD_BITSTUFF
) {
476 QhResult
->Result
|= EFI_USB_ERR_BITSTUFF
;
479 if (TdHw
->ErrorCount
== 0) {
480 QhResult
->Result
|= EFI_USB_ERR_TIMEOUT
;
486 } else if (State
& USBTD_ACTIVE
) {
488 // The TD is still active, no need to check further.
490 QhResult
->Result
|= EFI_USB_ERR_NOTEXECUTE
;
497 // Update the next data toggle, it is always the
498 // next to the last known-good TD's data toggle if
499 // any TD is executed OK
501 QhResult
->NextToggle
= (UINT8
) (1 - (UINT8
)TdHw
->DataToggle
);
504 // This TD is finished OK or met short packet read. Update the
505 // transfer length if it isn't a SETUP.
507 Len
= (TdHw
->ActualLen
+ 1) & 0x7FF;
509 if (TdHw
->PidCode
!= SETUP_PACKET_ID
) {
510 QhResult
->Complete
+= Len
;
514 // Short packet condition for full speed input TD, also
515 // terminate the transfer
517 if (!IsLow
&& (TdHw
->ShortPacket
== 1) && (Len
< Td
->DataLen
)) {
518 UHCI_DEBUG (("UhciCheckTdStatus: short packet read occured\n"));
530 // Check whether HC is halted. Don't move this up. It must be
531 // called after data toggle is successfully updated.
533 if (!UhciIsHcWorking (Uhc
->PciIo
)) {
534 QhResult
->Result
|= EFI_USB_ERR_SYSTEM
;
539 Uhc
->PciIo
->Flush (Uhc
->PciIo
);
542 UhciAckAllInterrupt (Uhc
);
549 Check the result of the transfer
551 @param Uhc The UHCI device
552 @param Td The first TDs of the transfer
553 @param TimeOut TimeOut value in milliseconds
554 @param IsLow Is Low Speed Device
555 @param QhResult The variable to return result
557 @retval EFI_SUCCESS The transfer finished with success
558 @retval EFI_DEVICE_ERROR Transfer failed
562 UhciExecuteTransfer (
568 OUT UHCI_QH_RESULT
*QhResult
577 Status
= EFI_SUCCESS
;
578 Delay
= (TimeOut
* STALL_1_MS
/ UHC_SYN_POLL
) + 1;
580 for (Index
= 0; Index
< Delay
; Index
++) {
581 Finished
= UhciCheckTdStatus (Uhc
, Td
, IsLow
, QhResult
);
584 // Transfer is OK or some error occured (TD inactive)
590 gBS
->Stall (UHC_SYN_POLL
);
594 UHCI_ERROR (("UhciExecuteTransfer: execution not finished for %dms\n", TimeOut
));
596 UHCI_DUMP_TDS ((Td
));
598 Status
= EFI_TIMEOUT
;
600 } else if (QhResult
->Result
!= EFI_USB_NOERROR
) {
601 UHCI_ERROR (("UhciExecuteTransfer: execution failed with result %x\n", QhResult
->Result
));
603 UHCI_DUMP_TDS ((Td
));
605 Status
= EFI_DEVICE_ERROR
;
613 Update Async Request, QH and TDs
615 @param AsyncReq The UHCI asynchronous transfer to update
616 @param Result Transfer reslut
617 @param ErrTdPos Error TD Position
625 IN UHCI_ASYNC_REQUEST
*AsyncReq
,
635 FirstTd
= AsyncReq
->FirstTd
;
637 if (Result
== EFI_USB_NOERROR
) {
639 // The last transfer succeeds. Then we need to update
640 // the Qh and Td for next round of transfer.
641 // 1. Update the TD's data toggle
642 // 2. Activate all the TDs
643 // 3. Link the TD to the queue head again since during
644 // execution, queue head's TD pointer is changed by
647 for (Td
= FirstTd
; Td
!= NULL
; Td
= Td
->NextTd
) {
648 Td
->TdHw
.DataToggle
= NextToggle
;
650 Td
->TdHw
.Status
|= USBTD_ACTIVE
;
653 UhciLinkTdToQh (Qh
, FirstTd
);
660 Create Async Request node, and Link to List
662 @param Uhc The UHCI device
663 @param Qh The queue head of the transfer
664 @param FirstTd First TD of the transfer
665 @param DevAddr Device Address
666 @param EndPoint EndPoint Address
667 @param DataLen Data length
668 @param Interval Polling Interval when inserted to frame list
669 @param Mapping Mapping value
670 @param Data Data buffer, unmapped
671 @param Callback Callback after interrupt transfeer
672 @param Context Callback Context passed as function parameter
673 @param IsLow Is Low Speed
675 @retval EFI_SUCCESS An asynchronous transfer is created
676 @retval EFI_INVALID_PARAMETER Paremeter is error
677 @retval EFI_OUT_OF_RESOURCES Failed because of resource shortage.
684 IN UHCI_TD_SW
*FirstTd
,
691 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback
,
696 UHCI_ASYNC_REQUEST
*AsyncReq
;
698 AsyncReq
= AllocatePool (sizeof (UHCI_ASYNC_REQUEST
));
700 if (AsyncReq
== NULL
) {
701 return EFI_OUT_OF_RESOURCES
;
705 // Fill Request field. Data is allocated host memory, not mapped
707 AsyncReq
->Signature
= UHCI_ASYNC_INT_SIGNATURE
;
708 AsyncReq
->DevAddr
= DevAddr
;
709 AsyncReq
->EndPoint
= EndPoint
;
710 AsyncReq
->DataLen
= DataLen
;
711 AsyncReq
->Interval
= Interval
;
712 AsyncReq
->Mapping
= Mapping
;
713 AsyncReq
->Data
= Data
;
714 AsyncReq
->Callback
= Callback
;
715 AsyncReq
->Context
= Context
;
717 AsyncReq
->FirstTd
= FirstTd
;
718 AsyncReq
->IsLow
= IsLow
;
721 // Insert the new interrupt transfer to the head of the list.
722 // The interrupt transfer's monitor function scans the whole
723 // list from head to tail. The new interrupt transfer MUST be
724 // added to the head of the list.
726 InsertHeadList (&(Uhc
->AsyncIntList
), &(AsyncReq
->Link
));
734 Free an asynchronous request's resource such as memory
736 @param Uhc The UHCI device
737 @param AsyncReq The asynchronous request to free
746 IN UHCI_ASYNC_REQUEST
*AsyncReq
749 ASSERT ((Uhc
!= NULL
) && (AsyncReq
!= NULL
));
751 UhciDestoryTds (Uhc
, AsyncReq
->FirstTd
);
752 UsbHcFreeMem (Uhc
->MemPool
, AsyncReq
->QhSw
, sizeof (UHCI_QH_SW
));
754 if (AsyncReq
->Mapping
!= NULL
) {
755 Uhc
->PciIo
->Unmap (Uhc
->PciIo
, AsyncReq
->Mapping
);
758 if (AsyncReq
->Data
!= NULL
) {
759 gBS
->FreePool (AsyncReq
->Data
);
762 gBS
->FreePool (AsyncReq
);
767 Unlink an asynchronous request's from UHC's asynchronus list.
768 also remove the queue head from the frame list. If FreeNow,
769 release its resource also. Otherwise, add the request to the
770 UHC's recycle list to wait for a while before release the memory.
771 Until then, hardware won't hold point to the request.
773 @param Uhc The UHCI device
774 @param AsyncReq The asynchronous request to free
775 @param FreeNow If TRUE, free the resource immediately, otherwise
776 add the request to recycle wait list.
785 IN UHCI_ASYNC_REQUEST
*AsyncReq
,
789 ASSERT ((Uhc
!= NULL
) && (AsyncReq
!= NULL
));
791 RemoveEntryList (&(AsyncReq
->Link
));
792 UhciUnlinkQhFromFrameList (Uhc
->FrameBase
, AsyncReq
->QhSw
);
795 UhciFreeAsyncReq (Uhc
, AsyncReq
);
798 // To sychronize with hardware, mark the queue head as inactive
799 // then add AsyncReq to UHC's recycle list
801 AsyncReq
->QhSw
->QhHw
.VerticalLink
= QH_VLINK (NULL
, TRUE
);
802 AsyncReq
->Recycle
= Uhc
->RecycleWait
;
803 Uhc
->RecycleWait
= AsyncReq
;
809 Delete Async Interrupt QH and TDs
811 @param Uhc The UHCI device
812 @param DevAddr Device Address
813 @param EndPoint EndPoint Address
814 @param Toggle The next data toggle to use
816 @retval EFI_SUCCESS The request is deleted
817 @retval EFI_INVALID_PARAMETER Paremeter is error
818 @retval EFI_NOT_FOUND The asynchronous isn't found
830 UHCI_ASYNC_REQUEST
*AsyncReq
;
831 UHCI_QH_RESULT QhResult
;
835 Status
= EFI_SUCCESS
;
838 // If no asynchronous interrupt transaction exists
840 if (IsListEmpty (&(Uhc
->AsyncIntList
))) {
845 // Find the asynchronous transfer to this device/endpoint pair
848 Link
= Uhc
->AsyncIntList
.ForwardLink
;
851 AsyncReq
= UHCI_ASYNC_INT_FROM_LINK (Link
);
852 Link
= Link
->ForwardLink
;
854 if ((AsyncReq
->DevAddr
== DevAddr
) && (AsyncReq
->EndPoint
== EndPoint
)) {
859 } while (Link
!= &(Uhc
->AsyncIntList
));
862 return EFI_NOT_FOUND
;
866 // Check the result of the async transfer then update it
867 // to get the next data toggle to use.
869 UhciCheckTdStatus (Uhc
, AsyncReq
->FirstTd
, AsyncReq
->IsLow
, &QhResult
);
870 *Toggle
= QhResult
.NextToggle
;
873 // Don't release the request now, keep it to synchronize with hardware.
875 UhciUnlinkAsyncReq (Uhc
, AsyncReq
, FALSE
);
881 Recycle the asynchronouse request. When a queue head
882 is unlinked from frame list, host controller hardware
883 may still hold a cached pointer to it. To synchronize
884 with hardware, the request is released in two steps:
885 first it is linked to the UHC's RecycleWait list. At
886 the next time UhciMonitorAsyncReqList is fired, it is
887 moved to UHC's Recylelist. Then, at another timer
888 activation, all the requests on Recycle list is freed.
889 This guarrantes that each unlink queue head keeps
890 existing for at least 50ms, far enough for the hardware
893 @param Uhc The UHCI device
900 UhciRecycleAsyncReq (
904 UHCI_ASYNC_REQUEST
*Req
;
905 UHCI_ASYNC_REQUEST
*Next
;
909 while (Req
!= NULL
) {
911 UhciFreeAsyncReq (Uhc
, Req
);
915 Uhc
->Recycle
= Uhc
->RecycleWait
;
916 Uhc
->RecycleWait
= NULL
;
922 Release all the asynchronous transfers on the lsit.
924 @param Uhc The UHCI device
930 UhciFreeAllAsyncReq (
935 UHCI_ASYNC_REQUEST
*AsyncReq
;
938 // Call UhciRecycleAsyncReq twice. The requests on Recycle
939 // will be released at the first call; The requests on
940 // RecycleWait will be released at the second call.
942 UhciRecycleAsyncReq (Uhc
);
943 UhciRecycleAsyncReq (Uhc
);
945 Head
= &(Uhc
->AsyncIntList
);
947 if (IsListEmpty (Head
)) {
951 while (!IsListEmpty (Head
)) {
952 AsyncReq
= UHCI_ASYNC_INT_FROM_LINK (Head
->ForwardLink
);
953 UhciUnlinkAsyncReq (Uhc
, AsyncReq
, TRUE
);
959 Interrupt transfer periodic check handler
961 @param Event The event of the time
962 @param Context Context of the event, pointer to USB_HC_DEV
968 UhciMonitorAsyncReqList (
973 UHCI_ASYNC_REQUEST
*AsyncReq
;
978 UHCI_QH_RESULT QhResult
;
980 Uhc
= (USB_HC_DEV
*) Context
;
983 // Recycle the asynchronous requests expired, and promote
984 // requests waiting to be recycled the next time when this
987 UhciRecycleAsyncReq (Uhc
);
989 if (IsListEmpty (&(Uhc
->AsyncIntList
))) {
994 // This loop must be delete safe
996 Link
= Uhc
->AsyncIntList
.ForwardLink
;
999 AsyncReq
= UHCI_ASYNC_INT_FROM_LINK (Link
);
1000 Link
= Link
->ForwardLink
;
1002 Finished
= UhciCheckTdStatus (Uhc
, AsyncReq
->FirstTd
, AsyncReq
->IsLow
, &QhResult
);
1009 // Copy the data to temporary buffer if there are some
1010 // data transferred. We may have zero-length packet
1014 if (QhResult
.Complete
!= 0) {
1015 Data
= AllocatePool (QhResult
.Complete
);
1021 CopyMem (Data
, AsyncReq
->FirstTd
->Data
, QhResult
.Complete
);
1024 UhciUpdateAsyncReq (AsyncReq
, QhResult
.Result
, QhResult
.NextToggle
);
1027 // Now, either transfer is SUCCESS or met errors since
1028 // we have skipped to next transfer earlier if current
1029 // transfer is still active.
1031 if (QhResult
.Result
== EFI_USB_NOERROR
) {
1032 AsyncReq
->Callback (Data
, QhResult
.Complete
, AsyncReq
->Context
, QhResult
.Result
);
1035 // Leave error recovery to its related device driver.
1036 // A common case of the error recovery is to re-submit
1037 // the interrupt transfer. When an interrupt transfer
1038 // is re-submitted, its position in the linked list is
1039 // changed. It is inserted to the head of the linked
1040 // list, while this function scans the whole list from
1041 // head to tail. Thus, the re-submitted interrupt transfer's
1042 // callback function will not be called again in this round.
1044 AsyncReq
->Callback (NULL
, 0, AsyncReq
->Context
, QhResult
.Result
);
1048 gBS
->FreePool (Data
);
1050 } while (Link
!= &(Uhc
->AsyncIntList
));