3 Copyright (c) 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 EHCI transfer scheduling routines
28 Create helper QTD/QH for the EHCI device
30 @param Ehc The EHCI device
32 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH
33 @retval EFI_SUCCESS Helper QH/QTD are created
48 // Create an inactive Qtd to terminate the short packet read.
50 Qtd
= EhcCreateQtd (Ehc
, NULL
, 0, QTD_PID_INPUT
, 0, 64);
53 return EFI_OUT_OF_RESOURCES
;
56 Qtd
->QtdHw
.Status
= QTD_STAT_HALTED
;
57 Ehc
->ShortReadStop
= Qtd
;
60 // Create a QH to act as the EHC reclamation header.
61 // Set the header to loopback to itself.
65 Ep
.Direction
= EfiUsbDataIn
;
66 Ep
.DevSpeed
= EFI_USB_SPEED_HIGH
;
71 Ep
.Type
= EHC_BULK_TRANSFER
;
74 Qh
= EhcCreateQh (Ehc
, &Ep
);
77 return EFI_OUT_OF_RESOURCES
;
81 QhHw
->HorizonLink
= QH_LINK (QhHw
, EHC_TYPE_QH
, FALSE
);
82 QhHw
->Status
= QTD_STAT_HALTED
;
83 QhHw
->ReclaimHead
= 1;
84 Ehc
->ReclaimHead
= Qh
;
87 // Create a dummy QH to act as the terminator for periodical schedule
90 Ep
.Type
= EHC_INT_TRANSFER_SYNC
;
92 Qh
= EhcCreateQh (Ehc
, &Ep
);
95 return EFI_OUT_OF_RESOURCES
;
98 Qh
->QhHw
.Status
= QTD_STAT_HALTED
;
107 Initialize the schedule data structure such as frame list
109 @param Ehc The EHCI device to init schedule data for
111 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data
112 @retval EFI_SUCCESS The schedule data is initialized
120 EFI_PCI_IO_PROTOCOL
*PciIo
;
122 EFI_PHYSICAL_ADDRESS PhyAddr
;
131 // First initialize the periodical schedule data:
132 // 1. Allocate and map the memory for the frame list
133 // 2. Create the help QTD/QH
134 // 3. Initialize the frame entries
135 // 4. Set the frame list register
140 Pages
= EFI_SIZE_TO_PAGES (Bytes
);
142 Status
= PciIo
->AllocateBuffer (
151 if (EFI_ERROR (Status
)) {
152 return EFI_OUT_OF_RESOURCES
;
155 Status
= PciIo
->Map (
157 EfiPciIoOperationBusMasterCommonBuffer
,
164 if (EFI_ERROR (Status
) || (Bytes
!= 4096)) {
165 PciIo
->FreeBuffer (PciIo
, Pages
, Buf
);
166 return EFI_OUT_OF_RESOURCES
;
169 Ehc
->PeriodFrameHost
= Buf
;
170 Ehc
->PeriodFrame
= (VOID
*) ((UINTN
) PhyAddr
);
171 Ehc
->PeriodFrameMap
= Map
;
172 Ehc
->High32bitAddr
= 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_BIT_IS_SET (Ehc
->HcCapParams
, HCCP_64BIT
),
185 if (Ehc
->MemPool
== NULL
) {
186 return EFI_OUT_OF_RESOURCES
;
189 Status
= EhcCreateHelpQ (Ehc
);
191 if (EFI_ERROR (Status
)) {
196 // Initialize the frame list entries then set the registers
198 Desc
= (UINT32
*) Ehc
->PeriodFrame
;
200 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
++) {
201 Desc
[Index
] = QH_LINK (Ehc
->PeriodOne
, EHC_TYPE_QH
, FALSE
);
204 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, EHC_LOW_32BIT (Ehc
->PeriodFrame
));
207 // Second initialize the asynchronous schedule:
208 // Only need to set the AsynListAddr register to
209 // the reclamation header
211 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, EHC_LOW_32BIT (Ehc
->ReclaimHead
));
218 Free the schedule data. It may be partially initialized.
220 @param Ehc The EHCI device
230 EFI_PCI_IO_PROTOCOL
*PciIo
;
232 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, 0);
233 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, 0);
235 if (Ehc
->PeriodOne
!= NULL
) {
236 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (EHC_QH
));
237 Ehc
->PeriodOne
= NULL
;
240 if (Ehc
->ReclaimHead
!= NULL
) {
241 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (EHC_QH
));
242 Ehc
->ReclaimHead
= NULL
;
245 if (Ehc
->ShortReadStop
!= NULL
) {
246 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (EHC_QTD
));
247 Ehc
->ShortReadStop
= NULL
;
250 if (Ehc
->MemPool
!= NULL
) {
251 UsbHcFreeMemPool (Ehc
->MemPool
);
255 if (Ehc
->PeriodFrame
!= NULL
) {
257 ASSERT (PciIo
!= NULL
);
259 PciIo
->Unmap (PciIo
, Ehc
->PeriodFrameMap
);
263 EFI_SIZE_TO_PAGES (EFI_PAGE_SIZE
),
267 Ehc
->PeriodFrame
= NULL
;
274 Link the queue head to the asynchronous schedule list.
275 UEFI only supports one CTRL/BULK transfer at a time
276 due to its interfaces. This simplifies the AsynList
277 management: A reclamation header is always linked to
278 the AsyncListAddr, the only active QH is appended to it.
280 @param Ehc The EHCI device
281 @param Qh The queue head to link
295 // Append the queue head after the reclaim header, then
296 // fix the hardware visiable parts (EHCI R1.0 page 72).
297 // ReclaimHead is always linked to the EHCI's AsynListAddr.
299 Head
= Ehc
->ReclaimHead
;
301 Qh
->NextQh
= Head
->NextQh
;
304 Qh
->QhHw
.HorizonLink
= QH_LINK (Head
, EHC_TYPE_QH
, FALSE
);;
305 Head
->QhHw
.HorizonLink
= QH_LINK (Qh
, EHC_TYPE_QH
, FALSE
);
310 Unlink a queue head from the asynchronous schedule list.
311 Need to synchronize with hardware
313 @param Ehc The EHCI device
314 @param Qh The queue head to unlink
320 EhcUnlinkQhFromAsync (
328 ASSERT (Ehc
->ReclaimHead
->NextQh
== Qh
);
331 // Remove the QH from reclamation head, then update the hardware
332 // visiable part: Only need to loopback the ReclaimHead. The Qh
333 // is pointing to ReclaimHead (which is staill in the list).
335 Head
= Ehc
->ReclaimHead
;
337 Head
->NextQh
= Qh
->NextQh
;
340 Head
->QhHw
.HorizonLink
= QH_LINK (Head
, EHC_TYPE_QH
, FALSE
);
343 // Set and wait the door bell to synchronize with the hardware
345 Status
= EhcSetAndWaitDoorBell (Ehc
, EHC_GENERIC_TIME
);
347 if (EFI_ERROR (Status
)) {
348 EHC_ERROR (("EhcUnlinkQhFromAsync: Failed to synchronize with doorbell\n"));
354 Link a queue head for interrupt transfer to the periodic
355 schedule frame list. This code is very much the same as
358 @param Ehc The EHCI device
359 @param Qh The queue head to link
375 Frames
= Ehc
->PeriodFrame
;
377 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
379 // First QH can't be NULL because we always keep PeriodOne
380 // heads on the frame list
382 ASSERT (!EHC_LINK_TERMINATED (Frames
[Index
]));
383 Next
= EHC_ADDR (Ehc
->High32bitAddr
, Frames
[Index
]);
387 // Now, insert the queue head (Qh) into this frame:
388 // 1. Find a queue head with the same poll interval, just insert
389 // Qh after this queue head, then we are done.
391 // 2. Find the position to insert the queue head into:
392 // Previous head's interval is bigger than Qh's
393 // Next head's interval is less than Qh's
394 // Then, insert the Qh between then
396 while (Next
->Interval
> Qh
->Interval
) {
401 ASSERT (Next
!= NULL
);
404 // The entry may have been linked into the frame by early insertation.
405 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
406 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
407 // It isn't necessary to compare all the QH with the same interval to
408 // Qh. This is because if there is other QH with the same interval, Qh
409 // should has been inserted after that at Frames[0] and at Frames[0] it is
410 // impossible for (Next == Qh)
416 if (Next
->Interval
== Qh
->Interval
) {
418 // If there is a QH with the same interval, it locates at
419 // Frames[0], and we can simply insert it after this QH. We
422 ASSERT ((Index
== 0) && (Qh
->NextQh
== NULL
));
430 Qh
->QhHw
.HorizonLink
= Prev
->QhHw
.HorizonLink
;
431 Prev
->QhHw
.HorizonLink
= QH_LINK (Qh
, EHC_TYPE_QH
, FALSE
);
436 // OK, find the right position, insert it in. If Qh's next
437 // link has already been set, it is in position. This is
438 // guarranted by 2^n polling interval.
440 if (Qh
->NextQh
== NULL
) {
442 Qh
->QhHw
.HorizonLink
= QH_LINK (Next
, EHC_TYPE_QH
, FALSE
);
446 Frames
[Index
] = QH_LINK (Qh
, EHC_TYPE_QH
, FALSE
);
449 Prev
->QhHw
.HorizonLink
= QH_LINK (Qh
, EHC_TYPE_QH
, FALSE
);
456 Unlink an interrupt queue head from the periodic
459 @param Ehc The EHCI device
460 @param Qh The queue head to unlink
466 EhcUnlinkQhFromPeriod (
476 Frames
= Ehc
->PeriodFrame
;
478 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
480 // Frame link can't be NULL because we always keep PeroidOne
483 ASSERT (!EHC_LINK_TERMINATED (Frames
[Index
]));
484 This
= EHC_ADDR (Ehc
->High32bitAddr
, Frames
[Index
]);
488 // Walk through the frame's QH list to find the
489 // queue head to remove
491 while ((This
!= NULL
) && (This
!= Qh
)) {
497 // Qh may have already been unlinked from this frame
498 // by early action. See the comments in EhcLinkQhToPeriod.
506 // Qh is the first entry in the frame
508 Frames
[Index
] = Qh
->QhHw
.HorizonLink
;
510 Prev
->NextQh
= Qh
->NextQh
;
511 Prev
->QhHw
.HorizonLink
= Qh
->QhHw
.HorizonLink
;
519 Check the URB's execution result and update the URB's
522 @param Ehc The EHCI device
523 @param Urb The URB to check result
525 @return Whether the result of URB transfer is finialized.
541 ASSERT ((Ehc
!= NULL
) && (Urb
!= NULL
) && (Urb
->Qh
!= NULL
));
546 Urb
->Result
= EFI_USB_NOERROR
;
548 if (EhcIsHalt (Ehc
) || EhcIsSysError (Ehc
)) {
549 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
553 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
554 Qtd
= EFI_LIST_CONTAINER (Entry
, EHC_QTD
, QtdList
);
556 State
= (UINT8
) QtdHw
->Status
;
558 if (EHC_BIT_IS_SET (State
, QTD_STAT_HALTED
)) {
560 // EHCI will halt the queue head when met some error.
561 // If it is halted, the result of URB is finialized.
563 if ((State
& QTD_STAT_ERR_MASK
) == 0) {
564 Urb
->Result
|= EFI_USB_ERR_STALL
;
567 if (EHC_BIT_IS_SET (State
, QTD_STAT_BABBLE_ERR
)) {
568 Urb
->Result
|= EFI_USB_ERR_BABBLE
;
571 if (EHC_BIT_IS_SET (State
, QTD_STAT_BUFF_ERR
)) {
572 Urb
->Result
|= EFI_USB_ERR_BUFFER
;
575 if (EHC_BIT_IS_SET (State
, QTD_STAT_TRANS_ERR
) && (QtdHw
->ErrCnt
== 0)) {
576 Urb
->Result
|= EFI_USB_ERR_TIMEOUT
;
582 } else if (EHC_BIT_IS_SET (State
, QTD_STAT_ACTIVE
)) {
584 // The QTD is still active, no need to check furthur.
586 Urb
->Result
|= EFI_USB_ERR_NOTEXECUTE
;
593 // This QTD is finished OK or met short packet read. Update the
594 // transfer length if it isn't a setup.
596 if (QtdHw
->Pid
!= QTD_PID_SETUP
) {
597 Urb
->Completed
+= Qtd
->DataLen
- QtdHw
->TotalBytes
;
600 if ((QtdHw
->TotalBytes
!= 0) && (QtdHw
->Pid
== QTD_PID_INPUT
)) {
601 EHC_DUMP_QH ((Urb
->Qh
, "Short packet read", FALSE
));
604 // Short packet read condition. If it isn't a setup transfer,
605 // no need to check furthur: the queue head will halt at the
606 // ShortReadStop. If it is a setup transfer, need to check the
607 // Status Stage of the setup transfer to get the finial result
609 if (QtdHw
->AltNext
== QTD_LINK (Ehc
->ShortReadStop
, FALSE
)) {
610 EHC_DEBUG (("EhcCheckUrbResult: Short packet read, break\n"));
616 EHC_DEBUG (("EhcCheckUrbResult: Short packet read, continue\n"));
623 // Return the data toggle set by EHCI hardware, bulk and interrupt
624 // transfer will use this to initialize the next transaction. For
625 // Control transfer, it always start a new data toggle sequence for
628 // NOTICE: don't move DT update before the loop, otherwise there is
629 // a race condition that DT is wrong.
631 Urb
->DataToggle
= (UINT8
) Urb
->Qh
->QhHw
.DataToggle
;
638 Execute the transfer by polling the URB. This is a synchronous operation.
640 @param Ehc The EHCI device
641 @param Urb The URB to execute
642 @param TimeOut The time to wait before abort, in millisecond.
644 @return EFI_DEVICE_ERROR : The transfer failed due to transfer error
645 @return EFI_TIMEOUT : The transfer failed due to time out
646 @return EFI_SUCCESS : The transfer finished OK
661 Status
= EFI_SUCCESS
;
662 Loop
= (TimeOut
* EHC_STALL_1_MILLISECOND
/ EHC_SYNC_POLL_TIME
) + 1;
665 for (Index
= 0; Index
< Loop
; Index
++) {
666 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
672 gBS
->Stall (EHC_SYNC_POLL_TIME
);
676 EHC_ERROR (("EhcExecTransfer: transfer not finished in %dms\n", TimeOut
));
677 EHC_DUMP_QH ((Urb
->Qh
, NULL
, FALSE
));
679 Status
= EFI_TIMEOUT
;
681 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
682 EHC_ERROR (("EhcExecTransfer: transfer failed with %x\n", Urb
->Result
));
683 EHC_DUMP_QH ((Urb
->Qh
, NULL
, FALSE
));
685 Status
= EFI_DEVICE_ERROR
;
693 Delete a single asynchronous interrupt transfer for
694 the device and endpoint
696 @param Ehc The EHCI device
697 @param DevAddr The address of the target device
698 @param EpNum The endpoint of the target
699 @param DataToggle Return the next data toggle to use
701 @retval EFI_SUCCESS An asynchronous transfer is removed
702 @retval EFI_NOT_FOUND No transfer for the device is found
706 EhciDelAsyncIntTransfer (
710 OUT UINT8
*DataToggle
716 EFI_USB_DATA_DIRECTION Direction
;
718 Direction
= ((EpNum
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
721 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
722 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
724 if ((Urb
->Ep
.DevAddr
== DevAddr
) && (Urb
->Ep
.EpAddr
== EpNum
) &&
725 (Urb
->Ep
.Direction
== Direction
)) {
727 // Check the URB status to retrieve the next data toggle
728 // from the associated queue head.
730 EhcCheckUrbResult (Ehc
, Urb
);
731 *DataToggle
= Urb
->DataToggle
;
733 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
734 RemoveEntryList (&Urb
->UrbList
);
736 gBS
->FreePool (Urb
->Data
);
737 EhcFreeUrb (Ehc
, Urb
);
742 return EFI_NOT_FOUND
;
747 Remove all the asynchronous interrutp transfers
749 @param Ehc The EHCI device
755 EhciDelAllAsyncIntTransfers (
763 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
764 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
766 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
767 RemoveEntryList (&Urb
->UrbList
);
769 gBS
->FreePool (Urb
->Data
);
770 EhcFreeUrb (Ehc
, Urb
);
777 Update the queue head for next round of asynchronous transfer
779 @param Urb The URB to update
786 EhcUpdateAsyncRequest (
799 if (Urb
->Result
== EFI_USB_NOERROR
) {
802 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
803 Qtd
= EFI_LIST_CONTAINER (Entry
, EHC_QTD
, QtdList
);
805 if (FirstQtd
== NULL
) {
810 // Update the QTD for next round of transfer. Host control
811 // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/
812 // Current Offset. These fields need to be updated. DT isn't
813 // used by interrupt transfer. It uses DT in queue head.
814 // Current Offset is in Page[0], only need to reset Page[0]
815 // to initial data buffer.
818 QtdHw
->Status
= QTD_STAT_ACTIVE
;
819 QtdHw
->ErrCnt
= QTD_MAX_ERR
;
821 QtdHw
->TotalBytes
= (UINT32
) Qtd
->DataLen
;
822 QtdHw
->Page
[0] = EHC_LOW_32BIT (Qtd
->Data
);
826 // Update QH for next round of transfer. Host control only
827 // touch the fields in transfer overlay area. Only need to
828 // zero out the overlay area and set NextQtd to the first
829 // QTD. DateToggle bit is left untouched.
831 QhHw
= &Urb
->Qh
->QhHw
;
832 QhHw
->CurQtd
= QTD_LINK (0, TRUE
);
840 QhHw
->TotalBytes
= 0;
842 for (Index
= 0; Index
< 5; Index
++) {
843 QhHw
->Page
[Index
] = 0;
844 QhHw
->PageHigh
[Index
] = 0;
847 QhHw
->NextQtd
= QTD_LINK (FirstQtd
, FALSE
);
855 Interrupt transfer periodic check handler
857 @param Event Interrupt event
858 @param Context Pointer to USB2_HC_DEV
864 EhcMoniteAsyncRequests (
877 OldTpl
= gBS
->RaiseTPL (EHC_TPL
);
878 Ehc
= (USB2_HC_DEV
*) Context
;
880 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
881 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
884 // Check the result of URB execution. If it is still
885 // active, check the next one.
887 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
894 // Allocate a buffer then copy the transferred data for user.
895 // If failed to allocate the buffer, update the URB for next
896 // round of transfer. Ignore the data of this round.
900 if (Urb
->Result
== EFI_USB_NOERROR
) {
901 ASSERT (Urb
->Completed
<= Urb
->DataLen
);
903 ProcBuf
= AllocatePool (Urb
->Completed
);
905 if (ProcBuf
== NULL
) {
906 EhcUpdateAsyncRequest (Urb
);
910 CopyMem (ProcBuf
, Urb
->Data
, Urb
->Completed
);
913 EhcUpdateAsyncRequest (Urb
);
916 // Leave error recovery to its related device driver. A
917 // common case of the error recovery is to re-submit the
918 // interrupt transfer which is linked to the head of the
919 // list. This function scans from head to tail. So the
920 // re-submitted interrupt transfer's callback function
921 // will not be called again in this round. Don't touch this
922 // URB after the callback, it may have been removed by the
925 if (Urb
->Callback
!= NULL
) {
927 // Restore the old TPL, USB bus maybe connect device in
928 // his callback. Some drivers may has a lower TPL restriction.
930 gBS
->RestoreTPL (OldTpl
);
931 (Urb
->Callback
) (ProcBuf
, Urb
->Completed
, Urb
->Context
, Urb
->Result
);
932 OldTpl
= gBS
->RaiseTPL (EHC_TPL
);
935 if (ProcBuf
!= NULL
) {
936 gBS
->FreePool (ProcBuf
);
940 gBS
->RestoreTPL (OldTpl
);