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
14 Create helper QTD/QH for the EHCI device.
16 @param Ehc The EHCI device.
18 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH.
19 @retval EFI_SUCCESS Helper QH/QTD are created.
31 EFI_PHYSICAL_ADDRESS PciAddr
;
34 // Create an inactive Qtd to terminate the short packet read.
36 Qtd
= EhcCreateQtd (Ehc
, NULL
, NULL
, 0, QTD_PID_INPUT
, 0, 64);
39 return EFI_OUT_OF_RESOURCES
;
42 Qtd
->QtdHw
.Status
= QTD_STAT_HALTED
;
43 Ehc
->ShortReadStop
= Qtd
;
46 // Create a QH to act as the EHC reclamation header.
47 // Set the header to loopback to itself.
51 Ep
.Direction
= EfiUsbDataIn
;
52 Ep
.DevSpeed
= EFI_USB_SPEED_HIGH
;
57 Ep
.Type
= EHC_BULK_TRANSFER
;
60 Qh
= EhcCreateQh (Ehc
, &Ep
);
63 return EFI_OUT_OF_RESOURCES
;
66 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
68 QhHw
->HorizonLink
= QH_LINK (PciAddr
+ OFFSET_OF (EHC_QH
, QhHw
), EHC_TYPE_QH
, FALSE
);
69 QhHw
->Status
= QTD_STAT_HALTED
;
70 QhHw
->ReclaimHead
= 1;
72 Ehc
->ReclaimHead
= Qh
;
75 // Create a dummy QH to act as the terminator for periodical schedule
78 Ep
.Type
= EHC_INT_TRANSFER_SYNC
;
80 Qh
= EhcCreateQh (Ehc
, &Ep
);
83 return EFI_OUT_OF_RESOURCES
;
86 Qh
->QhHw
.Status
= QTD_STAT_HALTED
;
93 Initialize the schedule data structure such as frame list.
95 @param Ehc The EHCI device to init schedule data.
97 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.
98 @retval EFI_SUCCESS The schedule data is initialized.
106 EFI_PCI_IO_PROTOCOL
*PciIo
;
108 EFI_PHYSICAL_ADDRESS PhyAddr
;
114 EFI_PHYSICAL_ADDRESS PciAddr
;
117 // First initialize the periodical schedule data:
118 // 1. Allocate and map the memory for the frame list
119 // 2. Create the help QTD/QH
120 // 3. Initialize the frame entries
121 // 4. Set the frame list register
126 Pages
= EFI_SIZE_TO_PAGES (Bytes
);
128 Status
= PciIo
->AllocateBuffer (
137 if (EFI_ERROR (Status
)) {
138 return EFI_OUT_OF_RESOURCES
;
141 Status
= PciIo
->Map (
143 EfiPciIoOperationBusMasterCommonBuffer
,
150 if (EFI_ERROR (Status
) || (Bytes
!= 4096)) {
151 PciIo
->FreeBuffer (PciIo
, Pages
, Buf
);
152 return EFI_OUT_OF_RESOURCES
;
155 Ehc
->PeriodFrame
= Buf
;
156 Ehc
->PeriodFrameMap
= Map
;
159 // Program the FRAMELISTBASE register with the low 32 bit addr
161 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, EHC_LOW_32BIT (PhyAddr
));
163 // Program the CTRLDSSEGMENT register with the high 32 bit addr
165 EhcWriteOpReg (Ehc
, EHC_CTRLDSSEG_OFFSET
, 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
->Support64BitDma
,
175 EHC_HIGH_32BIT (PhyAddr
)
178 if (Ehc
->MemPool
== NULL
) {
179 Status
= EFI_OUT_OF_RESOURCES
;
183 Status
= EhcCreateHelpQ (Ehc
);
185 if (EFI_ERROR (Status
)) {
190 // Initialize the frame list entries then set the registers
192 Ehc
->PeriodFrameHost
= AllocateZeroPool (EHC_FRAME_LEN
* sizeof (UINTN
));
193 if (Ehc
->PeriodFrameHost
== NULL
) {
194 Status
= EFI_OUT_OF_RESOURCES
;
198 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
200 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
++) {
202 // Store the pci bus address of the QH in period frame list which will be accessed by pci bus master.
204 ((UINT32
*)(Ehc
->PeriodFrame
))[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
206 // Store the host address of the QH in period frame list which will be accessed by host.
208 ((UINTN
*)(Ehc
->PeriodFrameHost
))[Index
] = (UINTN
)Ehc
->PeriodOne
;
212 // Second initialize the asynchronous schedule:
213 // Only need to set the AsynListAddr register to
214 // the reclamation header
216 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
217 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, EHC_LOW_32BIT (PciAddr
));
221 if (Ehc
->PeriodOne
!= NULL
) {
222 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
223 Ehc
->PeriodOne
= NULL
;
226 if (Ehc
->ReclaimHead
!= NULL
) {
227 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
228 Ehc
->ReclaimHead
= NULL
;
231 if (Ehc
->ShortReadStop
!= NULL
) {
232 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
233 Ehc
->ShortReadStop
= NULL
;
237 PciIo
->FreeBuffer (PciIo
, Pages
, Buf
);
238 PciIo
->Unmap (PciIo
, Map
);
244 Free the schedule data. It may be partially initialized.
246 @param Ehc The EHCI device.
254 EFI_PCI_IO_PROTOCOL
*PciIo
;
256 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, 0);
257 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, 0);
259 if (Ehc
->PeriodOne
!= NULL
) {
260 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
261 Ehc
->PeriodOne
= NULL
;
264 if (Ehc
->ReclaimHead
!= NULL
) {
265 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
266 Ehc
->ReclaimHead
= NULL
;
269 if (Ehc
->ShortReadStop
!= NULL
) {
270 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
271 Ehc
->ShortReadStop
= NULL
;
274 if (Ehc
->MemPool
!= NULL
) {
275 UsbHcFreeMemPool (Ehc
->MemPool
);
279 if (Ehc
->PeriodFrame
!= NULL
) {
281 ASSERT (PciIo
!= NULL
);
283 PciIo
->Unmap (PciIo
, Ehc
->PeriodFrameMap
);
287 EFI_SIZE_TO_PAGES (EFI_PAGE_SIZE
),
291 Ehc
->PeriodFrame
= NULL
;
294 if (Ehc
->PeriodFrameHost
!= NULL
) {
295 FreePool (Ehc
->PeriodFrameHost
);
296 Ehc
->PeriodFrameHost
= NULL
;
301 Link the queue head to the asynchronous schedule list.
302 UEFI only supports one CTRL/BULK transfer at a time
303 due to its interfaces. This simplifies the AsynList
304 management: A reclamation header is always linked to
305 the AsyncListAddr, the only active QH is appended to it.
307 @param Ehc The EHCI device.
308 @param Qh The queue head to link.
318 EFI_PHYSICAL_ADDRESS PciAddr
;
321 // Append the queue head after the reclaim header, then
322 // fix the hardware visiable parts (EHCI R1.0 page 72).
323 // ReclaimHead is always linked to the EHCI's AsynListAddr.
325 Head
= Ehc
->ReclaimHead
;
327 Qh
->NextQh
= Head
->NextQh
;
330 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
->NextQh
, sizeof (EHC_QH
));
331 Qh
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
332 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Head
->NextQh
, sizeof (EHC_QH
));
333 Head
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
337 Unlink a queue head from the asynchronous schedule list.
338 Need to synchronize with hardware.
340 @param Ehc The EHCI device.
341 @param Qh The queue head to unlink.
345 EhcUnlinkQhFromAsync (
352 EFI_PHYSICAL_ADDRESS PciAddr
;
354 ASSERT (Ehc
->ReclaimHead
->NextQh
== Qh
);
357 // Remove the QH from reclamation head, then update the hardware
358 // visiable part: Only need to loopback the ReclaimHead. The Qh
359 // is pointing to ReclaimHead (which is staill in the list).
361 Head
= Ehc
->ReclaimHead
;
363 Head
->NextQh
= Qh
->NextQh
;
366 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Head
->NextQh
, sizeof (EHC_QH
));
367 Head
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
370 // Set and wait the door bell to synchronize with the hardware
372 Status
= EhcSetAndWaitDoorBell (Ehc
, EHC_GENERIC_TIMEOUT
);
374 if (EFI_ERROR (Status
)) {
375 DEBUG ((DEBUG_ERROR
, "EhcUnlinkQhFromAsync: Failed to synchronize with doorbell\n"));
380 Link a queue head for interrupt transfer to the periodic
381 schedule frame list. This code is very much the same as
384 @param Ehc The EHCI device.
385 @param Qh The queue head to link.
397 EFI_PHYSICAL_ADDRESS PciAddr
;
399 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
401 // First QH can't be NULL because we always keep PeriodOne
402 // heads on the frame list
404 ASSERT (!EHC_LINK_TERMINATED (((UINT32
*)Ehc
->PeriodFrame
)[Index
]));
405 Next
= (EHC_QH
*)((UINTN
*)Ehc
->PeriodFrameHost
)[Index
];
409 // Now, insert the queue head (Qh) into this frame:
410 // 1. Find a queue head with the same poll interval, just insert
411 // Qh after this queue head, then we are done.
413 // 2. Find the position to insert the queue head into:
414 // Previous head's interval is bigger than Qh's
415 // Next head's interval is less than Qh's
416 // Then, insert the Qh between then
418 while (Next
->Interval
> Qh
->Interval
) {
423 ASSERT (Next
!= NULL
);
426 // The entry may have been linked into the frame by early insertation.
427 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
428 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
429 // It isn't necessary to compare all the QH with the same interval to
430 // Qh. This is because if there is other QH with the same interval, Qh
431 // should has been inserted after that at Frames[0] and at Frames[0] it is
432 // impossible for (Next == Qh)
438 if (Next
->Interval
== Qh
->Interval
) {
440 // If there is a QH with the same interval, it locates at
441 // Frames[0], and we can simply insert it after this QH. We
444 ASSERT ((Index
== 0) && (Qh
->NextQh
== NULL
));
452 Qh
->QhHw
.HorizonLink
= Prev
->QhHw
.HorizonLink
;
453 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
454 Prev
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
459 // OK, find the right position, insert it in. If Qh's next
460 // link has already been set, it is in position. This is
461 // guarranted by 2^n polling interval.
463 if (Qh
->NextQh
== NULL
) {
465 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Next
, sizeof (EHC_QH
));
466 Qh
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
469 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
472 ((UINT32
*)Ehc
->PeriodFrame
)[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
473 ((UINTN
*)Ehc
->PeriodFrameHost
)[Index
] = (UINTN
)Qh
;
476 Prev
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
482 Unlink an interrupt queue head from the periodic
485 @param Ehc The EHCI device.
486 @param Qh The queue head to unlink.
490 EhcUnlinkQhFromPeriod (
499 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
501 // Frame link can't be NULL because we always keep PeroidOne
504 ASSERT (!EHC_LINK_TERMINATED (((UINT32
*)Ehc
->PeriodFrame
)[Index
]));
505 This
= (EHC_QH
*)((UINTN
*)Ehc
->PeriodFrameHost
)[Index
];
509 // Walk through the frame's QH list to find the
510 // queue head to remove
512 while ((This
!= NULL
) && (This
!= Qh
)) {
518 // Qh may have already been unlinked from this frame
519 // by early action. See the comments in EhcLinkQhToPeriod.
527 // Qh is the first entry in the frame
529 ((UINT32
*)Ehc
->PeriodFrame
)[Index
] = Qh
->QhHw
.HorizonLink
;
530 ((UINTN
*)Ehc
->PeriodFrameHost
)[Index
] = (UINTN
)Qh
->NextQh
;
532 Prev
->NextQh
= Qh
->NextQh
;
533 Prev
->QhHw
.HorizonLink
= Qh
->QhHw
.HorizonLink
;
539 Check the URB's execution result and update the URB's
542 @param Ehc The EHCI device.
543 @param Urb The URB to check result.
545 @return Whether the result of URB transfer is finialized.
559 EFI_PHYSICAL_ADDRESS PciAddr
;
561 ASSERT ((Ehc
!= NULL
) && (Urb
!= NULL
) && (Urb
->Qh
!= NULL
));
566 Urb
->Result
= EFI_USB_NOERROR
;
568 if (EhcIsHalt (Ehc
) || EhcIsSysError (Ehc
)) {
569 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
573 BASE_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
574 Qtd
= EFI_LIST_CONTAINER (Entry
, EHC_QTD
, QtdList
);
576 State
= (UINT8
)QtdHw
->Status
;
578 if (EHC_BIT_IS_SET (State
, QTD_STAT_HALTED
)) {
580 // EHCI will halt the queue head when met some error.
581 // If it is halted, the result of URB is finialized.
583 if ((State
& QTD_STAT_ERR_MASK
) == 0) {
584 Urb
->Result
|= EFI_USB_ERR_STALL
;
587 if (EHC_BIT_IS_SET (State
, QTD_STAT_BABBLE_ERR
)) {
588 Urb
->Result
|= EFI_USB_ERR_BABBLE
;
591 if (EHC_BIT_IS_SET (State
, QTD_STAT_BUFF_ERR
)) {
592 Urb
->Result
|= EFI_USB_ERR_BUFFER
;
595 if (EHC_BIT_IS_SET (State
, QTD_STAT_TRANS_ERR
) && (QtdHw
->ErrCnt
== 0)) {
596 Urb
->Result
|= EFI_USB_ERR_TIMEOUT
;
601 } else if (EHC_BIT_IS_SET (State
, QTD_STAT_ACTIVE
)) {
603 // The QTD is still active, no need to check furthur.
605 Urb
->Result
|= EFI_USB_ERR_NOTEXECUTE
;
611 // This QTD is finished OK or met short packet read. Update the
612 // transfer length if it isn't a setup.
614 if (QtdHw
->Pid
!= QTD_PID_SETUP
) {
615 Urb
->Completed
+= Qtd
->DataLen
- QtdHw
->TotalBytes
;
618 if ((QtdHw
->TotalBytes
!= 0) && (QtdHw
->Pid
== QTD_PID_INPUT
)) {
619 EhcDumpQh (Urb
->Qh
, "Short packet read", FALSE
);
622 // Short packet read condition. If it isn't a setup transfer,
623 // no need to check furthur: the queue head will halt at the
624 // ShortReadStop. If it is a setup transfer, need to check the
625 // Status Stage of the setup transfer to get the finial result
627 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
628 if (QtdHw
->AltNext
== QTD_LINK (PciAddr
, FALSE
)) {
629 DEBUG ((DEBUG_VERBOSE
, "EhcCheckUrbResult: Short packet read, break\n"));
635 DEBUG ((DEBUG_VERBOSE
, "EhcCheckUrbResult: Short packet read, continue\n"));
642 // Return the data toggle set by EHCI hardware, bulk and interrupt
643 // transfer will use this to initialize the next transaction. For
644 // Control transfer, it always start a new data toggle sequence for
647 // NOTICE: don't move DT update before the loop, otherwise there is
648 // a race condition that DT is wrong.
650 Urb
->DataToggle
= (UINT8
)Urb
->Qh
->QhHw
.DataToggle
;
656 Execute the transfer by polling the URB. This is a synchronous operation.
658 @param Ehc The EHCI device.
659 @param Urb The URB to execute.
660 @param TimeOut The time to wait before abort, in millisecond.
662 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.
663 @return EFI_TIMEOUT The transfer failed due to time out.
664 @return EFI_SUCCESS The transfer finished OK.
678 BOOLEAN InfiniteLoop
;
680 Status
= EFI_SUCCESS
;
681 Loop
= TimeOut
* EHC_1_MILLISECOND
;
683 InfiniteLoop
= FALSE
;
686 // According to UEFI spec section 16.2.4, If Timeout is 0, then the caller
687 // must wait for the function to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR
694 for (Index
= 0; InfiniteLoop
|| (Index
< Loop
); Index
++) {
695 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
701 gBS
->Stall (EHC_1_MICROSECOND
);
705 DEBUG ((DEBUG_ERROR
, "EhcExecTransfer: transfer not finished in %dms\n", (UINT32
)TimeOut
));
706 EhcDumpQh (Urb
->Qh
, NULL
, FALSE
);
708 Status
= EFI_TIMEOUT
;
709 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
710 DEBUG ((DEBUG_ERROR
, "EhcExecTransfer: transfer failed with %x\n", Urb
->Result
));
711 EhcDumpQh (Urb
->Qh
, NULL
, FALSE
);
713 Status
= EFI_DEVICE_ERROR
;
720 Delete a single asynchronous interrupt transfer for
721 the device and endpoint.
723 @param Ehc The EHCI device.
724 @param DevAddr The address of the target device.
725 @param EpNum The endpoint of the target.
726 @param DataToggle Return the next data toggle to use.
728 @retval EFI_SUCCESS An asynchronous transfer is removed.
729 @retval EFI_NOT_FOUND No transfer for the device is found.
733 EhciDelAsyncIntTransfer (
737 OUT UINT8
*DataToggle
743 EFI_USB_DATA_DIRECTION Direction
;
745 Direction
= (((EpNum
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
);
748 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
749 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
751 if ((Urb
->Ep
.DevAddr
== DevAddr
) && (Urb
->Ep
.EpAddr
== EpNum
) &&
752 (Urb
->Ep
.Direction
== Direction
))
755 // Check the URB status to retrieve the next data toggle
756 // from the associated queue head.
758 EhcCheckUrbResult (Ehc
, Urb
);
759 *DataToggle
= Urb
->DataToggle
;
761 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
762 RemoveEntryList (&Urb
->UrbList
);
764 gBS
->FreePool (Urb
->Data
);
765 EhcFreeUrb (Ehc
, Urb
);
770 return EFI_NOT_FOUND
;
774 Remove all the asynchronous interrutp transfers.
776 @param Ehc The EHCI device.
780 EhciDelAllAsyncIntTransfers (
788 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
789 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
791 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
792 RemoveEntryList (&Urb
->UrbList
);
794 gBS
->FreePool (Urb
->Data
);
795 EhcFreeUrb (Ehc
, Urb
);
800 Insert a single asynchronous interrupt transfer for
801 the device and endpoint.
803 @param Ehc The EHCI device.
804 @param DevAddr The device address.
805 @param EpAddr Endpoint addrress & its direction.
806 @param DevSpeed The device speed.
807 @param Toggle Initial data toggle to use.
808 @param MaxPacket The max packet length of the endpoint.
809 @param Hub The transaction translator to use.
810 @param DataLen The length of data buffer.
811 @param Callback The function to call when data is transferred.
812 @param Context The context to the callback.
813 @param Interval The interval for interrupt transfer.
815 @return Created URB or NULL.
819 EhciInsertAsyncIntTransfer (
826 IN EFI_USB2_HC_TRANSACTION_TRANSLATOR
*Hub
,
828 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback
,
836 Data
= AllocatePool (DataLen
);
839 DEBUG ((DEBUG_ERROR
, "%a: failed to allocate buffer\n", __FUNCTION__
));
851 EHC_INT_TRANSFER_ASYNC
,
861 DEBUG ((DEBUG_ERROR
, "%a: failed to create URB\n", __FUNCTION__
));
862 gBS
->FreePool (Data
);
867 // New asynchronous transfer must inserted to the head.
868 // Check the comments in EhcMoniteAsyncRequests
870 EhcLinkQhToPeriod (Ehc
, Urb
->Qh
);
871 InsertHeadList (&Ehc
->AsyncIntTransfers
, &Urb
->UrbList
);
877 Flush data from PCI controller specific address to mapped system
880 @param Ehc The EHCI device.
881 @param Urb The URB to unmap.
883 @retval EFI_SUCCESS Success to flush data to mapped system memory.
884 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
888 EhcFlushAsyncIntMap (
894 EFI_PHYSICAL_ADDRESS PhyAddr
;
895 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
896 EFI_PCI_IO_PROTOCOL
*PciIo
;
903 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
904 MapOp
= EfiPciIoOperationBusMasterWrite
;
906 MapOp
= EfiPciIoOperationBusMasterRead
;
909 Status
= PciIo
->Unmap (PciIo
, Urb
->DataMap
);
910 if (EFI_ERROR (Status
)) {
916 Status
= PciIo
->Map (PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
917 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
921 Urb
->DataPhy
= (VOID
*)((UINTN
)PhyAddr
);
926 return EFI_DEVICE_ERROR
;
930 Update the queue head for next round of asynchronous transfer.
932 @param Ehc The EHCI device.
933 @param Urb The URB to update.
937 EhcUpdateAsyncRequest (
948 EFI_PHYSICAL_ADDRESS PciAddr
;
952 if (Urb
->Result
== EFI_USB_NOERROR
) {
955 BASE_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
956 Qtd
= EFI_LIST_CONTAINER (Entry
, EHC_QTD
, QtdList
);
958 if (FirstQtd
== NULL
) {
963 // Update the QTD for next round of transfer. Host control
964 // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/
965 // Current Offset. These fields need to be updated. DT isn't
966 // used by interrupt transfer. It uses DT in queue head.
967 // Current Offset is in Page[0], only need to reset Page[0]
968 // to initial data buffer.
971 QtdHw
->Status
= QTD_STAT_ACTIVE
;
972 QtdHw
->ErrCnt
= QTD_MAX_ERR
;
974 QtdHw
->TotalBytes
= (UINT32
)Qtd
->DataLen
;
976 // calculate physical address by offset.
978 PciAddr
= (UINTN
)Urb
->DataPhy
+ ((UINTN
)Qtd
->Data
- (UINTN
)Urb
->Data
);
979 QtdHw
->Page
[0] = EHC_LOW_32BIT (PciAddr
);
980 QtdHw
->PageHigh
[0] = EHC_HIGH_32BIT (PciAddr
);
984 // Update QH for next round of transfer. Host control only
985 // touch the fields in transfer overlay area. Only need to
986 // zero out the overlay area and set NextQtd to the first
987 // QTD. DateToggle bit is left untouched.
989 QhHw
= &Urb
->Qh
->QhHw
;
990 QhHw
->CurQtd
= QTD_LINK (0, TRUE
);
998 QhHw
->TotalBytes
= 0;
1000 for (Index
= 0; Index
< 5; Index
++) {
1001 QhHw
->Page
[Index
] = 0;
1002 QhHw
->PageHigh
[Index
] = 0;
1005 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, FirstQtd
, sizeof (EHC_QTD
));
1006 QhHw
->NextQtd
= QTD_LINK (PciAddr
, FALSE
);
1013 Interrupt transfer periodic check handler.
1015 @param Event Interrupt event.
1016 @param Context Pointer to USB2_HC_DEV.
1021 EhcMonitorAsyncRequests (
1035 OldTpl
= gBS
->RaiseTPL (EHC_TPL
);
1036 Ehc
= (USB2_HC_DEV
*)Context
;
1038 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
1039 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1042 // Check the result of URB execution. If it is still
1043 // active, check the next one.
1045 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
1052 // Flush any PCI posted write transactions from a PCI host
1053 // bridge to system memory.
1055 Status
= EhcFlushAsyncIntMap (Ehc
, Urb
);
1056 if (EFI_ERROR (Status
)) {
1057 DEBUG ((DEBUG_ERROR
, "EhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));
1061 // Allocate a buffer then copy the transferred data for user.
1062 // If failed to allocate the buffer, update the URB for next
1063 // round of transfer. Ignore the data of this round.
1067 if (Urb
->Result
== EFI_USB_NOERROR
) {
1069 // Make sure the data received from HW is no more than expected.
1071 if (Urb
->Completed
<= Urb
->DataLen
) {
1072 ProcBuf
= AllocatePool (Urb
->Completed
);
1075 if (ProcBuf
== NULL
) {
1076 EhcUpdateAsyncRequest (Ehc
, Urb
);
1080 CopyMem (ProcBuf
, Urb
->Data
, Urb
->Completed
);
1083 EhcUpdateAsyncRequest (Ehc
, Urb
);
1086 // Leave error recovery to its related device driver. A
1087 // common case of the error recovery is to re-submit the
1088 // interrupt transfer which is linked to the head of the
1089 // list. This function scans from head to tail. So the
1090 // re-submitted interrupt transfer's callback function
1091 // will not be called again in this round. Don't touch this
1092 // URB after the callback, it may have been removed by the
1095 if (Urb
->Callback
!= NULL
) {
1097 // Restore the old TPL, USB bus maybe connect device in
1098 // his callback. Some drivers may has a lower TPL restriction.
1100 gBS
->RestoreTPL (OldTpl
);
1101 (Urb
->Callback
)(ProcBuf
, Urb
->Completed
, Urb
->Context
, Urb
->Result
);
1102 OldTpl
= gBS
->RaiseTPL (EHC_TPL
);
1105 if (ProcBuf
!= NULL
) {
1110 gBS
->RestoreTPL (OldTpl
);