Commit 2775562a authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

[PATCH] USB: UHCI: fix obscure bug in enqueue()

This patch (as676) fixes a small bug in uhci-hcd's enqueue routine.  When
an URB is unlinked or gets an error and the completion handler queues
another URB for the same endpoint, the queue shouldn't be allowed to start
up again until the handler returns.  Not even if the new URB is the only
one on its queue.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 4de7d2c2
...@@ -1148,8 +1148,9 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd, ...@@ -1148,8 +1148,9 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd,
/* If the new URB is the first and only one on this QH then either /* If the new URB is the first and only one on this QH then either
* the QH is new and idle or else it's unlinked and waiting to * the QH is new and idle or else it's unlinked and waiting to
* become idle, so we can activate it right away. */ * become idle, so we can activate it right away. But only if the
if (qh->queue.next == &urbp->node) * queue isn't stopped. */
if (qh->queue.next == &urbp->node && !qh->is_stopped)
uhci_activate_qh(uhci, qh); uhci_activate_qh(uhci, qh);
goto done; goto done;
...@@ -1293,27 +1294,32 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh, ...@@ -1293,27 +1294,32 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh,
if (urb->status == -EINPROGRESS) /* Not dequeued */ if (urb->status == -EINPROGRESS) /* Not dequeued */
urb->status = status; urb->status = status;
else else
status = -ECONNRESET; status = ECONNRESET; /* Not -ECONNRESET */
spin_unlock(&urb->lock); spin_unlock(&urb->lock);
/* Dequeued but completed URBs can't be given back unless /* Dequeued but completed URBs can't be given back unless
* the QH is stopped or has finished unlinking. */ * the QH is stopped or has finished unlinking. */
if (status == -ECONNRESET && if (status == ECONNRESET) {
!(qh->is_stopped || QH_FINISHED_UNLINKING(qh))) if (QH_FINISHED_UNLINKING(qh))
return; qh->is_stopped = 1;
else if (!qh->is_stopped)
return;
}
uhci_giveback_urb(uhci, qh, urb, regs); uhci_giveback_urb(uhci, qh, urb, regs);
if (qh->is_stopped) if (status < 0)
break; break;
} }
/* If the QH is neither stopped nor finished unlinking (normal case), /* If the QH is neither stopped nor finished unlinking (normal case),
* our work here is done. */ * our work here is done. */
restart: if (QH_FINISHED_UNLINKING(qh))
if (!(qh->is_stopped || QH_FINISHED_UNLINKING(qh))) qh->is_stopped = 1;
else if (!qh->is_stopped)
return; return;
/* Otherwise give back each of the dequeued URBs */ /* Otherwise give back each of the dequeued URBs */
restart:
list_for_each_entry(urbp, &qh->queue, node) { list_for_each_entry(urbp, &qh->queue, node) {
urb = urbp->urb; urb = urbp->urb;
if (urb->status != -EINPROGRESS) { if (urb->status != -EINPROGRESS) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment