X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=MdeModulePkg%2FBus%2FPci%2FXhciDxe%2FXhciSched.c;h=f8174d8d03306429e4b95ac7d949004b2a3ea34a;hp=58a2f984a9450a127e065f95fa8ec35580ef15c7;hb=49be9c3c20cea7477b9c9e390c9f97735760e216;hpb=3719c2aa879bcbcf3f0ab1f710e81fe2dbc19d9a diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c b/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c index 58a2f984a9..f8174d8d03 100644 --- a/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c +++ b/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c @@ -604,8 +604,6 @@ XhcInitSched ( XhcWriteOpReg (Xhc, XHC_CRCR_OFFSET, XHC_LOW_32BIT(CmdRingPhy)); XhcWriteOpReg (Xhc, XHC_CRCR_OFFSET + 4, XHC_HIGH_32BIT (CmdRingPhy)); - DEBUG ((EFI_D_INFO, "XhcInitSched:XHC_CRCR=0x%x\n", Xhc->CmdRing.RingSeg0)); - // // Disable the 'interrupter enable' bit in USB_CMD // and clear IE & IP bit in all Interrupter X Management Registers. @@ -620,7 +618,10 @@ XhcInitSched ( // Allocate EventRing for Cmd, Ctrl, Bulk, Interrupt, AsynInterrupt transfer // CreateEventRing (Xhc, &Xhc->EventRing); - DEBUG ((EFI_D_INFO, "XhcInitSched:XHC_EVENTRING=0x%x\n", Xhc->EventRing.EventRingSeg0)); + DEBUG ((DEBUG_INFO, "XhcInitSched: Created CMD ring [%p~%p) EVENT ring [%p~%p)\n", + Xhc->CmdRing.RingSeg0, (UINTN)Xhc->CmdRing.RingSeg0 + sizeof (TRB_TEMPLATE) * CMD_RING_TRB_NUMBER, + Xhc->EventRing.EventRingSeg0, (UINTN)Xhc->EventRing.EventRingSeg0 + sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER + )); } /** @@ -695,6 +696,7 @@ Done: @param Urb The urb which doesn't get completed in a specified timeout range. @retval EFI_SUCCESS The dequeuing of the TDs is successful. + @retval EFI_ALREADY_STARTED The Urb is finished so no deque is needed. @retval Others Failed to stop the endpoint and dequeue the TDs. **/ @@ -722,7 +724,7 @@ XhcDequeueTrbFromEndpoint ( // // 1) Send Stop endpoint command to stop xHC from executing of the TDs on the endpoint // - Status = XhcStopEndpoint(Xhc, SlotId, Dci); + Status = XhcStopEndpoint(Xhc, SlotId, Dci, Urb); if (EFI_ERROR(Status)) { DEBUG ((EFI_D_ERROR, "XhcDequeueTrbFromEndpoint: Stop Endpoint Failed, Status = %r\n", Status)); goto Done; @@ -731,10 +733,20 @@ XhcDequeueTrbFromEndpoint ( // // 2)Set dequeue pointer // - Status = XhcSetTrDequeuePointer(Xhc, SlotId, Dci, Urb); - if (EFI_ERROR(Status)) { - DEBUG ((EFI_D_ERROR, "XhcDequeueTrbFromEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status)); - goto Done; + if (Urb->Finished && Urb->Result == EFI_USB_NOERROR) { + // + // Return Already Started to indicate the pending URB is finished. + // This fixes BULK data loss when transfer is detected as timeout + // but finished just before stopping endpoint. + // + Status = EFI_ALREADY_STARTED; + DEBUG ((DEBUG_INFO, "XhcDequeueTrbFromEndpoint: Pending URB is finished: Length Actual/Expect = %d/%d!\n", Urb->Completed, Urb->DataLen)); + } else { + Status = XhcSetTrDequeuePointer(Xhc, SlotId, Dci, Urb); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "XhcDequeueTrbFromEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status)); + goto Done; + } } // @@ -977,45 +989,42 @@ XhcFreeSched ( } /** - Check if the Trb is a transaction of the URBs in XHCI's asynchronous transfer list. + Check if the Trb is a transaction of the URB. - @param Xhc The XHCI Instance. - @param Trb The TRB to be checked. - @param Urb The pointer to the matched Urb. + @param Trb The TRB to be checked + @param Urb The URB to be checked. - @retval TRUE The Trb is matched with a transaction of the URBs in the async list. - @retval FALSE The Trb is not matched with any URBs in the async list. + @retval TRUE It is a transaction of the URB. + @retval FALSE It is not any transaction of the URB. **/ BOOLEAN -IsAsyncIntTrb ( +IsTransferRingTrb ( IN USB_XHCI_INSTANCE *Xhc, IN TRB_TEMPLATE *Trb, - OUT URB **Urb + IN URB *Urb ) { - LIST_ENTRY *Entry; - LIST_ENTRY *Next; - TRB_TEMPLATE *CheckedTrb; - URB *CheckedUrb; - UINTN Index; + LINK_TRB *LinkTrb; + TRB_TEMPLATE *CheckedTrb; + UINTN Index; + EFI_PHYSICAL_ADDRESS PhyAddr; - EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Xhc->AsyncIntTransfers) { - CheckedUrb = EFI_LIST_CONTAINER (Entry, URB, UrbList); - CheckedTrb = CheckedUrb->TrbStart; - for (Index = 0; Index < CheckedUrb->TrbNum; Index++) { - if (Trb == CheckedTrb) { - *Urb = CheckedUrb; - return TRUE; - } - CheckedTrb++; - // - // If the checked TRB is the link TRB at the end of the transfer ring, - // recircle it to the head of the ring. - // - if (CheckedTrb->Type == TRB_TYPE_LINK) { - CheckedTrb = (TRB_TEMPLATE*) CheckedUrb->Ring->RingSeg0; - } + CheckedTrb = Urb->TrbStart; + for (Index = 0; Index < Urb->TrbNum; Index++) { + if (Trb == CheckedTrb) { + return TRUE; + } + CheckedTrb++; + // + // If the checked TRB is the link TRB at the end of the transfer ring, + // recircle it to the head of the ring. + // + if (CheckedTrb->Type == TRB_TYPE_LINK) { + LinkTrb = (LINK_TRB *) CheckedTrb; + PhyAddr = (EFI_PHYSICAL_ADDRESS)(LinkTrb->PtrLo | LShiftU64 ((UINT64) LinkTrb->PtrHi, 32)); + CheckedTrb = (TRB_TEMPLATE *)(UINTN) UsbHcGetHostAddrForPciAddr (Xhc->MemPool, (VOID *)(UINTN) PhyAddr, sizeof (TRB_TEMPLATE)); + ASSERT (CheckedTrb == Urb->Ring->RingSeg0); } } @@ -1023,38 +1032,39 @@ IsAsyncIntTrb ( } /** - Check if the Trb is a transaction of the URB. + Check if the Trb is a transaction of the URBs in XHCI's asynchronous transfer list. - @param Trb The TRB to be checked - @param Urb The transfer ring to be checked. + @param Xhc The XHCI Instance. + @param Trb The TRB to be checked. + @param Urb The pointer to the matched Urb. - @retval TRUE It is a transaction of the URB. - @retval FALSE It is not any transaction of the URB. + @retval TRUE The Trb is matched with a transaction of the URBs in the async list. + @retval FALSE The Trb is not matched with any URBs in the async list. **/ BOOLEAN -IsTransferRingTrb ( +IsAsyncIntTrb ( + IN USB_XHCI_INSTANCE *Xhc, IN TRB_TEMPLATE *Trb, - IN URB *Urb + OUT URB **Urb ) { - TRB_TEMPLATE *CheckedTrb; - UINTN Index; - - CheckedTrb = Urb->Ring->RingSeg0; - - ASSERT (Urb->Ring->TrbNumber == CMD_RING_TRB_NUMBER || Urb->Ring->TrbNumber == TR_RING_TRB_NUMBER); + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + URB *CheckedUrb; - for (Index = 0; Index < Urb->Ring->TrbNumber; Index++) { - if (Trb == CheckedTrb) { + EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Xhc->AsyncIntTransfers) { + CheckedUrb = EFI_LIST_CONTAINER (Entry, URB, UrbList); + if (IsTransferRingTrb (Xhc, Trb, CheckedUrb)) { + *Urb = CheckedUrb; return TRUE; } - CheckedTrb++; } return FALSE; } + /** Check the URB's execution result and update the URB's result accordingly. @@ -1126,12 +1136,14 @@ XhcCheckUrbResult ( TRBPtr = (TRB_TEMPLATE *)(UINTN) UsbHcGetHostAddrForPciAddr (Xhc->MemPool, (VOID *)(UINTN) PhyAddr, sizeof (TRB_TEMPLATE)); // - // Update the status of Urb according to the finished event regardless of whether - // the urb is current checked one or in the XHCI's async transfer list. + // Update the status of URB including the pending URB, the URB that is currently checked, + // and URBs in the XHCI's async interrupt transfer list. // This way is used to avoid that those completed async transfer events don't get // handled in time and are flushed by newer coming events. // - if (IsTransferRingTrb (TRBPtr, Urb)) { + if (Xhc->PendingUrb != NULL && IsTransferRingTrb (Xhc, TRBPtr, Xhc->PendingUrb)) { + CheckedUrb = Xhc->PendingUrb; + } else if (IsTransferRingTrb (Xhc, TRBPtr, Urb)) { CheckedUrb = Urb; } else if (IsAsyncIntTrb (Xhc, TRBPtr, &AsyncUrb)) { CheckedUrb = AsyncUrb; @@ -1164,6 +1176,16 @@ XhcCheckUrbResult ( DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: TRANSACTION_ERROR! Completecode = %x\n",EvtTrb->Completecode)); goto EXIT; + case TRB_COMPLETION_STOPPED: + case TRB_COMPLETION_STOPPED_LENGTH_INVALID: + CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT; + CheckedUrb->Finished = TRUE; + // + // The pending URB is timeout and force stopped when stopping endpoint. + // Continue the loop to receive the Command Complete Event for stopping endpoint. + // + continue; + case TRB_COMPLETION_SHORT_PACKET: case TRB_COMPLETION_SUCCESS: if (EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) { @@ -1319,6 +1341,7 @@ XhciDelAsyncIntTransfer ( LIST_ENTRY *Next; URB *Urb; EFI_USB_DATA_DIRECTION Direction; + EFI_STATUS Status; Direction = ((EpNum & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut; EpNum &= 0x0F; @@ -1330,6 +1353,15 @@ XhciDelAsyncIntTransfer ( if ((Urb->Ep.BusAddr == BusAddr) && (Urb->Ep.EpAddr == EpNum) && (Urb->Ep.Direction == Direction)) { + // + // Device doesn't finish the IntTransfer until real data comes + // So the TRB should be removed as well. + // + Status = XhcDequeueTrbFromEndpoint (Xhc, Urb); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhciDelAsyncIntTransfer: XhcDequeueTrbFromEndpoint failed\n")); + } + RemoveEntryList (&Urb->UrbList); FreePool (Urb->Data); XhcFreeUrb (Xhc, Urb); @@ -1354,9 +1386,20 @@ XhciDelAllAsyncIntTransfers ( LIST_ENTRY *Entry; LIST_ENTRY *Next; URB *Urb; + EFI_STATUS Status; EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Xhc->AsyncIntTransfers) { Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList); + + // + // Device doesn't finish the IntTransfer until real data comes + // So the TRB should be removed as well. + // + Status = XhcDequeueTrbFromEndpoint (Xhc, Urb); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhciDelAllAsyncIntTransfers: XhcDequeueTrbFromEndpoint failed\n")); + } + RemoveEntryList (&Urb->UrbList); FreePool (Urb->Data); XhcFreeUrb (Xhc, Urb); @@ -2649,6 +2692,11 @@ XhcInitializeEndpointContext ( EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING)); Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); + DEBUG ((DEBUG_INFO, "Endpoint[%x]: Created BULK ring [%p~%p)\n", + EpDesc->EndpointAddress, + EndpointTransferRing->RingSeg0, + (UINTN) EndpointTransferRing->RingSeg0 + TR_RING_TRB_NUMBER * sizeof (TRB_TEMPLATE) + )); } break; @@ -2717,6 +2765,11 @@ XhcInitializeEndpointContext ( EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING)); Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); + DEBUG ((DEBUG_INFO, "Endpoint[%x]: Created INT ring [%p~%p)\n", + EpDesc->EndpointAddress, + EndpointTransferRing->RingSeg0, + (UINTN) EndpointTransferRing->RingSeg0 + TR_RING_TRB_NUMBER * sizeof (TRB_TEMPLATE) + )); } break; @@ -2831,6 +2884,11 @@ XhcInitializeEndpointContext64 ( EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING)); Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); + DEBUG ((DEBUG_INFO, "Endpoint64[%x]: Created BULK ring [%p~%p)\n", + EpDesc->EndpointAddress, + EndpointTransferRing->RingSeg0, + (UINTN) EndpointTransferRing->RingSeg0 + TR_RING_TRB_NUMBER * sizeof (TRB_TEMPLATE) + )); } break; @@ -2899,6 +2957,11 @@ XhcInitializeEndpointContext64 ( EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING)); Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); + DEBUG ((DEBUG_INFO, "Endpoint64[%x]: Created INT ring [%p~%p)\n", + EpDesc->EndpointAddress, + EndpointTransferRing->RingSeg0, + (UINTN) EndpointTransferRing->RingSeg0 + TR_RING_TRB_NUMBER * sizeof (TRB_TEMPLATE) + )); } break; @@ -3115,6 +3178,7 @@ XhcSetConfigCmd64 ( @param Xhc The XHCI Instance. @param SlotId The slot id to be configured. @param Dci The device context index of endpoint. + @param PendingUrb The pending URB to check completion status when stopping the end point. @retval EFI_SUCCESS Stop endpoint successfully. @retval Others Failed to stop endpoint. @@ -3125,7 +3189,8 @@ EFIAPI XhcStopEndpoint ( IN USB_XHCI_INSTANCE *Xhc, IN UINT8 SlotId, - IN UINT8 Dci + IN UINT8 Dci, + IN URB *PendingUrb OPTIONAL ) { EFI_STATUS Status; @@ -3134,6 +3199,29 @@ XhcStopEndpoint ( DEBUG ((EFI_D_INFO, "XhcStopEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId, Dci)); + // + // When XhcCheckUrbResult waits for the Stop_Endpoint completion, it also checks + // the PendingUrb completion status, because it's possible that the PendingUrb is + // finished just before stopping the end point, but after the looping check. + // + // The PendingUrb could be passed to XhcCmdTransfer to XhcExecTransfer to XhcCheckUrbResult + // through function parameter, but That will cause every consumer of XhcCmdTransfer, + // XhcExecTransfer and XhcCheckUrbResult pass a NULL PendingUrb. + // But actually only XhcCheckUrbResult is aware of the PendingUrb. + // So we choose to save the PendingUrb into the USB_XHCI_INSTANCE and use it in XhcCheckUrbResult. + // + ASSERT (Xhc->PendingUrb == NULL); + Xhc->PendingUrb = PendingUrb; + // + // Reset the URB result from Timeout to NoError. + // The USB result will be: + // changed to Timeout when Stop/StopInvalidLength Transfer Event is received, or + // remain NoError when Success/ShortPacket Transfer Event is received. + // + if (PendingUrb != NULL) { + PendingUrb->Result = EFI_USB_NOERROR; + } + // // Send stop endpoint command to transit Endpoint from running to stop state // @@ -3152,6 +3240,8 @@ XhcStopEndpoint ( DEBUG ((EFI_D_ERROR, "XhcStopEndpoint: Stop Endpoint Failed, Status = %r\n", Status)); } + Xhc->PendingUrb = NULL; + return Status; } @@ -3378,7 +3468,7 @@ XhcSetInterface ( // XHCI 4.3.6 - Setting Alternate Interfaces // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting. // - Status = XhcStopEndpoint (Xhc, SlotId, Dci); + Status = XhcStopEndpoint (Xhc, SlotId, Dci, NULL); if (EFI_ERROR (Status)) { return Status; } @@ -3580,7 +3670,7 @@ XhcSetInterface64 ( // XHCI 4.3.6 - Setting Alternate Interfaces // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting. // - Status = XhcStopEndpoint (Xhc, SlotId, Dci); + Status = XhcStopEndpoint (Xhc, SlotId, Dci, NULL); if (EFI_ERROR (Status)) { return Status; }