3 EHCI transfer scheduling routines.
5 Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
6 Copyright (c) Microsoft Corporation.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
15 Create helper QTD/QH for the EHCI device.
17 @param Ehc The EHCI device.
19 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH.
20 @retval EFI_SUCCESS Helper QH/QTD are created.
32 EFI_PHYSICAL_ADDRESS PciAddr
;
35 // Create an inactive Qtd to terminate the short packet read.
37 Qtd
= EhcCreateQtd (Ehc
, NULL
, NULL
, 0, QTD_PID_INPUT
, 0, 64);
40 return EFI_OUT_OF_RESOURCES
;
43 Qtd
->QtdHw
.Status
= QTD_STAT_HALTED
;
44 Ehc
->ShortReadStop
= Qtd
;
47 // Create a QH to act as the EHC reclamation header.
48 // Set the header to loopback to itself.
52 Ep
.Direction
= EfiUsbDataIn
;
53 Ep
.DevSpeed
= EFI_USB_SPEED_HIGH
;
58 Ep
.Type
= EHC_BULK_TRANSFER
;
61 Qh
= EhcCreateQh (Ehc
, &Ep
);
64 return EFI_OUT_OF_RESOURCES
;
67 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
69 QhHw
->HorizonLink
= QH_LINK (PciAddr
+ OFFSET_OF(EHC_QH
, QhHw
), EHC_TYPE_QH
, FALSE
);
70 QhHw
->Status
= QTD_STAT_HALTED
;
71 QhHw
->ReclaimHead
= 1;
73 Ehc
->ReclaimHead
= Qh
;
76 // Create a dummy QH to act as the terminator for periodical schedule
79 Ep
.Type
= EHC_INT_TRANSFER_SYNC
;
81 Qh
= EhcCreateQh (Ehc
, &Ep
);
84 return EFI_OUT_OF_RESOURCES
;
87 Qh
->QhHw
.Status
= QTD_STAT_HALTED
;
95 Initialize the schedule data structure such as frame list.
97 @param Ehc The EHCI device to init schedule data.
99 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.
100 @retval EFI_SUCCESS The schedule data is initialized.
108 EFI_PCI_IO_PROTOCOL
*PciIo
;
110 EFI_PHYSICAL_ADDRESS PhyAddr
;
116 EFI_PHYSICAL_ADDRESS PciAddr
;
119 // First initialize the periodical schedule data:
120 // 1. Allocate and map the memory for the frame list
121 // 2. Create the help QTD/QH
122 // 3. Initialize the frame entries
123 // 4. Set the frame list register
128 Pages
= EFI_SIZE_TO_PAGES (Bytes
);
130 Status
= PciIo
->AllocateBuffer (
139 if (EFI_ERROR (Status
)) {
140 return EFI_OUT_OF_RESOURCES
;
143 Status
= PciIo
->Map (
145 EfiPciIoOperationBusMasterCommonBuffer
,
152 if (EFI_ERROR (Status
) || (Bytes
!= 4096)) {
153 PciIo
->FreeBuffer (PciIo
, Pages
, Buf
);
154 return EFI_OUT_OF_RESOURCES
;
157 Ehc
->PeriodFrame
= Buf
;
158 Ehc
->PeriodFrameMap
= Map
;
161 // Program the FRAMELISTBASE register with the low 32 bit addr
163 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, EHC_LOW_32BIT (PhyAddr
));
165 // Program the CTRLDSSEGMENT register with the high 32 bit addr
167 EhcWriteOpReg (Ehc
, EHC_CTRLDSSEG_OFFSET
, EHC_HIGH_32BIT (PhyAddr
));
170 // Init memory pool management then create the helper
171 // QTD/QH. If failed, previously allocated resources
172 // will be freed by EhcFreeSched
174 Ehc
->MemPool
= UsbHcInitMemPool (
176 Ehc
->Support64BitDma
,
177 EHC_HIGH_32BIT (PhyAddr
)
180 if (Ehc
->MemPool
== NULL
) {
181 Status
= EFI_OUT_OF_RESOURCES
;
185 Status
= EhcCreateHelpQ (Ehc
);
187 if (EFI_ERROR (Status
)) {
192 // Initialize the frame list entries then set the registers
194 Ehc
->PeriodFrameHost
= AllocateZeroPool (EHC_FRAME_LEN
* sizeof (UINTN
));
195 if (Ehc
->PeriodFrameHost
== NULL
) {
196 Status
= EFI_OUT_OF_RESOURCES
;
200 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
202 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
++) {
204 // Store the pci bus address of the QH in period frame list which will be accessed by pci bus master.
206 ((UINT32
*)(Ehc
->PeriodFrame
))[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
208 // Store the host address of the QH in period frame list which will be accessed by host.
210 ((UINTN
*)(Ehc
->PeriodFrameHost
))[Index
] = (UINTN
)Ehc
->PeriodOne
;
214 // Second initialize the asynchronous schedule:
215 // Only need to set the AsynListAddr register to
216 // the reclamation header
218 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
219 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, EHC_LOW_32BIT (PciAddr
));
223 if (Ehc
->PeriodOne
!= NULL
) {
224 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
225 Ehc
->PeriodOne
= NULL
;
228 if (Ehc
->ReclaimHead
!= NULL
) {
229 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
230 Ehc
->ReclaimHead
= NULL
;
233 if (Ehc
->ShortReadStop
!= NULL
) {
234 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
235 Ehc
->ShortReadStop
= NULL
;
239 PciIo
->FreeBuffer (PciIo
, Pages
, Buf
);
240 PciIo
->Unmap (PciIo
, Map
);
247 Free the schedule data. It may be partially initialized.
249 @param Ehc The EHCI device.
257 EFI_PCI_IO_PROTOCOL
*PciIo
;
259 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, 0);
260 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, 0);
262 if (Ehc
->PeriodOne
!= NULL
) {
263 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
264 Ehc
->PeriodOne
= NULL
;
267 if (Ehc
->ReclaimHead
!= NULL
) {
268 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
269 Ehc
->ReclaimHead
= NULL
;
272 if (Ehc
->ShortReadStop
!= NULL
) {
273 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
274 Ehc
->ShortReadStop
= NULL
;
277 if (Ehc
->MemPool
!= NULL
) {
278 UsbHcFreeMemPool (Ehc
->MemPool
);
282 if (Ehc
->PeriodFrame
!= NULL
) {
284 ASSERT (PciIo
!= NULL
);
286 PciIo
->Unmap (PciIo
, Ehc
->PeriodFrameMap
);
290 EFI_SIZE_TO_PAGES (EFI_PAGE_SIZE
),
294 Ehc
->PeriodFrame
= NULL
;
297 if (Ehc
->PeriodFrameHost
!= NULL
) {
298 FreePool (Ehc
->PeriodFrameHost
);
299 Ehc
->PeriodFrameHost
= NULL
;
305 Link the queue head to the asynchronous schedule list.
306 UEFI only supports one CTRL/BULK transfer at a time
307 due to its interfaces. This simplifies the AsynList
308 management: A reclamation header is always linked to
309 the AsyncListAddr, the only active QH is appended to it.
311 @param Ehc The EHCI device.
312 @param Qh The queue head to link.
322 EFI_PHYSICAL_ADDRESS PciAddr
;
325 // Append the queue head after the reclaim header, then
326 // fix the hardware visiable parts (EHCI R1.0 page 72).
327 // ReclaimHead is always linked to the EHCI's AsynListAddr.
329 Head
= Ehc
->ReclaimHead
;
331 Qh
->NextQh
= Head
->NextQh
;
334 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
->NextQh
, sizeof (EHC_QH
));
335 Qh
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
336 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Head
->NextQh
, sizeof (EHC_QH
));
337 Head
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
342 Unlink a queue head from the asynchronous schedule list.
343 Need to synchronize with hardware.
345 @param Ehc The EHCI device.
346 @param Qh The queue head to unlink.
350 EhcUnlinkQhFromAsync (
357 EFI_PHYSICAL_ADDRESS PciAddr
;
359 ASSERT (Ehc
->ReclaimHead
->NextQh
== Qh
);
362 // Remove the QH from reclamation head, then update the hardware
363 // visiable part: Only need to loopback the ReclaimHead. The Qh
364 // is pointing to ReclaimHead (which is staill in the list).
366 Head
= Ehc
->ReclaimHead
;
368 Head
->NextQh
= Qh
->NextQh
;
371 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Head
->NextQh
, sizeof (EHC_QH
));
372 Head
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
375 // Set and wait the door bell to synchronize with the hardware
377 Status
= EhcSetAndWaitDoorBell (Ehc
, EHC_GENERIC_TIMEOUT
);
379 if (EFI_ERROR (Status
)) {
380 DEBUG ((EFI_D_ERROR
, "EhcUnlinkQhFromAsync: Failed to synchronize with doorbell\n"));
386 Link a queue head for interrupt transfer to the periodic
387 schedule frame list. This code is very much the same as
390 @param Ehc The EHCI device.
391 @param Qh The queue head to link.
403 EFI_PHYSICAL_ADDRESS PciAddr
;
405 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
407 // First QH can't be NULL because we always keep PeriodOne
408 // heads on the frame list
410 ASSERT (!EHC_LINK_TERMINATED (((UINT32
*)Ehc
->PeriodFrame
)[Index
]));
411 Next
= (EHC_QH
*)((UINTN
*)Ehc
->PeriodFrameHost
)[Index
];
415 // Now, insert the queue head (Qh) into this frame:
416 // 1. Find a queue head with the same poll interval, just insert
417 // Qh after this queue head, then we are done.
419 // 2. Find the position to insert the queue head into:
420 // Previous head's interval is bigger than Qh's
421 // Next head's interval is less than Qh's
422 // Then, insert the Qh between then
424 while (Next
->Interval
> Qh
->Interval
) {
429 ASSERT (Next
!= NULL
);
432 // The entry may have been linked into the frame by early insertation.
433 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
434 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
435 // It isn't necessary to compare all the QH with the same interval to
436 // Qh. This is because if there is other QH with the same interval, Qh
437 // should has been inserted after that at Frames[0] and at Frames[0] it is
438 // impossible for (Next == Qh)
444 if (Next
->Interval
== Qh
->Interval
) {
446 // If there is a QH with the same interval, it locates at
447 // Frames[0], and we can simply insert it after this QH. We
450 ASSERT ((Index
== 0) && (Qh
->NextQh
== NULL
));
458 Qh
->QhHw
.HorizonLink
= Prev
->QhHw
.HorizonLink
;
459 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
460 Prev
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
465 // OK, find the right position, insert it in. If Qh's next
466 // link has already been set, it is in position. This is
467 // guarranted by 2^n polling interval.
469 if (Qh
->NextQh
== NULL
) {
471 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Next
, sizeof (EHC_QH
));
472 Qh
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
475 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
478 ((UINT32
*)Ehc
->PeriodFrame
)[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
479 ((UINTN
*)Ehc
->PeriodFrameHost
)[Index
] = (UINTN
)Qh
;
482 Prev
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
489 Unlink an interrupt queue head from the periodic
492 @param Ehc The EHCI device.
493 @param Qh The queue head to unlink.
497 EhcUnlinkQhFromPeriod (
506 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
508 // Frame link can't be NULL because we always keep PeroidOne
511 ASSERT (!EHC_LINK_TERMINATED (((UINT32
*)Ehc
->PeriodFrame
)[Index
]));
512 This
= (EHC_QH
*)((UINTN
*)Ehc
->PeriodFrameHost
)[Index
];
516 // Walk through the frame's QH list to find the
517 // queue head to remove
519 while ((This
!= NULL
) && (This
!= Qh
)) {
525 // Qh may have already been unlinked from this frame
526 // by early action. See the comments in EhcLinkQhToPeriod.
534 // Qh is the first entry in the frame
536 ((UINT32
*)Ehc
->PeriodFrame
)[Index
] = Qh
->QhHw
.HorizonLink
;
537 ((UINTN
*)Ehc
->PeriodFrameHost
)[Index
] = (UINTN
)Qh
->NextQh
;
539 Prev
->NextQh
= Qh
->NextQh
;
540 Prev
->QhHw
.HorizonLink
= Qh
->QhHw
.HorizonLink
;
547 Check the URB's execution result and update the URB's
550 @param Ehc The EHCI device.
551 @param Urb The URB to check result.
553 @return Whether the result of URB transfer is finialized.
567 EFI_PHYSICAL_ADDRESS PciAddr
;
569 ASSERT ((Ehc
!= NULL
) && (Urb
!= NULL
) && (Urb
->Qh
!= NULL
));
574 Urb
->Result
= EFI_USB_NOERROR
;
576 if (EhcIsHalt (Ehc
) || EhcIsSysError (Ehc
)) {
577 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
581 BASE_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
582 Qtd
= EFI_LIST_CONTAINER (Entry
, EHC_QTD
, QtdList
);
584 State
= (UINT8
) QtdHw
->Status
;
586 if (EHC_BIT_IS_SET (State
, QTD_STAT_HALTED
)) {
588 // EHCI will halt the queue head when met some error.
589 // If it is halted, the result of URB is finialized.
591 if ((State
& QTD_STAT_ERR_MASK
) == 0) {
592 Urb
->Result
|= EFI_USB_ERR_STALL
;
595 if (EHC_BIT_IS_SET (State
, QTD_STAT_BABBLE_ERR
)) {
596 Urb
->Result
|= EFI_USB_ERR_BABBLE
;
599 if (EHC_BIT_IS_SET (State
, QTD_STAT_BUFF_ERR
)) {
600 Urb
->Result
|= EFI_USB_ERR_BUFFER
;
603 if (EHC_BIT_IS_SET (State
, QTD_STAT_TRANS_ERR
) && (QtdHw
->ErrCnt
== 0)) {
604 Urb
->Result
|= EFI_USB_ERR_TIMEOUT
;
610 } else if (EHC_BIT_IS_SET (State
, QTD_STAT_ACTIVE
)) {
612 // The QTD is still active, no need to check furthur.
614 Urb
->Result
|= EFI_USB_ERR_NOTEXECUTE
;
621 // This QTD is finished OK or met short packet read. Update the
622 // transfer length if it isn't a setup.
624 if (QtdHw
->Pid
!= QTD_PID_SETUP
) {
625 Urb
->Completed
+= Qtd
->DataLen
- QtdHw
->TotalBytes
;
628 if ((QtdHw
->TotalBytes
!= 0) && (QtdHw
->Pid
== QTD_PID_INPUT
)) {
629 EhcDumpQh (Urb
->Qh
, "Short packet read", FALSE
);
632 // Short packet read condition. If it isn't a setup transfer,
633 // no need to check furthur: the queue head will halt at the
634 // ShortReadStop. If it is a setup transfer, need to check the
635 // Status Stage of the setup transfer to get the finial result
637 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
638 if (QtdHw
->AltNext
== QTD_LINK (PciAddr
, FALSE
)) {
639 DEBUG ((EFI_D_VERBOSE
, "EhcCheckUrbResult: Short packet read, break\n"));
645 DEBUG ((EFI_D_VERBOSE
, "EhcCheckUrbResult: Short packet read, continue\n"));
652 // Return the data toggle set by EHCI hardware, bulk and interrupt
653 // transfer will use this to initialize the next transaction. For
654 // Control transfer, it always start a new data toggle sequence for
657 // NOTICE: don't move DT update before the loop, otherwise there is
658 // a race condition that DT is wrong.
660 Urb
->DataToggle
= (UINT8
) Urb
->Qh
->QhHw
.DataToggle
;
667 Execute the transfer by polling the URB. This is a synchronous operation.
669 @param Ehc The EHCI device.
670 @param Urb The URB to execute.
671 @param TimeOut The time to wait before abort, in millisecond.
673 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.
674 @return EFI_TIMEOUT The transfer failed due to time out.
675 @return EFI_SUCCESS The transfer finished OK.
689 BOOLEAN InfiniteLoop
;
691 Status
= EFI_SUCCESS
;
692 Loop
= TimeOut
* EHC_1_MILLISECOND
;
694 InfiniteLoop
= FALSE
;
697 // According to UEFI spec section 16.2.4, If Timeout is 0, then the caller
698 // must wait for the function to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR
705 for (Index
= 0; InfiniteLoop
|| (Index
< Loop
); Index
++) {
706 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
712 gBS
->Stall (EHC_1_MICROSECOND
);
716 DEBUG ((EFI_D_ERROR
, "EhcExecTransfer: transfer not finished in %dms\n", (UINT32
)TimeOut
));
717 EhcDumpQh (Urb
->Qh
, NULL
, FALSE
);
719 Status
= EFI_TIMEOUT
;
721 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
722 DEBUG ((EFI_D_ERROR
, "EhcExecTransfer: transfer failed with %x\n", Urb
->Result
));
723 EhcDumpQh (Urb
->Qh
, NULL
, FALSE
);
725 Status
= EFI_DEVICE_ERROR
;
733 Delete a single asynchronous interrupt transfer for
734 the device and endpoint.
736 @param Ehc The EHCI device.
737 @param DevAddr The address of the target device.
738 @param EpNum The endpoint of the target.
739 @param DataToggle Return the next data toggle to use.
741 @retval EFI_SUCCESS An asynchronous transfer is removed.
742 @retval EFI_NOT_FOUND No transfer for the device is found.
746 EhciDelAsyncIntTransfer (
750 OUT UINT8
*DataToggle
756 EFI_USB_DATA_DIRECTION Direction
;
758 Direction
= (((EpNum
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
);
761 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
762 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
764 if ((Urb
->Ep
.DevAddr
== DevAddr
) && (Urb
->Ep
.EpAddr
== EpNum
) &&
765 (Urb
->Ep
.Direction
== Direction
)) {
767 // Check the URB status to retrieve the next data toggle
768 // from the associated queue head.
770 EhcCheckUrbResult (Ehc
, Urb
);
771 *DataToggle
= Urb
->DataToggle
;
773 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
774 RemoveEntryList (&Urb
->UrbList
);
776 gBS
->FreePool (Urb
->Data
);
777 EhcFreeUrb (Ehc
, Urb
);
782 return EFI_NOT_FOUND
;
787 Remove all the asynchronous interrutp transfers.
789 @param Ehc The EHCI device.
793 EhciDelAllAsyncIntTransfers (
801 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
802 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
804 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
805 RemoveEntryList (&Urb
->UrbList
);
807 gBS
->FreePool (Urb
->Data
);
808 EhcFreeUrb (Ehc
, Urb
);
813 Insert a single asynchronous interrupt transfer for
814 the device and endpoint.
816 @param Ehc The EHCI device.
817 @param DevAddr The device address.
818 @param EpAddr Endpoint addrress & its direction.
819 @param DevSpeed The device speed.
820 @param Toggle Initial data toggle to use.
821 @param MaxPacket The max packet length of the endpoint.
822 @param Hub The transaction translator to use.
823 @param DataLen The length of data buffer.
824 @param Callback The function to call when data is transferred.
825 @param Context The context to the callback.
826 @param Interval The interval for interrupt transfer.
828 @return Created URB or NULL.
832 EhciInsertAsyncIntTransfer (
839 IN EFI_USB2_HC_TRANSACTION_TRANSLATOR
*Hub
,
841 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback
,
849 Data
= AllocatePool (DataLen
);
852 DEBUG ((DEBUG_ERROR
, "%a: failed to allocate buffer\n", __FUNCTION__
));
864 EHC_INT_TRANSFER_ASYNC
,
874 DEBUG ((DEBUG_ERROR
, "%a: failed to create URB\n", __FUNCTION__
));
875 gBS
->FreePool (Data
);
880 // New asynchronous transfer must inserted to the head.
881 // Check the comments in EhcMoniteAsyncRequests
883 EhcLinkQhToPeriod (Ehc
, Urb
->Qh
);
884 InsertHeadList (&Ehc
->AsyncIntTransfers
, &Urb
->UrbList
);
890 Flush data from PCI controller specific address to mapped system
893 @param Ehc The EHCI device.
894 @param Urb The URB to unmap.
896 @retval EFI_SUCCESS Success to flush data to mapped system memory.
897 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
901 EhcFlushAsyncIntMap (
907 EFI_PHYSICAL_ADDRESS PhyAddr
;
908 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
909 EFI_PCI_IO_PROTOCOL
*PciIo
;
916 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
917 MapOp
= EfiPciIoOperationBusMasterWrite
;
919 MapOp
= EfiPciIoOperationBusMasterRead
;
922 Status
= PciIo
->Unmap (PciIo
, Urb
->DataMap
);
923 if (EFI_ERROR (Status
)) {
929 Status
= PciIo
->Map (PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
930 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
934 Urb
->DataPhy
= (VOID
*) ((UINTN
) PhyAddr
);
939 return EFI_DEVICE_ERROR
;
944 Update the queue head for next round of asynchronous transfer.
946 @param Ehc The EHCI device.
947 @param Urb The URB to update.
951 EhcUpdateAsyncRequest (
962 EFI_PHYSICAL_ADDRESS PciAddr
;
966 if (Urb
->Result
== EFI_USB_NOERROR
) {
969 BASE_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
970 Qtd
= EFI_LIST_CONTAINER (Entry
, EHC_QTD
, QtdList
);
972 if (FirstQtd
== NULL
) {
977 // Update the QTD for next round of transfer. Host control
978 // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/
979 // Current Offset. These fields need to be updated. DT isn't
980 // used by interrupt transfer. It uses DT in queue head.
981 // Current Offset is in Page[0], only need to reset Page[0]
982 // to initial data buffer.
985 QtdHw
->Status
= QTD_STAT_ACTIVE
;
986 QtdHw
->ErrCnt
= QTD_MAX_ERR
;
988 QtdHw
->TotalBytes
= (UINT32
) Qtd
->DataLen
;
990 // calculate physical address by offset.
992 PciAddr
= (UINTN
)Urb
->DataPhy
+ ((UINTN
)Qtd
->Data
- (UINTN
)Urb
->Data
);
993 QtdHw
->Page
[0] = EHC_LOW_32BIT (PciAddr
);
994 QtdHw
->PageHigh
[0]= EHC_HIGH_32BIT (PciAddr
);
998 // Update QH for next round of transfer. Host control only
999 // touch the fields in transfer overlay area. Only need to
1000 // zero out the overlay area and set NextQtd to the first
1001 // QTD. DateToggle bit is left untouched.
1003 QhHw
= &Urb
->Qh
->QhHw
;
1004 QhHw
->CurQtd
= QTD_LINK (0, TRUE
);
1012 QhHw
->TotalBytes
= 0;
1014 for (Index
= 0; Index
< 5; Index
++) {
1015 QhHw
->Page
[Index
] = 0;
1016 QhHw
->PageHigh
[Index
] = 0;
1019 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, FirstQtd
, sizeof (EHC_QTD
));
1020 QhHw
->NextQtd
= QTD_LINK (PciAddr
, FALSE
);
1028 Interrupt transfer periodic check handler.
1030 @param Event Interrupt event.
1031 @param Context Pointer to USB2_HC_DEV.
1036 EhcMonitorAsyncRequests (
1050 OldTpl
= gBS
->RaiseTPL (EHC_TPL
);
1051 Ehc
= (USB2_HC_DEV
*) Context
;
1053 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
1054 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1057 // Check the result of URB execution. If it is still
1058 // active, check the next one.
1060 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
1067 // Flush any PCI posted write transactions from a PCI host
1068 // bridge to system memory.
1070 Status
= EhcFlushAsyncIntMap (Ehc
, Urb
);
1071 if (EFI_ERROR (Status
)) {
1072 DEBUG ((EFI_D_ERROR
, "EhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));
1076 // Allocate a buffer then copy the transferred data for user.
1077 // If failed to allocate the buffer, update the URB for next
1078 // round of transfer. Ignore the data of this round.
1082 if (Urb
->Result
== EFI_USB_NOERROR
) {
1084 // Make sure the data received from HW is no more than expected.
1086 if (Urb
->Completed
<= Urb
->DataLen
) {
1087 ProcBuf
= AllocatePool (Urb
->Completed
);
1090 if (ProcBuf
== NULL
) {
1091 EhcUpdateAsyncRequest (Ehc
, Urb
);
1095 CopyMem (ProcBuf
, Urb
->Data
, Urb
->Completed
);
1098 EhcUpdateAsyncRequest (Ehc
, Urb
);
1101 // Leave error recovery to its related device driver. A
1102 // common case of the error recovery is to re-submit the
1103 // interrupt transfer which is linked to the head of the
1104 // list. This function scans from head to tail. So the
1105 // re-submitted interrupt transfer's callback function
1106 // will not be called again in this round. Don't touch this
1107 // URB after the callback, it may have been removed by the
1110 if (Urb
->Callback
!= NULL
) {
1112 // Restore the old TPL, USB bus maybe connect device in
1113 // his callback. Some drivers may has a lower TPL restriction.
1115 gBS
->RestoreTPL (OldTpl
);
1116 (Urb
->Callback
) (ProcBuf
, Urb
->Completed
, Urb
->Context
, Urb
->Result
);
1117 OldTpl
= gBS
->RaiseTPL (EHC_TPL
);
1120 if (ProcBuf
!= NULL
) {
1125 gBS
->RestoreTPL (OldTpl
);