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
;
120 EFI_PHYSICAL_ADDRESS PciAddr
;
123 // First initialize the periodical schedule data:
124 // 1. Allocate and map the memory for the frame list
125 // 2. Create the help QTD/QH
126 // 3. Initialize the frame entries
127 // 4. Set the frame list register
132 Pages
= EFI_SIZE_TO_PAGES (Bytes
);
134 Status
= PciIo
->AllocateBuffer (
143 if (EFI_ERROR (Status
)) {
144 return EFI_OUT_OF_RESOURCES
;
147 Status
= PciIo
->Map (
149 EfiPciIoOperationBusMasterCommonBuffer
,
156 if (EFI_ERROR (Status
) || (Bytes
!= 4096)) {
157 PciIo
->FreeBuffer (PciIo
, Pages
, Buf
);
158 return EFI_OUT_OF_RESOURCES
;
161 Ehc
->PeriodFrame
= Buf
;
162 Ehc
->PeriodFrameMap
= Map
;
165 // Program the FRAMELISTBASE register with the low 32 bit addr
167 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, EHC_LOW_32BIT (PhyAddr
));
169 // Program the CTRLDSSEGMENT register with the high 32 bit addr
171 EhcWriteOpReg (Ehc
, EHC_CTRLDSSEG_OFFSET
, EHC_HIGH_32BIT (PhyAddr
));
174 // Init memory pool management then create the helper
175 // QTD/QH. If failed, previously allocated resources
176 // will be freed by EhcFreeSched
178 Ehc
->MemPool
= UsbHcInitMemPool (
180 EHC_BIT_IS_SET (Ehc
->HcCapParams
, HCCP_64BIT
),
181 EHC_HIGH_32BIT (PhyAddr
)
184 if (Ehc
->MemPool
== NULL
) {
188 Status
= EhcCreateHelpQ (Ehc
);
190 if (EFI_ERROR (Status
)) {
195 // Initialize the frame list entries then set the registers
197 Ehc
->PeriodFrameHost
= AllocateZeroPool (EHC_FRAME_LEN
* sizeof (UINTN
));
198 if (Ehc
->PeriodFrameHost
== NULL
) {
199 Status
= EFI_OUT_OF_RESOURCES
;
203 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
205 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
++) {
207 // Store the pci bus address of the QH in period frame list which will be accessed by pci bus master.
209 ((UINT32
*)(Ehc
->PeriodFrame
))[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
211 // Store the host address of the QH in period frame list which will be accessed by host.
213 ((UINTN
*)(Ehc
->PeriodFrameHost
))[Index
] = (UINTN
)Ehc
->PeriodOne
;
217 // Second initialize the asynchronous schedule:
218 // Only need to set the AsynListAddr register to
219 // the reclamation header
221 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
222 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, EHC_LOW_32BIT (PciAddr
));
226 PciIo
->FreeBuffer (PciIo
, Pages
, Buf
);
227 PciIo
->Unmap (PciIo
, Map
);
229 if (Ehc
->PeriodOne
!= NULL
) {
230 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
231 Ehc
->PeriodOne
= NULL
;
234 if (Ehc
->ReclaimHead
!= NULL
) {
235 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
236 Ehc
->ReclaimHead
= NULL
;
239 if (Ehc
->ShortReadStop
!= NULL
) {
240 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
241 Ehc
->ShortReadStop
= NULL
;
248 Free the schedule data. It may be partially initialized.
250 @param Ehc The EHCI device.
258 EFI_PCI_IO_PROTOCOL
*PciIo
;
260 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, 0);
261 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, 0);
263 if (Ehc
->PeriodOne
!= NULL
) {
264 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
265 Ehc
->PeriodOne
= NULL
;
268 if (Ehc
->ReclaimHead
!= NULL
) {
269 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
270 Ehc
->ReclaimHead
= NULL
;
273 if (Ehc
->ShortReadStop
!= NULL
) {
274 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
275 Ehc
->ShortReadStop
= NULL
;
278 if (Ehc
->MemPool
!= NULL
) {
279 UsbHcFreeMemPool (Ehc
->MemPool
);
283 if (Ehc
->PeriodFrame
!= NULL
) {
285 ASSERT (PciIo
!= NULL
);
287 PciIo
->Unmap (PciIo
, Ehc
->PeriodFrameMap
);
291 EFI_SIZE_TO_PAGES (EFI_PAGE_SIZE
),
295 Ehc
->PeriodFrame
= NULL
;
298 if (Ehc
->PeriodFrameHost
!= NULL
) {
299 FreePool (Ehc
->PeriodFrameHost
);
300 Ehc
->PeriodFrameHost
= NULL
;
306 Link the queue head to the asynchronous schedule list.
307 UEFI only supports one CTRL/BULK transfer at a time
308 due to its interfaces. This simplifies the AsynList
309 management: A reclamation header is always linked to
310 the AsyncListAddr, the only active QH is appended to it.
312 @param Ehc The EHCI device.
313 @param Qh The queue head to link.
323 EFI_PHYSICAL_ADDRESS PciAddr
;
326 // Append the queue head after the reclaim header, then
327 // fix the hardware visiable parts (EHCI R1.0 page 72).
328 // ReclaimHead is always linked to the EHCI's AsynListAddr.
330 Head
= Ehc
->ReclaimHead
;
332 Qh
->NextQh
= Head
->NextQh
;
335 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Head
, sizeof (EHC_QH
));
336 Qh
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
337 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
338 Head
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
343 Unlink a queue head from the asynchronous schedule list.
344 Need to synchronize with hardware.
346 @param Ehc The EHCI device.
347 @param Qh The queue head to unlink.
351 EhcUnlinkQhFromAsync (
358 EFI_PHYSICAL_ADDRESS PciAddr
;
360 ASSERT (Ehc
->ReclaimHead
->NextQh
== Qh
);
363 // Remove the QH from reclamation head, then update the hardware
364 // visiable part: Only need to loopback the ReclaimHead. The Qh
365 // is pointing to ReclaimHead (which is staill in the list).
367 Head
= Ehc
->ReclaimHead
;
369 Head
->NextQh
= Qh
->NextQh
;
372 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Head
, sizeof (EHC_QH
));
373 Head
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
376 // Set and wait the door bell to synchronize with the hardware
378 Status
= EhcSetAndWaitDoorBell (Ehc
, EHC_GENERIC_TIMEOUT
);
380 if (EFI_ERROR (Status
)) {
381 DEBUG ((EFI_D_ERROR
, "EhcUnlinkQhFromAsync: Failed to synchronize with doorbell\n"));
387 Link a queue head for interrupt transfer to the periodic
388 schedule frame list. This code is very much the same as
391 @param Ehc The EHCI device.
392 @param Qh The queue head to link.
404 EFI_PHYSICAL_ADDRESS PciAddr
;
406 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
408 // First QH can't be NULL because we always keep PeriodOne
409 // heads on the frame list
411 ASSERT (!EHC_LINK_TERMINATED (((UINT32
*)Ehc
->PeriodFrame
)[Index
]));
412 Next
= (EHC_QH
*)((UINTN
*)Ehc
->PeriodFrameHost
)[Index
];
416 // Now, insert the queue head (Qh) into this frame:
417 // 1. Find a queue head with the same poll interval, just insert
418 // Qh after this queue head, then we are done.
420 // 2. Find the position to insert the queue head into:
421 // Previous head's interval is bigger than Qh's
422 // Next head's interval is less than Qh's
423 // Then, insert the Qh between then
425 while (Next
->Interval
> Qh
->Interval
) {
430 ASSERT (Next
!= NULL
);
433 // The entry may have been linked into the frame by early insertation.
434 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
435 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
436 // It isn't necessary to compare all the QH with the same interval to
437 // Qh. This is because if there is other QH with the same interval, Qh
438 // should has been inserted after that at Frames[0] and at Frames[0] it is
439 // impossible for (Next == Qh)
445 if (Next
->Interval
== Qh
->Interval
) {
447 // If there is a QH with the same interval, it locates at
448 // Frames[0], and we can simply insert it after this QH. We
451 ASSERT ((Index
== 0) && (Qh
->NextQh
== NULL
));
459 Qh
->QhHw
.HorizonLink
= Prev
->QhHw
.HorizonLink
;
460 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
461 Prev
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
466 // OK, find the right position, insert it in. If Qh's next
467 // link has already been set, it is in position. This is
468 // guarranted by 2^n polling interval.
470 if (Qh
->NextQh
== NULL
) {
472 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Next
, sizeof (EHC_QH
));
473 Qh
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
476 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
479 ((UINT32
*)Ehc
->PeriodFrame
)[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
480 ((UINTN
*)Ehc
->PeriodFrameHost
)[Index
] = (UINTN
)Qh
;
483 Prev
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
490 Unlink an interrupt queue head from the periodic
493 @param Ehc The EHCI device.
494 @param Qh The queue head to unlink.
498 EhcUnlinkQhFromPeriod (
507 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
509 // Frame link can't be NULL because we always keep PeroidOne
512 ASSERT (!EHC_LINK_TERMINATED (((UINT32
*)Ehc
->PeriodFrame
)[Index
]));
513 This
= (EHC_QH
*)((UINTN
*)Ehc
->PeriodFrameHost
)[Index
];
517 // Walk through the frame's QH list to find the
518 // queue head to remove
520 while ((This
!= NULL
) && (This
!= Qh
)) {
526 // Qh may have already been unlinked from this frame
527 // by early action. See the comments in EhcLinkQhToPeriod.
535 // Qh is the first entry in the frame
537 ((UINT32
*)Ehc
->PeriodFrame
)[Index
] = Qh
->QhHw
.HorizonLink
;
538 ((UINTN
*)Ehc
->PeriodFrameHost
)[Index
] = (UINTN
)Qh
->NextQh
;
540 Prev
->NextQh
= Qh
->NextQh
;
541 Prev
->QhHw
.HorizonLink
= Qh
->QhHw
.HorizonLink
;
548 Check the URB's execution result and update the URB's
551 @param Ehc The EHCI device.
552 @param Urb The URB to check result.
554 @return Whether the result of URB transfer is finialized.
568 EFI_PHYSICAL_ADDRESS PciAddr
;
570 ASSERT ((Ehc
!= NULL
) && (Urb
!= NULL
) && (Urb
->Qh
!= NULL
));
575 Urb
->Result
= EFI_USB_NOERROR
;
577 if (EhcIsHalt (Ehc
) || EhcIsSysError (Ehc
)) {
578 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
582 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
583 Qtd
= EFI_LIST_CONTAINER (Entry
, EHC_QTD
, QtdList
);
585 State
= (UINT8
) QtdHw
->Status
;
587 if (EHC_BIT_IS_SET (State
, QTD_STAT_HALTED
)) {
589 // EHCI will halt the queue head when met some error.
590 // If it is halted, the result of URB is finialized.
592 if ((State
& QTD_STAT_ERR_MASK
) == 0) {
593 Urb
->Result
|= EFI_USB_ERR_STALL
;
596 if (EHC_BIT_IS_SET (State
, QTD_STAT_BABBLE_ERR
)) {
597 Urb
->Result
|= EFI_USB_ERR_BABBLE
;
600 if (EHC_BIT_IS_SET (State
, QTD_STAT_BUFF_ERR
)) {
601 Urb
->Result
|= EFI_USB_ERR_BUFFER
;
604 if (EHC_BIT_IS_SET (State
, QTD_STAT_TRANS_ERR
) && (QtdHw
->ErrCnt
== 0)) {
605 Urb
->Result
|= EFI_USB_ERR_TIMEOUT
;
611 } else if (EHC_BIT_IS_SET (State
, QTD_STAT_ACTIVE
)) {
613 // The QTD is still active, no need to check furthur.
615 Urb
->Result
|= EFI_USB_ERR_NOTEXECUTE
;
622 // This QTD is finished OK or met short packet read. Update the
623 // transfer length if it isn't a setup.
625 if (QtdHw
->Pid
!= QTD_PID_SETUP
) {
626 Urb
->Completed
+= Qtd
->DataLen
- QtdHw
->TotalBytes
;
629 if ((QtdHw
->TotalBytes
!= 0) && (QtdHw
->Pid
== QTD_PID_INPUT
)) {
630 EhcDumpQh (Urb
->Qh
, "Short packet read", FALSE
);
633 // Short packet read condition. If it isn't a setup transfer,
634 // no need to check furthur: the queue head will halt at the
635 // ShortReadStop. If it is a setup transfer, need to check the
636 // Status Stage of the setup transfer to get the finial result
638 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
639 if (QtdHw
->AltNext
== QTD_LINK (PciAddr
, FALSE
)) {
640 DEBUG ((EFI_D_INFO
, "EhcCheckUrbResult: Short packet read, break\n"));
646 DEBUG ((EFI_D_INFO
, "EhcCheckUrbResult: Short packet read, continue\n"));
653 // Return the data toggle set by EHCI hardware, bulk and interrupt
654 // transfer will use this to initialize the next transaction. For
655 // Control transfer, it always start a new data toggle sequence for
658 // NOTICE: don't move DT update before the loop, otherwise there is
659 // a race condition that DT is wrong.
661 Urb
->DataToggle
= (UINT8
) Urb
->Qh
->QhHw
.DataToggle
;
668 Execute the transfer by polling the URB. This is a synchronous operation.
670 @param Ehc The EHCI device.
671 @param Urb The URB to execute.
672 @param TimeOut The time to wait before abort, in millisecond.
674 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.
675 @return EFI_TIMEOUT The transfer failed due to time out.
676 @return EFI_SUCCESS The transfer finished OK.
691 Status
= EFI_SUCCESS
;
692 Loop
= (TimeOut
* EHC_1_MILLISECOND
/ EHC_SYNC_POLL_INTERVAL
) + 1;
695 for (Index
= 0; Index
< Loop
; Index
++) {
696 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
702 gBS
->Stall (EHC_SYNC_POLL_INTERVAL
);
706 DEBUG ((EFI_D_ERROR
, "EhcExecTransfer: transfer not finished in %dms\n", (UINT32
)TimeOut
));
707 EhcDumpQh (Urb
->Qh
, NULL
, FALSE
);
709 Status
= EFI_TIMEOUT
;
711 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
712 DEBUG ((EFI_D_ERROR
, "EhcExecTransfer: transfer failed with %x\n", Urb
->Result
));
713 EhcDumpQh (Urb
->Qh
, NULL
, FALSE
);
715 Status
= EFI_DEVICE_ERROR
;
723 Delete a single asynchronous interrupt transfer for
724 the device and endpoint.
726 @param Ehc The EHCI device.
727 @param DevAddr The address of the target device.
728 @param EpNum The endpoint of the target.
729 @param DataToggle Return the next data toggle to use.
731 @retval EFI_SUCCESS An asynchronous transfer is removed.
732 @retval EFI_NOT_FOUND No transfer for the device is found.
736 EhciDelAsyncIntTransfer (
740 OUT UINT8
*DataToggle
746 EFI_USB_DATA_DIRECTION Direction
;
748 Direction
= (((EpNum
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
);
751 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
752 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
754 if ((Urb
->Ep
.DevAddr
== DevAddr
) && (Urb
->Ep
.EpAddr
== EpNum
) &&
755 (Urb
->Ep
.Direction
== Direction
)) {
757 // Check the URB status to retrieve the next data toggle
758 // from the associated queue head.
760 EhcCheckUrbResult (Ehc
, Urb
);
761 *DataToggle
= Urb
->DataToggle
;
763 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
764 RemoveEntryList (&Urb
->UrbList
);
766 gBS
->FreePool (Urb
->Data
);
767 EhcFreeUrb (Ehc
, Urb
);
772 return EFI_NOT_FOUND
;
777 Remove all the asynchronous interrutp transfers.
779 @param Ehc The EHCI device.
783 EhciDelAllAsyncIntTransfers (
791 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
792 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
794 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
795 RemoveEntryList (&Urb
->UrbList
);
797 gBS
->FreePool (Urb
->Data
);
798 EhcFreeUrb (Ehc
, Urb
);
804 Flush data from PCI controller specific address to mapped system
807 @param Ehc The EHCI device.
808 @param Urb The URB to unmap.
810 @retval EFI_SUCCESS Success to flush data to mapped system memory.
811 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
815 EhcFlushAsyncIntMap (
821 EFI_PHYSICAL_ADDRESS PhyAddr
;
822 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
823 EFI_PCI_IO_PROTOCOL
*PciIo
;
830 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
831 MapOp
= EfiPciIoOperationBusMasterWrite
;
833 MapOp
= EfiPciIoOperationBusMasterRead
;
836 Status
= PciIo
->Unmap (PciIo
, Urb
->DataMap
);
837 if (EFI_ERROR (Status
)) {
843 Status
= PciIo
->Map (PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
844 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
848 Urb
->DataPhy
= (VOID
*) ((UINTN
) PhyAddr
);
853 return EFI_DEVICE_ERROR
;
858 Update the queue head for next round of asynchronous transfer.
860 @param Ehc The EHCI device.
861 @param Urb The URB to update.
865 EhcUpdateAsyncRequest (
876 EFI_PHYSICAL_ADDRESS PciAddr
;
880 if (Urb
->Result
== EFI_USB_NOERROR
) {
883 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
884 Qtd
= EFI_LIST_CONTAINER (Entry
, EHC_QTD
, QtdList
);
886 if (FirstQtd
== NULL
) {
891 // Update the QTD for next round of transfer. Host control
892 // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/
893 // Current Offset. These fields need to be updated. DT isn't
894 // used by interrupt transfer. It uses DT in queue head.
895 // Current Offset is in Page[0], only need to reset Page[0]
896 // to initial data buffer.
899 QtdHw
->Status
= QTD_STAT_ACTIVE
;
900 QtdHw
->ErrCnt
= QTD_MAX_ERR
;
902 QtdHw
->TotalBytes
= (UINT32
) Qtd
->DataLen
;
903 QtdHw
->Page
[0] = EHC_LOW_32BIT (Qtd
->Data
);
907 // Update QH for next round of transfer. Host control only
908 // touch the fields in transfer overlay area. Only need to
909 // zero out the overlay area and set NextQtd to the first
910 // QTD. DateToggle bit is left untouched.
912 QhHw
= &Urb
->Qh
->QhHw
;
913 QhHw
->CurQtd
= QTD_LINK (0, TRUE
);
921 QhHw
->TotalBytes
= 0;
923 for (Index
= 0; Index
< 5; Index
++) {
924 QhHw
->Page
[Index
] = 0;
925 QhHw
->PageHigh
[Index
] = 0;
928 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, FirstQtd
, sizeof (EHC_QTD
));
929 QhHw
->NextQtd
= QTD_LINK (PciAddr
, FALSE
);
937 Interrupt transfer periodic check handler.
939 @param Event Interrupt event.
940 @param Context Pointer to USB2_HC_DEV.
945 EhcMonitorAsyncRequests (
959 OldTpl
= gBS
->RaiseTPL (EHC_TPL
);
960 Ehc
= (USB2_HC_DEV
*) Context
;
962 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
963 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
966 // Check the result of URB execution. If it is still
967 // active, check the next one.
969 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
976 // Flush any PCI posted write transactions from a PCI host
977 // bridge to system memory.
979 Status
= EhcFlushAsyncIntMap (Ehc
, Urb
);
980 if (EFI_ERROR (Status
)) {
981 DEBUG ((EFI_D_ERROR
, "EhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));
985 // Allocate a buffer then copy the transferred data for user.
986 // If failed to allocate the buffer, update the URB for next
987 // round of transfer. Ignore the data of this round.
991 if (Urb
->Result
== EFI_USB_NOERROR
) {
992 ASSERT (Urb
->Completed
<= Urb
->DataLen
);
994 ProcBuf
= AllocatePool (Urb
->Completed
);
996 if (ProcBuf
== NULL
) {
997 EhcUpdateAsyncRequest (Ehc
, Urb
);
1001 CopyMem (ProcBuf
, Urb
->Data
, Urb
->Completed
);
1004 EhcUpdateAsyncRequest (Ehc
, Urb
);
1007 // Leave error recovery to its related device driver. A
1008 // common case of the error recovery is to re-submit the
1009 // interrupt transfer which is linked to the head of the
1010 // list. This function scans from head to tail. So the
1011 // re-submitted interrupt transfer's callback function
1012 // will not be called again in this round. Don't touch this
1013 // URB after the callback, it may have been removed by the
1016 if (Urb
->Callback
!= NULL
) {
1018 // Restore the old TPL, USB bus maybe connect device in
1019 // his callback. Some drivers may has a lower TPL restriction.
1021 gBS
->RestoreTPL (OldTpl
);
1022 (Urb
->Callback
) (ProcBuf
, Urb
->Completed
, Urb
->Context
, Urb
->Result
);
1023 OldTpl
= gBS
->RaiseTPL (EHC_TPL
);
1026 if (ProcBuf
!= NULL
) {
1031 gBS
->RestoreTPL (OldTpl
);