summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2006-05-06 00:32:02 +0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-06-22 02:04:10 +0400
commit2775562ad2af2fc131ef7987166db6e42217528f (patch)
tree87c0ba46d8daa8d4b9af6f54b34b21b52c3fa57e /drivers/usb
parent4de7d2c231a8624a47417977be0768c5b5257c4f (diff)
downloadlinux-2775562ad2af2fc131ef7987166db6e42217528f.tar.xz
[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: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/uhci-q.c24
1 files changed, 15 insertions, 9 deletions
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 8639e9035931..693e92c1bd9e 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -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
* the QH is new and idle or else it's unlinked and waiting to
- * become idle, so we can activate it right away. */
- if (qh->queue.next == &urbp->node)
+ * become idle, so we can activate it right away. But only if the
+ * queue isn't stopped. */
+ if (qh->queue.next == &urbp->node && !qh->is_stopped)
uhci_activate_qh(uhci, qh);
goto done;
@@ -1293,27 +1294,32 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh,
if (urb->status == -EINPROGRESS) /* Not dequeued */
urb->status = status;
else
- status = -ECONNRESET;
+ status = ECONNRESET; /* Not -ECONNRESET */
spin_unlock(&urb->lock);
/* Dequeued but completed URBs can't be given back unless
* the QH is stopped or has finished unlinking. */
- if (status == -ECONNRESET &&
- !(qh->is_stopped || QH_FINISHED_UNLINKING(qh)))
- return;
+ if (status == ECONNRESET) {
+ if (QH_FINISHED_UNLINKING(qh))
+ qh->is_stopped = 1;
+ else if (!qh->is_stopped)
+ return;
+ }
uhci_giveback_urb(uhci, qh, urb, regs);
- if (qh->is_stopped)
+ if (status < 0)
break;
}
/* If the QH is neither stopped nor finished unlinking (normal case),
* our work here is done. */
- restart:
- if (!(qh->is_stopped || QH_FINISHED_UNLINKING(qh)))
+ if (QH_FINISHED_UNLINKING(qh))
+ qh->is_stopped = 1;
+ else if (!qh->is_stopped)
return;
/* Otherwise give back each of the dequeued URBs */
+restart:
list_for_each_entry(urbp, &qh->queue, node) {
urb = urbp->urb;
if (urb->status != -EINPROGRESS) {