diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 656ed6470e4a..51008bad16cd 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -561,8 +561,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, * pointer command pending because the device can choose to start any * stream once the endpoint is on the HW schedule. */ - if ((ep_state & EP_STOP_CMD_PENDING) || (ep_state & SET_DEQ_PENDING) || - (ep_state & EP_HALTED) || (ep_state & EP_CLEARING_TT)) + if (ep_state & (EP_STOP_CMD_PENDING | SET_DEQ_PENDING | EP_HALTED | + EP_CLEARING_TT | EP_DROP_PENDING)) return; trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id)); @@ -995,8 +995,10 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci, * Can cause host hang. * Device will be reset to recover an inactive link, so don't do anything */ - if (rhub_port->link_inactive || !rhub_port->connected) + if (rhub_port->link_inactive || !rhub_port->connected) { + ep->ep_state |= EP_DROP_PENDING; return -ENODEV; + } /* add td to cancelled list and let reset ep handler take care of it */ if (reset_type == EP_HARD_RESET) { @@ -1066,6 +1068,13 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) td->urb, td->urb->stream_id); continue; } + + /* device disconnected or link error, ep will be dropped */ + if (ep->ep_state & EP_DROP_PENDING) { + td->cancel_status = TD_CLEARED; + continue; + } + /* * If a ring stopped on the TD we need to cancel then we have to * move the xHC endpoint ring dequeue pointer past this TD. @@ -1296,6 +1305,10 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, } } + /* link is inactive or disconnected, ep is not running and shouldn't be restarted */ + if (ep->vdev->rhub_port->link_inactive || !ep->vdev->rhub_port->connected) + ep->ep_state |= EP_DROP_PENDING; + /* will queue a set TR deq if stopped on a cancelled, uncleared TD */ xhci_invalidate_cancelled_tds(ep); ep->ep_state &= ~EP_STOP_CMD_PENDING; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6f830a43963f..71c25c2e71b9 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1852,7 +1852,7 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) } /* In this case no commands are pending but the endpoint is stopped */ - if (ep->ep_state & EP_CLEARING_TT) { + if (ep->ep_state & (EP_CLEARING_TT | EP_DROP_PENDING)) { /* and cancelled TDs can be given back right away */ xhci_dbg(xhci, "Invalidating TDs instantly on slot %d ep %d in state 0x%x\n", urb->dev->slot_id, ep_index, ep->ep_state); @@ -4089,6 +4089,9 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, ret = 0; command_cleanup: + for (i = 0; i < EP_CTX_PER_DEV; i++) + virt_dev->eps[i].ep_state &= ~EP_DROP_PENDING; + xhci_free_command(xhci, reset_device_cmd); return ret; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 0cc9edfd86f3..18d710abaf98 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -682,6 +682,7 @@ struct xhci_virt_ep { #define EP_SOFT_CLEAR_TOGGLE BIT(7) /* usb_hub_clear_tt_buffer is in progress */ #define EP_CLEARING_TT BIT(8) +#define EP_DROP_PENDING BIT(9) /* port disconnect or link error, don't restart */ /* ---- Related to URB cancellation ---- */ struct list_head cancelled_td_list; struct xhci_hcd *xhci;