diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2006-05-19 15:59:01 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-05-19 15:59:01 -0700 |
commit | f946bcab1c35689ca563072cde8ba3a4844fbd14 (patch) | |
tree | 3a684c968562ee2b8d489fee59cc9aff828a4c49 /usb | |
parent | 25041f79438a354d98305ea5650676997d915670 (diff) | |
download | patches-f946bcab1c35689ca563072cde8ba3a4844fbd14.tar.gz |
usb patches added
Diffstat (limited to 'usb')
-rw-r--r-- | usb/fix-a-deadlock-in-usbtest.patch | 35 | ||||
-rw-r--r-- | usb/uhci-fix-race-in-iso-dequeuing.patch | 143 | ||||
-rw-r--r-- | usb/uhci-remove-iso-tds-as-they-are-used.patch | 301 | ||||
-rw-r--r-- | usb/uhci-store-the-period-in-the-queue-header.patch | 220 | ||||
-rw-r--r-- | usb/uhci-use-integer-sized-frame-numbers.patch | 149 | ||||
-rw-r--r-- | usb/usb-allow-high-bandwidth-isochronous-packets-via-usbfs.patch | 33 | ||||
-rw-r--r-- | usb/usb-correct-the-usb-info-in-documentation-power-swsusp.txt.patch | 88 | ||||
-rw-r--r-- | usb/usb-remove-4088-byte-limit-on-usbfs-control-urbs.patch | 46 |
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; |