PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid\r
which is used to enable recovery function from USB Drivers.\r
\r
-Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>\r
- \r
-This program and the accompanying materials\r
-are licensed and made available under the terms and conditions\r
-of the BSD License which accompanies this distribution. The\r
-full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
+Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) Microsoft Corporation.<BR>\r
\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
\r
/**\r
Create helper QTD/QH for the EHCI device.\r
- \r
+\r
@param Ehc The EHCI device.\r
\r
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH.\r
**/\r
EFI_STATUS\r
EhcCreateHelpQ (\r
- IN PEI_USB2_HC_DEV *Ehc\r
+ IN PEI_USB2_HC_DEV *Ehc\r
)\r
{\r
- USB_ENDPOINT Ep;\r
- PEI_EHC_QH *Qh;\r
- QH_HW *QhHw;\r
- PEI_EHC_QTD *Qtd;\r
+ USB_ENDPOINT Ep;\r
+ PEI_EHC_QH *Qh;\r
+ QH_HW *QhHw;\r
+ PEI_EHC_QTD *Qtd;\r
\r
//\r
// Create an inactive Qtd to terminate the short packet read.\r
return EFI_OUT_OF_RESOURCES;\r
}\r
\r
- Qtd->QtdHw.Status = QTD_STAT_HALTED;\r
- Ehc->ShortReadStop = Qtd;\r
+ Qtd->QtdHw.Status = QTD_STAT_HALTED;\r
+ Ehc->ShortReadStop = Qtd;\r
\r
//\r
// Create a QH to act as the EHC reclamation header.\r
// Set the header to loopback to itself.\r
//\r
- Ep.DevAddr = 0;\r
- Ep.EpAddr = 1;\r
- Ep.Direction = EfiUsbDataIn;\r
- Ep.DevSpeed = EFI_USB_SPEED_HIGH;\r
- Ep.MaxPacket = 64;\r
- Ep.HubAddr = 0;\r
- Ep.HubPort = 0;\r
- Ep.Toggle = 0;\r
- Ep.Type = EHC_BULK_TRANSFER;\r
- Ep.PollRate = 1;\r
-\r
- Qh = EhcCreateQh (Ehc, &Ep);\r
+ Ep.DevAddr = 0;\r
+ Ep.EpAddr = 1;\r
+ Ep.Direction = EfiUsbDataIn;\r
+ Ep.DevSpeed = EFI_USB_SPEED_HIGH;\r
+ Ep.MaxPacket = 64;\r
+ Ep.HubAddr = 0;\r
+ Ep.HubPort = 0;\r
+ Ep.Toggle = 0;\r
+ Ep.Type = EHC_BULK_TRANSFER;\r
+ Ep.PollRate = 1;\r
+\r
+ Qh = EhcCreateQh (Ehc, &Ep);\r
\r
if (Qh == NULL) {\r
return EFI_OUT_OF_RESOURCES;\r
//\r
// Create a dummy QH to act as the terminator for periodical schedule\r
//\r
- Ep.EpAddr = 2;\r
- Ep.Type = EHC_INT_TRANSFER_SYNC;\r
+ Ep.EpAddr = 2;\r
+ Ep.Type = EHC_INT_TRANSFER_SYNC;\r
\r
- Qh = EhcCreateQh (Ehc, &Ep);\r
+ Qh = EhcCreateQh (Ehc, &Ep);\r
\r
if (Qh == NULL) {\r
return EFI_OUT_OF_RESOURCES;\r
\r
/**\r
Initialize the schedule data structure such as frame list.\r
- \r
+\r
@param Ehc The EHCI device to init schedule data for.\r
\r
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.\r
**/\r
EFI_STATUS\r
EhcInitSched (\r
- IN PEI_USB2_HC_DEV *Ehc\r
+ IN PEI_USB2_HC_DEV *Ehc\r
)\r
{\r
+ VOID *Buf;\r
EFI_PHYSICAL_ADDRESS PhyAddr;\r
VOID *Map;\r
UINTN Index;\r
UINT32 *Desc;\r
EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS PciAddr;\r
\r
//\r
// First initialize the periodical schedule data:\r
// The Frame List ocupies 4K bytes,\r
// and must be aligned on 4-Kbyte boundaries.\r
//\r
- Status = PeiServicesAllocatePages (\r
- EfiBootServicesCode,\r
+ Status = IoMmuAllocateBuffer (\r
+ Ehc->IoMmu,\r
1,\r
- &PhyAddr\r
+ &Buf,\r
+ &PhyAddr,\r
+ &Map\r
);\r
\r
- Map = NULL;\r
- Ehc->PeriodFrameHost = (VOID *)(UINTN)PhyAddr;\r
- Ehc->PeriodFrame = (VOID *)(UINTN)PhyAddr;\r
- Ehc->PeriodFrameMap = Map;\r
- Ehc->High32bitAddr = EHC_HIGH_32BIT (PhyAddr);\r
+ if (EFI_ERROR (Status) || (Buf == NULL)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Ehc->PeriodFrame = Buf;\r
+ Ehc->PeriodFrameMap = Map;\r
+ Ehc->High32bitAddr = EHC_HIGH_32BIT (PhyAddr);\r
\r
//\r
// Init memory pool management then create the helper\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
- \r
+\r
//\r
// Initialize the frame list entries then set the registers\r
//\r
- Desc = (UINT32 *) Ehc->PeriodFrame;\r
-\r
+ Desc = (UINT32 *)Ehc->PeriodFrame;\r
+ PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (PEI_EHC_QH));\r
for (Index = 0; Index < EHC_FRAME_LEN; Index++) {\r
- Desc[Index] = QH_LINK (Ehc->PeriodOne, EHC_TYPE_QH, FALSE);\r
+ Desc[Index] = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
}\r
\r
- EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (Ehc->PeriodFrame));\r
+ EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (PhyAddr));\r
\r
//\r
// Second initialize the asynchronous schedule:\r
// Only need to set the AsynListAddr register to\r
// the reclamation header\r
//\r
- EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (Ehc->ReclaimHead));\r
+ PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (PEI_EHC_QH));\r
+ EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (PciAddr));\r
return EFI_SUCCESS;\r
}\r
\r
/**\r
Free the schedule data. It may be partially initialized.\r
- \r
- @param Ehc The EHCI device. \r
+\r
+ @param Ehc The EHCI device.\r
\r
**/\r
VOID\r
EhcFreeSched (\r
- IN PEI_USB2_HC_DEV *Ehc\r
+ IN PEI_USB2_HC_DEV *Ehc\r
)\r
{\r
EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, 0);\r
EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, 0);\r
\r
if (Ehc->PeriodOne != NULL) {\r
- UsbHcFreeMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (PEI_EHC_QH));\r
+ UsbHcFreeMem (Ehc, Ehc->MemPool, Ehc->PeriodOne, sizeof (PEI_EHC_QH));\r
Ehc->PeriodOne = NULL;\r
}\r
\r
if (Ehc->ReclaimHead != NULL) {\r
- UsbHcFreeMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (PEI_EHC_QH));\r
+ UsbHcFreeMem (Ehc, Ehc->MemPool, Ehc->ReclaimHead, sizeof (PEI_EHC_QH));\r
Ehc->ReclaimHead = NULL;\r
}\r
\r
if (Ehc->ShortReadStop != NULL) {\r
- UsbHcFreeMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (PEI_EHC_QTD));\r
+ UsbHcFreeMem (Ehc, Ehc->MemPool, Ehc->ShortReadStop, sizeof (PEI_EHC_QTD));\r
Ehc->ShortReadStop = NULL;\r
}\r
\r
if (Ehc->MemPool != NULL) {\r
- UsbHcFreeMemPool (Ehc->MemPool);\r
+ UsbHcFreeMemPool (Ehc, Ehc->MemPool);\r
Ehc->MemPool = NULL;\r
}\r
\r
if (Ehc->PeriodFrame != NULL) {\r
+ IoMmuFreeBuffer (Ehc->IoMmu, 1, Ehc->PeriodFrame, Ehc->PeriodFrameMap);\r
Ehc->PeriodFrame = NULL;\r
}\r
}\r
due to its interfaces. This simplifies the AsynList\r
management: A reclamation header is always linked to\r
the AsyncListAddr, the only active QH is appended to it.\r
- \r
+\r
@param Ehc The EHCI device.\r
@param Qh The queue head to link.\r
\r
**/\r
VOID\r
EhcLinkQhToAsync (\r
- IN PEI_USB2_HC_DEV *Ehc,\r
- IN PEI_EHC_QH *Qh\r
+ IN PEI_USB2_HC_DEV *Ehc,\r
+ IN PEI_EHC_QH *Qh\r
)\r
{\r
- PEI_EHC_QH *Head;\r
+ PEI_EHC_QH *Head;\r
\r
//\r
// Append the queue head after the reclaim header, then\r
// fix the hardware visiable parts (EHCI R1.0 page 72).\r
// ReclaimHead is always linked to the EHCI's AsynListAddr.\r
//\r
- Head = Ehc->ReclaimHead;\r
+ Head = Ehc->ReclaimHead;\r
\r
- Qh->NextQh = Head->NextQh;\r
- Head->NextQh = Qh;\r
+ Qh->NextQh = Head->NextQh;\r
+ Head->NextQh = Qh;\r
\r
- Qh->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);;\r
- Head->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);\r
+ Qh->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);\r
+ Head->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);\r
}\r
\r
/**\r
Unlink a queue head from the asynchronous schedule list.\r
Need to synchronize with hardware.\r
- \r
+\r
@param Ehc The EHCI device.\r
@param Qh The queue head to unlink.\r
\r
**/\r
VOID\r
EhcUnlinkQhFromAsync (\r
- IN PEI_USB2_HC_DEV *Ehc,\r
- IN PEI_EHC_QH *Qh\r
+ IN PEI_USB2_HC_DEV *Ehc,\r
+ IN PEI_EHC_QH *Qh\r
)\r
{\r
- PEI_EHC_QH *Head;\r
+ PEI_EHC_QH *Head;\r
\r
ASSERT (Ehc->ReclaimHead->NextQh == Qh);\r
\r
// visiable part: Only need to loopback the ReclaimHead. The Qh\r
// is pointing to ReclaimHead (which is staill in the list).\r
//\r
- Head = Ehc->ReclaimHead;\r
+ Head = Ehc->ReclaimHead;\r
\r
- Head->NextQh = Qh->NextQh;\r
- Qh->NextQh = NULL;\r
+ Head->NextQh = Qh->NextQh;\r
+ Qh->NextQh = NULL;\r
\r
- Head->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);\r
+ Head->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);\r
\r
//\r
// Set and wait the door bell to synchronize with the hardware\r
//\r
EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIMEOUT);\r
- \r
- return;\r
-}\r
-\r
-/**\r
- Link a queue head for interrupt transfer to the periodic\r
- schedule frame list. This code is very much the same as\r
- that in UHCI.\r
- \r
- @param Ehc The EHCI device.\r
- @param Qh The queue head to link.\r
-\r
-**/\r
-VOID\r
-EhcLinkQhToPeriod (\r
- IN PEI_USB2_HC_DEV *Ehc,\r
- IN PEI_EHC_QH *Qh\r
- )\r
-{\r
- UINT32 *Frames;\r
- UINTN Index;\r
- PEI_EHC_QH *Prev;\r
- PEI_EHC_QH *Next;\r
-\r
- Frames = Ehc->PeriodFrame;\r
-\r
- for (Index = 0; Index < EHC_FRAME_LEN; Index += Qh->Interval) {\r
- //\r
- // First QH can't be NULL because we always keep PeriodOne\r
- // heads on the frame list\r
- //\r
- ASSERT (!EHC_LINK_TERMINATED (Frames[Index]));\r
- Next = EHC_ADDR (Ehc->High32bitAddr, Frames[Index]);\r
- Prev = NULL;\r
-\r
- //\r
- // Now, insert the queue head (Qh) into this frame:\r
- // 1. Find a queue head with the same poll interval, just insert\r
- // Qh after this queue head, then we are done.\r
- //\r
- // 2. Find the position to insert the queue head into:\r
- // Previous head's interval is bigger than Qh's\r
- // Next head's interval is less than Qh's\r
- // Then, insert the Qh between then\r
- //\r
- while (Next->Interval > Qh->Interval) {\r
- Prev = Next;\r
- Next = Next->NextQh;\r
- }\r
-\r
- ASSERT (Next != NULL);\r
-\r
- //\r
- // The entry may have been linked into the frame by early insertation.\r
- // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh\r
- // with Qh.Interval == 8 on the frame. If so, we are done with this frame.\r
- // It isn't necessary to compare all the QH with the same interval to\r
- // Qh. This is because if there is other QH with the same interval, Qh\r
- // should has been inserted after that at Frames[0] and at Frames[0] it is\r
- // impossible for (Next == Qh)\r
- //\r
- if (Next == Qh) {\r
- continue;\r
- }\r
-\r
- if (Next->Interval == Qh->Interval) {\r
- //\r
- // If there is a QH with the same interval, it locates at\r
- // Frames[0], and we can simply insert it after this QH. We\r
- // are all done.\r
- //\r
- ASSERT ((Index == 0) && (Qh->NextQh == NULL));\r
-\r
- Prev = Next;\r
- Next = Next->NextQh;\r
\r
- Qh->NextQh = Next;\r
- Prev->NextQh = Qh;\r
-\r
- Qh->QhHw.HorizonLink = Prev->QhHw.HorizonLink;\r
- Prev->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);\r
- break;\r
- }\r
- \r
- //\r
- // OK, find the right position, insert it in. If Qh's next\r
- // link has already been set, it is in position. This is\r
- // guarranted by 2^n polling interval.\r
- //\r
- if (Qh->NextQh == NULL) {\r
- Qh->NextQh = Next;\r
- Qh->QhHw.HorizonLink = QH_LINK (Next, EHC_TYPE_QH, FALSE);\r
- }\r
-\r
- if (Prev == NULL) {\r
- Frames[Index] = QH_LINK (Qh, EHC_TYPE_QH, FALSE);\r
- } else {\r
- Prev->NextQh = Qh;\r
- Prev->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);\r
- }\r
- }\r
-}\r
-\r
-/**\r
- Unlink an interrupt queue head from the periodic \r
- schedule frame list.\r
- \r
- @param Ehc The EHCI device.\r
- @param Qh The queue head to unlink.\r
-\r
-**/\r
-VOID\r
-EhcUnlinkQhFromPeriod (\r
- IN PEI_USB2_HC_DEV *Ehc,\r
- IN PEI_EHC_QH *Qh\r
- )\r
-{\r
- UINT32 *Frames;\r
- UINTN Index;\r
- PEI_EHC_QH *Prev;\r
- PEI_EHC_QH *This;\r
-\r
- Frames = Ehc->PeriodFrame;\r
-\r
- for (Index = 0; Index < EHC_FRAME_LEN; Index += Qh->Interval) {\r
- //\r
- // Frame link can't be NULL because we always keep PeroidOne\r
- // on the frame list\r
- //\r
- ASSERT (!EHC_LINK_TERMINATED (Frames[Index]));\r
- This = EHC_ADDR (Ehc->High32bitAddr, Frames[Index]);\r
- Prev = NULL;\r
-\r
- //\r
- // Walk through the frame's QH list to find the\r
- // queue head to remove\r
- //\r
- while ((This != NULL) && (This != Qh)) {\r
- Prev = This;\r
- This = This->NextQh;\r
- }\r
- \r
- //\r
- // Qh may have already been unlinked from this frame\r
- // by early action. See the comments in EhcLinkQhToPeriod.\r
- //\r
- if (This == NULL) {\r
- continue;\r
- }\r
-\r
- if (Prev == NULL) {\r
- //\r
- // Qh is the first entry in the frame\r
- //\r
- Frames[Index] = Qh->QhHw.HorizonLink;\r
- } else {\r
- Prev->NextQh = Qh->NextQh;\r
- Prev->QhHw.HorizonLink = Qh->QhHw.HorizonLink;\r
- }\r
- }\r
+ return;\r
}\r
\r
/**\r
Check the URB's execution result and update the URB's\r
- result accordingly. \r
+ result accordingly.\r
\r
@param Ehc The EHCI device.\r
@param Urb The URB to check result.\r
**/\r
BOOLEAN\r
EhcCheckUrbResult (\r
- IN PEI_USB2_HC_DEV *Ehc,\r
- IN PEI_URB *Urb\r
+ IN PEI_USB2_HC_DEV *Ehc,\r
+ IN PEI_URB *Urb\r
)\r
{\r
- EFI_LIST_ENTRY *Entry;\r
- PEI_EHC_QTD *Qtd;\r
- QTD_HW *QtdHw;\r
- UINT8 State;\r
- BOOLEAN Finished;\r
+ EFI_LIST_ENTRY *Entry;\r
+ PEI_EHC_QTD *Qtd;\r
+ QTD_HW *QtdHw;\r
+ UINT8 State;\r
+ BOOLEAN Finished;\r
\r
ASSERT ((Ehc != NULL) && (Urb != NULL) && (Urb->Qh != NULL));\r
\r
- Finished = TRUE;\r
- Urb->Completed = 0;\r
+ Finished = TRUE;\r
+ Urb->Completed = 0;\r
\r
- Urb->Result = EFI_USB_NOERROR;\r
+ Urb->Result = EFI_USB_NOERROR;\r
\r
if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {\r
Urb->Result |= EFI_USB_ERR_SYSTEM;\r
goto ON_EXIT;\r
}\r
\r
- EFI_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {\r
+ BASE_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {\r
Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);\r
QtdHw = &Qtd->QtdHw;\r
- State = (UINT8) QtdHw->Status;\r
+ State = (UINT8)QtdHw->Status;\r
\r
if (EHC_BIT_IS_SET (State, QTD_STAT_HALTED)) {\r
//\r
\r
Finished = TRUE;\r
goto ON_EXIT;\r
- \r
} else if (EHC_BIT_IS_SET (State, QTD_STAT_ACTIVE)) {\r
//\r
// The QTD is still active, no need to check furthur.\r
//\r
Urb->Result |= EFI_USB_ERR_NOTEXECUTE;\r
- \r
+\r
Finished = FALSE;\r
goto ON_EXIT;\r
-\r
} else {\r
//\r
// This QTD is finished OK or met short packet read. Update the\r
}\r
\r
if ((QtdHw->TotalBytes != 0) && (QtdHw->Pid == QTD_PID_INPUT)) {\r
- //EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));\r
+ // EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));\r
\r
//\r
// Short packet read condition. If it isn't a setup transfer,\r
// Status Stage of the setup transfer to get the finial result\r
//\r
if (QtdHw->AltNext == QTD_LINK (Ehc->ShortReadStop, FALSE)) {\r
- \r
Finished = TRUE;\r
goto ON_EXIT;\r
}\r
// NOTICE: don't move DT update before the loop, otherwise there is\r
// a race condition that DT is wrong.\r
//\r
- Urb->DataToggle = (UINT8) Urb->Qh->QhHw.DataToggle;\r
+ Urb->DataToggle = (UINT8)Urb->Qh->QhHw.DataToggle;\r
\r
return Finished;\r
}\r
\r
/**\r
Execute the transfer by polling the URB. This is a synchronous operation.\r
- \r
+\r
@param Ehc The EHCI device.\r
@param Urb The URB to execute.\r
@param TimeOut The time to wait before abort, in millisecond.\r
**/\r
EFI_STATUS\r
EhcExecTransfer (\r
- IN PEI_USB2_HC_DEV *Ehc,\r
- IN PEI_URB *Urb,\r
- IN UINTN TimeOut\r
+ IN PEI_USB2_HC_DEV *Ehc,\r
+ IN PEI_URB *Urb,\r
+ IN UINTN TimeOut\r
)\r
{\r
- EFI_STATUS Status;\r
- UINTN Index;\r
- UINTN Loop;\r
- BOOLEAN Finished;\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+ UINTN Loop;\r
+ BOOLEAN Finished;\r
+ BOOLEAN InfiniteLoop;\r
\r
- Status = EFI_SUCCESS;\r
- Loop = (TimeOut * EHC_1_MILLISECOND / EHC_SYNC_POLL_INTERVAL) + 1;\r
- Finished = FALSE;\r
+ Status = EFI_SUCCESS;\r
+ Loop = TimeOut * EHC_1_MILLISECOND;\r
+ Finished = FALSE;\r
+ InfiniteLoop = FALSE;\r
\r
- for (Index = 0; Index < Loop; Index++) {\r
+ //\r
+ // If Timeout is 0, then the caller must wait for the function to be completed\r
+ // until EFI_SUCCESS or EFI_DEVICE_ERROR is returned.\r
+ //\r
+ if (TimeOut == 0) {\r
+ InfiniteLoop = TRUE;\r
+ }\r
+\r
+ for (Index = 0; InfiniteLoop || (Index < Loop); Index++) {\r
Finished = EhcCheckUrbResult (Ehc, Urb);\r
\r
if (Finished) {\r
break;\r
}\r
\r
- MicroSecondDelay (EHC_SYNC_POLL_INTERVAL);\r
+ MicroSecondDelay (EHC_1_MICROSECOND);\r
}\r
\r
if (!Finished) {\r
\r
return Status;\r
}\r
-\r
-/**\r
- Delete a single asynchronous interrupt transfer for\r
- the device and endpoint.\r
- \r
- @param Ehc The EHCI device.\r
- @param DevAddr The address of the target device.\r
- @param EpNum The endpoint of the target.\r
- @param DataToggle Return the next data toggle to use.\r
-\r
- @retval EFI_NOT_FOUND No transfer for the device is found.\r
- @retval EFI_SUCCESS An asynchronous transfer is removed.\r
-\r
-**/\r
-EFI_STATUS\r
-EhciDelAsyncIntTransfer (\r
- IN PEI_USB2_HC_DEV *Ehc,\r
- IN UINT8 DevAddr,\r
- IN UINT8 EpNum, \r
- OUT UINT8 *DataToggle\r
- )\r
-{\r
- EFI_LIST_ENTRY *Entry;\r
- EFI_LIST_ENTRY *Next;\r
- PEI_URB *Urb;\r
- EFI_USB_DATA_DIRECTION Direction;\r
-\r
- Direction = (((EpNum & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut);\r
- EpNum &= 0x0F;\r
-\r
- EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {\r
- Urb = EFI_LIST_CONTAINER (Entry, PEI_URB, UrbList);\r
-\r
- if ((Urb->Ep.DevAddr == DevAddr) && (Urb->Ep.EpAddr == EpNum) && \r
- (Urb->Ep.Direction == Direction)) {\r
- //\r
- // Check the URB status to retrieve the next data toggle\r
- // from the associated queue head.\r
- //\r
- EhcCheckUrbResult (Ehc, Urb);\r
- *DataToggle = Urb->DataToggle;\r
-\r
- EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);\r
- RemoveEntryList (&Urb->UrbList);\r
-\r
- EhcFreeUrb (Ehc, Urb);\r
- return EFI_SUCCESS;\r
- }\r
- }\r
-\r
- return EFI_NOT_FOUND;\r
-}\r
-\r
-/**\r
- Remove all the asynchronous interrutp transfers.\r
- \r
- @param Ehc The EHCI device.\r
-\r
-**/\r
-VOID\r
-EhciDelAllAsyncIntTransfers (\r
- IN PEI_USB2_HC_DEV *Ehc\r
- )\r
-{\r
- EFI_LIST_ENTRY *Entry;\r
- EFI_LIST_ENTRY *Next;\r
- PEI_URB *Urb;\r
-\r
- EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {\r
- Urb = EFI_LIST_CONTAINER (Entry, PEI_URB, UrbList);\r
-\r
- EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);\r
- RemoveEntryList (&Urb->UrbList);\r
-\r
- EhcFreeUrb (Ehc, Urb);\r
- }\r
-}\r
-\r
-/**\r
- Flush data from PCI controller specific address to mapped system \r
- memory address.\r
- \r
- @param Ehc The EHCI device.\r
- @param Urb The URB to unmap.\r
-\r
- @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.\r
- @retval EFI_SUCCESS Success to flush data to mapped system memory.\r
-\r
-**/\r
-EFI_STATUS\r
-EhcFlushAsyncIntMap (\r
- IN PEI_USB2_HC_DEV *Ehc,\r
- IN PEI_URB *Urb\r
- )\r
-{\r
- EFI_PHYSICAL_ADDRESS PhyAddr;\r
-\r
- Urb->DataMap = NULL;\r
- PhyAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) Urb->Data;\r
- Urb->DataPhy = (VOID *) ((UINTN) PhyAddr);\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Update the queue head for next round of asynchronous transfer.\r
-\r
- @param Urb The URB to update.\r
-\r
-**/\r
-VOID\r
-EhcUpdateAsyncRequest (\r
- IN PEI_URB *Urb\r
- )\r
-{\r
- EFI_LIST_ENTRY *Entry;\r
- PEI_EHC_QTD *FirstQtd;\r
- QH_HW *QhHw;\r
- PEI_EHC_QTD *Qtd;\r
- QTD_HW *QtdHw;\r
- UINTN Index;\r
-\r
- Qtd = NULL;\r
-\r
- if (Urb->Result == EFI_USB_NOERROR) {\r
- FirstQtd = NULL;\r
-\r
- EFI_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {\r
- Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);\r
-\r
- if (FirstQtd == NULL) {\r
- FirstQtd = Qtd;\r
- }\r
- \r
- //\r
- // Update the QTD for next round of transfer. Host control\r
- // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/\r
- // Current Offset. These fields need to be updated. DT isn't\r
- // used by interrupt transfer. It uses DT in queue head.\r
- // Current Offset is in Page[0], only need to reset Page[0]\r
- // to initial data buffer.\r
- //\r
- QtdHw = &Qtd->QtdHw;\r
- QtdHw->Status = QTD_STAT_ACTIVE;\r
- QtdHw->ErrCnt = QTD_MAX_ERR;\r
- QtdHw->CurPage = 0;\r
- QtdHw->TotalBytes = (UINT32) Qtd->DataLen;\r
- QtdHw->Page[0] = EHC_LOW_32BIT (Qtd->Data);\r
- }\r
- \r
- //\r
- // Update QH for next round of transfer. Host control only\r
- // touch the fields in transfer overlay area. Only need to\r
- // zero out the overlay area and set NextQtd to the first\r
- // QTD. DateToggle bit is left untouched.\r
- //\r
- QhHw = &Urb->Qh->QhHw;\r
- QhHw->CurQtd = QTD_LINK (0, TRUE);\r
- QhHw->AltQtd = 0;\r
-\r
- QhHw->Status = 0;\r
- QhHw->Pid = 0;\r
- QhHw->ErrCnt = 0;\r
- QhHw->CurPage = 0;\r
- QhHw->Ioc = 0;\r
- QhHw->TotalBytes = 0;\r
-\r
- for (Index = 0; Index < 5; Index++) {\r
- QhHw->Page[Index] = 0;\r
- QhHw->PageHigh[Index] = 0;\r
- }\r
-\r
- QhHw->NextQtd = QTD_LINK (FirstQtd, FALSE);\r
- }\r
-\r
- return ;\r
-}\r
-\r
-/**\r
- Remove all the asynchronous interrutp transfers.\r
- \r
- @param Event Interrupt event.\r
- @param Context Pointer to PEI_USB2_HC_DEV.\r
-\r
-**/\r
-VOID\r
-EFIAPI\r
-EhcMoniteAsyncRequests (\r
- IN EFI_EVENT Event,\r
- IN VOID *Context\r
- )\r
-{\r
- PEI_USB2_HC_DEV *Ehc;\r
- EFI_LIST_ENTRY *Entry;\r
- EFI_LIST_ENTRY *Next;\r
- BOOLEAN Finished;\r
- UINT8 *ProcBuf;\r
- PEI_URB *Urb;\r
- UINTN PageNumber;\r
-\r
- Ehc = (PEI_USB2_HC_DEV *) Context;\r
-\r
- EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {\r
- Urb = EFI_LIST_CONTAINER (Entry, PEI_URB, UrbList);\r
-\r
- //\r
- // Check the result of URB execution. If it is still\r
- // active, check the next one.\r
- //\r
- Finished = EhcCheckUrbResult (Ehc, Urb);\r
-\r
- if (!Finished) {\r
- continue;\r
- }\r
-\r
- //\r
- // Flush any PCI posted write transactions from a PCI host \r
- // bridge to system memory.\r
- //\r
- EhcFlushAsyncIntMap (Ehc, Urb);\r
- \r
- //\r
- // Allocate a buffer then copy the transferred data for user.\r
- // If failed to allocate the buffer, update the URB for next\r
- // round of transfer. Ignore the data of this round.\r
- //\r
- ProcBuf = NULL;\r
-\r
- if (Urb->Result == EFI_USB_NOERROR) {\r
- ASSERT (Urb->Completed <= Urb->DataLen);\r
- PageNumber = Urb->Completed/PAGESIZE +1;\r
- PeiServicesAllocatePages (\r
- EfiBootServicesCode,\r
- PageNumber,\r
- (EFI_PHYSICAL_ADDRESS *)ProcBuf\r
- );\r
- if (ProcBuf == NULL) {\r
- EhcUpdateAsyncRequest (Urb);\r
- continue;\r
- }\r
-\r
- CopyMem (ProcBuf, Urb->Data, Urb->Completed);\r
- }\r
-\r
- EhcUpdateAsyncRequest (Urb);\r
-\r
- //\r
- // Leave error recovery to its related device driver. A\r
- // common case of the error recovery is to re-submit the\r
- // interrupt transfer which is linked to the head of the\r
- // list. This function scans from head to tail. So the\r
- // re-submitted interrupt transfer's callback function\r
- // will not be called again in this round. Don't touch this\r
- // URB after the callback, it may have been removed by the\r
- // callback.\r
- //\r
- if (Urb->Callback != NULL) {\r
- (Urb->Callback) (ProcBuf, Urb->Completed, Urb->Context, Urb->Result);\r
- }\r
-\r
- if (ProcBuf != NULL) {\r
- }\r
- }\r
-}\r