/** @file\r
\r
-Copyright (c) 2007, Intel Corporation\r
-All rights reserved. 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
+ EHCI transfer scheduling routines.\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
-\r
-Module Name:\r
-\r
- EhciSched.c\r
-\r
-Abstract:\r
-\r
- EHCI transfer scheduling routines\r
-\r
-Revision History\r
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
\r
\r
/**\r
- Create helper QTD/QH for the EHCI device\r
+ Create helper QTD/QH for the EHCI device.\r
\r
- @param Ehc The EHCI device\r
+ @param Ehc The EHCI device.\r
\r
- @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH\r
- @retval EFI_SUCCESS Helper QH/QTD are created\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH.\r
+ @retval EFI_SUCCESS Helper QH/QTD are created.\r
\r
**/\r
-STATIC\r
EFI_STATUS\r
EhcCreateHelpQ (\r
IN USB2_HC_DEV *Ehc\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
}\r
\r
\r
-\r
/**\r
- Initialize the schedule data structure such as frame list\r
+ Initialize the schedule data structure such as frame list.\r
\r
- @param Ehc The EHCI device to init schedule data for\r
+ @param Ehc The EHCI device to init schedule data.\r
\r
- @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data\r
- @retval EFI_SUCCESS The schedule data is initialized\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.\r
+ @retval EFI_SUCCESS The schedule data is initialized.\r
\r
**/\r
EFI_STATUS\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
\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
/**\r
Free the schedule data. It may be partially initialized.\r
\r
- @param Ehc The EHCI device\r
-\r
- @return None\r
+ @param Ehc The EHCI device.\r
\r
**/\r
VOID\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
\r
+ if (Ehc->PeriodFrameHost != NULL) {\r
+ FreePool (Ehc->PeriodFrameHost);\r
+ Ehc->PeriodFrameHost = NULL;\r
+ }\r
+}\r
\r
\r
/**\r
management: A reclamation header is always linked to\r
the AsyncListAddr, the only active QH is appended to it.\r
\r
- @param Ehc The EHCI device\r
- @param Qh The queue head to link\r
-\r
- @return None\r
+ @param Ehc The EHCI device.\r
+ @param Qh The queue head to link.\r
\r
**/\r
VOID\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
Unlink a queue head from the asynchronous schedule list.\r
- Need to synchronize with hardware\r
-\r
- @param Ehc The EHCI device\r
- @param Qh The queue head to unlink\r
+ Need to synchronize with hardware.\r
\r
- @return None\r
+ @param Ehc The EHCI device.\r
+ @param Qh The queue head to unlink.\r
\r
**/\r
VOID\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
//\r
- Status = EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIME);\r
+ Status = EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIMEOUT);\r
\r
if (EFI_ERROR (Status)) {\r
- EHC_ERROR (("EhcUnlinkQhFromAsync: Failed to synchronize with doorbell\n"));\r
+ DEBUG ((EFI_D_ERROR, "EhcUnlinkQhFromAsync: Failed to synchronize with doorbell\n"));\r
}\r
}\r
\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
- @return None\r
+ @param Ehc The EHCI device.\r
+ @param Qh The queue head to link.\r
\r
**/\r
VOID\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
\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
+ schedule frame list.\r
\r
- @return None\r
+ @param Ehc The EHCI device.\r
+ @param Qh The queue head to unlink.\r
\r
**/\r
VOID\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
}\r
\r
\r
-\r
/**\r
Check the URB's execution result and update the URB's\r
result accordingly.\r
\r
- @param Ehc The EHCI device\r
- @param Urb The URB to check result\r
+ @param Ehc The EHCI device.\r
+ @param Urb The URB to check result.\r
\r
@return Whether the result of URB transfer is finialized.\r
\r
**/\r
-STATIC\r
BOOLEAN\r
EhcCheckUrbResult (\r
IN USB2_HC_DEV *Ehc,\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
}\r
\r
if ((QtdHw->TotalBytes != 0) && (QtdHw->Pid == QTD_PID_INPUT)) {\r
- EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));\r
+ EhcDumpQh (Urb->Qh, "Short packet read", FALSE);\r
\r
//\r
// Short packet read condition. If it isn't a setup transfer,\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
- EHC_DEBUG (("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
- EHC_DEBUG (("EhcCheckUrbResult: Short packet read, continue\n"));\r
+ DEBUG ((EFI_D_VERBOSE, "EhcCheckUrbResult: Short packet read, continue\n"));\r
}\r
}\r
}\r
/**\r
Execute the transfer by polling the URB. This is a synchronous operation.\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
+ @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
- @return EFI_DEVICE_ERROR : The transfer failed due to transfer error\r
- @return EFI_TIMEOUT : The transfer failed due to time out\r
- @return EFI_SUCCESS : The transfer finished OK\r
+ @return EFI_DEVICE_ERROR The transfer failed due to transfer error.\r
+ @return EFI_TIMEOUT The transfer failed due to time out.\r
+ @return EFI_SUCCESS The transfer finished OK.\r
\r
**/\r
EFI_STATUS\r
UINTN Index;\r
UINTN Loop;\r
BOOLEAN Finished;\r
+ BOOLEAN InfiniteLoop;\r
+\r
+ Status = EFI_SUCCESS;\r
+ Loop = TimeOut * EHC_1_MILLISECOND;\r
+ Finished = FALSE;\r
+ InfiniteLoop = FALSE;\r
\r
- Status = EFI_SUCCESS;\r
- Loop = (TimeOut * EHC_STALL_1_MILLISECOND / EHC_SYNC_POLL_TIME) + 1;\r
- Finished = FALSE;\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; Index < Loop; Index++) {\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_TIME);\r
+ gBS->Stall (EHC_1_MICROSECOND);\r
}\r
\r
if (!Finished) {\r
- EHC_ERROR (("EhcExecTransfer: transfer not finished in %dms\n", TimeOut));\r
- EHC_DUMP_QH ((Urb->Qh, NULL, FALSE));\r
+ DEBUG ((EFI_D_ERROR, "EhcExecTransfer: transfer not finished in %dms\n", (UINT32)TimeOut));\r
+ EhcDumpQh (Urb->Qh, NULL, FALSE);\r
\r
Status = EFI_TIMEOUT;\r
\r
} else if (Urb->Result != EFI_USB_NOERROR) {\r
- EHC_ERROR (("EhcExecTransfer: transfer failed with %x\n", Urb->Result));\r
- EHC_DUMP_QH ((Urb->Qh, NULL, FALSE));\r
+ DEBUG ((EFI_D_ERROR, "EhcExecTransfer: transfer failed with %x\n", Urb->Result));\r
+ EhcDumpQh (Urb->Qh, NULL, FALSE);\r
\r
Status = EFI_DEVICE_ERROR;\r
}\r
\r
/**\r
Delete a single asynchronous interrupt transfer for\r
- the device and endpoint\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
+ @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_SUCCESS An asynchronous transfer is removed\r
- @retval EFI_NOT_FOUND No transfer for the device is found\r
+ @retval EFI_SUCCESS An asynchronous transfer is removed.\r
+ @retval EFI_NOT_FOUND No transfer for the device is found.\r
\r
**/\r
EFI_STATUS\r
URB *Urb;\r
EFI_USB_DATA_DIRECTION Direction;\r
\r
- Direction = ((EpNum & 0x80) ? EfiUsbDataIn : EfiUsbDataOut);\r
+ Direction = (((EpNum & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut);\r
EpNum &= 0x0F;\r
\r
EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {\r
\r
\r
/**\r
- Remove all the asynchronous interrutp transfers\r
-\r
- @param Ehc The EHCI device\r
+ Remove all the asynchronous interrutp transfers.\r
\r
- @return None\r
+ @param Ehc The EHCI device.\r
\r
**/\r
VOID\r
}\r
}\r
\r
-STATIC\r
-EFI_STATUS\r
-EhcFlushAsyncIntMap (\r
- IN USB2_HC_DEV *Ehc,\r
- IN URB *Urb\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
+{\r
+ VOID *Data;\r
+ URB *Urb;\r
\r
-Routine Description:\r
+ Data = AllocatePool (DataLen);\r
\r
- Flush data from PCI controller specific address to mapped system \r
- memory address.\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
-Arguments:\r
+/**\r
+ Flush data from PCI controller specific address to mapped system\r
+ memory address.\r
\r
- Ehc - The EHCI device\r
- Urb - The URB to unmap\r
- \r
-Returns:\r
+ @param Ehc The EHCI device.\r
+ @param Urb The URB to unmap.\r
\r
- EFI_SUCCESS - Success to flush data to mapped system memory\r
- EFI_DEVICE_ERROR - Fail to flush data to mapped system memory\r
+ @retval EFI_SUCCESS Success to flush data to mapped system memory.\r
+ @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.\r
\r
---*/\r
+**/\r
+EFI_STATUS\r
+EhcFlushAsyncIntMap (\r
+ IN USB2_HC_DEV *Ehc,\r
+ IN URB *Urb\r
+ )\r
{\r
EFI_STATUS Status;\r
EFI_PHYSICAL_ADDRESS PhyAddr;\r
} else {\r
MapOp = EfiPciIoOperationBusMasterRead;\r
}\r
- \r
+\r
Status = PciIo->Unmap (PciIo, Urb->DataMap);\r
if (EFI_ERROR (Status)) {\r
goto ON_ERROR;\r
}\r
\r
\r
-\r
/**\r
- Update the queue head for next round of asynchronous transfer\r
-\r
- @param Urb The URB to update\r
+ Update the queue head for next round of asynchronous transfer.\r
\r
- @return None\r
+ @param Ehc The EHCI device.\r
+ @param Urb The URB to update.\r
\r
**/\r
-STATIC\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
/**\r
- Interrupt transfer periodic check handler\r
-\r
- @param Event Interrupt event\r
- @param Context Pointer to USB2_HC_DEV\r
+ Interrupt transfer periodic check handler.\r
\r
- @return None\r
+ @param Event Interrupt event.\r
+ @param Context Pointer to USB2_HC_DEV.\r
\r
**/\r
VOID\r
-EhcMoniteAsyncRequests (\r
+EFIAPI\r
+EhcMonitorAsyncRequests (\r
IN EFI_EVENT Event,\r
IN VOID *Context\r
)\r
}\r
\r
//\r
- // Flush any PCI posted write transactions from a PCI host \r
+ // Flush any PCI posted write transactions from a PCI host\r
// bridge to system memory.\r
//\r
Status = EhcFlushAsyncIntMap (Ehc, Urb);\r
if (EFI_ERROR (Status)) {\r
- EHC_ERROR (("EhcMoniteAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));\r
+ DEBUG ((EFI_D_ERROR, "EhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));\r
}\r
- \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
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
}\r
\r
if (ProcBuf != NULL) {\r
- gBS->FreePool (ProcBuf);\r
+ FreePool (ProcBuf);\r
}\r
}\r
\r