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
) {
185 Status
= EFI_OUT_OF_RESOURCES
;
189 Status
= EhcCreateHelpQ (Ehc
);
191 if (EFI_ERROR (Status
)) {
196 // Initialize the frame list entries then set the registers
198 Ehc
->PeriodFrameHost
= AllocateZeroPool (EHC_FRAME_LEN
* sizeof (UINTN
));
199 if (Ehc
->PeriodFrameHost
== NULL
) {
200 Status
= EFI_OUT_OF_RESOURCES
;
204 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
206 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
++) {
208 // Store the pci bus address of the QH in period frame list which will be accessed by pci bus master.
210 ((UINT32
*)(Ehc
->PeriodFrame
))[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
212 // Store the host address of the QH in period frame list which will be accessed by host.
214 ((UINTN
*)(Ehc
->PeriodFrameHost
))[Index
] = (UINTN
)Ehc
->PeriodOne
;
218 // Second initialize the asynchronous schedule:
219 // Only need to set the AsynListAddr register to
220 // the reclamation header
222 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
223 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, EHC_LOW_32BIT (PciAddr
));
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
;
243 PciIo
->FreeBuffer (PciIo
, Pages
, Buf
);
244 PciIo
->Unmap (PciIo
, Map
);
251 Free the schedule data. It may be partially initialized.
253 @param Ehc The EHCI device.
261 EFI_PCI_IO_PROTOCOL
*PciIo
;
263 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, 0);
264 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, 0);
266 if (Ehc
->PeriodOne
!= NULL
) {
267 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
268 Ehc
->PeriodOne
= NULL
;
271 if (Ehc
->ReclaimHead
!= NULL
) {
272 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
273 Ehc
->ReclaimHead
= NULL
;
276 if (Ehc
->ShortReadStop
!= NULL
) {
277 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
278 Ehc
->ShortReadStop
= NULL
;
281 if (Ehc
->MemPool
!= NULL
) {
282 UsbHcFreeMemPool (Ehc
->MemPool
);
286 if (Ehc
->PeriodFrame
!= NULL
) {
288 ASSERT (PciIo
!= NULL
);
290 PciIo
->Unmap (PciIo
, Ehc
->PeriodFrameMap
);
294 EFI_SIZE_TO_PAGES (EFI_PAGE_SIZE
),
298 Ehc
->PeriodFrame
= NULL
;
301 if (Ehc
->PeriodFrameHost
!= NULL
) {
302 FreePool (Ehc
->PeriodFrameHost
);
303 Ehc
->PeriodFrameHost
= NULL
;
309 Link the queue head to the asynchronous schedule list.
310 UEFI only supports one CTRL/BULK transfer at a time
311 due to its interfaces. This simplifies the AsynList
312 management: A reclamation header is always linked to
313 the AsyncListAddr, the only active QH is appended to it.
315 @param Ehc The EHCI device.
316 @param Qh The queue head to link.
326 EFI_PHYSICAL_ADDRESS PciAddr
;
329 // Append the queue head after the reclaim header, then
330 // fix the hardware visiable parts (EHCI R1.0 page 72).
331 // ReclaimHead is always linked to the EHCI's AsynListAddr.
333 Head
= Ehc
->ReclaimHead
;
335 Qh
->NextQh
= Head
->NextQh
;
338 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Head
, sizeof (EHC_QH
));
339 Qh
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
340 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
341 Head
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
346 Unlink a queue head from the asynchronous schedule list.
347 Need to synchronize with hardware.
349 @param Ehc The EHCI device.
350 @param Qh The queue head to unlink.
354 EhcUnlinkQhFromAsync (
361 EFI_PHYSICAL_ADDRESS PciAddr
;
363 ASSERT (Ehc
->ReclaimHead
->NextQh
== Qh
);
366 // Remove the QH from reclamation head, then update the hardware
367 // visiable part: Only need to loopback the ReclaimHead. The Qh
368 // is pointing to ReclaimHead (which is staill in the list).
370 Head
= Ehc
->ReclaimHead
;
372 Head
->NextQh
= Qh
->NextQh
;
375 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Head
, sizeof (EHC_QH
));
376 Head
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
379 // Set and wait the door bell to synchronize with the hardware
381 Status
= EhcSetAndWaitDoorBell (Ehc
, EHC_GENERIC_TIMEOUT
);
383 if (EFI_ERROR (Status
)) {
384 DEBUG ((EFI_D_ERROR
, "EhcUnlinkQhFromAsync: Failed to synchronize with doorbell\n"));
390 Link a queue head for interrupt transfer to the periodic
391 schedule frame list. This code is very much the same as
394 @param Ehc The EHCI device.
395 @param Qh The queue head to link.
407 EFI_PHYSICAL_ADDRESS PciAddr
;
409 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
411 // First QH can't be NULL because we always keep PeriodOne
412 // heads on the frame list
414 ASSERT (!EHC_LINK_TERMINATED (((UINT32
*)Ehc
->PeriodFrame
)[Index
]));
415 Next
= (EHC_QH
*)((UINTN
*)Ehc
->PeriodFrameHost
)[Index
];
419 // Now, insert the queue head (Qh) into this frame:
420 // 1. Find a queue head with the same poll interval, just insert
421 // Qh after this queue head, then we are done.
423 // 2. Find the position to insert the queue head into:
424 // Previous head's interval is bigger than Qh's
425 // Next head's interval is less than Qh's
426 // Then, insert the Qh between then
428 while (Next
->Interval
> Qh
->Interval
) {
433 ASSERT (Next
!= NULL
);
436 // The entry may have been linked into the frame by early insertation.
437 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
438 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
439 // It isn't necessary to compare all the QH with the same interval to
440 // Qh. This is because if there is other QH with the same interval, Qh
441 // should has been inserted after that at Frames[0] and at Frames[0] it is
442 // impossible for (Next == Qh)
448 if (Next
->Interval
== Qh
->Interval
) {
450 // If there is a QH with the same interval, it locates at
451 // Frames[0], and we can simply insert it after this QH. We
454 ASSERT ((Index
== 0) && (Qh
->NextQh
== NULL
));
462 Qh
->QhHw
.HorizonLink
= Prev
->QhHw
.HorizonLink
;
463 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
464 Prev
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
469 // OK, find the right position, insert it in. If Qh's next
470 // link has already been set, it is in position. This is
471 // guarranted by 2^n polling interval.
473 if (Qh
->NextQh
== NULL
) {
475 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Next
, sizeof (EHC_QH
));
476 Qh
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
479 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
482 ((UINT32
*)Ehc
->PeriodFrame
)[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
483 ((UINTN
*)Ehc
->PeriodFrameHost
)[Index
] = (UINTN
)Qh
;
486 Prev
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
493 Unlink an interrupt queue head from the periodic
496 @param Ehc The EHCI device.
497 @param Qh The queue head to unlink.
501 EhcUnlinkQhFromPeriod (
510 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
512 // Frame link can't be NULL because we always keep PeroidOne
515 ASSERT (!EHC_LINK_TERMINATED (((UINT32
*)Ehc
->PeriodFrame
)[Index
]));
516 This
= (EHC_QH
*)((UINTN
*)Ehc
->PeriodFrameHost
)[Index
];
520 // Walk through the frame's QH list to find the
521 // queue head to remove
523 while ((This
!= NULL
) && (This
!= Qh
)) {
529 // Qh may have already been unlinked from this frame
530 // by early action. See the comments in EhcLinkQhToPeriod.
538 // Qh is the first entry in the frame
540 ((UINT32
*)Ehc
->PeriodFrame
)[Index
] = Qh
->QhHw
.HorizonLink
;
541 ((UINTN
*)Ehc
->PeriodFrameHost
)[Index
] = (UINTN
)Qh
->NextQh
;
543 Prev
->NextQh
= Qh
->NextQh
;
544 Prev
->QhHw
.HorizonLink
= Qh
->QhHw
.HorizonLink
;
551 Check the URB's execution result and update the URB's
554 @param Ehc The EHCI device.
555 @param Urb The URB to check result.
557 @return Whether the result of URB transfer is finialized.
571 EFI_PHYSICAL_ADDRESS PciAddr
;
573 ASSERT ((Ehc
!= NULL
) && (Urb
!= NULL
) && (Urb
->Qh
!= NULL
));
578 Urb
->Result
= EFI_USB_NOERROR
;
580 if (EhcIsHalt (Ehc
) || EhcIsSysError (Ehc
)) {
581 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
585 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
586 Qtd
= EFI_LIST_CONTAINER (Entry
, EHC_QTD
, QtdList
);
588 State
= (UINT8
) QtdHw
->Status
;
590 if (EHC_BIT_IS_SET (State
, QTD_STAT_HALTED
)) {
592 // EHCI will halt the queue head when met some error.
593 // If it is halted, the result of URB is finialized.
595 if ((State
& QTD_STAT_ERR_MASK
) == 0) {
596 Urb
->Result
|= EFI_USB_ERR_STALL
;
599 if (EHC_BIT_IS_SET (State
, QTD_STAT_BABBLE_ERR
)) {
600 Urb
->Result
|= EFI_USB_ERR_BABBLE
;
603 if (EHC_BIT_IS_SET (State
, QTD_STAT_BUFF_ERR
)) {
604 Urb
->Result
|= EFI_USB_ERR_BUFFER
;
607 if (EHC_BIT_IS_SET (State
, QTD_STAT_TRANS_ERR
) && (QtdHw
->ErrCnt
== 0)) {
608 Urb
->Result
|= EFI_USB_ERR_TIMEOUT
;
614 } else if (EHC_BIT_IS_SET (State
, QTD_STAT_ACTIVE
)) {
616 // The QTD is still active, no need to check furthur.
618 Urb
->Result
|= EFI_USB_ERR_NOTEXECUTE
;
625 // This QTD is finished OK or met short packet read. Update the
626 // transfer length if it isn't a setup.
628 if (QtdHw
->Pid
!= QTD_PID_SETUP
) {
629 Urb
->Completed
+= Qtd
->DataLen
- QtdHw
->TotalBytes
;
632 if ((QtdHw
->TotalBytes
!= 0) && (QtdHw
->Pid
== QTD_PID_INPUT
)) {
633 EhcDumpQh (Urb
->Qh
, "Short packet read", FALSE
);
636 // Short packet read condition. If it isn't a setup transfer,
637 // no need to check furthur: the queue head will halt at the
638 // ShortReadStop. If it is a setup transfer, need to check the
639 // Status Stage of the setup transfer to get the finial result
641 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
642 if (QtdHw
->AltNext
== QTD_LINK (PciAddr
, FALSE
)) {
643 DEBUG ((EFI_D_INFO
, "EhcCheckUrbResult: Short packet read, break\n"));
649 DEBUG ((EFI_D_INFO
, "EhcCheckUrbResult: Short packet read, continue\n"));
656 // Return the data toggle set by EHCI hardware, bulk and interrupt
657 // transfer will use this to initialize the next transaction. For
658 // Control transfer, it always start a new data toggle sequence for
661 // NOTICE: don't move DT update before the loop, otherwise there is
662 // a race condition that DT is wrong.
664 Urb
->DataToggle
= (UINT8
) Urb
->Qh
->QhHw
.DataToggle
;
671 Execute the transfer by polling the URB. This is a synchronous operation.
673 @param Ehc The EHCI device.
674 @param Urb The URB to execute.
675 @param TimeOut The time to wait before abort, in millisecond.
677 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.
678 @return EFI_TIMEOUT The transfer failed due to time out.
679 @return EFI_SUCCESS The transfer finished OK.
694 Status
= EFI_SUCCESS
;
695 Loop
= (TimeOut
* EHC_1_MILLISECOND
/ EHC_SYNC_POLL_INTERVAL
) + 1;
698 for (Index
= 0; Index
< Loop
; Index
++) {
699 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
705 gBS
->Stall (EHC_SYNC_POLL_INTERVAL
);
709 DEBUG ((EFI_D_ERROR
, "EhcExecTransfer: transfer not finished in %dms\n", (UINT32
)TimeOut
));
710 EhcDumpQh (Urb
->Qh
, NULL
, FALSE
);
712 Status
= EFI_TIMEOUT
;
714 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
715 DEBUG ((EFI_D_ERROR
, "EhcExecTransfer: transfer failed with %x\n", Urb
->Result
));
716 EhcDumpQh (Urb
->Qh
, NULL
, FALSE
);
718 Status
= EFI_DEVICE_ERROR
;
726 Delete a single asynchronous interrupt transfer for
727 the device and endpoint.
729 @param Ehc The EHCI device.
730 @param DevAddr The address of the target device.
731 @param EpNum The endpoint of the target.
732 @param DataToggle Return the next data toggle to use.
734 @retval EFI_SUCCESS An asynchronous transfer is removed.
735 @retval EFI_NOT_FOUND No transfer for the device is found.
739 EhciDelAsyncIntTransfer (
743 OUT UINT8
*DataToggle
749 EFI_USB_DATA_DIRECTION Direction
;
751 Direction
= (((EpNum
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
);
754 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
755 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
757 if ((Urb
->Ep
.DevAddr
== DevAddr
) && (Urb
->Ep
.EpAddr
== EpNum
) &&
758 (Urb
->Ep
.Direction
== Direction
)) {
760 // Check the URB status to retrieve the next data toggle
761 // from the associated queue head.
763 EhcCheckUrbResult (Ehc
, Urb
);
764 *DataToggle
= Urb
->DataToggle
;
766 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
767 RemoveEntryList (&Urb
->UrbList
);
769 gBS
->FreePool (Urb
->Data
);
770 EhcFreeUrb (Ehc
, Urb
);
775 return EFI_NOT_FOUND
;
780 Remove all the asynchronous interrutp transfers.
782 @param Ehc The EHCI device.
786 EhciDelAllAsyncIntTransfers (
794 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
795 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
797 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
798 RemoveEntryList (&Urb
->UrbList
);
800 gBS
->FreePool (Urb
->Data
);
801 EhcFreeUrb (Ehc
, Urb
);
807 Flush data from PCI controller specific address to mapped system
810 @param Ehc The EHCI device.
811 @param Urb The URB to unmap.
813 @retval EFI_SUCCESS Success to flush data to mapped system memory.
814 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
818 EhcFlushAsyncIntMap (
824 EFI_PHYSICAL_ADDRESS PhyAddr
;
825 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
826 EFI_PCI_IO_PROTOCOL
*PciIo
;
833 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
834 MapOp
= EfiPciIoOperationBusMasterWrite
;
836 MapOp
= EfiPciIoOperationBusMasterRead
;
839 Status
= PciIo
->Unmap (PciIo
, Urb
->DataMap
);
840 if (EFI_ERROR (Status
)) {
846 Status
= PciIo
->Map (PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
847 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
851 Urb
->DataPhy
= (VOID
*) ((UINTN
) PhyAddr
);
856 return EFI_DEVICE_ERROR
;
861 Update the queue head for next round of asynchronous transfer.
863 @param Ehc The EHCI device.
864 @param Urb The URB to update.
868 EhcUpdateAsyncRequest (
879 EFI_PHYSICAL_ADDRESS PciAddr
;
883 if (Urb
->Result
== EFI_USB_NOERROR
) {
886 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
887 Qtd
= EFI_LIST_CONTAINER (Entry
, EHC_QTD
, QtdList
);
889 if (FirstQtd
== NULL
) {
894 // Update the QTD for next round of transfer. Host control
895 // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/
896 // Current Offset. These fields need to be updated. DT isn't
897 // used by interrupt transfer. It uses DT in queue head.
898 // Current Offset is in Page[0], only need to reset Page[0]
899 // to initial data buffer.
902 QtdHw
->Status
= QTD_STAT_ACTIVE
;
903 QtdHw
->ErrCnt
= QTD_MAX_ERR
;
905 QtdHw
->TotalBytes
= (UINT32
) Qtd
->DataLen
;
906 QtdHw
->Page
[0] = EHC_LOW_32BIT (Qtd
->Data
);
910 // Update QH for next round of transfer. Host control only
911 // touch the fields in transfer overlay area. Only need to
912 // zero out the overlay area and set NextQtd to the first
913 // QTD. DateToggle bit is left untouched.
915 QhHw
= &Urb
->Qh
->QhHw
;
916 QhHw
->CurQtd
= QTD_LINK (0, TRUE
);
924 QhHw
->TotalBytes
= 0;
926 for (Index
= 0; Index
< 5; Index
++) {
927 QhHw
->Page
[Index
] = 0;
928 QhHw
->PageHigh
[Index
] = 0;
931 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, FirstQtd
, sizeof (EHC_QTD
));
932 QhHw
->NextQtd
= QTD_LINK (PciAddr
, FALSE
);
940 Interrupt transfer periodic check handler.
942 @param Event Interrupt event.
943 @param Context Pointer to USB2_HC_DEV.
948 EhcMonitorAsyncRequests (
962 OldTpl
= gBS
->RaiseTPL (EHC_TPL
);
963 Ehc
= (USB2_HC_DEV
*) Context
;
965 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
966 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
969 // Check the result of URB execution. If it is still
970 // active, check the next one.
972 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
979 // Flush any PCI posted write transactions from a PCI host
980 // bridge to system memory.
982 Status
= EhcFlushAsyncIntMap (Ehc
, Urb
);
983 if (EFI_ERROR (Status
)) {
984 DEBUG ((EFI_D_ERROR
, "EhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));
988 // Allocate a buffer then copy the transferred data for user.
989 // If failed to allocate the buffer, update the URB for next
990 // round of transfer. Ignore the data of this round.
994 if (Urb
->Result
== EFI_USB_NOERROR
) {
995 ASSERT (Urb
->Completed
<= Urb
->DataLen
);
997 ProcBuf
= AllocatePool (Urb
->Completed
);
999 if (ProcBuf
== NULL
) {
1000 EhcUpdateAsyncRequest (Ehc
, Urb
);
1004 CopyMem (ProcBuf
, Urb
->Data
, Urb
->Completed
);
1007 EhcUpdateAsyncRequest (Ehc
, Urb
);
1010 // Leave error recovery to its related device driver. A
1011 // common case of the error recovery is to re-submit the
1012 // interrupt transfer which is linked to the head of the
1013 // list. This function scans from head to tail. So the
1014 // re-submitted interrupt transfer's callback function
1015 // will not be called again in this round. Don't touch this
1016 // URB after the callback, it may have been removed by the
1019 if (Urb
->Callback
!= NULL
) {
1021 // Restore the old TPL, USB bus maybe connect device in
1022 // his callback. Some drivers may has a lower TPL restriction.
1024 gBS
->RestoreTPL (OldTpl
);
1025 (Urb
->Callback
) (ProcBuf
, Urb
->Completed
, Urb
->Context
, Urb
->Result
);
1026 OldTpl
= gBS
->RaiseTPL (EHC_TPL
);
1029 if (ProcBuf
!= NULL
) {
1034 gBS
->RestoreTPL (OldTpl
);