]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Pci/EhciDxe/EhciSched.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / EhciDxe / EhciSched.c
index 4c2dc86f06631b9ee7e6900c9dcbf7c87b9b2660..7db637aa2cb92b97ddafd952fe737767cef7b8da 100644 (file)
@@ -1,23 +1,9 @@
 /** @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
@@ -25,15 +11,14 @@ Revision History
 \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
@@ -43,11 +28,12 @@ EhcCreateHelpQ (
   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
@@ -77,10 +63,12 @@ EhcCreateHelpQ (
     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
@@ -102,14 +90,13 @@ EhcCreateHelpQ (
 }\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
@@ -124,8 +111,8 @@ EhcInitSched (
   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
@@ -166,10 +153,17 @@ EhcInitSched (
     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
@@ -178,48 +172,80 @@ EhcInitSched (
   //\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
@@ -261,13 +287,17 @@ EhcFreeSched (
     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
@@ -277,10 +307,8 @@ EhcFreeSched (
   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
@@ -290,6 +318,7 @@ EhcLinkQhToAsync (
   )\r
 {\r
   EHC_QH                  *Head;\r
+  EFI_PHYSICAL_ADDRESS    PciAddr;\r
 \r
   //\r
   // Append the queue head after the reclaim header, then\r
@@ -301,19 +330,19 @@ EhcLinkQhToAsync (
   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
@@ -324,6 +353,7 @@ EhcUnlinkQhFromAsync (
 {\r
   EHC_QH                  *Head;\r
   EFI_STATUS              Status;\r
+  EFI_PHYSICAL_ADDRESS    PciAddr;\r
 \r
   ASSERT (Ehc->ReclaimHead->NextQh == Qh);\r
 \r
@@ -337,15 +367,16 @@ EhcUnlinkQhFromAsync (
   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
@@ -355,10 +386,8 @@ EhcUnlinkQhFromAsync (
   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
@@ -367,20 +396,18 @@ EhcLinkQhToPeriod (
   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
@@ -428,7 +455,8 @@ EhcLinkQhToPeriod (
       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
@@ -439,14 +467,18 @@ EhcLinkQhToPeriod (
     //\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
@@ -454,12 +486,10 @@ EhcLinkQhToPeriod (
 \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
@@ -468,20 +498,17 @@ EhcUnlinkQhFromPeriod (
   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
@@ -505,7 +532,8 @@ EhcUnlinkQhFromPeriod (
       //\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
@@ -514,18 +542,16 @@ EhcUnlinkQhFromPeriod (
 }\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
@@ -537,6 +563,7 @@ EhcCheckUrbResult (
   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
@@ -598,7 +625,7 @@ EhcCheckUrbResult (
       }\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
@@ -606,14 +633,15 @@ EhcCheckUrbResult (
         // 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
@@ -637,13 +665,13 @@ ON_EXIT:
 /**\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
@@ -657,30 +685,41 @@ EhcExecTransfer (
   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
@@ -691,15 +730,15 @@ EhcExecTransfer (
 \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
@@ -715,7 +754,7 @@ EhciDelAsyncIntTransfer (
   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
@@ -744,11 +783,9 @@ EhciDelAsyncIntTransfer (
 \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
@@ -771,30 +808,99 @@ EhciDelAllAsyncIntTransfers (
   }\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
@@ -811,7 +917,7 @@ Returns:
   } else {\r
     MapOp = EfiPciIoOperationBusMasterRead;\r
   }\r
-  \r
+\r
   Status = PciIo->Unmap (PciIo, Urb->DataMap);\r
   if (EFI_ERROR (Status)) {\r
     goto ON_ERROR;\r
@@ -833,18 +939,16 @@ ON_ERROR:
 }\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
@@ -854,6 +958,7 @@ EhcUpdateAsyncRequest (
   EHC_QTD                 *Qtd;\r
   QTD_HW                  *QtdHw;\r
   UINTN                   Index;\r
+  EFI_PHYSICAL_ADDRESS    PciAddr;\r
 \r
   Qtd = NULL;\r
 \r
@@ -880,7 +985,12 @@ EhcUpdateAsyncRequest (
       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
@@ -897,7 +1007,7 @@ EhcUpdateAsyncRequest (
     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
@@ -905,7 +1015,8 @@ EhcUpdateAsyncRequest (
       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
@@ -913,16 +1024,15 @@ EhcUpdateAsyncRequest (
 \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
@@ -953,14 +1063,14 @@ EhcMoniteAsyncRequests (
     }\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
@@ -969,19 +1079,22 @@ EhcMoniteAsyncRequests (
     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
@@ -1004,7 +1117,7 @@ EhcMoniteAsyncRequests (
     }\r
 \r
     if (ProcBuf != NULL) {\r
-      gBS->FreePool (ProcBuf);\r
+      FreePool (ProcBuf);\r
     }\r
   }\r
 \r