]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c
MdeModulePkg UhciPei: Remove redundant functions
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / XhciDxe / XhciSched.c
index f7f3409686b8c9d13484b0f2cbb571475b63809b..6a2ef4cd5d48270aee5f19364b5597aa5795a576 100644 (file)
@@ -2,7 +2,7 @@
 \r
   XHCI transfer scheduling routines.\r
 \r
-Copyright (c) 2011 - 2017, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2011 - 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
@@ -201,7 +201,7 @@ XhcFreeUrb (
   if ((Xhc == NULL) || (Urb == NULL)) {\r
     return;\r
   }\r
-  \r
+\r
   if (Urb->DataMap != NULL) {\r
     Xhc->PciIo->Unmap (Xhc->PciIo, Urb->DataMap);\r
   }\r
@@ -259,22 +259,25 @@ XhcCreateTransferTrb (
   } else {\r
     EPType  = (UINT8) ((DEVICE_CONTEXT_64 *)OutputContext)->EP[Dci-1].EPType;\r
   }\r
-  \r
-  if (Urb->Data != NULL) {\r
+\r
+  //\r
+  // No need to remap.\r
+  //\r
+  if ((Urb->Data != NULL) && (Urb->DataMap == NULL)) {\r
     if (((UINT8) (Urb->Ep.Direction)) == EfiUsbDataIn) {\r
       MapOp = EfiPciIoOperationBusMasterWrite;\r
     } else {\r
       MapOp = EfiPciIoOperationBusMasterRead;\r
     }\r
-    \r
+\r
     Len = Urb->DataLen;\r
     Status  = Xhc->PciIo->Map (Xhc->PciIo, MapOp, Urb->Data, &Len, &PhyAddr, &Map);\r
-    \r
+\r
     if (EFI_ERROR (Status) || (Len != Urb->DataLen)) {\r
       DEBUG ((EFI_D_ERROR, "XhcCreateTransferTrb: Fail to map Urb->Data.\n"));\r
       return EFI_OUT_OF_RESOURCES;\r
     }\r
-    \r
+\r
     Urb->DataPhy  = (VOID *) ((UINTN) PhyAddr);\r
     Urb->DataMap  = Map;\r
   }\r
@@ -466,7 +469,7 @@ XhcInitSched (
   VOID                  *Dcbaa;\r
   EFI_PHYSICAL_ADDRESS  DcbaaPhy;\r
   UINT64                CmdRing;\r
-  EFI_PHYSICAL_ADDRESS  CmdRingPhy; \r
+  EFI_PHYSICAL_ADDRESS  CmdRingPhy;\r
   UINTN                 Entries;\r
   UINT32                MaxScratchpadBufs;\r
   UINT64                *ScratchBuf;\r
@@ -517,7 +520,7 @@ XhcInitSched (
     ScratchEntryMap = AllocateZeroPool (sizeof (UINTN) * MaxScratchpadBufs);\r
     ASSERT (ScratchEntryMap != NULL);\r
     Xhc->ScratchEntryMap = ScratchEntryMap;\r
-    \r
+\r
     //\r
     // Allocate the buffer to record the host address for each entry\r
     //\r
@@ -530,7 +533,7 @@ XhcInitSched (
                Xhc->PciIo,\r
                EFI_SIZE_TO_PAGES (MaxScratchpadBufs * sizeof (UINT64)),\r
                Xhc->PageSize,\r
-               (VOID **) &ScratchBuf, \r
+               (VOID **) &ScratchBuf,\r
                &ScratchPhy,\r
                &Xhc->ScratchMap\r
                );\r
@@ -656,7 +659,7 @@ XhcRecoverHaltedEndpoint (
   }\r
   Dci = XhcEndpointToDci (Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction));\r
   ASSERT (Dci < 32);\r
-  \r
+\r
   DEBUG ((EFI_D_INFO, "Recovery Halted Slot = %x,Dci = %x\n", SlotId, Dci));\r
 \r
   //\r
@@ -696,6 +699,7 @@ Done:
   @param  Urb                   The urb which doesn't get completed in a specified timeout range.\r
 \r
   @retval EFI_SUCCESS           The dequeuing of the TDs is successful.\r
+  @retval EFI_ALREADY_STARTED   The Urb is finished so no deque is needed.\r
   @retval Others                Failed to stop the endpoint and dequeue the TDs.\r
 \r
 **/\r
@@ -717,13 +721,13 @@ XhcDequeueTrbFromEndpoint (
   }\r
   Dci = XhcEndpointToDci (Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction));\r
   ASSERT (Dci < 32);\r
-  \r
+\r
   DEBUG ((EFI_D_INFO, "Stop Slot = %x,Dci = %x\n", SlotId, Dci));\r
 \r
   //\r
   // 1) Send Stop endpoint command to stop xHC from executing of the TDs on the endpoint\r
   //\r
-  Status = XhcStopEndpoint(Xhc, SlotId, Dci);\r
+  Status = XhcStopEndpoint(Xhc, SlotId, Dci, Urb);\r
   if (EFI_ERROR(Status)) {\r
     DEBUG ((EFI_D_ERROR, "XhcDequeueTrbFromEndpoint: Stop Endpoint Failed, Status = %r\n", Status));\r
     goto Done;\r
@@ -732,10 +736,20 @@ XhcDequeueTrbFromEndpoint (
   //\r
   // 2)Set dequeue pointer\r
   //\r
-  Status = XhcSetTrDequeuePointer(Xhc, SlotId, Dci, Urb);\r
-  if (EFI_ERROR(Status)) {\r
-    DEBUG ((EFI_D_ERROR, "XhcDequeueTrbFromEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status));\r
-    goto Done;\r
+  if (Urb->Finished && Urb->Result == EFI_USB_NOERROR) {\r
+    //\r
+    // Return Already Started to indicate the pending URB is finished.\r
+    // This fixes BULK data loss when transfer is detected as timeout\r
+    // but finished just before stopping endpoint.\r
+    //\r
+    Status = EFI_ALREADY_STARTED;\r
+    DEBUG ((DEBUG_INFO, "XhcDequeueTrbFromEndpoint: Pending URB is finished: Length Actual/Expect = %d/%d!\n", Urb->Completed, Urb->DataLen));\r
+  } else {\r
+    Status = XhcSetTrDequeuePointer(Xhc, SlotId, Dci, Urb);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((DEBUG_ERROR, "XhcDequeueTrbFromEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status));\r
+      goto Done;\r
+    }\r
   }\r
 \r
   //\r
@@ -778,9 +792,9 @@ CreateEventRing (
   EventRing->TrbNumber        = EVENT_RING_TRB_NUMBER;\r
   EventRing->EventRingDequeue = (TRB_TEMPLATE *) EventRing->EventRingSeg0;\r
   EventRing->EventRingEnqueue = (TRB_TEMPLATE *) EventRing->EventRingSeg0;\r
-  \r
+\r
   DequeuePhy = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Buf, Size);\r
-  \r
+\r
   //\r
   // Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it to '1'\r
   // and toggling it every time the Event Ring Dequeue Pointer wraps back to the beginning of the Event Ring.\r
@@ -939,7 +953,7 @@ XhcFreeSched (
 {\r
   UINT32                  Index;\r
   UINT64                  *ScratchEntry;\r
-  \r
+\r
   if (Xhc->ScratchBuf != NULL) {\r
     ScratchEntry = Xhc->ScratchEntry;\r
     for (Index = 0; Index < Xhc->MaxScratchpadBufs; Index++) {\r
@@ -960,14 +974,14 @@ XhcFreeSched (
     UsbHcFreeMem (Xhc->MemPool, Xhc->CmdRing.RingSeg0, sizeof (TRB_TEMPLATE) * CMD_RING_TRB_NUMBER);\r
     Xhc->CmdRing.RingSeg0 = NULL;\r
   }\r
-  \r
+\r
   XhcFreeEventRing (Xhc,&Xhc->EventRing);\r
 \r
   if (Xhc->DCBAA != NULL) {\r
     UsbHcFreeMem (Xhc->MemPool, Xhc->DCBAA, (Xhc->MaxSlotsEn + 1) * sizeof(UINT64));\r
     Xhc->DCBAA = NULL;\r
   }\r
-  \r
+\r
   //\r
   // Free memory pool at last\r
   //\r
@@ -980,6 +994,7 @@ XhcFreeSched (
 /**\r
   Check if the Trb is a transaction of the URB.\r
 \r
+  @param Xhc    The XHCI Instance.\r
   @param Trb    The TRB to be checked\r
   @param Urb    The URB to be checked.\r
 \r
@@ -1117,7 +1132,7 @@ XhcCheckUrbResult (
     if ((EvtTrb->Type != TRB_TYPE_COMMAND_COMPLT_EVENT) && (EvtTrb->Type != TRB_TYPE_TRANS_EVENT)) {\r
       continue;\r
     }\r
-    \r
+\r
     //\r
     // Need convert pci device address to host address\r
     //\r
@@ -1125,19 +1140,21 @@ XhcCheckUrbResult (
     TRBPtr = (TRB_TEMPLATE *)(UINTN) UsbHcGetHostAddrForPciAddr (Xhc->MemPool, (VOID *)(UINTN) PhyAddr, sizeof (TRB_TEMPLATE));\r
 \r
     //\r
-    // Update the status of Urb according to the finished event regardless of whether\r
-    // the urb is current checked one or in the XHCI's async transfer list.\r
+    // Update the status of URB including the pending URB, the URB that is currently checked,\r
+    // and URBs in the XHCI's async interrupt transfer list.\r
     // This way is used to avoid that those completed async transfer events don't get\r
     // handled in time and are flushed by newer coming events.\r
     //\r
-    if (IsTransferRingTrb (Xhc, TRBPtr, Urb)) {\r
+    if (Xhc->PendingUrb != NULL && IsTransferRingTrb (Xhc, TRBPtr, Xhc->PendingUrb)) {\r
+      CheckedUrb = Xhc->PendingUrb;\r
+    } else if (IsTransferRingTrb (Xhc, TRBPtr, Urb)) {\r
       CheckedUrb = Urb;\r
-    } else if (IsAsyncIntTrb (Xhc, TRBPtr, &AsyncUrb)) {    \r
+    } else if (IsAsyncIntTrb (Xhc, TRBPtr, &AsyncUrb)) {\r
       CheckedUrb = AsyncUrb;\r
     } else {\r
       continue;\r
     }\r
-  \r
+\r
     switch (EvtTrb->Completecode) {\r
       case TRB_COMPLETION_STALL_ERROR:\r
         CheckedUrb->Result  |= EFI_USB_ERR_STALL;\r
@@ -1163,6 +1180,16 @@ XhcCheckUrbResult (
         DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: TRANSACTION_ERROR! Completecode = %x\n",EvtTrb->Completecode));\r
         goto EXIT;\r
 \r
+      case TRB_COMPLETION_STOPPED:\r
+      case TRB_COMPLETION_STOPPED_LENGTH_INVALID:\r
+        CheckedUrb->Result  |= EFI_USB_ERR_TIMEOUT;\r
+        CheckedUrb->Finished = TRUE;\r
+        //\r
+        // The pending URB is timeout and force stopped when stopping endpoint.\r
+        // Continue the loop to receive the Command Complete Event for stopping endpoint.\r
+        //\r
+        continue;\r
+\r
       case TRB_COMPLETION_SHORT_PACKET:\r
       case TRB_COMPLETION_SUCCESS:\r
         if (EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) {\r
@@ -1649,7 +1676,7 @@ XhcPollPortStatusChange (
         Status = XhcInitializeDeviceSlot64 (Xhc, ParentRouteChart, Port, RouteChart, Speed);\r
       }\r
     }\r
-  } \r
+  }\r
 \r
   return Status;\r
 }\r
@@ -3117,7 +3144,7 @@ XhcSetConfigCmd64 (
     if (Dci > MaxDci) {\r
       MaxDci = Dci;\r
     }\r
\r
+\r
     IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length);\r
   }\r
 \r
@@ -3155,6 +3182,7 @@ XhcSetConfigCmd64 (
   @param  Xhc                   The XHCI Instance.\r
   @param  SlotId                The slot id to be configured.\r
   @param  Dci                   The device context index of endpoint.\r
+  @param  PendingUrb            The pending URB to check completion status when stopping the end point.\r
 \r
   @retval EFI_SUCCESS           Stop endpoint successfully.\r
   @retval Others                Failed to stop endpoint.\r
@@ -3165,7 +3193,8 @@ EFIAPI
 XhcStopEndpoint (\r
   IN USB_XHCI_INSTANCE      *Xhc,\r
   IN UINT8                  SlotId,\r
-  IN UINT8                  Dci\r
+  IN UINT8                  Dci,\r
+  IN URB                    *PendingUrb  OPTIONAL\r
   )\r
 {\r
   EFI_STATUS                    Status;\r
@@ -3174,6 +3203,29 @@ XhcStopEndpoint (
 \r
   DEBUG ((EFI_D_INFO, "XhcStopEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId, Dci));\r
 \r
+  //\r
+  // When XhcCheckUrbResult waits for the Stop_Endpoint completion, it also checks\r
+  // the PendingUrb completion status, because it's possible that the PendingUrb is\r
+  // finished just before stopping the end point, but after the looping check.\r
+  //\r
+  // The PendingUrb could be passed to XhcCmdTransfer to XhcExecTransfer to XhcCheckUrbResult\r
+  // through function parameter, but That will cause every consumer of XhcCmdTransfer,\r
+  // XhcExecTransfer and XhcCheckUrbResult pass a NULL PendingUrb.\r
+  // But actually only XhcCheckUrbResult is aware of the PendingUrb.\r
+  // So we choose to save the PendingUrb into the USB_XHCI_INSTANCE and use it in XhcCheckUrbResult.\r
+  //\r
+  ASSERT (Xhc->PendingUrb == NULL);\r
+  Xhc->PendingUrb = PendingUrb;\r
+  //\r
+  // Reset the URB result from Timeout to NoError.\r
+  // The USB result will be:\r
+  //   changed to Timeout when Stop/StopInvalidLength Transfer Event is received, or\r
+  //   remain NoError when Success/ShortPacket Transfer Event is received.\r
+  //\r
+  if (PendingUrb != NULL) {\r
+    PendingUrb->Result = EFI_USB_NOERROR;\r
+  }\r
+\r
   //\r
   // Send stop endpoint command to transit Endpoint from running to stop state\r
   //\r
@@ -3192,6 +3244,8 @@ XhcStopEndpoint (
     DEBUG ((EFI_D_ERROR, "XhcStopEndpoint: Stop Endpoint Failed, Status = %r\n", Status));\r
   }\r
 \r
+  Xhc->PendingUrb = NULL;\r
+\r
   return Status;\r
 }\r
 \r
@@ -3418,7 +3472,7 @@ XhcSetInterface (
       // XHCI 4.3.6 - Setting Alternate Interfaces\r
       // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.\r
       //\r
-      Status = XhcStopEndpoint (Xhc, SlotId, Dci);\r
+      Status = XhcStopEndpoint (Xhc, SlotId, Dci, NULL);\r
       if (EFI_ERROR (Status)) {\r
         return Status;\r
       }\r
@@ -3620,7 +3674,7 @@ XhcSetInterface64 (
       // XHCI 4.3.6 - Setting Alternate Interfaces\r
       // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.\r
       //\r
-      Status = XhcStopEndpoint (Xhc, SlotId, Dci);\r
+      Status = XhcStopEndpoint (Xhc, SlotId, Dci, NULL);\r
       if (EFI_ERROR (Status)) {\r
         return Status;\r
       }\r