aboutsummaryrefslogtreecommitdiffstats
path: root/usb
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2006-05-19 15:59:01 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2006-05-19 15:59:01 -0700
commitf946bcab1c35689ca563072cde8ba3a4844fbd14 (patch)
tree3a684c968562ee2b8d489fee59cc9aff828a4c49 /usb
parent25041f79438a354d98305ea5650676997d915670 (diff)
downloadpatches-f946bcab1c35689ca563072cde8ba3a4844fbd14.tar.gz
usb patches added
Diffstat (limited to 'usb')
-rw-r--r--usb/fix-a-deadlock-in-usbtest.patch35
-rw-r--r--usb/uhci-fix-race-in-iso-dequeuing.patch143
-rw-r--r--usb/uhci-remove-iso-tds-as-they-are-used.patch301
-rw-r--r--usb/uhci-store-the-period-in-the-queue-header.patch220
-rw-r--r--usb/uhci-use-integer-sized-frame-numbers.patch149
-rw-r--r--usb/usb-allow-high-bandwidth-isochronous-packets-via-usbfs.patch33
-rw-r--r--usb/usb-correct-the-usb-info-in-documentation-power-swsusp.txt.patch88
-rw-r--r--usb/usb-remove-4088-byte-limit-on-usbfs-control-urbs.patch46
8 files changed, 1015 insertions, 0 deletions
diff --git a/usb/fix-a-deadlock-in-usbtest.patch b/usb/fix-a-deadlock-in-usbtest.patch
new file mode 100644
index 0000000000000..2d85421f8c593
--- /dev/null
+++ b/usb/fix-a-deadlock-in-usbtest.patch
@@ -0,0 +1,35 @@
+From vagabon.xyz@gmail.com Mon May 15 10:24:04 2006
+Message-ID: <cda58cb80605151023k1bf9d1ebp6f8b8612435a75c9@mail.gmail.com>
+Date: Mon, 15 May 2006 19:23:53 +0200
+From: "Franck Bui-Huu" <vagabon.xyz@gmail.com>
+To: "David Brownell" <david-b@pacbell.net>, greg@kroah.com
+Subject: [PATCH] Fix a deadlock in usbtest
+Cc: Franck <vagabon.xyz@gmail.com>
+Content-Disposition: inline
+
+ctrl_complete functions acquires ctx->lock and tries to unlink
+all queued urbs in case of errors through usb_unlink_urb func.
+In its turn usb_unlink_urb calls, through the hcd driver,
+usb_hcd_giveback_urb which calls ctrl_complete again. At this
+time, ctx->lock is already taken by the same function.
+
+Signed-off-by: Franck Bui-Huu <vagabon.xyz@gmail.com>
+Cc: David Brownell <david-b@pacbell.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/misc/usbtest.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- gregkh-2.6.orig/drivers/usb/misc/usbtest.c
++++ gregkh-2.6/drivers/usb/misc/usbtest.c
+@@ -802,7 +802,9 @@ error:
+
+ if (u == urb || !u->dev)
+ continue;
++ spin_unlock(&ctx->lock);
+ status = usb_unlink_urb (u);
++ spin_lock(&ctx->lock);
+ switch (status) {
+ case -EINPROGRESS:
+ case -EBUSY:
diff --git a/usb/uhci-fix-race-in-iso-dequeuing.patch b/usb/uhci-fix-race-in-iso-dequeuing.patch
new file mode 100644
index 0000000000000..e355bc9ce0b4e
--- /dev/null
+++ b/usb/uhci-fix-race-in-iso-dequeuing.patch
@@ -0,0 +1,143 @@
+From stern@rowland.harvard.edu Fri May 19 13:39:58 2006
+Date: Fri, 19 May 2006 16:39:52 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Subject: UHCI: fix race in ISO dequeuing
+Message-ID: <Pine.LNX.4.44L0.0605191634590.5761-100000@iolanthe.rowland.org>
+
+This patch (as688) fixes a small race in uhci-hcd. Because ISO queues
+aren't controlled by queue headers, they can't be unlinked. Only
+individual URBs can. So whenever multiple ISO URBs are dequeued, it's
+necessary to make sure the hardware is done with each one. We can't
+assume that dequeuing the first URB will suffice to unlink the entire
+queue.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/uhci-q.c | 48 ++++++++++++++++++++++++++++++++++------------
+ 1 file changed, 36 insertions(+), 12 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/host/uhci-q.c
++++ gregkh-2.6/drivers/usb/host/uhci-q.c
+@@ -194,7 +194,6 @@ static void uhci_unlink_isochronous_tds(
+
+ list_for_each_entry(td, &urbp->td_list, list)
+ uhci_remove_td_from_frame_list(uhci, td);
+- wmb();
+ }
+
+ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
+@@ -253,17 +252,25 @@ static void uhci_free_qh(struct uhci_hcd
+ * When a queue is stopped and a dequeued URB is given back, adjust
+ * the previous TD link (if the URB isn't first on the queue) or
+ * save its toggle value (if it is first and is currently executing).
++ *
++ * Returns 0 if the URB should not yet be given back, 1 otherwise.
+ */
+-static void uhci_cleanup_queue(struct uhci_qh *qh,
++static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh,
+ struct urb *urb)
+ {
+ struct urb_priv *urbp = urb->hcpriv;
+ struct uhci_td *td;
++ int ret = 1;
+
+ /* Isochronous pipes don't use toggles and their TD link pointers
+- * get adjusted during uhci_urb_dequeue(). */
+- if (qh->type == USB_ENDPOINT_XFER_ISOC)
+- return;
++ * get adjusted during uhci_urb_dequeue(). But since their queues
++ * cannot truly be stopped, we have to watch out for dequeues
++ * occurring after the nominal unlink frame. */
++ if (qh->type == USB_ENDPOINT_XFER_ISOC) {
++ ret = (uhci->frame_number + uhci->is_stopped !=
++ qh->unlink_frame);
++ return ret;
++ }
+
+ /* If the URB isn't first on its queue, adjust the link pointer
+ * of the last TD in the previous URB. The toggle doesn't need
+@@ -279,24 +286,25 @@ static void uhci_cleanup_queue(struct uh
+ td = list_entry(urbp->td_list.prev, struct uhci_td,
+ list);
+ ptd->link = td->link;
+- return;
++ return ret;
+ }
+
+ /* If the QH element pointer is UHCI_PTR_TERM then then currently
+ * executing URB has already been unlinked, so this one isn't it. */
+ if (qh_element(qh) == UHCI_PTR_TERM)
+- return;
++ return ret;
+ qh->element = UHCI_PTR_TERM;
+
+ /* Control pipes have to worry about toggles */
+ if (qh->type == USB_ENDPOINT_XFER_CONTROL)
+- return;
++ return ret;
+
+ /* Save the next toggle value */
+ WARN_ON(list_empty(&urbp->td_list));
+ td = list_entry(urbp->td_list.next, struct uhci_td, list);
+ qh->needs_fixup = 1;
+ qh->initial_toggle = uhci_toggle(td_token(td));
++ return ret;
+ }
+
+ /*
+@@ -953,7 +961,6 @@ static int uhci_submit_isochronous(struc
+ } else {
+ /* FIXME: Sanity check */
+ }
+- urb->start_frame &= (UHCI_NUMFRAMES - 1);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ td = uhci_alloc_td(uhci);
+@@ -1120,16 +1127,26 @@ static int uhci_urb_dequeue(struct usb_h
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ unsigned long flags;
+ struct urb_priv *urbp;
++ struct uhci_qh *qh;
+
+ spin_lock_irqsave(&uhci->lock, flags);
+ urbp = urb->hcpriv;
+ if (!urbp) /* URB was never linked! */
+ goto done;
++ qh = urbp->qh;
+
+ /* Remove Isochronous TDs from the frame list ASAP */
+- if (urbp->qh->type == USB_ENDPOINT_XFER_ISOC)
++ if (qh->type == USB_ENDPOINT_XFER_ISOC) {
+ uhci_unlink_isochronous_tds(uhci, urb);
+- uhci_unlink_qh(uhci, urbp->qh);
++ mb();
++
++ /* If the URB has already started, update the QH unlink time */
++ uhci_get_current_frame_number(uhci);
++ if (uhci_frame_before_eq(urb->start_frame, uhci->frame_number))
++ qh->unlink_frame = uhci->frame_number;
++ }
++
++ uhci_unlink_qh(uhci, qh);
+
+ done:
+ spin_unlock_irqrestore(&uhci->lock, flags);
+@@ -1250,7 +1267,14 @@ restart:
+ list_for_each_entry(urbp, &qh->queue, node) {
+ urb = urbp->urb;
+ if (urb->status != -EINPROGRESS) {
+- uhci_cleanup_queue(qh, urb);
++
++ /* Fix up the TD links and save the toggles for
++ * non-Isochronous queues. For Isochronous queues,
++ * test for too-recent dequeues. */
++ if (!uhci_cleanup_queue(uhci, qh, urb)) {
++ qh->is_stopped = 0;
++ return;
++ }
+ uhci_giveback_urb(uhci, qh, urb, regs);
+ goto restart;
+ }
diff --git a/usb/uhci-remove-iso-tds-as-they-are-used.patch b/usb/uhci-remove-iso-tds-as-they-are-used.patch
new file mode 100644
index 0000000000000..8ccabf5641cb3
--- /dev/null
+++ b/usb/uhci-remove-iso-tds-as-they-are-used.patch
@@ -0,0 +1,301 @@
+From stern@rowland.harvard.edu Fri May 19 13:52:46 2006
+Date: Fri, 19 May 2006 16:52:35 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Subject: UHCI: remove ISO TDs as they are used
+Message-ID: <Pine.LNX.4.44L0.0605191644570.5761-100000@iolanthe.rowland.org>
+
+This patch (as690) does the same thing for ISO TDs as as680 did for
+non-ISO TDs: free them as they are used rather than all at once when an
+URB is complete. At the same time it fixes a minor buglet (I'm not
+aware of it ever affecting anyone): An ISO TD should be retired when its
+frame is over, regardless of whether or not the hardware has marked it
+inactive.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/uhci-debug.c | 14 +++--
+ drivers/usb/host/uhci-hcd.h | 10 +++-
+ drivers/usb/host/uhci-q.c | 105 +++++++++++++++++++++++++++++-------------
+ 3 files changed, 91 insertions(+), 38 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/host/uhci-debug.c
++++ gregkh-2.6/drivers/usb/host/uhci-debug.c
+@@ -127,7 +127,8 @@ static int uhci_show_urbp(struct urb_pri
+
+ i = nactive = ninactive = 0;
+ list_for_each_entry(td, &urbp->td_list, list) {
+- if (++i <= 10 || debug > 2) {
++ if (urbp->qh->type != USB_ENDPOINT_XFER_ISOC &&
++ (++i <= 10 || debug > 2)) {
+ out += sprintf(out, "%*s%d: ", space + 2, "", i);
+ out += uhci_show_td(td, out, len - (out - buf), 0);
+ } else {
+@@ -168,8 +169,9 @@ static int uhci_show_qh(struct uhci_qh *
+ space, "", qh, qtype,
+ le32_to_cpu(qh->link), le32_to_cpu(element));
+ if (qh->type == USB_ENDPOINT_XFER_ISOC)
+- out += sprintf(out, "%*s period %d\n",
+- space, "", qh->period);
++ out += sprintf(out, "%*s period %d frame %x desc [%p]\n",
++ space, "", qh->period, qh->iso_frame,
++ qh->iso_packet_desc);
+
+ if (element & UHCI_PTR_QH)
+ out += sprintf(out, "%*s Element points to QH (bug?)\n", space, "");
+@@ -331,8 +333,10 @@ static int uhci_show_status(struct uhci_
+ out += sprintf(out, " sof = %02x\n", sof);
+ out += uhci_show_sc(1, portsc1, out, len - (out - buf));
+ out += uhci_show_sc(2, portsc2, out, len - (out - buf));
+- out += sprintf(out, "Most recent frame: %x\n",
+- uhci->frame_number);
++ out += sprintf(out, "Most recent frame: %x (%d) "
++ "Last ISO frame: %x (%d)\n",
++ uhci->frame_number, uhci->frame_number & 1023,
++ uhci->last_iso_frame, uhci->last_iso_frame & 1023);
+
+ return out - buf;
+ }
+--- gregkh-2.6.orig/drivers/usb/host/uhci-hcd.h
++++ gregkh-2.6/drivers/usb/host/uhci-hcd.h
+@@ -128,8 +128,6 @@ struct uhci_qh {
+ __le32 element; /* Queue element (TD) pointer */
+
+ /* Software fields */
+- dma_addr_t dma_handle;
+-
+ struct list_head node; /* Node in the list of QHs */
+ struct usb_host_endpoint *hep; /* Endpoint information */
+ struct usb_device *udev;
+@@ -138,13 +136,19 @@ struct uhci_qh {
+ struct uhci_td *dummy_td; /* Dummy TD to end the queue */
+ struct uhci_td *post_td; /* Last TD completed */
+
++ struct usb_iso_packet_descriptor *iso_packet_desc;
++ /* Next urb->iso_frame_desc entry */
+ unsigned long advance_jiffies; /* Time of last queue advance */
+ unsigned int unlink_frame; /* When the QH was unlinked */
+ unsigned int period; /* For Interrupt and Isochronous QHs */
++ unsigned int iso_frame; /* Frame # for iso_packet_desc */
++ int iso_status; /* Status for Isochronous URBs */
+
+ int state; /* QH_STATE_xxx; see above */
+ int type; /* Queue type (control, bulk, etc) */
+
++ dma_addr_t dma_handle;
++
+ unsigned int initial_toggle:1; /* Endpoint's current toggle value */
+ unsigned int needs_fixup:1; /* Must fix the TD toggle values */
+ unsigned int is_stopped:1; /* Queue was stopped by error/unlink */
+@@ -386,6 +390,8 @@ struct uhci_hcd {
+ unsigned int frame_number; /* As of last check */
+ unsigned int is_stopped;
+ #define UHCI_IS_STOPPED 9999 /* Larger than a frame # */
++ unsigned int last_iso_frame; /* Frame of last scan */
++ unsigned int cur_iso_frame; /* Frame for current scan */
+
+ unsigned int scan_in_progress:1; /* Schedule scan is running */
+ unsigned int need_rescan:1; /* Redo the schedule scan */
+--- gregkh-2.6.orig/drivers/usb/host/uhci-q.c
++++ gregkh-2.6/drivers/usb/host/uhci-q.c
+@@ -184,6 +184,24 @@ static inline void uhci_remove_td_from_f
+ td->frame = -1;
+ }
+
++static inline void uhci_remove_tds_from_frame(struct uhci_hcd *uhci,
++ unsigned int framenum)
++{
++ struct uhci_td *ftd, *ltd;
++
++ framenum &= (UHCI_NUMFRAMES - 1);
++
++ ftd = uhci->frame_cpu[framenum];
++ if (ftd) {
++ ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
++ uhci->frame[framenum] = ltd->link;
++ uhci->frame_cpu[framenum] = NULL;
++
++ while (!list_empty(&ftd->fl_list))
++ list_del_init(ftd->fl_list.prev);
++ }
++}
++
+ /*
+ * Remove all the TDs for an Isochronous URB from the frame list
+ */
+@@ -523,7 +541,6 @@ static int uhci_map_status(int status, i
+ return -ENOSR;
+ if (status & TD_CTRL_STALLED) /* Stalled */
+ return -EPIPE;
+- WARN_ON(status & TD_CTRL_ACTIVE); /* Active */
+ return 0;
+ }
+
+@@ -960,12 +977,12 @@ static int uhci_submit_isochronous(struc
+ return -EFBIG;
+
+ /* Check the period and figure out the starting frame number */
+- uhci_get_current_frame_number(uhci);
+ if (qh->period == 0) {
+ if (urb->transfer_flags & URB_ISO_ASAP) {
++ uhci_get_current_frame_number(uhci);
+ urb->start_frame = uhci->frame_number + 10;
+ } else {
+- i = urb->start_frame - uhci->frame_number;
++ i = urb->start_frame - uhci->last_iso_frame;
+ if (i <= 0 || i >= UHCI_NUMFRAMES)
+ return -EINVAL;
+ }
+@@ -974,7 +991,7 @@ static int uhci_submit_isochronous(struc
+
+ } else { /* Pick up where the last URB leaves off */
+ if (list_empty(&qh->queue)) {
+- frame = uhci->frame_number + 10;
++ frame = qh->iso_frame;
+ } else {
+ struct urb *lurb;
+
+@@ -986,11 +1003,12 @@ static int uhci_submit_isochronous(struc
+ }
+ if (urb->transfer_flags & URB_ISO_ASAP)
+ urb->start_frame = frame;
+- /* FIXME: Sanity check */
++ else if (urb->start_frame != frame)
++ return -EINVAL;
+ }
+
+ /* Make sure we won't have to go too far into the future */
+- if (uhci_frame_before_eq(uhci->frame_number + UHCI_NUMFRAMES,
++ if (uhci_frame_before_eq(uhci->last_iso_frame + UHCI_NUMFRAMES,
+ urb->start_frame + urb->number_of_packets *
+ urb->interval))
+ return -EFBIG;
+@@ -1020,7 +1038,13 @@ static int uhci_submit_isochronous(struc
+ frame = urb->start_frame;
+ list_for_each_entry(td, &urbp->td_list, list) {
+ uhci_insert_td_in_frame_list(uhci, td, frame);
+- frame += urb->interval;
++ frame += qh->period;
++ }
++
++ if (list_empty(&qh->queue)) {
++ qh->iso_packet_desc = &urb->iso_frame_desc[0];
++ qh->iso_frame = urb->start_frame;
++ qh->iso_status = 0;
+ }
+
+ return 0;
+@@ -1028,37 +1052,44 @@ static int uhci_submit_isochronous(struc
+
+ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
+ {
+- struct uhci_td *td;
+- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+- int status;
+- int i, ret = 0;
+-
+- urb->actual_length = urb->error_count = 0;
++ struct uhci_td *td, *tmp;
++ struct urb_priv *urbp = urb->hcpriv;
++ struct uhci_qh *qh = urbp->qh;
+
+- i = 0;
+- list_for_each_entry(td, &urbp->td_list, list) {
++ list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
++ unsigned int ctrlstat;
++ int status;
+ int actlength;
+- unsigned int ctrlstat = td_status(td);
+
+- if (ctrlstat & TD_CTRL_ACTIVE)
++ if (uhci_frame_before_eq(uhci->cur_iso_frame, qh->iso_frame))
+ return -EINPROGRESS;
+
+- actlength = uhci_actual_length(ctrlstat);
+- urb->iso_frame_desc[i].actual_length = actlength;
+- urb->actual_length += actlength;
+-
+- status = uhci_map_status(uhci_status_bits(ctrlstat),
+- usb_pipeout(urb->pipe));
+- urb->iso_frame_desc[i].status = status;
++ uhci_remove_tds_from_frame(uhci, qh->iso_frame);
++
++ ctrlstat = td_status(td);
++ if (ctrlstat & TD_CTRL_ACTIVE) {
++ status = -EXDEV; /* TD was added too late? */
++ } else {
++ status = uhci_map_status(uhci_status_bits(ctrlstat),
++ usb_pipeout(urb->pipe));
++ actlength = uhci_actual_length(ctrlstat);
++
++ urb->actual_length += actlength;
++ qh->iso_packet_desc->actual_length = actlength;
++ qh->iso_packet_desc->status = status;
++ }
++
+ if (status) {
+ urb->error_count++;
+- ret = status;
++ qh->iso_status = status;
+ }
+
+- i++;
++ uhci_remove_td_from_urbp(td);
++ uhci_free_td(uhci, td);
++ qh->iso_frame += qh->period;
++ ++qh->iso_packet_desc;
+ }
+-
+- return ret;
++ return qh->iso_status;
+ }
+
+ static int uhci_urb_enqueue(struct usb_hcd *hcd,
+@@ -1119,6 +1150,7 @@ static int uhci_urb_enqueue(struct usb_h
+ }
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
++ urb->error_count = 0;
+ bustime = usb_check_bandwidth(urb->dev, urb);
+ if (bustime < 0) {
+ ret = bustime;
+@@ -1200,9 +1232,18 @@ __acquires(uhci->lock)
+ {
+ struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
+
+- /* Isochronous TDs get unlinked directly from the frame list */
+- if (qh->type == USB_ENDPOINT_XFER_ISOC)
+- uhci_unlink_isochronous_tds(uhci, urb);
++ /* When giving back the first URB in an Isochronous queue,
++ * reinitialize the QH's iso-related members for the next URB. */
++ if (qh->type == USB_ENDPOINT_XFER_ISOC &&
++ urbp->node.prev == &qh->queue &&
++ urbp->node.next != &qh->queue) {
++ struct urb *nurb = list_entry(urbp->node.next,
++ struct urb_priv, node)->urb;
++
++ qh->iso_packet_desc = &nurb->iso_frame_desc[0];
++ qh->iso_frame = nurb->start_frame;
++ qh->iso_status = 0;
++ }
+
+ /* Take the URB off the QH's queue. If the queue is now empty,
+ * this is a perfect time for a toggle fixup. */
+@@ -1434,6 +1475,7 @@ rescan:
+
+ uhci_clear_next_interrupt(uhci);
+ uhci_get_current_frame_number(uhci);
++ uhci->cur_iso_frame = uhci->frame_number;
+
+ /* Go through all the QH queues and process the URBs in each one */
+ for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) {
+@@ -1451,6 +1493,7 @@ rescan:
+ }
+ }
+
++ uhci->last_iso_frame = uhci->cur_iso_frame;
+ if (uhci->need_rescan)
+ goto rescan;
+ uhci->scan_in_progress = 0;
diff --git a/usb/uhci-store-the-period-in-the-queue-header.patch b/usb/uhci-store-the-period-in-the-queue-header.patch
new file mode 100644
index 0000000000000..e126d7414497b
--- /dev/null
+++ b/usb/uhci-store-the-period-in-the-queue-header.patch
@@ -0,0 +1,220 @@
+From stern@rowland.harvard.edu Fri May 19 13:45:07 2006
+Date: Fri, 19 May 2006 16:44:55 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Subject: UHCI: store the period in the queue header
+Message-ID: <Pine.LNX.4.44L0.0605191639540.5761-100000@iolanthe.rowland.org>
+
+This patch (as689) stores the period for periodic transfers (interrupt
+and ISO) in the queue header. This is necessary for proper bandwidth
+tracking (not yet implemented). It also makes the scheduling of ISO
+transfers a bit more rigorous, with checks for out-of-bounds frame
+numbers.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/uhci-debug.c | 5 ++
+ drivers/usb/host/uhci-hcd.h | 36 ++-------------------
+ drivers/usb/host/uhci-q.c | 72 ++++++++++++++++++++++++++++++++----------
+ 3 files changed, 63 insertions(+), 50 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/host/uhci-debug.c
++++ gregkh-2.6/drivers/usb/host/uhci-debug.c
+@@ -153,7 +153,7 @@ static int uhci_show_qh(struct uhci_qh *
+ char *qtype;
+
+ /* Try to make sure there's enough memory */
+- if (len < 80 * 6)
++ if (len < 80 * 7)
+ return 0;
+
+ switch (qh->type) {
+@@ -167,6 +167,9 @@ static int uhci_show_qh(struct uhci_qh *
+ out += sprintf(out, "%*s[%p] %s QH link (%08x) element (%08x)\n",
+ space, "", qh, qtype,
+ le32_to_cpu(qh->link), le32_to_cpu(element));
++ if (qh->type == USB_ENDPOINT_XFER_ISOC)
++ out += sprintf(out, "%*s period %d\n",
++ space, "", qh->period);
+
+ if (element & UHCI_PTR_QH)
+ out += sprintf(out, "%*s Element points to QH (bug?)\n", space, "");
+--- gregkh-2.6.orig/drivers/usb/host/uhci-hcd.h
++++ gregkh-2.6/drivers/usb/host/uhci-hcd.h
+@@ -140,6 +140,8 @@ struct uhci_qh {
+
+ unsigned long advance_jiffies; /* Time of last queue advance */
+ unsigned int unlink_frame; /* When the QH was unlinked */
++ unsigned int period; /* For Interrupt and Isochronous QHs */
++
+ int state; /* QH_STATE_xxx; see above */
+ int type; /* Queue type (control, bulk, etc) */
+
+@@ -315,38 +317,8 @@ static inline u32 td_status(struct uhci_
+ #define skel_bulk_qh skelqh[12]
+ #define skel_term_qh skelqh[13]
+
+-/*
+- * Search tree for determining where <interval> fits in the skelqh[]
+- * skeleton.
+- *
+- * An interrupt request should be placed into the slowest skelqh[]
+- * which meets the interval/period/frequency requirement.
+- * An interrupt request is allowed to be faster than <interval> but not slower.
+- *
+- * For a given <interval>, this function returns the appropriate/matching
+- * skelqh[] index value.
+- */
+-static inline int __interval_to_skel(int interval)
+-{
+- if (interval < 16) {
+- if (interval < 4) {
+- if (interval < 2)
+- return 9; /* int1 for 0-1 ms */
+- return 8; /* int2 for 2-3 ms */
+- }
+- if (interval < 8)
+- return 7; /* int4 for 4-7 ms */
+- return 6; /* int8 for 8-15 ms */
+- }
+- if (interval < 64) {
+- if (interval < 32)
+- return 5; /* int16 for 16-31 ms */
+- return 4; /* int32 for 32-63 ms */
+- }
+- if (interval < 128)
+- return 3; /* int64 for 64-127 ms */
+- return 2; /* int128 for 128-255 ms (Max.) */
+-}
++/* Find the skelqh entry corresponding to an interval exponent */
++#define UHCI_SKEL_INDEX(exponent) (9 - exponent)
+
+
+ /*
+--- gregkh-2.6.orig/drivers/usb/host/uhci-q.c
++++ gregkh-2.6/drivers/usb/host/uhci-q.c
+@@ -763,6 +763,7 @@ static int uhci_submit_common(struct uhc
+ wmb();
+ qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE);
+ qh->dummy_td = td;
++ qh->period = urb->interval;
+
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), toggle);
+@@ -790,14 +791,30 @@ static inline int uhci_submit_bulk(struc
+ return ret;
+ }
+
+-static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
++static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
+ struct uhci_qh *qh)
+ {
++ int exponent;
++
+ /* USB 1.1 interrupt transfers only involve one packet per interval.
+ * Drivers can submit URBs of any length, but longer ones will need
+ * multiple intervals to complete.
+ */
+- qh->skel = uhci->skelqh[__interval_to_skel(urb->interval)];
++
++ /* Figure out which power-of-two queue to use */
++ for (exponent = 7; exponent >= 0; --exponent) {
++ if ((1 << exponent) <= urb->interval)
++ break;
++ }
++ if (exponent < 0)
++ return -EINVAL;
++ urb->interval = 1 << exponent;
++
++ if (qh->period == 0)
++ qh->skel = uhci->skelqh[UHCI_SKEL_INDEX(exponent)];
++ else if (qh->period != urb->interval)
++ return -EINVAL; /* Can't change the period */
++
+ return uhci_submit_common(uhci, urb, qh);
+ }
+
+@@ -937,31 +954,50 @@ static int uhci_submit_isochronous(struc
+ unsigned long destination, status;
+ struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
+
+- if (urb->number_of_packets > 900) /* 900? Why? */
++ /* Values must not be too big (could overflow below) */
++ if (urb->interval >= UHCI_NUMFRAMES ||
++ urb->number_of_packets >= UHCI_NUMFRAMES)
+ return -EFBIG;
+
+- status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
+- destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
++ /* Check the period and figure out the starting frame number */
++ uhci_get_current_frame_number(uhci);
++ if (qh->period == 0) {
++ if (urb->transfer_flags & URB_ISO_ASAP) {
++ urb->start_frame = uhci->frame_number + 10;
++ } else {
++ i = urb->start_frame - uhci->frame_number;
++ if (i <= 0 || i >= UHCI_NUMFRAMES)
++ return -EINVAL;
++ }
++ } else if (qh->period != urb->interval) {
++ return -EINVAL; /* Can't change the period */
+
+- /* Figure out the starting frame number */
+- if (urb->transfer_flags & URB_ISO_ASAP) {
++ } else { /* Pick up where the last URB leaves off */
+ if (list_empty(&qh->queue)) {
+- uhci_get_current_frame_number(uhci);
+- urb->start_frame = (uhci->frame_number + 10);
+-
+- } else { /* Go right after the last one */
+- struct urb *last_urb;
++ frame = uhci->frame_number + 10;
++ } else {
++ struct urb *lurb;
+
+- last_urb = list_entry(qh->queue.prev,
++ lurb = list_entry(qh->queue.prev,
+ struct urb_priv, node)->urb;
+- urb->start_frame = (last_urb->start_frame +
+- last_urb->number_of_packets *
+- last_urb->interval);
++ frame = lurb->start_frame +
++ lurb->number_of_packets *
++ lurb->interval;
+ }
+- } else {
++ if (urb->transfer_flags & URB_ISO_ASAP)
++ urb->start_frame = frame;
+ /* FIXME: Sanity check */
+ }
+
++ /* Make sure we won't have to go too far into the future */
++ if (uhci_frame_before_eq(uhci->frame_number + UHCI_NUMFRAMES,
++ urb->start_frame + urb->number_of_packets *
++ urb->interval))
++ return -EFBIG;
++
++ status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
++ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
++
+ for (i = 0; i < urb->number_of_packets; i++) {
+ td = uhci_alloc_td(uhci);
+ if (!td)
+@@ -978,6 +1014,7 @@ static int uhci_submit_isochronous(struc
+ td->status |= __constant_cpu_to_le32(TD_CTRL_IOC);
+
+ qh->skel = uhci->skel_iso_qh;
++ qh->period = urb->interval;
+
+ /* Add the TDs to the frame list */
+ frame = urb->start_frame;
+@@ -1206,6 +1243,7 @@ __acquires(uhci->lock)
+ uhci_unlink_qh(uhci, qh);
+
+ /* Bandwidth stuff not yet implemented */
++ qh->period = 0;
+ }
+ }
+
diff --git a/usb/uhci-use-integer-sized-frame-numbers.patch b/usb/uhci-use-integer-sized-frame-numbers.patch
new file mode 100644
index 0000000000000..a8f9e05294b9c
--- /dev/null
+++ b/usb/uhci-use-integer-sized-frame-numbers.patch
@@ -0,0 +1,149 @@
+From stern@rowland.harvard.edu Fri May 19 13:35:02 2006
+Date: Fri, 19 May 2006 16:34:57 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Subject: UHCI: use integer-sized frame numbers
+Message-ID: <Pine.LNX.4.44L0.0605191628330.5761-100000@iolanthe.rowland.org>
+
+This patch (as687) changes uhci-hcd to keep track of frame numbers as
+full-sized integers rather than 11-bit values. This makes them a lot
+easier to handle and makes it possible to schedule beyond a 2-second
+window, should anyone ever want to do so.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/uhci-debug.c | 4 +++-
+ drivers/usb/host/uhci-hcd.c | 40 ++++++++++++++++++++++------------------
+ drivers/usb/host/uhci-hcd.h | 3 +++
+ 3 files changed, 28 insertions(+), 19 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/host/uhci-debug.c
++++ gregkh-2.6/drivers/usb/host/uhci-debug.c
+@@ -289,7 +289,7 @@ static int uhci_show_status(struct uhci_
+ unsigned short portsc1, portsc2;
+
+ /* Try to make sure there's enough memory */
+- if (len < 80 * 6)
++ if (len < 80 * 9)
+ return 0;
+
+ usbcmd = inw(io_addr + 0);
+@@ -328,6 +328,8 @@ static int uhci_show_status(struct uhci_
+ out += sprintf(out, " sof = %02x\n", sof);
+ out += uhci_show_sc(1, portsc1, out, len - (out - buf));
+ out += uhci_show_sc(2, portsc2, out, len - (out - buf));
++ out += sprintf(out, "Most recent frame: %x\n",
++ uhci->frame_number);
+
+ return out - buf;
+ }
+--- gregkh-2.6.orig/drivers/usb/host/uhci-hcd.c
++++ gregkh-2.6/drivers/usb/host/uhci-hcd.c
+@@ -13,7 +13,7 @@
+ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
+ * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
+ * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
+- * (C) Copyright 2004-2005 Alan Stern, stern@rowland.harvard.edu
++ * (C) Copyright 2004-2006 Alan Stern, stern@rowland.harvard.edu
+ *
+ * Intel documents this fairly well, and as far as I know there
+ * are no royalties or anything like that, but even so there are
+@@ -31,7 +31,6 @@
+ #include <linux/ioport.h>
+ #include <linux/sched.h>
+ #include <linux/slab.h>
+-#include <linux/smp_lock.h>
+ #include <linux/errno.h>
+ #include <linux/unistd.h>
+ #include <linux/interrupt.h>
+@@ -146,7 +145,8 @@ static void configure_hc(struct uhci_hcd
+ outl(uhci->frame_dma_handle, uhci->io_addr + USBFLBASEADD);
+
+ /* Set the current frame number */
+- outw(uhci->frame_number, uhci->io_addr + USBFRNUM);
++ outw(uhci->frame_number & UHCI_MAX_SOF_NUMBER,
++ uhci->io_addr + USBFRNUM);
+
+ /* Mark controller as not halted before we enable interrupts */
+ uhci_to_hcd(uhci)->state = HC_STATE_SUSPENDED;
+@@ -239,7 +239,6 @@ __acquires(uhci->lock)
+ dev_warn(uhci_dev(uhci), "Controller not stopped yet!\n");
+
+ uhci_get_current_frame_number(uhci);
+- smp_wmb();
+
+ uhci->rh_state = new_state;
+ uhci->is_stopped = UHCI_IS_STOPPED;
+@@ -253,7 +252,6 @@ static void start_rh(struct uhci_hcd *uh
+ {
+ uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
+ uhci->is_stopped = 0;
+- smp_wmb();
+
+ /* Mark it configured and running with a 64-byte max packet.
+ * All interrupts are enabled, even though RESUME won't do anything.
+@@ -360,12 +358,21 @@ static irqreturn_t uhci_irq(struct usb_h
+
+ /*
+ * Store the current frame number in uhci->frame_number if the controller
+- * is runnning
++ * is runnning. Expand from 11 bits (of which we use only 10) to a
++ * full-sized integer.
++ *
++ * Like many other parts of the driver, this code relies on being polled
++ * more than once per second as long as the controller is running.
+ */
+ static void uhci_get_current_frame_number(struct uhci_hcd *uhci)
+ {
+- if (!uhci->is_stopped)
+- uhci->frame_number = inw(uhci->io_addr + USBFRNUM);
++ if (!uhci->is_stopped) {
++ unsigned delta;
++
++ delta = (inw(uhci->io_addr + USBFRNUM) - uhci->frame_number) &
++ (UHCI_NUMFRAMES - 1);
++ uhci->frame_number += delta;
++ }
+ }
+
+ /*
+@@ -798,18 +805,15 @@ done:
+ static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
+ {
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+- unsigned long flags;
+- int is_stopped;
+- int frame_number;
++ unsigned frame_number;
++ unsigned delta;
+
+ /* Minimize latency by avoiding the spinlock */
+- local_irq_save(flags);
+- is_stopped = uhci->is_stopped;
+- smp_rmb();
+- frame_number = (is_stopped ? uhci->frame_number :
+- inw(uhci->io_addr + USBFRNUM));
+- local_irq_restore(flags);
+- return frame_number;
++ frame_number = uhci->frame_number;
++ barrier();
++ delta = (inw(uhci->io_addr + USBFRNUM) - frame_number) &
++ (UHCI_NUMFRAMES - 1);
++ return frame_number + delta;
+ }
+
+ static const char hcd_name[] = "uhci_hcd";
+--- gregkh-2.6.orig/drivers/usb/host/uhci-hcd.h
++++ gregkh-2.6/drivers/usb/host/uhci-hcd.h
+@@ -448,6 +448,9 @@ static inline struct usb_hcd *uhci_to_hc
+
+ #define uhci_dev(u) (uhci_to_hcd(u)->self.controller)
+
++/* Utility macro for comparing frame numbers */
++#define uhci_frame_before_eq(f1, f2) (0 <= (int) ((f2) - (f1)))
++
+
+ /*
+ * Private per-URB data
diff --git a/usb/usb-allow-high-bandwidth-isochronous-packets-via-usbfs.patch b/usb/usb-allow-high-bandwidth-isochronous-packets-via-usbfs.patch
new file mode 100644
index 0000000000000..d0b1aebcab5bf
--- /dev/null
+++ b/usb/usb-allow-high-bandwidth-isochronous-packets-via-usbfs.patch
@@ -0,0 +1,33 @@
+From micah@vmware.com Fri May 19 11:26:27 2006
+Message-ID: <446E0DD0.2070907@vmware.com>
+Date: Fri, 19 May 2006 11:26:24 -0700
+From: Micah Dowty <micah@vmware.com>
+To: linux-usb-devel@lists.sourceforge.net
+Cc: Greg KH <greg@kroah.com>
+Subject: USB: Allow high-bandwidth isochronous packets via usbfs
+
+This patch increases an arbitrary limit on the size of
+individual isochronous packets submitted via usbfs. The
+limit is still arbitrary, but it's now large enough to
+support the maximum packet size used by high-bandwidth
+isochronous transfers.
+
+Signed-off-by: Micah Dowty <micah@vmware.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/devio.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- gregkh-2.6.orig/drivers/usb/core/devio.c
++++ gregkh-2.6/drivers/usb/core/devio.c
+@@ -982,7 +982,8 @@ static int proc_do_submiturb(struct dev_
+ return -EFAULT;
+ }
+ for (totlen = u = 0; u < uurb->number_of_packets; u++) {
+- if (isopkt[u].length > 1023) {
++ /* arbitrary limit, sufficient for USB 2.0 high-bandwidth iso */
++ if (isopkt[u].length > 8192) {
+ kfree(isopkt);
+ return -EINVAL;
+ }
diff --git a/usb/usb-correct-the-usb-info-in-documentation-power-swsusp.txt.patch b/usb/usb-correct-the-usb-info-in-documentation-power-swsusp.txt.patch
new file mode 100644
index 0000000000000..1b0b43c66ba82
--- /dev/null
+++ b/usb/usb-correct-the-usb-info-in-documentation-power-swsusp.txt.patch
@@ -0,0 +1,88 @@
+From david-b@pacbell.net Tue May 16 17:42:31 2006
+From: David Brownell <david-b@pacbell.net>
+To: Greg KH <greg@kroah.com>
+Subject: USB: correct the USB info in Documentation/power/swsusp.txt
+Date: Tue, 16 May 2006 17:33:14 -0700
+Cc: linux-pm@lists.osdl.org
+Message-Id: <200605161733.15144.david-b@pacbell.net>
+
+
+The swsusp.txt documentation harshes confusingly on USB, and this patch
+addresses the issue. It's harsh because it blames USB for some issues
+that are generic to all drivers -- especially those supporting removable
+media -- and it's confusing since it says that USB has the issue with
+"suspend" not just swsusp ... while in reality, USB doesn't have the
+issue when real system suspend states are used.
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Acked-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ Documentation/power/swsusp.txt | 39 ++++++++++++++++++++++++---------------
+ 1 file changed, 24 insertions(+), 15 deletions(-)
+
+--- gregkh-2.6.orig/Documentation/power/swsusp.txt
++++ gregkh-2.6/Documentation/power/swsusp.txt
+@@ -18,10 +18,11 @@ Some warnings, first.
+ *
+ * (*) suspend/resume support is needed to make it safe.
+ *
+- * If you have any filesystems on USB devices mounted before suspend,
++ * If you have any filesystems on USB devices mounted before software suspend,
+ * they won't be accessible after resume and you may lose data, as though
+- * you have unplugged the USB devices with mounted filesystems on them
+- * (see the FAQ below for details).
++ * you have unplugged the USB devices with mounted filesystems on them;
++ * see the FAQ below for details. (This is not true for more traditional
++ * power states like "standby", which normally don't turn USB off.)
+
+ You need to append resume=/dev/your_swap_partition to kernel command
+ line. Then you suspend by
+@@ -204,7 +205,7 @@ Q: There don't seem to be any generally
+ distinctions between SUSPEND and FREEZE.
+
+ A: Doing SUSPEND when you are asked to do FREEZE is always correct,
+-but it may be unneccessarily slow. If you want USB to stay simple,
++but it may be unneccessarily slow. If you want your driver to stay simple,
+ slowness may not matter to you. It can always be fixed later.
+
+ For devices like disk it does matter, you do not want to spindown for
+@@ -357,17 +358,25 @@ Q: Is this true that if I have a mounted
+ I suspend to disk, I can lose data unless the filesystem has been mounted
+ with "sync"?
+
+-A: That's right. It depends on your hardware, and it could be true even for
+-suspend-to-RAM. In fact, even with "-o sync" you can lose data if your
+-programs have information in buffers they haven't written out to disk.
+-
+-If you're lucky, your hardware will support low-power modes for USB
+-controllers while the system is asleep. Lots of hardware doesn't,
+-however. Shutting off the power to a USB controller is equivalent to
+-unplugging all the attached devices.
++A: That's right ... if you disconnect that device, you may lose data.
++In fact, even with "-o sync" you can lose data if your programs have
++information in buffers they haven't written out to a disk you disconnect,
++or if you disconnect before the device finished saving data you wrote.
++
++Software suspend normally powers down USB controllers, which is equivalent
++to disconnecting all USB devices attached to your system.
++
++Your system might well support low-power modes for its USB controllers
++while the system is asleep, maintaining the connection, using true sleep
++modes like "suspend-to-RAM" or "standby". (Don't write "disk" to the
++/sys/power/state file; write "standby" or "mem".) We've not seen any
++hardware that can use these modes through software suspend, although in
++theory some systems might support "platform" or "firmware" modes that
++won't break the USB connections.
+
+ Remember that it's always a bad idea to unplug a disk drive containing a
+-mounted filesystem. With USB that's true even when your system is asleep!
+-The safest thing is to unmount all USB-based filesystems before suspending
+-and remount them after resuming.
++mounted filesystem. That's true even when your system is asleep! The
++safest thing is to unmount all filesystems on removable media (such USB,
++Firewire, CompactFlash, MMC, external SATA, or even IDE hotplug bays)
++before suspending; then remount them after resuming.
+
diff --git a/usb/usb-remove-4088-byte-limit-on-usbfs-control-urbs.patch b/usb/usb-remove-4088-byte-limit-on-usbfs-control-urbs.patch
new file mode 100644
index 0000000000000..eac62622620ea
--- /dev/null
+++ b/usb/usb-remove-4088-byte-limit-on-usbfs-control-urbs.patch
@@ -0,0 +1,46 @@
+From micah@vmware.com Fri May 19 11:20:23 2006
+Message-ID: <446E0C5B.3080204@vmware.com>
+Date: Fri, 19 May 2006 11:20:11 -0700
+From: Micah Dowty <micah@vmware.com>
+To: linux-usb-devel@lists.sourceforge.net
+Cc: Greg KH <greg@kroah.com>
+Subject: USB: Remove 4088-byte limit on usbfs control URBs
+
+This patch removes the artificial 4088-byte limit that usbfs
+currently places on Control transfers. The USB spec does not
+specify a strict limit on the size of an entire control transfer.
+It does, however, state that the data stage "follows the same
+protocol rules as bulk transfers." (USB 2, 8.5.3)
+
+The level of support for large control transfers in real host
+controllers varies, but it's important to support at least 4K
+transfers. Windows enforces a maximum control transfer size
+of 4K, so there exists some hardware that requires a full 4096
+byte data stage. Without this patch, we fall short of that by
+8 bytes on architectures with a 4K page size, and it becomes
+impossible to support such hardware with a user-space driver.
+
+Since any limit placed on control transfers by usbfs would be
+arbitrary, this patch replaces the PAGE_SIZE limit with the same
+arbitrary limit used by bulk transfers.
+
+Signed-off-by: Micah Dowty <micah@vmware.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/devio.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/core/devio.c
++++ gregkh-2.6/drivers/usb/core/devio.c
+@@ -923,8 +923,8 @@ static int proc_do_submiturb(struct dev_
+ if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ != USB_ENDPOINT_XFER_CONTROL)
+ return -EINVAL;
+- /* min 8 byte setup packet, max arbitrary */
+- if (uurb->buffer_length < 8 || uurb->buffer_length > PAGE_SIZE)
++ /* min 8 byte setup packet, max 8 byte setup plus an arbitrary data stage */
++ if (uurb->buffer_length < 8 || uurb->buffer_length > (8 + MAX_USBFS_BUFFER_SIZE))
+ return -EINVAL;
+ if (!(dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL)))
+ return -ENOMEM;