\r
EHCI transfer scheduling routines.\r
\r
-Copyright (c) 2007 - 2009, Intel Corporation\r
-All rights reserved. This program and the accompanying materials\r
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
are licensed and made available under the terms and conditions of the BSD License\r
which accompanies this distribution. The full text of the license may be found at\r
http://opensource.org/licenses/bsd-license.php\r
EHC_QH *Qh;\r
QH_HW *QhHw;\r
EHC_QTD *Qtd;\r
+ EFI_PHYSICAL_ADDRESS PciAddr;\r
\r
//\r
// Create an inactive Qtd to terminate the short packet read.\r
//\r
- Qtd = EhcCreateQtd (Ehc, NULL, 0, QTD_PID_INPUT, 0, 64);\r
+ Qtd = EhcCreateQtd (Ehc, NULL, NULL, 0, QTD_PID_INPUT, 0, 64);\r
\r
if (Qtd == NULL) {\r
return EFI_OUT_OF_RESOURCES;\r
return EFI_OUT_OF_RESOURCES;\r
}\r
\r
+ PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));\r
QhHw = &Qh->QhHw;\r
- QhHw->HorizonLink = QH_LINK (QhHw, EHC_TYPE_QH, FALSE);\r
+ QhHw->HorizonLink = QH_LINK (PciAddr + OFFSET_OF(EHC_QH, QhHw), EHC_TYPE_QH, FALSE);\r
QhHw->Status = QTD_STAT_HALTED;\r
QhHw->ReclaimHead = 1;\r
+ Qh->NextQh = Qh;\r
Ehc->ReclaimHead = Qh;\r
\r
//\r
UINTN Pages;\r
UINTN Bytes;\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
return EFI_OUT_OF_RESOURCES;\r
}\r
\r
- Ehc->PeriodFrameHost = Buf;\r
- Ehc->PeriodFrame = (VOID *) ((UINTN) PhyAddr);\r
+ Ehc->PeriodFrame = Buf;\r
Ehc->PeriodFrameMap = Map;\r
- Ehc->High32bitAddr = EHC_HIGH_32BIT (PhyAddr);\r
+\r
+ //\r
+ // Program the FRAMELISTBASE register with the low 32 bit addr\r
+ //\r
+ EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (PhyAddr));\r
+ //\r
+ // Program the CTRLDSSEGMENT register with the high 32 bit addr\r
+ //\r
+ EhcWriteOpReg (Ehc, EHC_CTRLDSSEG_OFFSET, EHC_HIGH_32BIT (PhyAddr));\r
\r
//\r
// Init memory pool management then create the helper\r
//\r
Ehc->MemPool = UsbHcInitMemPool (\r
PciIo,\r
- EHC_BIT_IS_SET (Ehc->HcCapParams, HCCP_64BIT),\r
- Ehc->High32bitAddr\r
+ Ehc->Support64BitDma,\r
+ EHC_HIGH_32BIT (PhyAddr)\r
);\r
\r
if (Ehc->MemPool == NULL) {\r
- return EFI_OUT_OF_RESOURCES;\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ErrorExit1;\r
}\r
\r
Status = EhcCreateHelpQ (Ehc);\r
\r
if (EFI_ERROR (Status)) {\r
- return Status;\r
+ goto ErrorExit;\r
}\r
\r
//\r
// Initialize the frame list entries then set the registers\r
//\r
- Desc = (UINT32 *) Ehc->PeriodFrame;\r
+ Ehc->PeriodFrameHost = AllocateZeroPool (EHC_FRAME_LEN * sizeof (UINTN));\r
+ if (Ehc->PeriodFrameHost == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ErrorExit;\r
+ }\r
+\r
+ PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (EHC_QH));\r
\r
for (Index = 0; Index < EHC_FRAME_LEN; Index++) {\r
- Desc[Index] = QH_LINK (Ehc->PeriodOne, EHC_TYPE_QH, FALSE);\r
+ //\r
+ // Store the pci bus address of the QH in period frame list which will be accessed by pci bus master.\r
+ //\r
+ ((UINT32 *)(Ehc->PeriodFrame))[Index] = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
+ //\r
+ // Store the host address of the QH in period frame list which will be accessed by host.\r
+ //\r
+ ((UINTN *)(Ehc->PeriodFrameHost))[Index] = (UINTN)Ehc->PeriodOne;\r
}\r
\r
- EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (Ehc->PeriodFrame));\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 (EHC_QH));\r
+ EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (PciAddr));\r
return EFI_SUCCESS;\r
+\r
+ErrorExit:\r
+ if (Ehc->PeriodOne != NULL) {\r
+ UsbHcFreeMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (EHC_QH));\r
+ Ehc->PeriodOne = NULL;\r
+ }\r
+\r
+ if (Ehc->ReclaimHead != NULL) {\r
+ UsbHcFreeMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (EHC_QH));\r
+ Ehc->ReclaimHead = NULL;\r
+ }\r
+\r
+ if (Ehc->ShortReadStop != NULL) {\r
+ UsbHcFreeMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (EHC_QTD));\r
+ Ehc->ShortReadStop = NULL;\r
+ }\r
+\r
+ErrorExit1:\r
+ PciIo->FreeBuffer (PciIo, Pages, Buf);\r
+ PciIo->Unmap (PciIo, Map);\r
+\r
+ return Status;\r
}\r
\r
\r
PciIo->FreeBuffer (\r
PciIo,\r
EFI_SIZE_TO_PAGES (EFI_PAGE_SIZE),\r
- Ehc->PeriodFrameHost\r
+ Ehc->PeriodFrame\r
);\r
\r
Ehc->PeriodFrame = NULL;\r
}\r
+\r
+ if (Ehc->PeriodFrameHost != NULL) {\r
+ FreePool (Ehc->PeriodFrameHost);\r
+ Ehc->PeriodFrameHost = NULL;\r
+ }\r
}\r
\r
\r
)\r
{\r
EHC_QH *Head;\r
+ EFI_PHYSICAL_ADDRESS PciAddr;\r
\r
//\r
// Append the queue head after the reclaim header, then\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
+ PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh->NextQh, sizeof (EHC_QH));\r
+ Qh->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
+ PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Head->NextQh, sizeof (EHC_QH));\r
+ Head->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
}\r
\r
\r
{\r
EHC_QH *Head;\r
EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS PciAddr;\r
\r
ASSERT (Ehc->ReclaimHead->NextQh == Qh);\r
\r
Head->NextQh = Qh->NextQh;\r
Qh->NextQh = NULL;\r
\r
- Head->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);\r
+ PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Head->NextQh, sizeof (EHC_QH));\r
+ Head->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
\r
//\r
// Set and wait the door bell to synchronize with the hardware\r
IN EHC_QH *Qh\r
)\r
{\r
- UINT32 *Frames;\r
UINTN Index;\r
EHC_QH *Prev;\r
EHC_QH *Next;\r
-\r
- Frames = Ehc->PeriodFrame;\r
+ EFI_PHYSICAL_ADDRESS PciAddr;\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
+ ASSERT (!EHC_LINK_TERMINATED (((UINT32*)Ehc->PeriodFrame)[Index]));\r
+ Next = (EHC_QH*)((UINTN*)Ehc->PeriodFrameHost)[Index];\r
Prev = NULL;\r
\r
//\r
Prev->NextQh = Qh;\r
\r
Qh->QhHw.HorizonLink = Prev->QhHw.HorizonLink;\r
- Prev->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);\r
+ PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));\r
+ Prev->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
break;\r
}\r
\r
//\r
if (Qh->NextQh == NULL) {\r
Qh->NextQh = Next;\r
- Qh->QhHw.HorizonLink = QH_LINK (Next, EHC_TYPE_QH, FALSE);\r
+ PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Next, sizeof (EHC_QH));\r
+ Qh->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
}\r
\r
+ PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));\r
+\r
if (Prev == NULL) {\r
- Frames[Index] = QH_LINK (Qh, EHC_TYPE_QH, FALSE);\r
+ ((UINT32*)Ehc->PeriodFrame)[Index] = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
+ ((UINTN*)Ehc->PeriodFrameHost)[Index] = (UINTN)Qh;\r
} else {\r
Prev->NextQh = Qh;\r
- Prev->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);\r
+ Prev->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
}\r
}\r
}\r
IN EHC_QH *Qh\r
)\r
{\r
- UINT32 *Frames;\r
UINTN Index;\r
EHC_QH *Prev;\r
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
+ ASSERT (!EHC_LINK_TERMINATED (((UINT32*)Ehc->PeriodFrame)[Index]));\r
+ This = (EHC_QH*)((UINTN*)Ehc->PeriodFrameHost)[Index];\r
Prev = NULL;\r
\r
//\r
//\r
// Qh is the first entry in the frame\r
//\r
- Frames[Index] = Qh->QhHw.HorizonLink;\r
+ ((UINT32*)Ehc->PeriodFrame)[Index] = Qh->QhHw.HorizonLink;\r
+ ((UINTN*)Ehc->PeriodFrameHost)[Index] = (UINTN)Qh->NextQh;\r
} else {\r
Prev->NextQh = Qh->NextQh;\r
Prev->QhHw.HorizonLink = Qh->QhHw.HorizonLink;\r
QTD_HW *QtdHw;\r
UINT8 State;\r
BOOLEAN Finished;\r
+ EFI_PHYSICAL_ADDRESS PciAddr;\r
\r
ASSERT ((Ehc != NULL) && (Urb != NULL) && (Urb->Qh != NULL));\r
\r
// ShortReadStop. If it is a setup transfer, need to check the\r
// Status Stage of the setup transfer to get the finial result\r
//\r
- if (QtdHw->AltNext == QTD_LINK (Ehc->ShortReadStop, FALSE)) {\r
- DEBUG ((EFI_D_INFO, "EhcCheckUrbResult: Short packet read, break\n"));\r
+ PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (EHC_QTD));\r
+ if (QtdHw->AltNext == QTD_LINK (PciAddr, FALSE)) {\r
+ DEBUG ((EFI_D_VERBOSE, "EhcCheckUrbResult: Short packet read, break\n"));\r
\r
Finished = TRUE;\r
goto ON_EXIT;\r
}\r
\r
- DEBUG ((EFI_D_INFO, "EhcCheckUrbResult: Short packet read, continue\n"));\r
+ DEBUG ((EFI_D_VERBOSE, "EhcCheckUrbResult: Short packet read, continue\n"));\r
}\r
}\r
}\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
+ // According to UEFI spec section 16.2.4, If Timeout is 0, then the caller\r
+ // must wait for the function to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR\r
+ // 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
- gBS->Stall (EHC_SYNC_POLL_INTERVAL);\r
+ gBS->Stall (EHC_1_MICROSECOND);\r
}\r
\r
if (!Finished) {\r
}\r
}\r
\r
+/**\r
+ Insert a single asynchronous interrupt transfer for\r
+ the device and endpoint.\r
+\r
+ @param Ehc The EHCI device.\r
+ @param DevAddr The device address.\r
+ @param EpAddr Endpoint addrress & its direction.\r
+ @param DevSpeed The device speed.\r
+ @param Toggle Initial data toggle to use.\r
+ @param MaxPacket The max packet length of the endpoint.\r
+ @param Hub The transaction translator to use.\r
+ @param DataLen The length of data buffer.\r
+ @param Callback The function to call when data is transferred.\r
+ @param Context The context to the callback.\r
+ @param Interval The interval for interrupt transfer.\r
+\r
+ @return Created URB or NULL.\r
+\r
+**/\r
+URB *\r
+EhciInsertAsyncIntTransfer (\r
+ IN USB2_HC_DEV *Ehc,\r
+ IN UINT8 DevAddr,\r
+ IN UINT8 EpAddr,\r
+ IN UINT8 DevSpeed,\r
+ IN UINT8 Toggle,\r
+ IN UINTN MaxPacket,\r
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Hub,\r
+ IN UINTN DataLen,\r
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,\r
+ IN VOID *Context,\r
+ IN UINTN Interval\r
+ )\r
+{\r
+ VOID *Data;\r
+ URB *Urb;\r
+\r
+ Data = AllocatePool (DataLen);\r
+\r
+ if (Data == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "%a: failed to allocate buffer\n", __FUNCTION__));\r
+ return NULL;\r
+ }\r
+\r
+ Urb = EhcCreateUrb (\r
+ Ehc,\r
+ DevAddr,\r
+ EpAddr,\r
+ DevSpeed,\r
+ Toggle,\r
+ MaxPacket,\r
+ Hub,\r
+ EHC_INT_TRANSFER_ASYNC,\r
+ NULL,\r
+ Data,\r
+ DataLen,\r
+ Callback,\r
+ Context,\r
+ Interval\r
+ );\r
+\r
+ if (Urb == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "%a: failed to create URB\n", __FUNCTION__));\r
+ gBS->FreePool (Data);\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // New asynchronous transfer must inserted to the head.\r
+ // Check the comments in EhcMoniteAsyncRequests\r
+ //\r
+ EhcLinkQhToPeriod (Ehc, Urb->Qh);\r
+ InsertHeadList (&Ehc->AsyncIntTransfers, &Urb->UrbList);\r
+\r
+ return Urb;\r
+}\r
\r
/**\r
Flush data from PCI controller specific address to mapped system\r
/**\r
Update the queue head for next round of asynchronous transfer.\r
\r
+ @param Ehc The EHCI device.\r
@param Urb The URB to update.\r
\r
**/\r
VOID\r
EhcUpdateAsyncRequest (\r
+ IN USB2_HC_DEV *Ehc,\r
IN URB *Urb\r
)\r
{\r
EHC_QTD *Qtd;\r
QTD_HW *QtdHw;\r
UINTN Index;\r
+ EFI_PHYSICAL_ADDRESS PciAddr;\r
\r
Qtd = NULL;\r
\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
+ // calculate physical address by offset.\r
+ //\r
+ PciAddr = (UINTN)Urb->DataPhy + ((UINTN)Qtd->Data - (UINTN)Urb->Data);\r
+ QtdHw->Page[0] = EHC_LOW_32BIT (PciAddr);\r
+ QtdHw->PageHigh[0]= EHC_HIGH_32BIT (PciAddr);\r
}\r
\r
//\r
QhHw->Pid = 0;\r
QhHw->ErrCnt = 0;\r
QhHw->CurPage = 0;\r
- QhHw->IOC = 0;\r
+ QhHw->Ioc = 0;\r
QhHw->TotalBytes = 0;\r
\r
for (Index = 0; Index < 5; Index++) {\r
QhHw->PageHigh[Index] = 0;\r
}\r
\r
- QhHw->NextQtd = QTD_LINK (FirstQtd, FALSE);\r
+ PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, FirstQtd, sizeof (EHC_QTD));\r
+ QhHw->NextQtd = QTD_LINK (PciAddr, FALSE);\r
}\r
\r
return ;\r
\r
**/\r
VOID\r
+EFIAPI\r
EhcMonitorAsyncRequests (\r
IN EFI_EVENT Event,\r
IN VOID *Context\r
ProcBuf = NULL;\r
\r
if (Urb->Result == EFI_USB_NOERROR) {\r
- ASSERT (Urb->Completed <= Urb->DataLen);\r
-\r
- ProcBuf = AllocatePool (Urb->Completed);\r
+ //\r
+ // Make sure the data received from HW is no more than expected.\r
+ //\r
+ if (Urb->Completed <= Urb->DataLen) {\r
+ ProcBuf = AllocatePool (Urb->Completed);\r
+ }\r
\r
if (ProcBuf == NULL) {\r
- EhcUpdateAsyncRequest (Urb);\r
+ EhcUpdateAsyncRequest (Ehc, Urb);\r
continue;\r
}\r
\r
CopyMem (ProcBuf, Urb->Data, Urb->Completed);\r
}\r
\r
- EhcUpdateAsyncRequest (Urb);\r
+ EhcUpdateAsyncRequest (Ehc, Urb);\r
\r
//\r
// Leave error recovery to its related device driver. A\r