Some devices use a clear endpoint halt request as a soft reset, even if the endpoint is not halted. This will clear the toggle and sequence on the device side. xHCI however refuses to reset a non-halted endpoint, so instead we need to issue a configure endpoint command on xHCI to clear its host side toggle and sequence, and get it in sync with the device side. This is a respin of a old patch that was reverted as it had a stale endpoint context dequeue value which caused regression. commit 27082e2654dc ("xhci: Clear the host side toggle manually when endpoint is 'soft reset'") Signed-off-by: Mathias Nyman <mathias.nyman@xxxxxxxxxxxxxxx> --- drivers/usb/host/xhci-ring.c | 5 ++- drivers/usb/host/xhci.c | 105 ++++++++++++++++++++++++++++++++++++------- drivers/usb/host/xhci.h | 2 + 3 files changed, 95 insertions(+), 17 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 86476c6..91a1a82 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1829,9 +1829,10 @@ static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci, xhci_queue_reset_ep(xhci, command, slot_id, ep_index, reset_type); - if (reset_type == EP_HARD_RESET) + if (reset_type == EP_HARD_RESET) { + ep->ep_state |= EP_HARD_CLEAR_TOGGLE; xhci_cleanup_stalled_ring(xhci, ep_index, stream_id, td); - + } xhci_ring_cmd_db(xhci); } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index f9d8a32..05b22b8 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1356,6 +1356,11 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag ret = -EINVAL; goto free_priv; } + if (*ep_state & EP_SOFT_CLEAR_TOGGLE) { + xhci_warn(xhci, "Can't enqueue URB while manually clearing toggle\n"); + ret = -EINVAL; + goto free_priv; + } switch (usb_endpoint_type(&urb->ep->desc)) { @@ -2869,33 +2874,103 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index, } } -/* Called when clearing halted device. The core should have sent the control - * message to clear the device halt condition. The host side of the halt should - * already be cleared with a reset endpoint command issued when the STALL tx - * event was received. +/* + * Called after usb core issues a clear halt control message. + * The host side of the halt should already be cleared by a reset endpoint + * command issued when the STALL event was received. * - * Context: in_interrupt + * The reset endpoint command may only be issued to endpoints in the halted + * state. For software that wishes to reset the data toggle or sequence number + * of an endpoint that isn't in the halted state this function will issue a + * configure endpoint command with the Drop and Add bits set for the target + * endpoint. Refer to the additional note in xhci spcification section 4.6.8. */ static void xhci_endpoint_reset(struct usb_hcd *hcd, - struct usb_host_endpoint *ep) + struct usb_host_endpoint *host_ep) { struct xhci_hcd *xhci; + struct usb_device *udev; + struct xhci_virt_device *vdev; + struct xhci_virt_ep *ep; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_command *stop_cmd, *cfg_cmd; + unsigned int ep_index; + unsigned long flags; + u32 ep_flag; xhci = hcd_to_xhci(hcd); + if (!host_ep->hcpriv) + return; + udev = (struct usb_device *) host_ep->hcpriv; + vdev = xhci->devs[udev->slot_id]; + ep_index = xhci_get_endpoint_index(&host_ep->desc); + ep = &vdev->eps[ep_index]; + + /* Bail out if toggle is already being cleared by a endpoint reset */ + if (ep->ep_state & EP_HARD_CLEAR_TOGGLE) { + ep->ep_state &= ~EP_HARD_CLEAR_TOGGLE; + return; + } + /* Only interrupt and bulk ep's use data toggle, USB2 spec 5.5.4-> */ + if (usb_endpoint_xfer_control(&host_ep->desc) || + usb_endpoint_xfer_isoc(&host_ep->desc)) + return; + + ep_flag = xhci_get_endpoint_flag(&host_ep->desc); + + if (ep_flag == SLOT_FLAG || ep_flag == EP0_FLAG) + return; + + stop_cmd = xhci_alloc_command(xhci, true, GFP_NOWAIT); + if (!stop_cmd) + return; + + cfg_cmd = xhci_alloc_command_with_ctx(xhci, true, GFP_NOWAIT); + if (!cfg_cmd) + goto cleanup; + + spin_lock_irqsave(&xhci->lock, flags); + + /* block queuing new trbs and ringing ep doorbell */ + ep->ep_state |= EP_SOFT_CLEAR_TOGGLE; /* - * We might need to implement the config ep cmd in xhci 4.8.1 note: - * The Reset Endpoint Command may only be issued to endpoints in the - * Halted state. If software wishes reset the Data Toggle or Sequence - * Number of an endpoint that isn't in the Halted state, then software - * may issue a Configure Endpoint Command with the Drop and Add bits set - * for the target endpoint. that is in the Stopped state. + * Make sure endpoint ring is empty before resetting the toggle/seq. + * Driver is required to synchronously cancel all transfer request. + * Stop the endpoint to force xHC to update the output context */ - /* For now just print debug to follow the situation */ - xhci_dbg(xhci, "Endpoint 0x%x ep reset callback called\n", - ep->desc.bEndpointAddress); + if (!list_empty(&ep->ring->td_list)) { + dev_err(&udev->dev, "EP not empty, refuse reset\n"); + spin_unlock_irqrestore(&xhci->lock, flags); + goto cleanup; + } + xhci_queue_stop_endpoint(xhci, stop_cmd, udev->slot_id, ep_index, 0); + xhci_ring_cmd_db(xhci); + spin_unlock_irqrestore(&xhci->lock, flags); + + wait_for_completion(stop_cmd->completion); + + spin_lock_irqsave(&xhci->lock, flags); + + /* config ep command clears toggle if add and drop ep flags are set */ + ctrl_ctx = xhci_get_input_control_ctx(cfg_cmd->in_ctx); + xhci_setup_input_ctx_for_config_ep(xhci, cfg_cmd->in_ctx, vdev->out_ctx, + ctrl_ctx, ep_flag, ep_flag); + xhci_endpoint_copy(xhci, cfg_cmd->in_ctx, vdev->out_ctx, ep_index); + + xhci_queue_configure_endpoint(xhci, cfg_cmd, cfg_cmd->in_ctx->dma, + udev->slot_id, false); + xhci_ring_cmd_db(xhci); + spin_unlock_irqrestore(&xhci->lock, flags); + + wait_for_completion(cfg_cmd->completion); + + ep->ep_state &= ~EP_SOFT_CLEAR_TOGGLE; + xhci_free_command(xhci, cfg_cmd); +cleanup: + xhci_free_command(xhci, stop_cmd); } static int xhci_check_streams_endpoint(struct xhci_hcd *xhci, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index e4d7d3d..9f236da 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -921,6 +921,8 @@ struct xhci_virt_ep { #define EP_HAS_STREAMS (1 << 4) /* Transitioning the endpoint to not using streams, don't enqueue URBs */ #define EP_GETTING_NO_STREAMS (1 << 5) +#define EP_HARD_CLEAR_TOGGLE (1 << 6) +#define EP_SOFT_CLEAR_TOGGLE (1 << 7) /* ---- Related to URB cancellation ---- */ struct list_head cancelled_td_list; /* Watchdog timer for stop endpoint command to cancel URBs */ -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html