3 EHCI transfer scheduling routines.
5 Copyright (c) 2007 - 2013, Intel Corporation. All rights reserved.<BR>
6 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;
78 Ehc
->ReclaimHead
= Qh
;
81 // Create a dummy QH to act as the terminator for periodical schedule
84 Ep
.Type
= EHC_INT_TRANSFER_SYNC
;
86 Qh
= EhcCreateQh (Ehc
, &Ep
);
89 return EFI_OUT_OF_RESOURCES
;
92 Qh
->QhHw
.Status
= QTD_STAT_HALTED
;
100 Initialize the schedule data structure such as frame list.
102 @param Ehc The EHCI device to init schedule data.
104 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.
105 @retval EFI_SUCCESS The schedule data is initialized.
113 EFI_PCI_IO_PROTOCOL
*PciIo
;
115 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
->PeriodFrame
= Buf
;
163 Ehc
->PeriodFrameMap
= Map
;
166 // Program the FRAMELISTBASE register with the low 32 bit addr
168 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, EHC_LOW_32BIT (PhyAddr
));
170 // Program the CTRLDSSEGMENT register with the high 32 bit addr
172 EhcWriteOpReg (Ehc
, EHC_CTRLDSSEG_OFFSET
, EHC_HIGH_32BIT (PhyAddr
));
175 // Init memory pool management then create the helper
176 // QTD/QH. If failed, previously allocated resources
177 // will be freed by EhcFreeSched
179 Ehc
->MemPool
= UsbHcInitMemPool (
181 Ehc
->Support64BitDma
,
182 EHC_HIGH_32BIT (PhyAddr
)
185 if (Ehc
->MemPool
== NULL
) {
186 Status
= EFI_OUT_OF_RESOURCES
;
190 Status
= EhcCreateHelpQ (Ehc
);
192 if (EFI_ERROR (Status
)) {
197 // Initialize the frame list entries then set the registers
199 Ehc
->PeriodFrameHost
= AllocateZeroPool (EHC_FRAME_LEN
* sizeof (UINTN
));
200 if (Ehc
->PeriodFrameHost
== NULL
) {
201 Status
= EFI_OUT_OF_RESOURCES
;
205 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
207 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
++) {
209 // Store the pci bus address of the QH in period frame list which will be accessed by pci bus master.
211 ((UINT32
*)(Ehc
->PeriodFrame
))[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
213 // Store the host address of the QH in period frame list which will be accessed by host.
215 ((UINTN
*)(Ehc
->PeriodFrameHost
))[Index
] = (UINTN
)Ehc
->PeriodOne
;
219 // Second initialize the asynchronous schedule:
220 // Only need to set the AsynListAddr register to
221 // the reclamation header
223 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
224 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, EHC_LOW_32BIT (PciAddr
));
228 if (Ehc
->PeriodOne
!= NULL
) {
229 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
230 Ehc
->PeriodOne
= NULL
;
233 if (Ehc
->ReclaimHead
!= NULL
) {
234 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
235 Ehc
->ReclaimHead
= NULL
;
238 if (Ehc
->ShortReadStop
!= NULL
) {
239 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
240 Ehc
->ShortReadStop
= NULL
;
244 PciIo
->FreeBuffer (PciIo
, Pages
, Buf
);
245 PciIo
->Unmap (PciIo
, Map
);
252 Free the schedule data. It may be partially initialized.
254 @param Ehc The EHCI device.
262 EFI_PCI_IO_PROTOCOL
*PciIo
;
264 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, 0);
265 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, 0);
267 if (Ehc
->PeriodOne
!= NULL
) {
268 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
269 Ehc
->PeriodOne
= NULL
;
272 if (Ehc
->ReclaimHead
!= NULL
) {
273 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
274 Ehc
->ReclaimHead
= NULL
;
277 if (Ehc
->ShortReadStop
!= NULL
) {
278 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
279 Ehc
->ShortReadStop
= NULL
;
282 if (Ehc
->MemPool
!= NULL
) {
283 UsbHcFreeMemPool (Ehc
->MemPool
);
287 if (Ehc
->PeriodFrame
!= NULL
) {
289 ASSERT (PciIo
!= NULL
);
291 PciIo
->Unmap (PciIo
, Ehc
->PeriodFrameMap
);
295 EFI_SIZE_TO_PAGES (EFI_PAGE_SIZE
),
299 Ehc
->PeriodFrame
= NULL
;
302 if (Ehc
->PeriodFrameHost
!= NULL
) {
303 FreePool (Ehc
->PeriodFrameHost
);
304 Ehc
->PeriodFrameHost
= NULL
;
310 Link the queue head to the asynchronous schedule list.
311 UEFI only supports one CTRL/BULK transfer at a time
312 due to its interfaces. This simplifies the AsynList
313 management: A reclamation header is always linked to
314 the AsyncListAddr, the only active QH is appended to it.
316 @param Ehc The EHCI device.
317 @param Qh The queue head to link.
327 EFI_PHYSICAL_ADDRESS PciAddr
;
330 // Append the queue head after the reclaim header, then
331 // fix the hardware visiable parts (EHCI R1.0 page 72).
332 // ReclaimHead is always linked to the EHCI's AsynListAddr.
334 Head
= Ehc
->ReclaimHead
;
336 Qh
->NextQh
= Head
->NextQh
;
339 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
->NextQh
, sizeof (EHC_QH
));
340 Qh
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
341 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Head
->NextQh
, sizeof (EHC_QH
));
342 Head
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
347 Unlink a queue head from the asynchronous schedule list.
348 Need to synchronize with hardware.
350 @param Ehc The EHCI device.
351 @param Qh The queue head to unlink.
355 EhcUnlinkQhFromAsync (
362 EFI_PHYSICAL_ADDRESS PciAddr
;
364 ASSERT (Ehc
->ReclaimHead
->NextQh
== Qh
);
367 // Remove the QH from reclamation head, then update the hardware
368 // visiable part: Only need to loopback the ReclaimHead. The Qh
369 // is pointing to ReclaimHead (which is staill in the list).
371 Head
= Ehc
->ReclaimHead
;
373 Head
->NextQh
= Qh
->NextQh
;
376 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Head
->NextQh
, sizeof (EHC_QH
));
377 Head
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
380 // Set and wait the door bell to synchronize with the hardware
382 Status
= EhcSetAndWaitDoorBell (Ehc
, EHC_GENERIC_TIMEOUT
);
384 if (EFI_ERROR (Status
)) {
385 DEBUG ((EFI_D_ERROR
, "EhcUnlinkQhFromAsync: Failed to synchronize with doorbell\n"));
391 Link a queue head for interrupt transfer to the periodic
392 schedule frame list. This code is very much the same as
395 @param Ehc The EHCI device.
396 @param Qh The queue head to link.
408 EFI_PHYSICAL_ADDRESS PciAddr
;
410 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
412 // First QH can't be NULL because we always keep PeriodOne
413 // heads on the frame list
415 ASSERT (!EHC_LINK_TERMINATED (((UINT32
*)Ehc
->PeriodFrame
)[Index
]));
416 Next
= (EHC_QH
*)((UINTN
*)Ehc
->PeriodFrameHost
)[Index
];
420 // Now, insert the queue head (Qh) into this frame:
421 // 1. Find a queue head with the same poll interval, just insert
422 // Qh after this queue head, then we are done.
424 // 2. Find the position to insert the queue head into:
425 // Previous head's interval is bigger than Qh's
426 // Next head's interval is less than Qh's
427 // Then, insert the Qh between then
429 while (Next
->Interval
> Qh
->Interval
) {
434 ASSERT (Next
!= NULL
);
437 // The entry may have been linked into the frame by early insertation.
438 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
439 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
440 // It isn't necessary to compare all the QH with the same interval to
441 // Qh. This is because if there is other QH with the same interval, Qh
442 // should has been inserted after that at Frames[0] and at Frames[0] it is
443 // impossible for (Next == Qh)
449 if (Next
->Interval
== Qh
->Interval
) {
451 // If there is a QH with the same interval, it locates at
452 // Frames[0], and we can simply insert it after this QH. We
455 ASSERT ((Index
== 0) && (Qh
->NextQh
== NULL
));
463 Qh
->QhHw
.HorizonLink
= Prev
->QhHw
.HorizonLink
;
464 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
465 Prev
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
470 // OK, find the right position, insert it in. If Qh's next
471 // link has already been set, it is in position. This is
472 // guarranted by 2^n polling interval.
474 if (Qh
->NextQh
== NULL
) {
476 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Next
, sizeof (EHC_QH
));
477 Qh
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
480 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Qh
, sizeof (EHC_QH
));
483 ((UINT32
*)Ehc
->PeriodFrame
)[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
484 ((UINTN
*)Ehc
->PeriodFrameHost
)[Index
] = (UINTN
)Qh
;
487 Prev
->QhHw
.HorizonLink
= QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
494 Unlink an interrupt queue head from the periodic
497 @param Ehc The EHCI device.
498 @param Qh The queue head to unlink.
502 EhcUnlinkQhFromPeriod (
511 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
513 // Frame link can't be NULL because we always keep PeroidOne
516 ASSERT (!EHC_LINK_TERMINATED (((UINT32
*)Ehc
->PeriodFrame
)[Index
]));
517 This
= (EHC_QH
*)((UINTN
*)Ehc
->PeriodFrameHost
)[Index
];
521 // Walk through the frame's QH list to find the
522 // queue head to remove
524 while ((This
!= NULL
) && (This
!= Qh
)) {
530 // Qh may have already been unlinked from this frame
531 // by early action. See the comments in EhcLinkQhToPeriod.
539 // Qh is the first entry in the frame
541 ((UINT32
*)Ehc
->PeriodFrame
)[Index
] = Qh
->QhHw
.HorizonLink
;
542 ((UINTN
*)Ehc
->PeriodFrameHost
)[Index
] = (UINTN
)Qh
->NextQh
;
544 Prev
->NextQh
= Qh
->NextQh
;
545 Prev
->QhHw
.HorizonLink
= Qh
->QhHw
.HorizonLink
;
552 Check the URB's execution result and update the URB's
555 @param Ehc The EHCI device.
556 @param Urb The URB to check result.
558 @return Whether the result of URB transfer is finialized.
572 EFI_PHYSICAL_ADDRESS PciAddr
;
574 ASSERT ((Ehc
!= NULL
) && (Urb
!= NULL
) && (Urb
->Qh
!= NULL
));
579 Urb
->Result
= EFI_USB_NOERROR
;
581 if (EhcIsHalt (Ehc
) || EhcIsSysError (Ehc
)) {
582 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
586 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
587 Qtd
= EFI_LIST_CONTAINER (Entry
, EHC_QTD
, QtdList
);
589 State
= (UINT8
) QtdHw
->Status
;
591 if (EHC_BIT_IS_SET (State
, QTD_STAT_HALTED
)) {
593 // EHCI will halt the queue head when met some error.
594 // If it is halted, the result of URB is finialized.
596 if ((State
& QTD_STAT_ERR_MASK
) == 0) {
597 Urb
->Result
|= EFI_USB_ERR_STALL
;
600 if (EHC_BIT_IS_SET (State
, QTD_STAT_BABBLE_ERR
)) {
601 Urb
->Result
|= EFI_USB_ERR_BABBLE
;
604 if (EHC_BIT_IS_SET (State
, QTD_STAT_BUFF_ERR
)) {
605 Urb
->Result
|= EFI_USB_ERR_BUFFER
;
608 if (EHC_BIT_IS_SET (State
, QTD_STAT_TRANS_ERR
) && (QtdHw
->ErrCnt
== 0)) {
609 Urb
->Result
|= EFI_USB_ERR_TIMEOUT
;
615 } else if (EHC_BIT_IS_SET (State
, QTD_STAT_ACTIVE
)) {
617 // The QTD is still active, no need to check furthur.
619 Urb
->Result
|= EFI_USB_ERR_NOTEXECUTE
;
626 // This QTD is finished OK or met short packet read. Update the
627 // transfer length if it isn't a setup.
629 if (QtdHw
->Pid
!= QTD_PID_SETUP
) {
630 Urb
->Completed
+= Qtd
->DataLen
- QtdHw
->TotalBytes
;
633 if ((QtdHw
->TotalBytes
!= 0) && (QtdHw
->Pid
== QTD_PID_INPUT
)) {
634 EhcDumpQh (Urb
->Qh
, "Short packet read", FALSE
);
637 // Short packet read condition. If it isn't a setup transfer,
638 // no need to check furthur: the queue head will halt at the
639 // ShortReadStop. If it is a setup transfer, need to check the
640 // Status Stage of the setup transfer to get the finial result
642 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
643 if (QtdHw
->AltNext
== QTD_LINK (PciAddr
, FALSE
)) {
644 DEBUG ((EFI_D_VERBOSE
, "EhcCheckUrbResult: Short packet read, break\n"));
650 DEBUG ((EFI_D_VERBOSE
, "EhcCheckUrbResult: Short packet read, continue\n"));
657 // Return the data toggle set by EHCI hardware, bulk and interrupt
658 // transfer will use this to initialize the next transaction. For
659 // Control transfer, it always start a new data toggle sequence for
662 // NOTICE: don't move DT update before the loop, otherwise there is
663 // a race condition that DT is wrong.
665 Urb
->DataToggle
= (UINT8
) Urb
->Qh
->QhHw
.DataToggle
;
672 Execute the transfer by polling the URB. This is a synchronous operation.
674 @param Ehc The EHCI device.
675 @param Urb The URB to execute.
676 @param TimeOut The time to wait before abort, in millisecond.
678 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.
679 @return EFI_TIMEOUT The transfer failed due to time out.
680 @return EFI_SUCCESS The transfer finished OK.
694 BOOLEAN InfiniteLoop
;
696 Status
= EFI_SUCCESS
;
697 Loop
= TimeOut
* EHC_1_MILLISECOND
;
699 InfiniteLoop
= FALSE
;
702 // According to UEFI spec section 16.2.4, If Timeout is 0, then the caller
703 // must wait for the function to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR
710 for (Index
= 0; InfiniteLoop
|| (Index
< Loop
); Index
++) {
711 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
717 gBS
->Stall (EHC_1_MICROSECOND
);
721 DEBUG ((EFI_D_ERROR
, "EhcExecTransfer: transfer not finished in %dms\n", (UINT32
)TimeOut
));
722 EhcDumpQh (Urb
->Qh
, NULL
, FALSE
);
724 Status
= EFI_TIMEOUT
;
726 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
727 DEBUG ((EFI_D_ERROR
, "EhcExecTransfer: transfer failed with %x\n", Urb
->Result
));
728 EhcDumpQh (Urb
->Qh
, NULL
, FALSE
);
730 Status
= EFI_DEVICE_ERROR
;
738 Delete a single asynchronous interrupt transfer for
739 the device and endpoint.
741 @param Ehc The EHCI device.
742 @param DevAddr The address of the target device.
743 @param EpNum The endpoint of the target.
744 @param DataToggle Return the next data toggle to use.
746 @retval EFI_SUCCESS An asynchronous transfer is removed.
747 @retval EFI_NOT_FOUND No transfer for the device is found.
751 EhciDelAsyncIntTransfer (
755 OUT UINT8
*DataToggle
761 EFI_USB_DATA_DIRECTION Direction
;
763 Direction
= (((EpNum
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
);
766 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
767 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
769 if ((Urb
->Ep
.DevAddr
== DevAddr
) && (Urb
->Ep
.EpAddr
== EpNum
) &&
770 (Urb
->Ep
.Direction
== Direction
)) {
772 // Check the URB status to retrieve the next data toggle
773 // from the associated queue head.
775 EhcCheckUrbResult (Ehc
, Urb
);
776 *DataToggle
= Urb
->DataToggle
;
778 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
779 RemoveEntryList (&Urb
->UrbList
);
781 gBS
->FreePool (Urb
->Data
);
782 EhcFreeUrb (Ehc
, Urb
);
787 return EFI_NOT_FOUND
;
792 Remove all the asynchronous interrutp transfers.
794 @param Ehc The EHCI device.
798 EhciDelAllAsyncIntTransfers (
806 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
807 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
809 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
810 RemoveEntryList (&Urb
->UrbList
);
812 gBS
->FreePool (Urb
->Data
);
813 EhcFreeUrb (Ehc
, Urb
);
819 Flush data from PCI controller specific address to mapped system
822 @param Ehc The EHCI device.
823 @param Urb The URB to unmap.
825 @retval EFI_SUCCESS Success to flush data to mapped system memory.
826 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
830 EhcFlushAsyncIntMap (
836 EFI_PHYSICAL_ADDRESS PhyAddr
;
837 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
838 EFI_PCI_IO_PROTOCOL
*PciIo
;
845 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
846 MapOp
= EfiPciIoOperationBusMasterWrite
;
848 MapOp
= EfiPciIoOperationBusMasterRead
;
851 Status
= PciIo
->Unmap (PciIo
, Urb
->DataMap
);
852 if (EFI_ERROR (Status
)) {
858 Status
= PciIo
->Map (PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
859 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
863 Urb
->DataPhy
= (VOID
*) ((UINTN
) PhyAddr
);
868 return EFI_DEVICE_ERROR
;
873 Update the queue head for next round of asynchronous transfer.
875 @param Ehc The EHCI device.
876 @param Urb The URB to update.
880 EhcUpdateAsyncRequest (
891 EFI_PHYSICAL_ADDRESS PciAddr
;
895 if (Urb
->Result
== EFI_USB_NOERROR
) {
898 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
899 Qtd
= EFI_LIST_CONTAINER (Entry
, EHC_QTD
, QtdList
);
901 if (FirstQtd
== NULL
) {
906 // Update the QTD for next round of transfer. Host control
907 // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/
908 // Current Offset. These fields need to be updated. DT isn't
909 // used by interrupt transfer. It uses DT in queue head.
910 // Current Offset is in Page[0], only need to reset Page[0]
911 // to initial data buffer.
914 QtdHw
->Status
= QTD_STAT_ACTIVE
;
915 QtdHw
->ErrCnt
= QTD_MAX_ERR
;
917 QtdHw
->TotalBytes
= (UINT32
) Qtd
->DataLen
;
919 // calculate physical address by offset.
921 PciAddr
= (UINTN
)Urb
->DataPhy
+ ((UINTN
)Qtd
->Data
- (UINTN
)Urb
->Data
);
922 QtdHw
->Page
[0] = EHC_LOW_32BIT (PciAddr
);
923 QtdHw
->PageHigh
[0]= EHC_HIGH_32BIT (PciAddr
);
927 // Update QH for next round of transfer. Host control only
928 // touch the fields in transfer overlay area. Only need to
929 // zero out the overlay area and set NextQtd to the first
930 // QTD. DateToggle bit is left untouched.
932 QhHw
= &Urb
->Qh
->QhHw
;
933 QhHw
->CurQtd
= QTD_LINK (0, TRUE
);
941 QhHw
->TotalBytes
= 0;
943 for (Index
= 0; Index
< 5; Index
++) {
944 QhHw
->Page
[Index
] = 0;
945 QhHw
->PageHigh
[Index
] = 0;
948 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, FirstQtd
, sizeof (EHC_QTD
));
949 QhHw
->NextQtd
= QTD_LINK (PciAddr
, FALSE
);
957 Interrupt transfer periodic check handler.
959 @param Event Interrupt event.
960 @param Context Pointer to USB2_HC_DEV.
965 EhcMonitorAsyncRequests (
979 OldTpl
= gBS
->RaiseTPL (EHC_TPL
);
980 Ehc
= (USB2_HC_DEV
*) Context
;
982 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
983 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
986 // Check the result of URB execution. If it is still
987 // active, check the next one.
989 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
996 // Flush any PCI posted write transactions from a PCI host
997 // bridge to system memory.
999 Status
= EhcFlushAsyncIntMap (Ehc
, Urb
);
1000 if (EFI_ERROR (Status
)) {
1001 DEBUG ((EFI_D_ERROR
, "EhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));
1005 // Allocate a buffer then copy the transferred data for user.
1006 // If failed to allocate the buffer, update the URB for next
1007 // round of transfer. Ignore the data of this round.
1011 if (Urb
->Result
== EFI_USB_NOERROR
) {
1012 ASSERT (Urb
->Completed
<= Urb
->DataLen
);
1014 ProcBuf
= AllocatePool (Urb
->Completed
);
1016 if (ProcBuf
== NULL
) {
1017 EhcUpdateAsyncRequest (Ehc
, Urb
);
1021 CopyMem (ProcBuf
, Urb
->Data
, Urb
->Completed
);
1024 EhcUpdateAsyncRequest (Ehc
, Urb
);
1027 // Leave error recovery to its related device driver. A
1028 // common case of the error recovery is to re-submit the
1029 // interrupt transfer which is linked to the head of the
1030 // list. This function scans from head to tail. So the
1031 // re-submitted interrupt transfer's callback function
1032 // will not be called again in this round. Don't touch this
1033 // URB after the callback, it may have been removed by the
1036 if (Urb
->Callback
!= NULL
) {
1038 // Restore the old TPL, USB bus maybe connect device in
1039 // his callback. Some drivers may has a lower TPL restriction.
1041 gBS
->RestoreTPL (OldTpl
);
1042 (Urb
->Callback
) (ProcBuf
, Urb
->Completed
, Urb
->Context
, Urb
->Result
);
1043 OldTpl
= gBS
->RaiseTPL (EHC_TPL
);
1046 if (ProcBuf
!= NULL
) {
1051 gBS
->RestoreTPL (OldTpl
);