3 EHCI transfer scheduling routines.
5 Copyright (c) 2007 - 2010, Intel Corporation
6 All rights reserved. This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
20 Create helper QTD/QH for the EHCI device.
22 @param Ehc The EHCI device.
24 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH.
25 @retval EFI_SUCCESS Helper QH/QTD are created.
37 EFI_PHYSICAL_ADDRESS PciAddr
;
40 // Create an inactive Qtd to terminate the short packet read.
42 Qtd
= EhcCreateQtd (Ehc
, NULL
, NULL
, 0, QTD_PID_INPUT
, 0, 64);
45 return EFI_OUT_OF_RESOURCES
;
48 Qtd
->QtdHw
.Status
= QTD_STAT_HALTED
;
49 Ehc
->ShortReadStop
= Qtd
;
52 // Create a QH to act as the EHC reclamation header.
53 // Set the header to loopback to itself.
57 Ep
.Direction
= EfiUsbDataIn
;
58 Ep
.DevSpeed
= EFI_USB_SPEED_HIGH
;
63 Ep
.Type
= EHC_BULK_TRANSFER
;
66 Qh
= EhcCreateQh (Ehc
, &Ep
);
69 return EFI_OUT_OF_RESOURCES
;
72 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
74 QhHw
->HorizonLink
= QH_LINK (PciAddr
+ OFFSET_OF(EHC_QH
, QhHw
), EHC_TYPE_QH
, FALSE
);
75 QhHw
->Status
= QTD_STAT_HALTED
;
76 QhHw
->ReclaimHead
= 1;
77 Ehc
->ReclaimHead
= Qh
;
80 // Create a dummy QH to act as the terminator for periodical schedule
83 Ep
.Type
= EHC_INT_TRANSFER_SYNC
;
85 Qh
= EhcCreateQh (Ehc
, &Ep
);
88 return EFI_OUT_OF_RESOURCES
;
91 Qh
->QhHw
.Status
= QTD_STAT_HALTED
;
99 Initialize the schedule data structure such as frame list.
101 @param Ehc The EHCI device to init schedule data.
103 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.
104 @retval EFI_SUCCESS The schedule data is initialized.
112 EFI_PCI_IO_PROTOCOL
*PciIo
;
114 EFI_PHYSICAL_ADDRESS PhyAddr
;
121 EFI_PHYSICAL_ADDRESS PciAddr
;
124 // First initialize the periodical schedule data:
125 // 1. Allocate and map the memory for the frame list
126 // 2. Create the help QTD/QH
127 // 3. Initialize the frame entries
128 // 4. Set the frame list register
133 Pages
= EFI_SIZE_TO_PAGES (Bytes
);
135 Status
= PciIo
->AllocateBuffer (
144 if (EFI_ERROR (Status
)) {
145 return EFI_OUT_OF_RESOURCES
;
148 Status
= PciIo
->Map (
150 EfiPciIoOperationBusMasterCommonBuffer
,
157 if (EFI_ERROR (Status
) || (Bytes
!= 4096)) {
158 PciIo
->FreeBuffer (PciIo
, Pages
, Buf
);
159 return EFI_OUT_OF_RESOURCES
;
162 Ehc
->PeriodFrameHost
= Buf
;
163 Ehc
->PeriodFrame
= (VOID
*) ((UINTN
) PhyAddr
);
164 Ehc
->PeriodFrameMap
= Map
;
165 Ehc
->High32bitAddr
= EHC_HIGH_32BIT (PhyAddr
);
168 // Init memory pool management then create the helper
169 // QTD/QH. If failed, previously allocated resources
170 // will be freed by EhcFreeSched
172 Ehc
->MemPool
= UsbHcInitMemPool (
174 EHC_BIT_IS_SET (Ehc
->HcCapParams
, HCCP_64BIT
),
178 if (Ehc
->MemPool
== NULL
) {
179 return EFI_OUT_OF_RESOURCES
;
182 Status
= EhcCreateHelpQ (Ehc
);
184 if (EFI_ERROR (Status
)) {
189 // Initialize the frame list entries then set the registers
191 Desc
= (UINT32
*) Ehc
->PeriodFrameHost
;
193 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
++) {
194 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
195 Desc
[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
198 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, EHC_LOW_32BIT (Ehc
->PeriodFrame
));
201 // Second initialize the asynchronous schedule:
202 // Only need to set the AsynListAddr register to
203 // the reclamation header
205 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
206 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, EHC_LOW_32BIT (PciAddr
));
212 Free the schedule data. It may be partially initialized.
214 @param Ehc The EHCI device.
222 EFI_PCI_IO_PROTOCOL
*PciIo
;
224 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, 0);
225 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, 0);
227 if (Ehc
->PeriodOne
!= NULL
) {
228 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
229 Ehc
->PeriodOne
= NULL
;
232 if (Ehc
->ReclaimHead
!= NULL
) {
233 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
234 Ehc
->ReclaimHead
= NULL
;
237 if (Ehc
->ShortReadStop
!= NULL
) {
238 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
239 Ehc
->ShortReadStop
= NULL
;
242 if (Ehc
->MemPool
!= NULL
) {
243 UsbHcFreeMemPool (Ehc
->MemPool
);
247 if (Ehc
->PeriodFrameHost
!= NULL
) {
249 ASSERT (PciIo
!= NULL
);
251 PciIo
->Unmap (PciIo
, Ehc
->PeriodFrameMap
);
255 EFI_SIZE_TO_PAGES (EFI_PAGE_SIZE
),
259 Ehc
->PeriodFrameHost
= NULL
;
260 Ehc
->PeriodFrame
= NULL
;
266 Link the queue head to the asynchronous schedule list.
267 UEFI only supports one CTRL/BULK transfer at a time
268 due to its interfaces. This simplifies the AsynList
269 management: A reclamation header is always linked to
270 the AsyncListAddr, the only active QH is appended to it.
272 @param Ehc The EHCI device.
273 @param Qh The queue head to link.
283 EFI_PHYSICAL_ADDRESS PciAddr
;
286 // Append the queue head after the reclaim header, then
287 // fix the hardware visiable parts (EHCI R1.0 page 72).
288 // ReclaimHead is always linked to the EHCI's AsynListAddr.
290 Head
= Ehc
->ReclaimHead
;
292 Qh
->NextQh
= Head
->NextQh
;
295 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Head
, sizeof (EHC_QH
));
296 Qh
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);;
297 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
298 Head
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
303 Unlink a queue head from the asynchronous schedule list.
304 Need to synchronize with hardware.
306 @param Ehc The EHCI device.
307 @param Qh The queue head to unlink.
311 EhcUnlinkQhFromAsync (
318 EFI_PHYSICAL_ADDRESS PciAddr
;
320 ASSERT (Ehc
->ReclaimHead
->NextQh
== Qh
);
323 // Remove the QH from reclamation head, then update the hardware
324 // visiable part: Only need to loopback the ReclaimHead. The Qh
325 // is pointing to ReclaimHead (which is staill in the list).
327 Head
= Ehc
->ReclaimHead
;
329 Head
->NextQh
= Qh
->NextQh
;
332 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Head
, sizeof (EHC_QH
));
333 Head
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
336 // Set and wait the door bell to synchronize with the hardware
338 Status
= EhcSetAndWaitDoorBell (Ehc
, EHC_GENERIC_TIMEOUT
);
340 if (EFI_ERROR (Status
)) {
341 DEBUG ((EFI_D_ERROR
, "EhcUnlinkQhFromAsync: Failed to synchronize with doorbell\n"));
347 Link a queue head for interrupt transfer to the periodic
348 schedule frame list. This code is very much the same as
351 @param Ehc The EHCI device.
352 @param Qh The queue head to link.
365 EFI_PHYSICAL_ADDRESS PciAddr
;
367 Frames
= Ehc
->PeriodFrameHost
;
369 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
371 // First QH can't be NULL because we always keep PeriodOne
372 // heads on the frame list
374 ASSERT (!EHC_LINK_TERMINATED (Frames
[Index
]));
375 Next
= EHC_ADDR (Ehc
->High32bitAddr
, Frames
[Index
]);
379 // Now, insert the queue head (Qh) into this frame:
380 // 1. Find a queue head with the same poll interval, just insert
381 // Qh after this queue head, then we are done.
383 // 2. Find the position to insert the queue head into:
384 // Previous head's interval is bigger than Qh's
385 // Next head's interval is less than Qh's
386 // Then, insert the Qh between then
388 while (Next
->Interval
> Qh
->Interval
) {
393 ASSERT (Next
!= NULL
);
396 // The entry may have been linked into the frame by early insertation.
397 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
398 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
399 // It isn't necessary to compare all the QH with the same interval to
400 // Qh. This is because if there is other QH with the same interval, Qh
401 // should has been inserted after that at Frames[0] and at Frames[0] it is
402 // impossible for (Next == Qh)
408 if (Next
->Interval
== Qh
->Interval
) {
410 // If there is a QH with the same interval, it locates at
411 // Frames[0], and we can simply insert it after this QH. We
414 ASSERT ((Index
== 0) && (Qh
->NextQh
== NULL
));
422 Qh
->QhHw
.HorizonLink
= Prev
->QhHw
.HorizonLink
;
423 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
424 Prev
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
429 // OK, find the right position, insert it in. If Qh's next
430 // link has already been set, it is in position. This is
431 // guarranted by 2^n polling interval.
433 if (Qh
->NextQh
== NULL
) {
435 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Next
, sizeof (EHC_QH
));
436 Qh
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
439 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
442 Frames
[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
445 Prev
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
452 Unlink an interrupt queue head from the periodic
455 @param Ehc The EHCI device.
456 @param Qh The queue head to unlink.
460 EhcUnlinkQhFromPeriod (
470 Frames
= Ehc
->PeriodFrameHost
;
472 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
474 // Frame link can't be NULL because we always keep PeroidOne
477 ASSERT (!EHC_LINK_TERMINATED (Frames
[Index
]));
478 This
= EHC_ADDR (Ehc
->High32bitAddr
, Frames
[Index
]);
482 // Walk through the frame's QH list to find the
483 // queue head to remove
485 while ((This
!= NULL
) && (This
!= Qh
)) {
491 // Qh may have already been unlinked from this frame
492 // by early action. See the comments in EhcLinkQhToPeriod.
500 // Qh is the first entry in the frame
502 Frames
[Index
] = Qh
->QhHw
.HorizonLink
;
504 Prev
->NextQh
= Qh
->NextQh
;
505 Prev
->QhHw
.HorizonLink
= Qh
->QhHw
.HorizonLink
;
512 Check the URB's execution result and update the URB's
515 @param Ehc The EHCI device.
516 @param Urb The URB to check result.
518 @return Whether the result of URB transfer is finialized.
532 EFI_PHYSICAL_ADDRESS PciAddr
;
534 ASSERT ((Ehc
!= NULL
) && (Urb
!= NULL
) && (Urb
->Qh
!= NULL
));
539 Urb
->Result
= EFI_USB_NOERROR
;
541 if (EhcIsHalt (Ehc
) || EhcIsSysError (Ehc
)) {
542 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
546 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
547 Qtd
= EFI_LIST_CONTAINER (Entry
, EHC_QTD
, QtdList
);
549 State
= (UINT8
) QtdHw
->Status
;
551 if (EHC_BIT_IS_SET (State
, QTD_STAT_HALTED
)) {
553 // EHCI will halt the queue head when met some error.
554 // If it is halted, the result of URB is finialized.
556 if ((State
& QTD_STAT_ERR_MASK
) == 0) {
557 Urb
->Result
|= EFI_USB_ERR_STALL
;
560 if (EHC_BIT_IS_SET (State
, QTD_STAT_BABBLE_ERR
)) {
561 Urb
->Result
|= EFI_USB_ERR_BABBLE
;
564 if (EHC_BIT_IS_SET (State
, QTD_STAT_BUFF_ERR
)) {
565 Urb
->Result
|= EFI_USB_ERR_BUFFER
;
568 if (EHC_BIT_IS_SET (State
, QTD_STAT_TRANS_ERR
) && (QtdHw
->ErrCnt
== 0)) {
569 Urb
->Result
|= EFI_USB_ERR_TIMEOUT
;
575 } else if (EHC_BIT_IS_SET (State
, QTD_STAT_ACTIVE
)) {
577 // The QTD is still active, no need to check furthur.
579 Urb
->Result
|= EFI_USB_ERR_NOTEXECUTE
;
586 // This QTD is finished OK or met short packet read. Update the
587 // transfer length if it isn't a setup.
589 if (QtdHw
->Pid
!= QTD_PID_SETUP
) {
590 Urb
->Completed
+= Qtd
->DataLen
- QtdHw
->TotalBytes
;
593 if ((QtdHw
->TotalBytes
!= 0) && (QtdHw
->Pid
== QTD_PID_INPUT
)) {
594 EhcDumpQh (Urb
->Qh
, "Short packet read", FALSE
);
597 // Short packet read condition. If it isn't a setup transfer,
598 // no need to check furthur: the queue head will halt at the
599 // ShortReadStop. If it is a setup transfer, need to check the
600 // Status Stage of the setup transfer to get the finial result
602 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
603 if (QtdHw
->AltNext
== QTD_LINK (PciAddr
, FALSE
)) {
604 DEBUG ((EFI_D_INFO
, "EhcCheckUrbResult: Short packet read, break\n"));
610 DEBUG ((EFI_D_INFO
, "EhcCheckUrbResult: Short packet read, continue\n"));
617 // Return the data toggle set by EHCI hardware, bulk and interrupt
618 // transfer will use this to initialize the next transaction. For
619 // Control transfer, it always start a new data toggle sequence for
622 // NOTICE: don't move DT update before the loop, otherwise there is
623 // a race condition that DT is wrong.
625 Urb
->DataToggle
= (UINT8
) Urb
->Qh
->QhHw
.DataToggle
;
632 Execute the transfer by polling the URB. This is a synchronous operation.
634 @param Ehc The EHCI device.
635 @param Urb The URB to execute.
636 @param TimeOut The time to wait before abort, in millisecond.
638 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.
639 @return EFI_TIMEOUT The transfer failed due to time out.
640 @return EFI_SUCCESS The transfer finished OK.
655 Status
= EFI_SUCCESS
;
656 Loop
= (TimeOut
* EHC_1_MILLISECOND
/ EHC_SYNC_POLL_INTERVAL
) + 1;
659 for (Index
= 0; Index
< Loop
; Index
++) {
660 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
666 gBS
->Stall (EHC_SYNC_POLL_INTERVAL
);
670 DEBUG ((EFI_D_ERROR
, "EhcExecTransfer: transfer not finished in %dms\n", (UINT32
)TimeOut
));
671 EhcDumpQh (Urb
->Qh
, NULL
, FALSE
);
673 Status
= EFI_TIMEOUT
;
675 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
676 DEBUG ((EFI_D_ERROR
, "EhcExecTransfer: transfer failed with %x\n", Urb
->Result
));
677 EhcDumpQh (Urb
->Qh
, NULL
, FALSE
);
679 Status
= EFI_DEVICE_ERROR
;
687 Delete a single asynchronous interrupt transfer for
688 the device and endpoint.
690 @param Ehc The EHCI device.
691 @param DevAddr The address of the target device.
692 @param EpNum The endpoint of the target.
693 @param DataToggle Return the next data toggle to use.
695 @retval EFI_SUCCESS An asynchronous transfer is removed.
696 @retval EFI_NOT_FOUND No transfer for the device is found.
700 EhciDelAsyncIntTransfer (
704 OUT UINT8
*DataToggle
710 EFI_USB_DATA_DIRECTION Direction
;
712 Direction
= (((EpNum
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
);
715 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
716 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
718 if ((Urb
->Ep
.DevAddr
== DevAddr
) && (Urb
->Ep
.EpAddr
== EpNum
) &&
719 (Urb
->Ep
.Direction
== Direction
)) {
721 // Check the URB status to retrieve the next data toggle
722 // from the associated queue head.
724 EhcCheckUrbResult (Ehc
, Urb
);
725 *DataToggle
= Urb
->DataToggle
;
727 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
728 RemoveEntryList (&Urb
->UrbList
);
730 gBS
->FreePool (Urb
->Data
);
731 EhcFreeUrb (Ehc
, Urb
);
736 return EFI_NOT_FOUND
;
741 Remove all the asynchronous interrutp transfers.
743 @param Ehc The EHCI device.
747 EhciDelAllAsyncIntTransfers (
755 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
756 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
758 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
759 RemoveEntryList (&Urb
->UrbList
);
761 gBS
->FreePool (Urb
->Data
);
762 EhcFreeUrb (Ehc
, Urb
);
768 Flush data from PCI controller specific address to mapped system
771 @param Ehc The EHCI device.
772 @param Urb The URB to unmap.
774 @retval EFI_SUCCESS Success to flush data to mapped system memory.
775 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
779 EhcFlushAsyncIntMap (
785 EFI_PHYSICAL_ADDRESS PhyAddr
;
786 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
787 EFI_PCI_IO_PROTOCOL
*PciIo
;
794 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
795 MapOp
= EfiPciIoOperationBusMasterWrite
;
797 MapOp
= EfiPciIoOperationBusMasterRead
;
800 Status
= PciIo
->Unmap (PciIo
, Urb
->DataMap
);
801 if (EFI_ERROR (Status
)) {
807 Status
= PciIo
->Map (PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
808 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
812 Urb
->DataPhy
= (VOID
*) ((UINTN
) PhyAddr
);
817 return EFI_DEVICE_ERROR
;
822 Update the queue head for next round of asynchronous transfer.
824 @param Ehc The EHCI device.
825 @param Urb The URB to update.
829 EhcUpdateAsyncRequest (
840 EFI_PHYSICAL_ADDRESS PciAddr
;
844 if (Urb
->Result
== EFI_USB_NOERROR
) {
847 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
848 Qtd
= EFI_LIST_CONTAINER (Entry
, EHC_QTD
, QtdList
);
850 if (FirstQtd
== NULL
) {
855 // Update the QTD for next round of transfer. Host control
856 // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/
857 // Current Offset. These fields need to be updated. DT isn't
858 // used by interrupt transfer. It uses DT in queue head.
859 // Current Offset is in Page[0], only need to reset Page[0]
860 // to initial data buffer.
863 QtdHw
->Status
= QTD_STAT_ACTIVE
;
864 QtdHw
->ErrCnt
= QTD_MAX_ERR
;
866 QtdHw
->TotalBytes
= (UINT32
) Qtd
->DataLen
;
867 QtdHw
->Page
[0] = EHC_LOW_32BIT (Qtd
->Data
);
871 // Update QH for next round of transfer. Host control only
872 // touch the fields in transfer overlay area. Only need to
873 // zero out the overlay area and set NextQtd to the first
874 // QTD. DateToggle bit is left untouched.
876 QhHw
= &Urb
->Qh
->QhHw
;
877 QhHw
->CurQtd
= QTD_LINK (0, TRUE
);
885 QhHw
->TotalBytes
= 0;
887 for (Index
= 0; Index
< 5; Index
++) {
888 QhHw
->Page
[Index
] = 0;
889 QhHw
->PageHigh
[Index
] = 0;
892 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, FirstQtd
, sizeof (EHC_QTD
));
893 QhHw
->NextQtd
= QTD_LINK (PciAddr
, FALSE
);
901 Interrupt transfer periodic check handler.
903 @param Event Interrupt event.
904 @param Context Pointer to USB2_HC_DEV.
909 EhcMonitorAsyncRequests (
923 OldTpl
= gBS
->RaiseTPL (EHC_TPL
);
924 Ehc
= (USB2_HC_DEV
*) Context
;
926 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
927 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
930 // Check the result of URB execution. If it is still
931 // active, check the next one.
933 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
940 // Flush any PCI posted write transactions from a PCI host
941 // bridge to system memory.
943 Status
= EhcFlushAsyncIntMap (Ehc
, Urb
);
944 if (EFI_ERROR (Status
)) {
945 DEBUG ((EFI_D_ERROR
, "EhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));
949 // Allocate a buffer then copy the transferred data for user.
950 // If failed to allocate the buffer, update the URB for next
951 // round of transfer. Ignore the data of this round.
955 if (Urb
->Result
== EFI_USB_NOERROR
) {
956 ASSERT (Urb
->Completed
<= Urb
->DataLen
);
958 ProcBuf
= AllocatePool (Urb
->Completed
);
960 if (ProcBuf
== NULL
) {
961 EhcUpdateAsyncRequest (Ehc
, Urb
);
965 CopyMem (ProcBuf
, Urb
->Data
, Urb
->Completed
);
968 EhcUpdateAsyncRequest (Ehc
, Urb
);
971 // Leave error recovery to its related device driver. A
972 // common case of the error recovery is to re-submit the
973 // interrupt transfer which is linked to the head of the
974 // list. This function scans from head to tail. So the
975 // re-submitted interrupt transfer's callback function
976 // will not be called again in this round. Don't touch this
977 // URB after the callback, it may have been removed by the
980 if (Urb
->Callback
!= NULL
) {
982 // Restore the old TPL, USB bus maybe connect device in
983 // his callback. Some drivers may has a lower TPL restriction.
985 gBS
->RestoreTPL (OldTpl
);
986 (Urb
->Callback
) (ProcBuf
, Urb
->Completed
, Urb
->Context
, Urb
->Result
);
987 OldTpl
= gBS
->RaiseTPL (EHC_TPL
);
990 if (ProcBuf
!= NULL
) {
995 gBS
->RestoreTPL (OldTpl
);