USB: EHCI: update toggle state for linked QHs
This patch (as1245) fixes a bug in ehci-hcd. When an URB is queued for an endpoint whose QH is already in the LINKED state, the QH doesn't get refreshed. As a result, if usb_clear_halt() was called during the time that the QH was linked but idle, the data toggle value in the QH doesn't get reset. The symptom is that after a clear_halt, data gets lost and transfers time out. This problem is starting to show up now because the "ehci-hcd unlink speedups" patch causes QHs with no queued URBs to remain linked for a suitable time. The patch utilizes the new endpoint_reset mechanism to fix the problem. When an endpoint is reset, the new method forcibly unlinks the QH (if necessary) and safely updates the toggle value. This allows qh_update() to be simplified and avoids using usb_device's toggle bits in a rather unintuitive way. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> CC: David Brownell <david-b@pacbell.net> Tested-by: David <david@unsolicited.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
5effabbe9e
commit
b18ffd49e8
9 changed files with 53 additions and 18 deletions
|
@ -97,6 +97,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
|
|||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
|
|
|
@ -309,6 +309,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
|
|||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
|
|
|
@ -1024,6 +1024,51 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
|
|||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
struct ehci_qh *qh;
|
||||
int eptype = usb_endpoint_type(&ep->desc);
|
||||
|
||||
if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT)
|
||||
return;
|
||||
|
||||
rescan:
|
||||
spin_lock_irq(&ehci->lock);
|
||||
qh = ep->hcpriv;
|
||||
|
||||
/* For Bulk and Interrupt endpoints we maintain the toggle state
|
||||
* in the hardware; the toggle bits in udev aren't used at all.
|
||||
* When an endpoint is reset by usb_clear_halt() we must reset
|
||||
* the toggle bit in the QH.
|
||||
*/
|
||||
if (qh) {
|
||||
if (!list_empty(&qh->qtd_list)) {
|
||||
WARN_ONCE(1, "clear_halt for a busy endpoint\n");
|
||||
} else if (qh->qh_state == QH_STATE_IDLE) {
|
||||
qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
|
||||
} else {
|
||||
/* It's not safe to write into the overlay area
|
||||
* while the QH is active. Unlink it first and
|
||||
* wait for the unlink to complete.
|
||||
*/
|
||||
if (qh->qh_state == QH_STATE_LINKED) {
|
||||
if (eptype == USB_ENDPOINT_XFER_BULK) {
|
||||
unlink_async(ehci, qh);
|
||||
} else {
|
||||
intr_deschedule(ehci, qh);
|
||||
(void) qh_schedule(ehci, qh);
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
schedule_timeout_uninterruptible(1);
|
||||
goto rescan;
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
}
|
||||
|
||||
static int ehci_get_frame (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
|
|
|
@ -51,6 +51,7 @@ static const struct hc_driver ixp4xx_ehci_hc_driver = {
|
|||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
.get_frame_number = ehci_get_frame,
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
|
|
|
@ -149,6 +149,7 @@ static const struct hc_driver ehci_orion_hc_driver = {
|
|||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
|
|
|
@ -388,6 +388,7 @@ static const struct hc_driver ehci_pci_hc_driver = {
|
|||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
|
|
|
@ -61,6 +61,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = {
|
|||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
|
|
|
@ -65,6 +65,7 @@ static const struct hc_driver ps3_ehci_hc_driver = {
|
|||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
.get_frame_number = ehci_get_frame,
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
|
|
|
@ -93,22 +93,6 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
|
|||
qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
|
||||
qh->hw_alt_next = EHCI_LIST_END(ehci);
|
||||
|
||||
/* Except for control endpoints, we make hardware maintain data
|
||||
* toggle (like OHCI) ... here (re)initialize the toggle in the QH,
|
||||
* and set the pseudo-toggle in udev. Only usb_clear_halt() will
|
||||
* ever clear it.
|
||||
*/
|
||||
if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
|
||||
unsigned is_out, epnum;
|
||||
|
||||
is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
|
||||
epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f;
|
||||
if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
|
||||
qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
|
||||
usb_settoggle (qh->dev, epnum, is_out, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
|
||||
wmb ();
|
||||
qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
|
||||
|
@ -850,7 +834,6 @@ qh_make (
|
|||
qh->qh_state = QH_STATE_IDLE;
|
||||
qh->hw_info1 = cpu_to_hc32(ehci, info1);
|
||||
qh->hw_info2 = cpu_to_hc32(ehci, info2);
|
||||
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
|
||||
qh_refresh (ehci, qh);
|
||||
return qh;
|
||||
}
|
||||
|
@ -881,7 +864,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
|||
}
|
||||
}
|
||||
|
||||
/* clear halt and/or toggle; and maybe recover from silicon quirk */
|
||||
/* clear halt and maybe recover from silicon quirk */
|
||||
if (qh->qh_state == QH_STATE_IDLE)
|
||||
qh_refresh (ehci, qh);
|
||||
|
||||
|
|
Loading…
Reference in a new issue