aboutsummaryrefslogtreecommitdiffstats
path: root/usb
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2006-05-16 15:07:29 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2006-05-16 15:07:29 -0700
commitf0e3ae84732e61fe198a11197b7d0bef9952a6c5 (patch)
tree958882e94d5370ed4c96220724f3493f3d0256b1 /usb
parent91e341fcb2ec42630ceb6a9c5a38396f033116f6 (diff)
downloadpatches-f0e3ae84732e61fe198a11197b7d0bef9952a6c5.tar.gz
usb patches added
Diffstat (limited to 'usb')
-rw-r--r--usb/uhci-common-result-routine-for-control-bulk-interrupt.patch394
-rw-r--r--usb/uhci-eliminate-the-td-removal-list.patch295
-rw-r--r--usb/uhci-move-code-for-cleaning-up-unlinked-urbs.patch108
-rw-r--r--usb/uhci-reimplement-fsbr.patch399
-rw-r--r--usb/uhci-remove-non-iso-tds-as-they-are-used.patch237
-rw-r--r--usb/uhci-work-around-old-intel-bug.patch58
-rw-r--r--usb/usb-add-yealink-phones-to-the-hid_quirk_ignore-blacklist.patch43
-rw-r--r--usb/usb-cdc-acm-add-a-new-special-case-for-modems-with-buggy-firmware.patch306
-rw-r--r--usb/usb-hid-hidbp-input-drivers-fix-various-usb-input-hid-input.c-bugs-that-make-apple-mighty-mouse-work-poorly.patch2
-rw-r--r--usb/usb-hid-read-busywait-fix.patch32
-rw-r--r--usb/usb-ohci-avoids-root-hub-timer-polling.patch287
-rw-r--r--usb/usb-usbnet-zaurus-mtu-fixup.patch84
-rw-r--r--usb/usbhid-automatically-set-hid_quirk_noget-for-keyboards-and-mice.patch63
13 files changed, 2307 insertions, 1 deletions
diff --git a/usb/uhci-common-result-routine-for-control-bulk-interrupt.patch b/usb/uhci-common-result-routine-for-control-bulk-interrupt.patch
new file mode 100644
index 0000000000000..964e49a1b2187
--- /dev/null
+++ b/usb/uhci-common-result-routine-for-control-bulk-interrupt.patch
@@ -0,0 +1,394 @@
+From stern@rowland.harvard.edu Fri May 12 08:14:30 2006
+Date: Fri, 12 May 2006 11:14:25 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Subject: UHCI: Common result routine for Control/Bulk/Interrupt
+Message-ID: <Pine.LNX.4.44L0.0605121108040.6415-100000@iolanthe.rowland.org>
+
+This patch (as679) combines the result routine for Control URBs with the
+routine for Bulk/Interrupt URBs. Along the way I eliminated the
+debugging printouts for Control transfers unless the debugging level is
+set higher than 1. I also eliminated a long-unused (#ifdef'ed-out)
+section that works around some buggy old APC BackUPS devices.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/uhci-q.c | 342 ++++++++++++++++------------------------------
+ 1 file changed, 125 insertions(+), 217 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/host/uhci-q.c
++++ gregkh-2.6/drivers/usb/host/uhci-q.c
+@@ -619,134 +619,6 @@ nomem:
+ }
+
+ /*
+- * If control-IN transfer was short, the status packet wasn't sent.
+- * This routine changes the element pointer in the QH to point at the
+- * status TD. It's safe to do this even while the QH is live, because
+- * the hardware only updates the element pointer following a successful
+- * transfer. The inactive TD for the short packet won't cause an update,
+- * so the pointer won't get overwritten. The next time the controller
+- * sees this QH, it will send the status packet.
+- */
+-static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb)
+-{
+- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+- struct uhci_td *td;
+-
+- urbp->short_transfer = 1;
+-
+- td = list_entry(urbp->td_list.prev, struct uhci_td, list);
+- urbp->qh->element = cpu_to_le32(td->dma_handle);
+-
+- return -EINPROGRESS;
+-}
+-
+-
+-static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb)
+-{
+- struct list_head *tmp, *head;
+- struct urb_priv *urbp = urb->hcpriv;
+- struct uhci_td *td;
+- unsigned int status;
+- int ret = 0;
+-
+- head = &urbp->td_list;
+- if (urbp->short_transfer) {
+- tmp = head->prev;
+- goto status_stage;
+- }
+-
+- urb->actual_length = 0;
+-
+- tmp = head->next;
+- td = list_entry(tmp, struct uhci_td, list);
+-
+- /* The first TD is the SETUP stage, check the status, but skip */
+- /* the count */
+- status = uhci_status_bits(td_status(td));
+- if (status & TD_CTRL_ACTIVE)
+- return -EINPROGRESS;
+-
+- if (status)
+- goto td_error;
+-
+- /* The rest of the TDs (but the last) are data */
+- tmp = tmp->next;
+- while (tmp != head && tmp->next != head) {
+- unsigned int ctrlstat;
+-
+- td = list_entry(tmp, struct uhci_td, list);
+- tmp = tmp->next;
+-
+- ctrlstat = td_status(td);
+- status = uhci_status_bits(ctrlstat);
+- if (status & TD_CTRL_ACTIVE)
+- return -EINPROGRESS;
+-
+- urb->actual_length += uhci_actual_length(ctrlstat);
+-
+- if (status)
+- goto td_error;
+-
+- /* Check to see if we received a short packet */
+- if (uhci_actual_length(ctrlstat) <
+- uhci_expected_length(td_token(td))) {
+- if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+- ret = -EREMOTEIO;
+- goto err;
+- }
+-
+- return usb_control_retrigger_status(uhci, urb);
+- }
+- }
+-
+-status_stage:
+- td = list_entry(tmp, struct uhci_td, list);
+-
+- /* Control status stage */
+- status = td_status(td);
+-
+-#ifdef I_HAVE_BUGGY_APC_BACKUPS
+- /* APC BackUPS Pro kludge */
+- /* It tries to send all of the descriptor instead of the amount */
+- /* we requested */
+- if (status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */
+- status & TD_CTRL_ACTIVE &&
+- status & TD_CTRL_NAK)
+- return 0;
+-#endif
+-
+- status = uhci_status_bits(status);
+- if (status & TD_CTRL_ACTIVE)
+- return -EINPROGRESS;
+-
+- if (status)
+- goto td_error;
+-
+- return 0;
+-
+-td_error:
+- ret = uhci_map_status(status, uhci_packetout(td_token(td)));
+-
+-err:
+- if ((debug == 1 && ret != -EPIPE) || debug > 1) {
+- /* Some debugging code */
+- dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n",
+- __FUNCTION__, status);
+-
+- if (errbuf) {
+- /* Print the chain for debugging purposes */
+- uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
+- lprintk(errbuf);
+- }
+- }
+-
+- /* Note that the queue has stopped */
+- urbp->qh->element = UHCI_PTR_TERM;
+- urbp->qh->is_stopped = 1;
+- return ret;
+-}
+-
+-/*
+ * Common submit for bulk and interrupt
+ */
+ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
+@@ -864,86 +736,6 @@ nomem:
+ return -ENOMEM;
+ }
+
+-/*
+- * Common result for bulk and interrupt
+- */
+-static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
+-{
+- struct urb_priv *urbp = urb->hcpriv;
+- struct uhci_td *td;
+- unsigned int status = 0;
+- int ret = 0;
+-
+- urb->actual_length = 0;
+-
+- list_for_each_entry(td, &urbp->td_list, list) {
+- unsigned int ctrlstat = td_status(td);
+-
+- status = uhci_status_bits(ctrlstat);
+- if (status & TD_CTRL_ACTIVE)
+- return -EINPROGRESS;
+-
+- urb->actual_length += uhci_actual_length(ctrlstat);
+-
+- if (status)
+- goto td_error;
+-
+- if (uhci_actual_length(ctrlstat) <
+- uhci_expected_length(td_token(td))) {
+- if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+- ret = -EREMOTEIO;
+- goto err;
+- }
+-
+- /*
+- * This URB stopped short of its end. We have to
+- * fix up the toggles of the following URBs on the
+- * queue and restart the queue.
+- *
+- * Do this only the first time we encounter the
+- * short URB.
+- */
+- if (!urbp->short_transfer) {
+- urbp->short_transfer = 1;
+- urbp->qh->initial_toggle =
+- uhci_toggle(td_token(td)) ^ 1;
+- uhci_fixup_toggles(urbp->qh, 1);
+-
+- td = list_entry(urbp->td_list.prev,
+- struct uhci_td, list);
+- urbp->qh->element = td->link;
+- }
+- break;
+- }
+- }
+-
+- return 0;
+-
+-td_error:
+- ret = uhci_map_status(status, uhci_packetout(td_token(td)));
+-
+- if ((debug == 1 && ret != -EPIPE) || debug > 1) {
+- /* Some debugging code */
+- dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n",
+- __FUNCTION__, status);
+-
+- if (debug > 1 && errbuf) {
+- /* Print the chain for debugging purposes */
+- uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
+- lprintk(errbuf);
+- }
+- }
+-err:
+-
+- /* Note that the queue has stopped and save the next toggle value */
+- urbp->qh->element = UHCI_PTR_TERM;
+- urbp->qh->is_stopped = 1;
+- urbp->qh->needs_fixup = 1;
+- urbp->qh->initial_toggle = uhci_toggle(td_token(td)) ^
+- (ret == -EREMOTEIO);
+- return ret;
+-}
+-
+ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
+ struct uhci_qh *qh)
+ {
+@@ -972,6 +764,129 @@ static inline int uhci_submit_interrupt(
+ }
+
+ /*
++ * Fix up the data structures following a short transfer
++ */
++static int uhci_fixup_short_transfer(struct uhci_hcd *uhci,
++ struct uhci_qh *qh, struct urb_priv *urbp,
++ struct uhci_td *short_td)
++{
++ struct uhci_td *td;
++ int ret = 0;
++
++ td = list_entry(urbp->td_list.prev, struct uhci_td, list);
++ if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
++ urbp->short_transfer = 1;
++
++ /* When a control transfer is short, we have to restart
++ * the queue at the status stage transaction, which is
++ * the last TD. */
++ qh->element = cpu_to_le32(td->dma_handle);
++ ret = -EINPROGRESS;
++
++ } else if (!urbp->short_transfer) {
++ urbp->short_transfer = 1;
++
++ /* When a bulk/interrupt transfer is short, we have to
++ * fix up the toggles of the following URBs on the queue
++ * before restarting the queue at the next URB. */
++ qh->initial_toggle = uhci_toggle(td_token(short_td)) ^ 1;
++ uhci_fixup_toggles(qh, 1);
++
++ qh->element = td->link;
++ }
++
++ return ret;
++}
++
++/*
++ * Common result for control, bulk, and interrupt
++ */
++static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
++{
++ struct urb_priv *urbp = urb->hcpriv;
++ struct uhci_qh *qh = urbp->qh;
++ struct uhci_td *td;
++ struct list_head *tmp;
++ unsigned status;
++ int ret = 0;
++
++ tmp = urbp->td_list.next;
++
++ if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
++ if (urbp->short_transfer)
++ tmp = urbp->td_list.prev;
++ else
++ urb->actual_length = -8; /* SETUP packet */
++ } else
++ urb->actual_length = 0;
++
++
++ while (tmp != &urbp->td_list) {
++ unsigned int ctrlstat;
++ int len;
++
++ td = list_entry(tmp, struct uhci_td, list);
++ tmp = tmp->next;
++
++ ctrlstat = td_status(td);
++ status = uhci_status_bits(ctrlstat);
++ if (status & TD_CTRL_ACTIVE)
++ return -EINPROGRESS;
++
++ len = uhci_actual_length(ctrlstat);
++ urb->actual_length += len;
++
++ if (status) {
++ ret = uhci_map_status(status,
++ uhci_packetout(td_token(td)));
++ if ((debug == 1 && ret != -EPIPE) || debug > 1) {
++ /* Some debugging code */
++ dev_dbg(uhci_dev(uhci),
++ "%s: failed with status %x\n",
++ __FUNCTION__, status);
++
++ if (debug > 1 && errbuf) {
++ /* Print the chain for debugging */
++ uhci_show_qh(urbp->qh, errbuf,
++ ERRBUF_LEN, 0);
++ lprintk(errbuf);
++ }
++ }
++
++ } else if (len < uhci_expected_length(td_token(td))) {
++
++ /* We received a short packet */
++ if (urb->transfer_flags & URB_SHORT_NOT_OK)
++ ret = -EREMOTEIO;
++ else if (ctrlstat & TD_CTRL_SPD)
++ ret = 1;
++ }
++
++ if (ret != 0)
++ goto err;
++ }
++ return ret;
++
++err:
++ if (ret < 0) {
++ /* In case a control transfer gets an error
++ * during the setup stage */
++ urb->actual_length = max(urb->actual_length, 0);
++
++ /* Note that the queue has stopped and save
++ * the next toggle value */
++ qh->element = UHCI_PTR_TERM;
++ qh->is_stopped = 1;
++ qh->needs_fixup = (qh->type != USB_ENDPOINT_XFER_CONTROL);
++ qh->initial_toggle = uhci_toggle(td_token(td)) ^
++ (ret == -EREMOTEIO);
++
++ } else /* Short packet received */
++ ret = uhci_fixup_short_transfer(uhci, qh, urbp, td);
++ return ret;
++}
++
++/*
+ * Isochronous transfers
+ */
+ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
+@@ -1276,17 +1191,10 @@ static void uhci_scan_qh(struct uhci_hcd
+ urbp = list_entry(qh->queue.next, struct urb_priv, node);
+ urb = urbp->urb;
+
+- switch (qh->type) {
+- case USB_ENDPOINT_XFER_CONTROL:
+- status = uhci_result_control(uhci, urb);
+- break;
+- case USB_ENDPOINT_XFER_ISOC:
++ if (qh->type == USB_ENDPOINT_XFER_ISOC)
+ status = uhci_result_isochronous(uhci, urb);
+- break;
+- default: /* USB_ENDPOINT_XFER_BULK or _INT */
++ else
+ status = uhci_result_common(uhci, urb);
+- break;
+- }
+ if (status == -EINPROGRESS)
+ break;
+
diff --git a/usb/uhci-eliminate-the-td-removal-list.patch b/usb/uhci-eliminate-the-td-removal-list.patch
new file mode 100644
index 0000000000000..1ee74b5d4308e
--- /dev/null
+++ b/usb/uhci-eliminate-the-td-removal-list.patch
@@ -0,0 +1,295 @@
+From stern@rowland.harvard.edu Fri May 12 08:29:09 2006
+Date: Fri, 12 May 2006 11:29:04 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Subject: UHCI: Eliminate the TD-removal list
+Message-ID: <Pine.LNX.4.44L0.0605121123200.6415-100000@iolanthe.rowland.org>
+
+This patch (as682) gets rid of the TD-removal list in uhci-hcd. It is
+no longer needed because now TDs are not freed until we know the
+hardware isn't using them. It also simplifies the code for adding and
+removing TDs to/from URBs.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/uhci-hcd.c | 1
+ drivers/usb/host/uhci-hcd.h | 5 --
+ drivers/usb/host/uhci-q.c | 92 ++++++++++++--------------------------------
+ 3 files changed, 27 insertions(+), 71 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/host/uhci-hcd.c
++++ gregkh-2.6/drivers/usb/host/uhci-hcd.c
+@@ -492,7 +492,6 @@ static int uhci_start(struct usb_hcd *hc
+
+ spin_lock_init(&uhci->lock);
+
+- INIT_LIST_HEAD(&uhci->td_remove_list);
+ INIT_LIST_HEAD(&uhci->idle_qh_list);
+
+ init_waitqueue_head(&uhci->waitqh);
+--- gregkh-2.6.orig/drivers/usb/host/uhci-hcd.h
++++ gregkh-2.6/drivers/usb/host/uhci-hcd.h
+@@ -228,7 +228,6 @@ struct uhci_td {
+ dma_addr_t dma_handle;
+
+ struct list_head list;
+- struct list_head remove_list;
+
+ int frame; /* for iso: what frame? */
+ struct list_head fl_list;
+@@ -420,10 +419,6 @@ struct uhci_hcd {
+ unsigned long resuming_ports;
+ unsigned long ports_timeout; /* Time to stop signalling */
+
+- /* List of TDs that are done, but waiting to be freed (race) */
+- struct list_head td_remove_list;
+- unsigned int td_remove_age; /* Age in frames */
+-
+ struct list_head idle_qh_list; /* Where the idle QHs live */
+
+ int rh_numports; /* Number of root-hub ports */
+--- gregkh-2.6.orig/drivers/usb/host/uhci-q.c
++++ gregkh-2.6/drivers/usb/host/uhci-q.c
+@@ -16,7 +16,6 @@
+ * (C) Copyright 2004-2005 Alan Stern, stern@rowland.harvard.edu
+ */
+
+-static void uhci_free_pending_tds(struct uhci_hcd *uhci);
+
+ /*
+ * Technically, updating td->status here is a race, but it's not really a
+@@ -51,7 +50,6 @@ static struct uhci_td *uhci_alloc_td(str
+ td->frame = -1;
+
+ INIT_LIST_HEAD(&td->list);
+- INIT_LIST_HEAD(&td->remove_list);
+ INIT_LIST_HEAD(&td->fl_list);
+
+ return td;
+@@ -61,8 +59,6 @@ static void uhci_free_td(struct uhci_hcd
+ {
+ if (!list_empty(&td->list))
+ dev_warn(uhci_dev(uhci), "td %p still in list!\n", td);
+- if (!list_empty(&td->remove_list))
+- dev_warn(uhci_dev(uhci), "td %p still in remove_list!\n", td);
+ if (!list_empty(&td->fl_list))
+ dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td);
+
+@@ -77,6 +73,16 @@ static inline void uhci_fill_td(struct u
+ td->buffer = cpu_to_le32(buffer);
+ }
+
++static void uhci_add_td_to_urbp(struct uhci_td *td, struct urb_priv *urbp)
++{
++ list_add_tail(&td->list, &urbp->td_list);
++}
++
++static void uhci_remove_td_from_urbp(struct uhci_td *td)
++{
++ list_del_init(&td->list);
++}
++
+ /*
+ * We insert Isochronous URBs directly into the frame list at the beginning
+ */
+@@ -421,21 +427,6 @@ static inline struct urb_priv *uhci_allo
+ return urbp;
+ }
+
+-static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
+-{
+- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+-
+- list_add_tail(&td->list, &urbp->td_list);
+-}
+-
+-static void uhci_remove_td_from_urb(struct uhci_td *td)
+-{
+- if (list_empty(&td->list))
+- return;
+-
+- list_del_init(&td->list);
+-}
+-
+ static void uhci_free_urb_priv(struct uhci_hcd *uhci,
+ struct urb_priv *urbp)
+ {
+@@ -445,20 +436,9 @@ static void uhci_free_urb_priv(struct uh
+ dev_warn(uhci_dev(uhci), "urb %p still on QH's list!\n",
+ urbp->urb);
+
+- uhci_get_current_frame_number(uhci);
+- if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age) {
+- uhci_free_pending_tds(uhci);
+- uhci->td_remove_age = uhci->frame_number;
+- }
+-
+- /* Check to see if the remove list is empty. Set the IOC bit */
+- /* to force an interrupt so we can remove the TDs. */
+- if (list_empty(&uhci->td_remove_list))
+- uhci_set_next_interrupt(uhci);
+-
+ list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
+- uhci_remove_td_from_urb(td);
+- list_add(&td->remove_list, &uhci->td_remove_list);
++ uhci_remove_td_from_urbp(td);
++ uhci_free_td(uhci, td);
+ }
+
+ urbp->urb->hcpriv = NULL;
+@@ -529,6 +509,7 @@ static int uhci_submit_control(struct uh
+ int len = urb->transfer_buffer_length;
+ dma_addr_t data = urb->transfer_dma;
+ __le32 *plink;
++ struct urb_priv *urbp = urb->hcpriv;
+
+ /* The "pipe" thing contains the destination in bits 8--18 */
+ destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
+@@ -542,7 +523,7 @@ static int uhci_submit_control(struct uh
+ * Build the TD for the control request setup packet
+ */
+ td = qh->dummy_td;
+- uhci_add_td_to_urb(urb, td);
++ uhci_add_td_to_urbp(td, urbp);
+ uhci_fill_td(td, status, destination | uhci_explen(8),
+ urb->setup_dma);
+ plink = &td->link;
+@@ -574,7 +555,7 @@ static int uhci_submit_control(struct uh
+ /* Alternate Data0/1 (start with Data1) */
+ destination ^= TD_TOKEN_TOGGLE;
+
+- uhci_add_td_to_urb(urb, td);
++ uhci_add_td_to_urbp(td, urbp);
+ uhci_fill_td(td, status, destination | uhci_explen(pktsze),
+ data);
+ plink = &td->link;
+@@ -605,7 +586,7 @@ static int uhci_submit_control(struct uh
+
+ status &= ~TD_CTRL_SPD;
+
+- uhci_add_td_to_urb(urb, td);
++ uhci_add_td_to_urbp(td, urbp);
+ uhci_fill_td(td, status | TD_CTRL_IOC,
+ destination | uhci_explen(0), 0);
+ plink = &td->link;
+@@ -640,7 +621,7 @@ static int uhci_submit_control(struct uh
+
+ nomem:
+ /* Remove the dummy TD from the td_list so it doesn't get freed */
+- uhci_remove_td_from_urb(qh->dummy_td);
++ uhci_remove_td_from_urbp(qh->dummy_td);
+ return -ENOMEM;
+ }
+
+@@ -656,6 +637,7 @@ static int uhci_submit_common(struct uhc
+ int len = urb->transfer_buffer_length;
+ dma_addr_t data = urb->transfer_dma;
+ __le32 *plink;
++ struct urb_priv *urbp = urb->hcpriv;
+ unsigned int toggle;
+
+ if (len < 0)
+@@ -693,7 +675,7 @@ static int uhci_submit_common(struct uhc
+ goto nomem;
+ *plink = cpu_to_le32(td->dma_handle);
+ }
+- uhci_add_td_to_urb(urb, td);
++ uhci_add_td_to_urbp(td, urbp);
+ uhci_fill_td(td, status,
+ destination | uhci_explen(pktsze) |
+ (toggle << TD_TOKEN_TOGGLE_SHIFT),
+@@ -721,7 +703,7 @@ static int uhci_submit_common(struct uhc
+ goto nomem;
+ *plink = cpu_to_le32(td->dma_handle);
+
+- uhci_add_td_to_urb(urb, td);
++ uhci_add_td_to_urbp(td, urbp);
+ uhci_fill_td(td, status,
+ destination | uhci_explen(0) |
+ (toggle << TD_TOKEN_TOGGLE_SHIFT),
+@@ -758,7 +740,7 @@ static int uhci_submit_common(struct uhc
+
+ nomem:
+ /* Remove the dummy TD from the td_list so it doesn't get freed */
+- uhci_remove_td_from_urb(qh->dummy_td);
++ uhci_remove_td_from_urbp(qh->dummy_td);
+ return -ENOMEM;
+ }
+
+@@ -830,8 +812,8 @@ static int uhci_fixup_short_transfer(str
+ td = list_entry(tmp, struct uhci_td, list);
+ tmp = tmp->prev;
+
+- uhci_remove_td_from_urb(td);
+- list_add(&td->remove_list, &uhci->td_remove_list);
++ uhci_remove_td_from_urbp(td);
++ uhci_free_td(uhci, td);
+ }
+ return ret;
+ }
+@@ -885,10 +867,9 @@ static int uhci_result_common(struct uhc
+ ret = 1;
+ }
+
+- uhci_remove_td_from_urb(td);
++ uhci_remove_td_from_urbp(td);
+ if (qh->post_td)
+- list_add(&qh->post_td->remove_list,
+- &uhci->td_remove_list);
++ uhci_free_td(uhci, qh->post_td);
+ qh->post_td = td;
+
+ if (ret != 0)
+@@ -957,7 +938,7 @@ static int uhci_submit_isochronous(struc
+ if (!td)
+ return -ENOMEM;
+
+- uhci_add_td_to_urb(urb, td);
++ uhci_add_td_to_urbp(td, urbp);
+ uhci_fill_td(td, status, destination |
+ uhci_explen(urb->iso_frame_desc[i].length),
+ urb->transfer_dma +
+@@ -1267,17 +1248,6 @@ restart:
+ uhci_make_qh_idle(uhci, qh);
+ }
+
+-static void uhci_free_pending_tds(struct uhci_hcd *uhci)
+-{
+- struct uhci_td *td, *tmp;
+-
+- list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) {
+- list_del_init(&td->remove_list);
+-
+- uhci_free_td(uhci, td);
+- }
+-}
+-
+ /*
+ * Process events in the schedule, but only in one thread at a time
+ */
+@@ -1298,9 +1268,6 @@ static void uhci_scan_schedule(struct uh
+ uhci_clear_next_interrupt(uhci);
+ uhci_get_current_frame_number(uhci);
+
+- if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age)
+- uhci_free_pending_tds(uhci);
+-
+ /* Go through all the QH queues and process the URBs in each one */
+ for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) {
+ uhci->next_qh = list_entry(uhci->skelqh[i]->node.next,
+@@ -1316,12 +1283,7 @@ static void uhci_scan_schedule(struct uh
+ goto rescan;
+ uhci->scan_in_progress = 0;
+
+- /* If the controller is stopped, we can finish these off right now */
+- if (uhci->is_stopped)
+- uhci_free_pending_tds(uhci);
+-
+- if (list_empty(&uhci->td_remove_list) &&
+- list_empty(&uhci->skel_unlink_qh->node))
++ if (list_empty(&uhci->skel_unlink_qh->node))
+ uhci_clear_next_interrupt(uhci);
+ else
+ uhci_set_next_interrupt(uhci);
diff --git a/usb/uhci-move-code-for-cleaning-up-unlinked-urbs.patch b/usb/uhci-move-code-for-cleaning-up-unlinked-urbs.patch
new file mode 100644
index 0000000000000..200c41f89ab66
--- /dev/null
+++ b/usb/uhci-move-code-for-cleaning-up-unlinked-urbs.patch
@@ -0,0 +1,108 @@
+From stern@rowland.harvard.edu Fri May 12 08:23:29 2006
+Date: Fri, 12 May 2006 11:23:19 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Subject: UHCI: Move code for cleaning up unlinked URBs
+Message-ID: <Pine.LNX.4.44L0.0605121119210.6415-100000@iolanthe.rowland.org>
+
+This patch (as681) moves some code for cleaning up after unlinked URBs
+out of the general completion pathway into the unlinking pathway.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/uhci-q.c | 57 ++++++++++++++++++++++++++--------------------
+ 1 file changed, 33 insertions(+), 24 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/host/uhci-q.c
++++ gregkh-2.6/drivers/usb/host/uhci-q.c
+@@ -204,25 +204,49 @@ static void uhci_free_qh(struct uhci_hcd
+ }
+
+ /*
+- * When the currently executing URB is dequeued, save its current toggle value
++ * 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).
+ */
+-static void uhci_save_toggle(struct uhci_qh *qh, struct urb *urb)
++static void uhci_cleanup_queue(struct uhci_qh *qh,
++ struct urb *urb)
+ {
+- struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
++ struct urb_priv *urbp = urb->hcpriv;
+ struct uhci_td *td;
+
++ /* 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;
++
++ /* 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
++ * to be saved since this URB can't be executing yet. */
++ if (qh->queue.next != &urbp->node) {
++ struct urb_priv *purbp;
++ struct uhci_td *ptd;
++
++ purbp = list_entry(urbp->node.prev, struct urb_priv, node);
++ WARN_ON(list_empty(&purbp->td_list));
++ ptd = list_entry(purbp->td_list.prev, struct uhci_td,
++ list);
++ td = list_entry(urbp->td_list.prev, struct uhci_td,
++ list);
++ ptd->link = td->link;
++ return;
++ }
++
+ /* 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 ||
+- qh->queue.next != &urbp->node)
++ if (qh_element(qh) == UHCI_PTR_TERM)
+ return;
+ qh->element = UHCI_PTR_TERM;
+
+- /* Only bulk and interrupt pipes have to worry about toggles */
+- if (!(qh->type == USB_ENDPOINT_XFER_BULK ||
+- qh->type == USB_ENDPOINT_XFER_INT))
++ /* Control pipes have to worry about toggles */
++ if (qh->type == USB_ENDPOINT_XFER_CONTROL)
+ return;
+
++ /* 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;
+@@ -1121,21 +1145,6 @@ __acquires(uhci->lock)
+ if (qh->type == USB_ENDPOINT_XFER_ISOC)
+ uhci_unlink_isochronous_tds(uhci, urb);
+
+- /* If the URB isn't first on its queue, adjust the link pointer
+- * of the last TD in the previous URB. */
+- else if (qh->queue.next != &urbp->node) {
+- struct urb_priv *purbp;
+- struct uhci_td *ptd, *ltd;
+-
+- purbp = list_entry(urbp->node.prev, struct urb_priv, node);
+- WARN_ON(list_empty(&purbp->td_list));
+- ptd = list_entry(purbp->td_list.prev, struct uhci_td,
+- list);
+- ltd = list_entry(urbp->td_list.prev, struct uhci_td,
+- list);
+- ptd->link = ltd->link;
+- }
+-
+ /* Take the URB off the QH's queue. If the queue is now empty,
+ * this is a perfect time for a toggle fixup. */
+ list_del_init(&urbp->node);
+@@ -1237,7 +1246,7 @@ restart:
+ list_for_each_entry(urbp, &qh->queue, node) {
+ urb = urbp->urb;
+ if (urb->status != -EINPROGRESS) {
+- uhci_save_toggle(qh, urb);
++ uhci_cleanup_queue(qh, urb);
+ uhci_giveback_urb(uhci, qh, urb, regs);
+ goto restart;
+ }
diff --git a/usb/uhci-reimplement-fsbr.patch b/usb/uhci-reimplement-fsbr.patch
new file mode 100644
index 0000000000000..d089447364a6e
--- /dev/null
+++ b/usb/uhci-reimplement-fsbr.patch
@@ -0,0 +1,399 @@
+From stern@rowland.harvard.edu Fri May 12 08:35:50 2006
+Date: Fri, 12 May 2006 11:35:45 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Subject: UHCI: Reimplement FSBR
+Message-ID: <Pine.LNX.4.44L0.0605121129050.6415-100000@iolanthe.rowland.org>
+
+This patch (as683) re-implements Full-Speed Bandwidth Reclamation (FSBR)
+properly. It keeps track of which endpoint queues have advanced, and
+when none have advanced for a sufficiently long time, FSBR is turned
+off. The next TD on each of the non-moving queues is modified to
+generate an interrupt on completion, so that FSBR can be re-enabled as
+soon as the hardware starts to make some progress.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/uhci-debug.c | 3
+ drivers/usb/host/uhci-hcd.c | 13 ---
+ drivers/usb/host/uhci-hcd.h | 15 +++
+ drivers/usb/host/uhci-hub.c | 1
+ drivers/usb/host/uhci-q.c | 168 ++++++++++++++++++++++++++++++++----------
+ 5 files changed, 143 insertions(+), 57 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/host/uhci-debug.c
++++ gregkh-2.6/drivers/usb/host/uhci-debug.c
+@@ -274,7 +274,8 @@ static int uhci_show_root_hub_state(stru
+ default:
+ rh_state = "?"; break;
+ }
+- out += sprintf(out, "Root-hub state: %s\n", rh_state);
++ out += sprintf(out, "Root-hub state: %s FSBR: %d\n",
++ rh_state, uhci->fsbr_is_on);
+ return out - buf;
+ }
+
+--- gregkh-2.6.orig/drivers/usb/host/uhci-hcd.c
++++ gregkh-2.6/drivers/usb/host/uhci-hcd.c
+@@ -88,15 +88,6 @@ static void suspend_rh(struct uhci_hcd *
+ static void wakeup_rh(struct uhci_hcd *uhci);
+ static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
+
+-/* If a transfer is still active after this much time, turn off FSBR */
+-#define IDLE_TIMEOUT msecs_to_jiffies(50)
+-#define FSBR_DELAY msecs_to_jiffies(50)
+-
+-/* When we timeout an idle transfer for FSBR, we'll switch it over to */
+-/* depth first traversal. We'll do it in groups of this number of TDs */
+-/* to make sure it doesn't hog all of the bandwidth */
+-#define DEPTH_INTERVAL 5
+-
+ #include "uhci-debug.c"
+ #include "uhci-q.c"
+ #include "uhci-hub.c"
+@@ -255,6 +246,7 @@ __acquires(uhci->lock)
+ uhci_to_hcd(uhci)->poll_rh = !int_enable;
+
+ uhci_scan_schedule(uhci, NULL);
++ uhci_fsbr_off(uhci);
+ }
+
+ static void start_rh(struct uhci_hcd *uhci)
+@@ -487,9 +479,6 @@ static int uhci_start(struct usb_hcd *hc
+
+ hcd->uses_new_polling = 1;
+
+- uhci->fsbr = 0;
+- uhci->fsbrtimeout = 0;
+-
+ spin_lock_init(&uhci->lock);
+
+ INIT_LIST_HEAD(&uhci->idle_qh_list);
+--- gregkh-2.6.orig/drivers/usb/host/uhci-hcd.h
++++ gregkh-2.6/drivers/usb/host/uhci-hcd.h
+@@ -84,6 +84,13 @@
+ #define CAN_SCHEDULE_FRAMES 1000 /* how far in the future frames
+ * can be scheduled */
+
++/* When no queues need Full-Speed Bandwidth Reclamation,
++ * delay this long before turning FSBR off */
++#define FSBR_OFF_DELAY msecs_to_jiffies(400)
++
++/* If a queue hasn't advanced after this much time, assume it is stuck */
++#define QH_WAIT_TIMEOUT msecs_to_jiffies(200)
++
+
+ /*
+ * Queue Headers
+@@ -131,6 +138,7 @@ struct uhci_qh {
+ struct uhci_td *dummy_td; /* Dummy TD to end the queue */
+ struct uhci_td *post_td; /* Last TD completed */
+
++ unsigned long advance_jiffies; /* Time of last queue advance */
+ unsigned int unlink_frame; /* When the QH was unlinked */
+ int state; /* QH_STATE_xxx; see above */
+ int type; /* Queue type (control, bulk, etc) */
+@@ -138,6 +146,7 @@ struct uhci_qh {
+ 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 */
++ unsigned int wait_expired:1; /* QH_WAIT_TIMEOUT has expired */
+ } __attribute__((aligned(16)));
+
+ /*
+@@ -397,8 +406,7 @@ struct uhci_hcd {
+ __le32 *frame;
+ void **frame_cpu; /* CPU's frame list */
+
+- int fsbr; /* Full-speed bandwidth reclamation */
+- unsigned long fsbrtimeout; /* FSBR delay */
++ unsigned long fsbr_jiffies; /* Time when FSBR was last wanted */
+
+ enum uhci_rh_state rh_state;
+ unsigned long auto_stop_time; /* When to AUTO_STOP */
+@@ -413,6 +421,7 @@ struct uhci_hcd {
+ unsigned int working_RD:1; /* Suspended root hub doesn't
+ need to be polled */
+ unsigned int is_initialized:1; /* Data structure is usable */
++ unsigned int fsbr_is_on:1; /* FSBR is turned on */
+
+ /* Support for port suspend/resume/reset */
+ unsigned long port_c_suspend; /* Bit-arrays of ports */
+@@ -451,7 +460,7 @@ struct urb_priv {
+ struct uhci_qh *qh; /* QH for this URB */
+ struct list_head td_list;
+
+- unsigned fsbr : 1; /* URB turned on FSBR */
++ unsigned fsbr:1; /* URB wants FSBR */
+ };
+
+
+--- gregkh-2.6.orig/drivers/usb/host/uhci-hub.c
++++ gregkh-2.6/drivers/usb/host/uhci-hub.c
+@@ -173,7 +173,6 @@ static int uhci_hub_status_data(struct u
+ uhci_scan_schedule(uhci, NULL);
+ if (uhci->hc_inaccessible)
+ goto done;
+- check_fsbr(uhci);
+ uhci_check_ports(uhci);
+
+ status = get_hub_status_data(uhci, buf);
+--- gregkh-2.6.orig/drivers/usb/host/uhci-q.c
++++ gregkh-2.6/drivers/usb/host/uhci-q.c
+@@ -37,6 +37,46 @@ static inline void uhci_clear_next_inter
+ uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC);
+ }
+
++
++/*
++ * Full-Speed Bandwidth Reclamation (FSBR).
++ * We turn on FSBR whenever a queue that wants it is advancing,
++ * and leave it on for a short time thereafter.
++ */
++static void uhci_fsbr_on(struct uhci_hcd *uhci)
++{
++ uhci->fsbr_is_on = 1;
++ uhci->skel_term_qh->link = cpu_to_le32(
++ uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
++}
++
++static void uhci_fsbr_off(struct uhci_hcd *uhci)
++{
++ uhci->fsbr_is_on = 0;
++ uhci->skel_term_qh->link = UHCI_PTR_TERM;
++}
++
++static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
++{
++ struct urb_priv *urbp = urb->hcpriv;
++
++ if (!(urb->transfer_flags & URB_NO_FSBR))
++ urbp->fsbr = 1;
++}
++
++static void uhci_qh_wants_fsbr(struct uhci_hcd *uhci, struct uhci_qh *qh)
++{
++ struct urb_priv *urbp =
++ list_entry(qh->queue.next, struct urb_priv, node);
++
++ if (urbp->fsbr) {
++ uhci->fsbr_jiffies = jiffies;
++ if (!uhci->fsbr_is_on)
++ uhci_fsbr_on(uhci);
++ }
++}
++
++
+ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci)
+ {
+ dma_addr_t dma_handle;
+@@ -331,6 +371,10 @@ static void uhci_activate_qh(struct uhci
+ qh->element = cpu_to_le32(td->dma_handle);
+ }
+
++ /* Treat the queue as if it has just advanced */
++ qh->wait_expired = 0;
++ qh->advance_jiffies = jiffies;
++
+ if (qh->state == QH_STATE_ACTIVE)
+ return;
+ qh->state = QH_STATE_ACTIVE;
+@@ -445,28 +489,6 @@ static void uhci_free_urb_priv(struct uh
+ kmem_cache_free(uhci_up_cachep, urbp);
+ }
+
+-static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+-{
+- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+-
+- if ((!(urb->transfer_flags & URB_NO_FSBR)) && !urbp->fsbr) {
+- urbp->fsbr = 1;
+- if (!uhci->fsbr++ && !uhci->fsbrtimeout)
+- uhci->skel_term_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
+- }
+-}
+-
+-static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+-{
+- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+-
+- if ((!(urb->transfer_flags & URB_NO_FSBR)) && urbp->fsbr) {
+- urbp->fsbr = 0;
+- if (!--uhci->fsbr)
+- uhci->fsbrtimeout = jiffies + FSBR_DELAY;
+- }
+-}
+-
+ /*
+ * Map status to standard result codes
+ *
+@@ -613,7 +635,7 @@ static int uhci_submit_control(struct uh
+ qh->skel = uhci->skel_ls_control_qh;
+ else {
+ qh->skel = uhci->skel_fs_control_qh;
+- uhci_inc_fsbr(uhci, urb);
++ uhci_add_fsbr(uhci, urb);
+ }
+
+ urb->actual_length = -8; /* Account for the SETUP packet */
+@@ -756,7 +778,7 @@ static inline int uhci_submit_bulk(struc
+ qh->skel = uhci->skel_bulk_qh;
+ ret = uhci_submit_common(uhci, urb, qh);
+ if (ret == 0)
+- uhci_inc_fsbr(uhci, urb);
++ uhci_add_fsbr(uhci, urb);
+ return ret;
+ }
+
+@@ -1075,8 +1097,10 @@ static int uhci_urb_enqueue(struct usb_h
+ * the QH is new and idle or else it's unlinked and waiting to
+ * 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)
++ if (qh->queue.next == &urbp->node && !qh->is_stopped) {
+ uhci_activate_qh(uhci, qh);
++ uhci_qh_wants_fsbr(uhci, qh);
++ }
+ goto done;
+
+ err_submit_failed:
+@@ -1135,7 +1159,6 @@ __acquires(uhci->lock)
+ qh->needs_fixup = 0;
+ }
+
+- uhci_dec_fsbr(uhci, urb); /* Safe since it checks */
+ uhci_free_urb_priv(uhci, urbp);
+
+ switch (qh->type) {
+@@ -1239,6 +1262,18 @@ restart:
+ if (!list_empty(&qh->queue)) {
+ if (qh->needs_fixup)
+ uhci_fixup_toggles(qh, 0);
++
++ /* If the first URB on the queue wants FSBR but its time
++ * limit has expired, set the next TD to interrupt on
++ * completion before reactivating the QH. */
++ urbp = list_entry(qh->queue.next, struct urb_priv, node);
++ if (urbp->fsbr && qh->wait_expired) {
++ struct uhci_td *td = list_entry(urbp->td_list.next,
++ struct uhci_td, list);
++
++ td->status |= __cpu_to_le32(TD_CTRL_IOC);
++ }
++
+ uhci_activate_qh(uhci, qh);
+ }
+
+@@ -1249,6 +1284,62 @@ restart:
+ }
+
+ /*
++ * Check for queues that have made some forward progress.
++ * Returns 0 if the queue is not Isochronous, is ACTIVE, and
++ * has not advanced since last examined; 1 otherwise.
++ */
++static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
++{
++ struct urb_priv *urbp = NULL;
++ struct uhci_td *td;
++ int ret = 1;
++ unsigned status;
++
++ if (qh->type == USB_ENDPOINT_XFER_ISOC)
++ return ret;
++
++ /* Treat an UNLINKING queue as though it hasn't advanced.
++ * This is okay because reactivation will treat it as though
++ * it has advanced, and if it is going to become IDLE then
++ * this doesn't matter anyway. Furthermore it's possible
++ * for an UNLINKING queue not to have any URBs at all, or
++ * for its first URB not to have any TDs (if it was dequeued
++ * just as it completed). So it's not easy in any case to
++ * test whether such queues have advanced. */
++ if (qh->state != QH_STATE_ACTIVE) {
++ urbp = NULL;
++ status = 0;
++
++ } else {
++ urbp = list_entry(qh->queue.next, struct urb_priv, node);
++ td = list_entry(urbp->td_list.next, struct uhci_td, list);
++ status = td_status(td);
++ if (!(status & TD_CTRL_ACTIVE)) {
++
++ /* We're okay, the queue has advanced */
++ qh->wait_expired = 0;
++ qh->advance_jiffies = jiffies;
++ return ret;
++ }
++ ret = 0;
++ }
++
++ /* The queue hasn't advanced; check for timeout */
++ if (!qh->wait_expired && time_after(jiffies,
++ qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
++ qh->wait_expired = 1;
++
++ /* If the current URB wants FSBR, unlink it temporarily
++ * so that we can safely set the next TD to interrupt on
++ * completion. That way we'll know as soon as the queue
++ * starts moving again. */
++ if (urbp && urbp->fsbr && !(status & TD_CTRL_IOC))
++ uhci_unlink_qh(uhci, qh);
++ }
++ return ret;
++}
++
++/*
+ * Process events in the schedule, but only in one thread at a time
+ */
+ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
+@@ -1262,7 +1353,7 @@ static void uhci_scan_schedule(struct uh
+ return;
+ }
+ uhci->scan_in_progress = 1;
+- rescan:
++rescan:
+ uhci->need_rescan = 0;
+
+ uhci_clear_next_interrupt(uhci);
+@@ -1275,7 +1366,12 @@ static void uhci_scan_schedule(struct uh
+ while ((qh = uhci->next_qh) != uhci->skelqh[i]) {
+ uhci->next_qh = list_entry(qh->node.next,
+ struct uhci_qh, node);
+- uhci_scan_qh(uhci, qh, regs);
++
++ if (uhci_advance_check(uhci, qh)) {
++ uhci_scan_qh(uhci, qh, regs);
++ if (qh->state == QH_STATE_ACTIVE)
++ uhci_qh_wants_fsbr(uhci, qh);
++ }
+ }
+ }
+
+@@ -1283,20 +1379,12 @@ static void uhci_scan_schedule(struct uh
+ goto rescan;
+ uhci->scan_in_progress = 0;
+
++ if (uhci->fsbr_is_on && time_after(jiffies,
++ uhci->fsbr_jiffies + FSBR_OFF_DELAY))
++ uhci_fsbr_off(uhci);
++
+ if (list_empty(&uhci->skel_unlink_qh->node))
+ uhci_clear_next_interrupt(uhci);
+ else
+ uhci_set_next_interrupt(uhci);
+ }
+-
+-static void check_fsbr(struct uhci_hcd *uhci)
+-{
+- /* For now, don't scan URBs for FSBR timeouts.
+- * Add it back in later... */
+-
+- /* Really disable FSBR */
+- if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
+- uhci->fsbrtimeout = 0;
+- uhci->skel_term_qh->link = UHCI_PTR_TERM;
+- }
+-}
diff --git a/usb/uhci-remove-non-iso-tds-as-they-are-used.patch b/usb/uhci-remove-non-iso-tds-as-they-are-used.patch
new file mode 100644
index 0000000000000..d086c397ab8ac
--- /dev/null
+++ b/usb/uhci-remove-non-iso-tds-as-they-are-used.patch
@@ -0,0 +1,237 @@
+From stern@rowland.harvard.edu Fri May 12 08:19:29 2006
+Date: Fri, 12 May 2006 11:19:19 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Subject: UHCI: Remove non-iso TDs as they are used
+Message-ID: <Pine.LNX.4.44L0.0605121114260.6415-100000@iolanthe.rowland.org>
+
+This patch (as680) frees non-isochronous TDs as they are used, rather
+than all at once when an URB is complete. Although not a terribly
+important change in itself, it opens the door to a later enhancement
+that will reduce storage requirements by allocating only a limited
+number of TDs at any time for each endpoint queue.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/uhci-debug.c | 1
+ drivers/usb/host/uhci-hcd.h | 5 +-
+ drivers/usb/host/uhci-q.c | 78 ++++++++++++++++++++++--------------------
+ 3 files changed, 45 insertions(+), 39 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/host/uhci-debug.c
++++ gregkh-2.6/drivers/usb/host/uhci-debug.c
+@@ -119,6 +119,7 @@ static int uhci_show_urbp(struct urb_pri
+ }
+
+ out += sprintf(out, "%s%s", ptype, (urbp->fsbr ? " FSBR" : ""));
++ out += sprintf(out, " Actlen=%d", urbp->urb->actual_length);
+
+ if (urbp->urb->status != -EINPROGRESS)
+ out += sprintf(out, " Status=%d", urbp->urb->status);
+--- gregkh-2.6.orig/drivers/usb/host/uhci-hcd.h
++++ gregkh-2.6/drivers/usb/host/uhci-hcd.h
+@@ -129,6 +129,7 @@ struct uhci_qh {
+ struct list_head queue; /* Queue of urbps for this QH */
+ struct uhci_qh *skel; /* Skeleton for this QH */
+ struct uhci_td *dummy_td; /* Dummy TD to end the queue */
++ struct uhci_td *post_td; /* Last TD completed */
+
+ unsigned int unlink_frame; /* When the QH was unlinked */
+ int state; /* QH_STATE_xxx; see above */
+@@ -136,7 +137,7 @@ struct uhci_qh {
+
+ 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 an error */
++ unsigned int is_stopped:1; /* Queue was stopped by error/unlink */
+ } __attribute__((aligned(16)));
+
+ /*
+@@ -456,8 +457,6 @@ struct urb_priv {
+ struct list_head td_list;
+
+ unsigned fsbr : 1; /* URB turned on FSBR */
+- unsigned short_transfer : 1; /* URB got a short transfer, no
+- * need to rescan */
+ };
+
+
+--- gregkh-2.6.orig/drivers/usb/host/uhci-q.c
++++ gregkh-2.6/drivers/usb/host/uhci-q.c
+@@ -161,6 +161,7 @@ static struct uhci_qh *uhci_alloc_qh(str
+ if (!qh)
+ return NULL;
+
++ memset(qh, 0, sizeof(*qh));
+ qh->dma_handle = dma_handle;
+
+ qh->element = UHCI_PTR_TERM;
+@@ -183,7 +184,6 @@ static struct uhci_qh *uhci_alloc_qh(str
+
+ } else { /* Skeleton QH */
+ qh->state = QH_STATE_ACTIVE;
+- qh->udev = NULL;
+ qh->type = -1;
+ }
+ return qh;
+@@ -223,16 +223,10 @@ static void uhci_save_toggle(struct uhci
+ qh->type == USB_ENDPOINT_XFER_INT))
+ return;
+
+- /* Find the first active TD; that's the device's toggle state */
+- list_for_each_entry(td, &urbp->td_list, list) {
+- if (td_status(td) & TD_CTRL_ACTIVE) {
+- qh->needs_fixup = 1;
+- qh->initial_toggle = uhci_toggle(td_token(td));
+- return;
+- }
+- }
+-
+- WARN_ON(1);
++ 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));
+ }
+
+ /*
+@@ -372,6 +366,12 @@ static void uhci_make_qh_idle(struct uhc
+ list_move(&qh->node, &uhci->idle_qh_list);
+ qh->state = QH_STATE_IDLE;
+
++ /* Now that the QH is idle, its post_td isn't being used */
++ if (qh->post_td) {
++ uhci_free_td(uhci, qh->post_td);
++ qh->post_td = NULL;
++ }
++
+ /* If anyone is waiting for a QH to become idle, wake them up */
+ if (uhci->num_waiting)
+ wake_up_all(&uhci->waitqh);
+@@ -610,6 +610,8 @@ static int uhci_submit_control(struct uh
+ qh->skel = uhci->skel_fs_control_qh;
+ uhci_inc_fsbr(uhci, urb);
+ }
++
++ urb->actual_length = -8; /* Account for the SETUP packet */
+ return 0;
+
+ nomem:
+@@ -767,34 +769,46 @@ static inline int uhci_submit_interrupt(
+ * Fix up the data structures following a short transfer
+ */
+ static int uhci_fixup_short_transfer(struct uhci_hcd *uhci,
+- struct uhci_qh *qh, struct urb_priv *urbp,
+- struct uhci_td *short_td)
++ struct uhci_qh *qh, struct urb_priv *urbp)
+ {
+ struct uhci_td *td;
+- int ret = 0;
++ struct list_head *tmp;
++ int ret;
+
+ td = list_entry(urbp->td_list.prev, struct uhci_td, list);
+ if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
+- urbp->short_transfer = 1;
+
+ /* When a control transfer is short, we have to restart
+ * the queue at the status stage transaction, which is
+ * the last TD. */
++ WARN_ON(list_empty(&urbp->td_list));
+ qh->element = cpu_to_le32(td->dma_handle);
++ tmp = td->list.prev;
+ ret = -EINPROGRESS;
+
+- } else if (!urbp->short_transfer) {
+- urbp->short_transfer = 1;
++ } else {
+
+ /* When a bulk/interrupt transfer is short, we have to
+ * fix up the toggles of the following URBs on the queue
+ * before restarting the queue at the next URB. */
+- qh->initial_toggle = uhci_toggle(td_token(short_td)) ^ 1;
++ qh->initial_toggle = uhci_toggle(td_token(qh->post_td)) ^ 1;
+ uhci_fixup_toggles(qh, 1);
+
++ if (list_empty(&urbp->td_list))
++ td = qh->post_td;
+ qh->element = td->link;
++ tmp = urbp->td_list.prev;
++ ret = 0;
+ }
+
++ /* Remove all the TDs we skipped over, from tmp back to the start */
++ while (tmp != &urbp->td_list) {
++ td = list_entry(tmp, struct uhci_td, list);
++ tmp = tmp->prev;
++
++ uhci_remove_td_from_urb(td);
++ list_add(&td->remove_list, &uhci->td_remove_list);
++ }
+ return ret;
+ }
+
+@@ -805,29 +819,14 @@ static int uhci_result_common(struct uhc
+ {
+ struct urb_priv *urbp = urb->hcpriv;
+ struct uhci_qh *qh = urbp->qh;
+- struct uhci_td *td;
+- struct list_head *tmp;
++ struct uhci_td *td, *tmp;
+ unsigned status;
+ int ret = 0;
+
+- tmp = urbp->td_list.next;
+-
+- if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
+- if (urbp->short_transfer)
+- tmp = urbp->td_list.prev;
+- else
+- urb->actual_length = -8; /* SETUP packet */
+- } else
+- urb->actual_length = 0;
+-
+-
+- while (tmp != &urbp->td_list) {
++ list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
+ unsigned int ctrlstat;
+ int len;
+
+- td = list_entry(tmp, struct uhci_td, list);
+- tmp = tmp->next;
+-
+ ctrlstat = td_status(td);
+ status = uhci_status_bits(ctrlstat);
+ if (status & TD_CTRL_ACTIVE)
+@@ -862,6 +861,12 @@ static int uhci_result_common(struct uhc
+ ret = 1;
+ }
+
++ uhci_remove_td_from_urb(td);
++ if (qh->post_td)
++ list_add(&qh->post_td->remove_list,
++ &uhci->td_remove_list);
++ qh->post_td = td;
++
+ if (ret != 0)
+ goto err;
+ }
+@@ -882,7 +887,7 @@ err:
+ (ret == -EREMOTEIO);
+
+ } else /* Short packet received */
+- ret = uhci_fixup_short_transfer(uhci, qh, urbp, td);
++ ret = uhci_fixup_short_transfer(uhci, qh, urbp);
+ return ret;
+ }
+
+@@ -1123,6 +1128,7 @@ __acquires(uhci->lock)
+ struct uhci_td *ptd, *ltd;
+
+ purbp = list_entry(urbp->node.prev, struct urb_priv, node);
++ WARN_ON(list_empty(&purbp->td_list));
+ ptd = list_entry(purbp->td_list.prev, struct uhci_td,
+ list);
+ ltd = list_entry(urbp->td_list.prev, struct uhci_td,
diff --git a/usb/uhci-work-around-old-intel-bug.patch b/usb/uhci-work-around-old-intel-bug.patch
new file mode 100644
index 0000000000000..81bb18eb14da5
--- /dev/null
+++ b/usb/uhci-work-around-old-intel-bug.patch
@@ -0,0 +1,58 @@
+From stern@rowland.harvard.edu Fri May 12 08:42:11 2006
+Date: Fri, 12 May 2006 11:41:59 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Subject: UHCI: Work around old Intel bug
+Message-ID: <Pine.LNX.4.44L0.0605121136450.6415-100000@iolanthe.rowland.org>
+
+Some old Intel UHCI controllers have a bug that has shown up in a few
+systems (the PIIX3 "Neptune" chip set). Until now there has not been
+any simple way to work around the bug, but the lastest changes in
+uhci-hcd have made it easy. This patch (as684) adds the work-around.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/uhci-q.c | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+--- gregkh-2.6.orig/drivers/usb/host/uhci-q.c
++++ gregkh-2.6/drivers/usb/host/uhci-q.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
+ */
+
+
+@@ -1287,6 +1287,11 @@ restart:
+ * Check for queues that have made some forward progress.
+ * Returns 0 if the queue is not Isochronous, is ACTIVE, and
+ * has not advanced since last examined; 1 otherwise.
++ *
++ * Early Intel controllers have a bug which causes qh->element sometimes
++ * not to advance when a TD completes successfully. The queue remains
++ * stuck on the inactive completed TD. We detect such cases and advance
++ * the element pointer by hand.
+ */
+ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
+ {
+@@ -1327,6 +1332,15 @@ static int uhci_advance_check(struct uhc
+ /* The queue hasn't advanced; check for timeout */
+ if (!qh->wait_expired && time_after(jiffies,
+ qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
++
++ /* Detect the Intel bug and work around it */
++ if (qh->post_td && qh_element(qh) ==
++ cpu_to_le32(qh->post_td->dma_handle)) {
++ qh->element = qh->post_td->link;
++ qh->advance_jiffies = jiffies;
++ return 1;
++ }
++
+ qh->wait_expired = 1;
+
+ /* If the current URB wants FSBR, unlink it temporarily
diff --git a/usb/usb-add-yealink-phones-to-the-hid_quirk_ignore-blacklist.patch b/usb/usb-add-yealink-phones-to-the-hid_quirk_ignore-blacklist.patch
new file mode 100644
index 0000000000000..2553ffde05a37
--- /dev/null
+++ b/usb/usb-add-yealink-phones-to-the-hid_quirk_ignore-blacklist.patch
@@ -0,0 +1,43 @@
+From vojtech@suse.cz Mon May 15 03:34:45 2006
+Date: Mon, 15 May 2006 12:34:43 +0200
+From: Vojtech Pavlik <vojtech@suse.cz>
+To: Henk Vergonet <Henk.Vergonet@gmail.com>
+Cc: gregkh@suse.de
+Subject: USB: add YEALINK phones to the HID_QUIRK_IGNORE blacklist
+Message-ID: <20060515103443.GB19333@suse.cz>
+Content-Disposition: inline
+
+From: Henk Vergonet <Henk.Vergonet@gmail.com>
+
+Keys on Yealink based phones will not function properly when using the
+generic HID driver. This patch prevents the generic HID code from
+grabbing the device before the regular yealink driver can get a grip on
+it.
+
+Signed-off-by: Henk Vergonet <Henk.Vergonet@gmail.com>
+Signed-off-by: Vojtech Pavlik <vojtech@suse.cz>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/input/hid-core.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- gregkh-2.6.orig/drivers/usb/input/hid-core.c
++++ gregkh-2.6/drivers/usb/input/hid-core.c
+@@ -1563,6 +1563,8 @@ void hid_init_reports(struct hid_device
+ #define USB_VENDOR_ID_CREATIVELABS 0x062a
+ #define USB_DEVICE_ID_CREATIVELABS_SILVERCREST 0x0201
+
++#define USB_VENDOR_ID_YEALINK 0x6993
++#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001
+ /*
+ * Alphabetically sorted blacklist by quirk type.
+ */
+@@ -1671,6 +1673,7 @@ static const struct hid_blacklist {
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_DTF + 3, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
++ { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
+
+ { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE },
diff --git a/usb/usb-cdc-acm-add-a-new-special-case-for-modems-with-buggy-firmware.patch b/usb/usb-cdc-acm-add-a-new-special-case-for-modems-with-buggy-firmware.patch
new file mode 100644
index 0000000000000..68d4a7a73b679
--- /dev/null
+++ b/usb/usb-cdc-acm-add-a-new-special-case-for-modems-with-buggy-firmware.patch
@@ -0,0 +1,306 @@
+From neukum@fachschaft.cup.uni-muenchen.de Sat May 13 14:18:42 2006
+Date: Sat, 13 May 2006 22:50:47 +0200 (CEST)
+From: Oliver Neukum <neukum@fachschaft.cup.uni-muenchen.de>
+To: greg@kroah.com
+Subject: USB: cdc-acm: add a new special case for modems with buggy firmware
+Message-ID: <Pine.LNX.4.58.0605132247360.24460@fachschaft.cup.uni-muenchen.de>
+
+this fixes the "duplicated text" bug. There's a modem that cannot cope
+with large transfers and more than one urb in flight. This patch adds a
+special case to the driver.
+
+Signed-off-by: Oliver Neukum <oliver@neukum.name>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/class/cdc-acm.c | 82 ++++++++++++++++++++++++++------------------
+ drivers/usb/class/cdc-acm.h | 16 ++++----
+ 2 files changed, 59 insertions(+), 39 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/class/cdc-acm.c
++++ gregkh-2.6/drivers/usb/class/cdc-acm.c
+@@ -127,8 +127,8 @@ static int acm_wb_alloc(struct acm *acm)
+ wb->use = 1;
+ return wbn;
+ }
+- wbn = (wbn + 1) % ACM_NWB;
+- if (++i >= ACM_NWB)
++ wbn = (wbn + 1) % ACM_NW;
++ if (++i >= ACM_NW)
+ return -1;
+ }
+ }
+@@ -142,10 +142,9 @@ static int acm_wb_is_avail(struct acm *a
+ {
+ int i, n;
+
+- n = 0;
+- for (i = 0; i < ACM_NWB; i++) {
+- if (!acm->wb[i].use)
+- n++;
++ n = ACM_NW;
++ for (i = 0; i < ACM_NW; i++) {
++ n -= acm->wb[i].use;
+ }
+ return n;
+ }
+@@ -167,7 +166,7 @@ static void acm_write_done(struct acm *a
+ acm->write_ready = 1;
+ wbn = acm->write_current;
+ acm_wb_free(acm, wbn);
+- acm->write_current = (wbn + 1) % ACM_NWB;
++ acm->write_current = (wbn + 1) % ACM_NW;
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+ }
+
+@@ -291,22 +290,32 @@ static void acm_read_bulk(struct urb *ur
+ struct acm_rb *buf;
+ struct acm_ru *rcv = urb->context;
+ struct acm *acm = rcv->instance;
++ int status = urb->status;
+ dbg("Entering acm_read_bulk with status %d\n", urb->status);
+
+ if (!ACM_READY(acm))
+ return;
+
+- if (urb->status)
+- dev_dbg(&acm->data->dev, "bulk rx status %d\n", urb->status);
++ if (status)
++ dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
+
+ buf = rcv->buffer;
+ buf->size = urb->actual_length;
+
+- spin_lock(&acm->read_lock);
+- list_add_tail(&rcv->list, &acm->spare_read_urbs);
+- list_add_tail(&buf->list, &acm->filled_read_bufs);
+- spin_unlock(&acm->read_lock);
+-
++ if (likely(status == 0)) {
++ spin_lock(&acm->read_lock);
++ list_add_tail(&rcv->list, &acm->spare_read_urbs);
++ list_add_tail(&buf->list, &acm->filled_read_bufs);
++ spin_unlock(&acm->read_lock);
++ } else {
++ /* we drop the buffer due to an error */
++ spin_lock(&acm->read_lock);
++ list_add_tail(&rcv->list, &acm->spare_read_urbs);
++ list_add(&buf->list, &acm->spare_read_bufs);
++ spin_unlock(&acm->read_lock);
++ /* nevertheless the tasklet must be kicked unconditionally
++ so the queue cannot dry up */
++ }
+ tasklet_schedule(&acm->urb_task);
+ }
+
+@@ -464,10 +473,10 @@ static int acm_tty_open(struct tty_struc
+ INIT_LIST_HEAD(&acm->spare_read_urbs);
+ INIT_LIST_HEAD(&acm->spare_read_bufs);
+ INIT_LIST_HEAD(&acm->filled_read_bufs);
+- for (i = 0; i < ACM_NRU; i++) {
++ for (i = 0; i < acm->rx_buflimit; i++) {
+ list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
+ }
+- for (i = 0; i < ACM_NRB; i++) {
++ for (i = 0; i < acm->rx_buflimit; i++) {
+ list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
+ }
+
+@@ -488,14 +497,15 @@ bail_out:
+
+ static void acm_tty_unregister(struct acm *acm)
+ {
+- int i;
++ int i,nr;
+
++ nr = acm->rx_buflimit;
+ tty_unregister_device(acm_tty_driver, acm->minor);
+ usb_put_intf(acm->control);
+ acm_table[acm->minor] = NULL;
+ usb_free_urb(acm->ctrlurb);
+ usb_free_urb(acm->writeurb);
+- for (i = 0; i < ACM_NRU; i++)
++ for (i = 0; i < nr; i++)
+ usb_free_urb(acm->ru[i].urb);
+ kfree(acm);
+ }
+@@ -503,18 +513,19 @@ static void acm_tty_unregister(struct ac
+ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
+ {
+ struct acm *acm = tty->driver_data;
+- int i;
++ int i,nr;
+
+ if (!acm || !acm->used)
+ return;
+
++ nr = acm->rx_buflimit;
+ mutex_lock(&open_mutex);
+ if (!--acm->used) {
+ if (acm->dev) {
+ acm_set_control(acm, acm->ctrlout = 0);
+ usb_kill_urb(acm->ctrlurb);
+ usb_kill_urb(acm->writeurb);
+- for (i = 0; i < ACM_NRU; i++)
++ for (i = 0; i < nr; i++)
+ usb_kill_urb(acm->ru[i].urb);
+ } else
+ acm_tty_unregister(acm);
+@@ -576,7 +587,7 @@ static int acm_tty_chars_in_buffer(struc
+ /*
+ * This is inaccurate (overcounts), but it works.
+ */
+- return (ACM_NWB - acm_wb_is_avail(acm)) * acm->writesize;
++ return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
+ }
+
+ static void acm_tty_throttle(struct tty_struct *tty)
+@@ -712,7 +723,7 @@ static void acm_write_buffers_free(struc
+ int i;
+ struct acm_wb *wb;
+
+- for (wb = &acm->wb[0], i = 0; i < ACM_NWB; i++, wb++) {
++ for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
+ usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah);
+ }
+ }
+@@ -723,7 +734,7 @@ static int acm_write_buffers_alloc(struc
+ int i;
+ struct acm_wb *wb;
+
+- for (wb = &acm->wb[0], i = 0; i < ACM_NWB; i++, wb++) {
++ for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
+ wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
+ &wb->dmah);
+ if (!wb->buf) {
+@@ -760,10 +771,14 @@ static int acm_probe (struct usb_interfa
+ int call_interface_num = -1;
+ int data_interface_num;
+ unsigned long quirks;
++ int num_rx_buf;
+ int i;
+
+- /* handle quirks deadly to normal probing*/
++ /* normal quirks */
+ quirks = (unsigned long)id->driver_info;
++ num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
++
++ /* handle quirks deadly to normal probing*/
+ if (quirks == NO_UNION_NORMAL) {
+ data_interface = usb_ifnum_to_if(usb_dev, 1);
+ control_interface = usb_ifnum_to_if(usb_dev, 0);
+@@ -900,7 +915,7 @@ skip_normal_probe:
+ }
+
+ ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
+- readsize = le16_to_cpu(epread->wMaxPacketSize)*2;
++ readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
+ acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize);
+ acm->control = control_interface;
+ acm->data = data_interface;
+@@ -909,6 +924,7 @@ skip_normal_probe:
+ acm->ctrl_caps = ac_management_function;
+ acm->ctrlsize = ctrlsize;
+ acm->readsize = readsize;
++ acm->rx_buflimit = num_rx_buf;
+ acm->urb_task.func = acm_rx_tasklet;
+ acm->urb_task.data = (unsigned long) acm;
+ INIT_WORK(&acm->work, acm_softint, acm);
+@@ -935,7 +951,7 @@ skip_normal_probe:
+ dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
+ goto alloc_fail5;
+ }
+- for (i = 0; i < ACM_NRU; i++) {
++ for (i = 0; i < num_rx_buf; i++) {
+ struct acm_ru *rcv = &(acm->ru[i]);
+
+ if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
+@@ -946,10 +962,9 @@ skip_normal_probe:
+ rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ rcv->instance = acm;
+ }
+- for (i = 0; i < ACM_NRB; i++) {
++ for (i = 0; i < num_rx_buf; i++) {
+ struct acm_rb *buf = &(acm->rb[i]);
+
+- // Using usb_buffer_alloc instead of kmalloc as Oliver suggested
+ if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
+ dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
+ goto alloc_fail7;
+@@ -988,9 +1003,9 @@ skip_normal_probe:
+ return 0;
+
+ alloc_fail7:
+- for (i = 0; i < ACM_NRB; i++)
++ for (i = 0; i < num_rx_buf; i++)
+ usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
+- for (i = 0; i < ACM_NRU; i++)
++ for (i = 0; i < num_rx_buf; i++)
+ usb_free_urb(acm->ru[i].urb);
+ usb_free_urb(acm->ctrlurb);
+ alloc_fail5:
+@@ -1027,7 +1042,7 @@ static void acm_disconnect(struct usb_in
+
+ usb_kill_urb(acm->ctrlurb);
+ usb_kill_urb(acm->writeurb);
+- for (i = 0; i < ACM_NRU; i++)
++ for (i = 0; i < acm->rx_buflimit; i++)
+ usb_kill_urb(acm->ru[i].urb);
+
+ INIT_LIST_HEAD(&acm->filled_read_bufs);
+@@ -1039,7 +1054,7 @@ static void acm_disconnect(struct usb_in
+
+ acm_write_buffers_free(acm);
+ usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
+- for (i = 0; i < ACM_NRB; i++)
++ for (i = 0; i < acm->rx_buflimit; i++)
+ usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
+
+ usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf);
+@@ -1068,6 +1083,9 @@ static struct usb_device_id acm_ids[] =
+ { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
+ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+ },
++ { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
++ .driver_info = SINGLE_RX_URB, /* firmware bug */
++ },
+ /* control interfaces with various AT-command sets */
+ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+ USB_CDC_ACM_PROTO_AT_V25TER) },
+--- gregkh-2.6.orig/drivers/usb/class/cdc-acm.h
++++ gregkh-2.6/drivers/usb/class/cdc-acm.h
+@@ -56,11 +56,11 @@
+ * in line disciplines. They ask for empty space amount, receive our URB size,
+ * and proceed to issue several 1-character writes, assuming they will fit.
+ * The very first write takes a complete URB. Fortunately, this only happens
+- * when processing onlcr, so we only need 2 buffers.
++ * when processing onlcr, so we only need 2 buffers. These values must be
++ * powers of 2.
+ */
+-#define ACM_NWB 2
+-#define ACM_NRU 16
+-#define ACM_NRB 16
++#define ACM_NW 2
++#define ACM_NR 16
+
+ struct acm_wb {
+ unsigned char *buf;
+@@ -91,9 +91,10 @@ struct acm {
+ struct urb *ctrlurb, *writeurb; /* urbs */
+ u8 *ctrl_buffer; /* buffers of urbs */
+ dma_addr_t ctrl_dma; /* dma handles of buffers */
+- struct acm_wb wb[ACM_NWB];
+- struct acm_ru ru[ACM_NRU];
+- struct acm_rb rb[ACM_NRB];
++ struct acm_wb wb[ACM_NW];
++ struct acm_ru ru[ACM_NR];
++ struct acm_rb rb[ACM_NR];
++ int rx_buflimit;
+ int rx_endpoint;
+ spinlock_t read_lock;
+ struct list_head spare_read_urbs;
+@@ -122,3 +123,4 @@ struct acm {
+
+ /* constants describing various quirks and errors */
+ #define NO_UNION_NORMAL 1
++#define SINGLE_RX_URB 2
diff --git a/usb/usb-hid-hidbp-input-drivers-fix-various-usb-input-hid-input.c-bugs-that-make-apple-mighty-mouse-work-poorly.patch b/usb/usb-hid-hidbp-input-drivers-fix-various-usb-input-hid-input.c-bugs-that-make-apple-mighty-mouse-work-poorly.patch
index a5289118d70c0..cc9f64cdc22df 100644
--- a/usb/usb-hid-hidbp-input-drivers-fix-various-usb-input-hid-input.c-bugs-that-make-apple-mighty-mouse-work-poorly.patch
+++ b/usb/usb-hid-hidbp-input-drivers-fix-various-usb-input-hid-input.c-bugs-that-make-apple-mighty-mouse-work-poorly.patch
@@ -55,7 +55,7 @@ Signed-off-by: Bart Massey <bart@cs.pdx.edu>
#define USB_VENDOR_ID_CHERRY 0x046a
#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023
-@@ -1689,7 +1689,7 @@ static const struct hid_blacklist {
+@@ -1692,7 +1692,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_SILVERCREST, USB_DEVICE_ID_SILVERCREST_KB, HID_QUIRK_NOGET },
diff --git a/usb/usb-hid-read-busywait-fix.patch b/usb/usb-hid-read-busywait-fix.patch
new file mode 100644
index 0000000000000..5478c82acdf56
--- /dev/null
+++ b/usb/usb-hid-read-busywait-fix.patch
@@ -0,0 +1,32 @@
+From akpm@osdl.org Mon May 15 21:52:17 2006
+Message-Id: <200605160452.k4G4q8kK028913@shell0.pdx.osdl.net>
+From: David Micon <DMicon@pelco.com>
+Subject: USB: HID read busywait fix
+To: DMicon@pelco.com, dtor_core@ameritech.net, greg@kroah.com, vojtech@suse.cz
+Date: Mon, 15 May 2006 21:48:54 -0700
+
+
+From: David Micon <DMicon@pelco.com>
+
+Make a read of a HID device block until data is available. Without it, the
+read goes into a busy-wait loop until data is available.
+
+Acked-by: Vojtech Pavlik <vojtech@suse.cz>
+Cc: Dmitry Torokhov <dtor_core@ameritech.net>
+Signed-off-by: Andrew Morton <akpm@osdl.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/input/hiddev.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- gregkh-2.6.orig/drivers/usb/input/hiddev.c
++++ gregkh-2.6/drivers/usb/input/hiddev.c
+@@ -317,6 +317,7 @@ static ssize_t hiddev_read(struct file *
+ }
+
+ schedule();
++ set_current_state(TASK_INTERRUPTIBLE);
+ }
+
+ set_current_state(TASK_RUNNING);
diff --git a/usb/usb-ohci-avoids-root-hub-timer-polling.patch b/usb/usb-ohci-avoids-root-hub-timer-polling.patch
new file mode 100644
index 0000000000000..2fc890e2bcaa6
--- /dev/null
+++ b/usb/usb-ohci-avoids-root-hub-timer-polling.patch
@@ -0,0 +1,287 @@
+From linux-usb-devel-admin@lists.sourceforge.net Tue May 16 05:23:39 2006
+From: David Brownell <david-b@pacbell.net>
+Message-Id: <200605121849.09003.david-b@pacbell.net>
+Subject: USB: OHCI avoids root hub timer polling
+Date: Fri, 12 May 2006 18:49:08 -0700
+
+
+This teaches OHCI to use the root hub status change (RHSC) IRQ, bypassing
+the root hub timer except to trigger autosuspend. Avoiding that timer means
+that mechanisms like "dynamic tick" or "variable scheduler timeouts" can
+now can be used to leave the CPU in low modes for longer intervals.
+
+If there are any OHCI implementations where INTR_RHSC doesn't work, they
+will need to turn off the "uses_new_polling" flag.
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/ohci-at91.c | 2 -
+ drivers/usb/host/ohci-au1xxx.c | 1
+ drivers/usb/host/ohci-hcd.c | 23 +++++++++++++-------
+ drivers/usb/host/ohci-hub.c | 45 +++++++++++++++++++++++++++-------------
+ drivers/usb/host/ohci-lh7a404.c | 1
+ drivers/usb/host/ohci-omap.c | 1
+ drivers/usb/host/ohci-pci.c | 1
+ drivers/usb/host/ohci-ppc-soc.c | 1
+ drivers/usb/host/ohci-pxa27x.c | 1
+ drivers/usb/host/ohci-s3c2410.c | 1
+ drivers/usb/host/ohci-sa1111.c | 1
+ drivers/usb/host/ohci.h | 2 -
+ 12 files changed, 56 insertions(+), 24 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/host/ohci-at91.c
++++ gregkh-2.6/drivers/usb/host/ohci-at91.c
+@@ -230,7 +230,7 @@ static const struct hc_driver ohci_at91_
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+-
++ .hub_irq_enable = ohci_rhsc_enable,
+ #ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+--- gregkh-2.6.orig/drivers/usb/host/ohci-au1xxx.c
++++ gregkh-2.6/drivers/usb/host/ohci-au1xxx.c
+@@ -289,6 +289,7 @@ static const struct hc_driver ohci_au1xx
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
++ .hub_irq_enable = ohci_rhsc_enable,
+ #ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+--- gregkh-2.6.orig/drivers/usb/host/ohci-hcd.c
++++ gregkh-2.6/drivers/usb/host/ohci-hcd.c
+@@ -102,7 +102,7 @@
+
+ #include "../core/hcd.h"
+
+-#define DRIVER_VERSION "2005 April 22"
++#define DRIVER_VERSION "2006 May 12"
+ #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
+ #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
+
+@@ -111,7 +111,7 @@
+ #undef OHCI_VERBOSE_DEBUG /* not always helpful */
+
+ /* For initializing controller (mask in an HCFS mode too) */
+-#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
++#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
+ #define OHCI_INTR_INIT \
+ (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
+
+@@ -447,7 +447,7 @@ static int ohci_init (struct ohci_hcd *o
+
+ disable (ohci);
+ ohci->regs = hcd->regs;
+- ohci->next_statechange = jiffies;
++ ohci->next_statechange = jiffies + msecs_to_jiffies (250);
+
+ /* REVISIT this BIOS handshake is now moved into PCI "quirks", and
+ * was never needed for most non-PCI systems ... remove the code?
+@@ -637,11 +637,12 @@ retry:
+ ohci_readl (ohci, &ohci->regs->periodicstart));
+ return -EOVERFLOW;
+ }
++ hcd->uses_new_polling = 1;
+
+- /* start controller operations */
++ /* start controller operations */
+ ohci->hc_control &= OHCI_CTRL_RWC;
+- ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
+- ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
++ ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
++ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ hcd->state = HC_STATE_RUNNING;
+
+ /* wake on ConnectStatusChange, matching external hubs */
+@@ -649,7 +650,7 @@ retry:
+
+ /* Choose the interrupts we care about now, others later on demand */
+ mask = OHCI_INTR_INIT;
+- ohci_writel (ohci, mask, &ohci->regs->intrstatus);
++ ohci_writel (ohci, ~0, &ohci->regs->intrstatus);
+ ohci_writel (ohci, mask, &ohci->regs->intrenable);
+
+ /* handle root hub init quirks ... */
+@@ -710,7 +711,13 @@ static irqreturn_t ohci_irq (struct usb_
+ /* interrupt for some other device? */
+ } else if ((ints &= ohci_readl (ohci, &regs->intrenable)) == 0) {
+ return IRQ_NOTMINE;
+- }
++ }
++
++ if (ints & OHCI_INTR_RHSC) {
++ ohci_vdbg (ohci, "rhsc\n");
++ ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrstatus);
++ usb_hcd_poll_rh_status(hcd);
++ }
+
+ if (ints & OHCI_INTR_UE) {
+ disable (ohci);
+--- gregkh-2.6.orig/drivers/usb/host/ohci-hub.c
++++ gregkh-2.6/drivers/usb/host/ohci-hub.c
+@@ -36,6 +36,14 @@
+
+ /*-------------------------------------------------------------------------*/
+
++/* hcd->hub_irq_enable() ? */
++static void ohci_rhsc_enable (struct usb_hcd *hcd)
++{
++ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
++
++ ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
++}
++
+ #ifdef CONFIG_PM
+
+ #define OHCI_SCHED_ENABLES \
+@@ -123,6 +131,9 @@ static int ohci_bus_suspend (struct usb_
+ /* no resumes until devices finish suspending */
+ ohci->next_statechange = jiffies + msecs_to_jiffies (5);
+
++ /* RHSC or RD irq will wakeup; no timer polling */
++ hcd->poll_rh = 0;
++
+ done:
+ /* external suspend vs self autosuspend ... same effect */
+ if (status == 0)
+@@ -349,35 +360,41 @@ ohci_hub_status_data (struct usb_hcd *hc
+ continue;
+ }
+
+- /* can suspend if no ports are enabled; or if all all
++
++ /* can suspend if no ports are enabled; or if all the
+ * enabled ports are suspended AND remote wakeup is on.
+ */
+ if (!(status & RH_PS_CCS))
+ continue;
+- if ((status & RH_PS_PSS) && can_suspend)
+- continue;
+- can_suspend = 0;
++ if (!(status & RH_PS_PSS))
++ can_suspend = 0;
+ }
++
+ done:
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+ #ifdef CONFIG_PM
+- /* save power by suspending idle root hubs;
+- * INTR_RD wakes us when there's work
++ /* save power by suspending idle root hubs and avoiding timer polls;
++ * INTR_RD or INTR_RHSC wakes us when there's work, and polling is
++ * used only to defer autosuspend.
+ */
+ if (can_suspend
+ && !changed
+ && !ohci->ed_rm_list
+ && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
+ & ohci->hc_control)
+- == OHCI_USB_OPER
+- && time_after (jiffies, ohci->next_statechange)
+- && usb_trylock_device (hcd->self.root_hub) == 0
+- ) {
+- ohci_vdbg (ohci, "autosuspend\n");
+- (void) ohci_bus_suspend (hcd);
+- usb_unlock_device (hcd->self.root_hub);
+- }
++ == OHCI_USB_OPER) {
++ /* if we can't yet autosuspend, poll until we can */
++ if (time_after (jiffies, ohci->next_statechange)
++ && usb_trylock_device (hcd->self.root_hub) == 0
++ ) {
++ ohci_vdbg (ohci, "autosuspend\n");
++ (void) ohci_bus_suspend (hcd);
++ usb_unlock_device (hcd->self.root_hub);
++ } else
++ hcd->poll_rh = 1;
++ } else
++ hcd->poll_rh = 0;
+ #endif
+
+ return changed ? length : 0;
+--- gregkh-2.6.orig/drivers/usb/host/ohci-lh7a404.c
++++ gregkh-2.6/drivers/usb/host/ohci-lh7a404.c
+@@ -196,6 +196,7 @@ static const struct hc_driver ohci_lh7a4
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
++ .hub_irq_enable = ohci_rhsc_enable,
+ #ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+--- gregkh-2.6.orig/drivers/usb/host/ohci-omap.c
++++ gregkh-2.6/drivers/usb/host/ohci-omap.c
+@@ -429,6 +429,7 @@ static const struct hc_driver ohci_omap_
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
++ .hub_irq_enable = ohci_rhsc_enable,
+ #ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+--- gregkh-2.6.orig/drivers/usb/host/ohci-pci.c
++++ gregkh-2.6/drivers/usb/host/ohci-pci.c
+@@ -194,6 +194,7 @@ static const struct hc_driver ohci_pci_h
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
++ .hub_irq_enable = ohci_rhsc_enable,
+ #ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+--- gregkh-2.6.orig/drivers/usb/host/ohci-ppc-soc.c
++++ gregkh-2.6/drivers/usb/host/ohci-ppc-soc.c
+@@ -166,6 +166,7 @@ static const struct hc_driver ohci_ppc_s
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
++ .hub_irq_enable = ohci_rhsc_enable,
+ #ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+--- gregkh-2.6.orig/drivers/usb/host/ohci-pxa27x.c
++++ gregkh-2.6/drivers/usb/host/ohci-pxa27x.c
+@@ -285,6 +285,7 @@ static const struct hc_driver ohci_pxa27
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
++ .hub_irq_enable = ohci_rhsc_enable,
+ #ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+--- gregkh-2.6.orig/drivers/usb/host/ohci-s3c2410.c
++++ gregkh-2.6/drivers/usb/host/ohci-s3c2410.c
+@@ -465,6 +465,7 @@ static const struct hc_driver ohci_s3c24
+ */
+ .hub_status_data = ohci_s3c2410_hub_status_data,
+ .hub_control = ohci_s3c2410_hub_control,
++ .hub_irq_enable = ohci_rhsc_enable,
+ #ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+--- gregkh-2.6.orig/drivers/usb/host/ohci-sa1111.c
++++ gregkh-2.6/drivers/usb/host/ohci-sa1111.c
+@@ -235,6 +235,7 @@ static const struct hc_driver ohci_sa111
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
++ .hub_irq_enable = ohci_rhsc_enable,
+ #ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+--- gregkh-2.6.orig/drivers/usb/host/ohci.h
++++ gregkh-2.6/drivers/usb/host/ohci.h
+@@ -385,7 +385,7 @@ struct ohci_hcd {
+ */
+ int num_ports;
+ int load [NUM_INTS];
+- u32 hc_control; /* copy of hc control reg */
++ u32 hc_control; /* copy of hc control reg */
+ unsigned long next_statechange; /* suspend/resume */
+ u32 fminterval; /* saved register */
+
diff --git a/usb/usb-usbnet-zaurus-mtu-fixup.patch b/usb/usb-usbnet-zaurus-mtu-fixup.patch
new file mode 100644
index 0000000000000..6af007b932acd
--- /dev/null
+++ b/usb/usb-usbnet-zaurus-mtu-fixup.patch
@@ -0,0 +1,84 @@
+From david-b@pacbell.net Fri May 12 19:24:42 2006
+From: David Brownell <david-b@pacbell.net>
+Subject: USB: usbnet, zaurus mtu fixup
+Date: Fri, 12 May 2006 19:24:34 -0700
+Cc: Greg KH <greg@kroah.com>
+Message-Id: <200605121924.35188.david-b@pacbell.net>
+
+This includes an MTU fixup which could affect larger packets with newer
+Zaurii, described as http://bugzilla.kernel.org/show_bug.cgi?id=6286;
+plus minor whitespace cleanup.
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/net/zaurus.c | 17 +++++++++++------
+ 1 file changed, 11 insertions(+), 6 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/net/zaurus.c
++++ gregkh-2.6/drivers/usb/net/zaurus.c
+@@ -109,7 +109,7 @@ static const struct driver_info zaurus_s
+ .check_connect = always_connected,
+ .bind = zaurus_bind,
+ .unbind = usbnet_cdc_unbind,
+- .tx_fixup = zaurus_tx_fixup,
++ .tx_fixup = zaurus_tx_fixup,
+ };
+ #define ZAURUS_STRONGARM_INFO ((unsigned long)&zaurus_sl5x00_info)
+
+@@ -119,7 +119,7 @@ static const struct driver_info zaurus_p
+ .check_connect = always_connected,
+ .bind = zaurus_bind,
+ .unbind = usbnet_cdc_unbind,
+- .tx_fixup = zaurus_tx_fixup,
++ .tx_fixup = zaurus_tx_fixup,
+ };
+ #define ZAURUS_PXA_INFO ((unsigned long)&zaurus_pxa_info)
+
+@@ -129,7 +129,7 @@ static const struct driver_info olympus_
+ .check_connect = always_connected,
+ .bind = zaurus_bind,
+ .unbind = usbnet_cdc_unbind,
+- .tx_fixup = zaurus_tx_fixup,
++ .tx_fixup = zaurus_tx_fixup,
+ };
+ #define OLYMPUS_MXL_INFO ((unsigned long)&olympus_mxl_info)
+
+@@ -228,6 +228,11 @@ bad_detail:
+ detail->bDetailData[2]);
+ goto bad_desc;
+ }
++
++ /* same extra framing as for non-BLAN mode */
++ dev->net->hard_header_len += 6;
++ dev->rx_urb_size = dev->net->hard_header_len
++ + dev->net->mtu;
+ break;
+ }
+ next_desc:
+@@ -258,7 +263,7 @@ static const struct driver_info bogus_md
+ .description = "pseudo-MDLM (BLAN) device",
+ .flags = FLAG_FRAMING_Z,
+ .check_connect = always_connected,
+- .tx_fixup = zaurus_tx_fixup,
++ .tx_fixup = zaurus_tx_fixup,
+ .bind = blan_mdlm_bind,
+ };
+
+@@ -367,13 +372,13 @@ static struct usb_driver zaurus_driver =
+
+ static int __init zaurus_init(void)
+ {
+- return usb_register(&zaurus_driver);
++ return usb_register(&zaurus_driver);
+ }
+ module_init(zaurus_init);
+
+ static void __exit zaurus_exit(void)
+ {
+- usb_deregister(&zaurus_driver);
++ usb_deregister(&zaurus_driver);
+ }
+ module_exit(zaurus_exit);
+
diff --git a/usb/usbhid-automatically-set-hid_quirk_noget-for-keyboards-and-mice.patch b/usb/usbhid-automatically-set-hid_quirk_noget-for-keyboards-and-mice.patch
new file mode 100644
index 0000000000000..b2389a89f130c
--- /dev/null
+++ b/usb/usbhid-automatically-set-hid_quirk_noget-for-keyboards-and-mice.patch
@@ -0,0 +1,63 @@
+From stern@rowland.harvard.edu Mon May 15 11:49:13 2006
+Date: Mon, 15 May 2006 14:49:04 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+cc: Vojtech Pavlik <vojtech@suse.cz>, Dmitry Torokhov <dtor_core@ameritech.net>
+Subject: usbhid: automatically set HID_QUIRK_NOGET for keyboards and mice
+Message-ID: <Pine.LNX.4.44L0.0605151443140.6363-100000@iolanthe.rowland.org>
+
+It seems to be relatively common for USB keyboards and mice to dislike
+being polled for reports. Since there's no need to poll a keyboard or
+a mouse, this patch (as685) automatically sets the HID_QUIRK_NOGET flag
+for devices that advertise themselves as either sort of device with boot
+protocol support.
+
+This won't cure all the problems since some devices don't support the
+boot protocol, but it's simple and easy and it should fix quite a few
+problems.
+
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+
+---
+ drivers/usb/input/hid-core.c | 8 ++++++++
+ drivers/usb/input/hid.h | 8 ++++++++
+ 2 files changed, 16 insertions(+)
+
+--- gregkh-2.6.orig/drivers/usb/input/hid-core.c
++++ gregkh-2.6/drivers/usb/input/hid-core.c
+@@ -1797,6 +1797,14 @@ static struct hid_device *usb_hid_config
+ (hid_blacklist[n].idProduct == le16_to_cpu(dev->descriptor.idProduct)))
+ quirks = hid_blacklist[n].quirks;
+
++ /* Many keyboards and mice don't like to be polled for reports,
++ * so we will always set the HID_QUIRK_NOGET flag for them. */
++ if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
++ if (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD ||
++ interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
++ quirks |= HID_QUIRK_NOGET;
++ }
++
+ if (quirks & HID_QUIRK_IGNORE)
+ return NULL;
+
+--- gregkh-2.6.orig/drivers/usb/input/hid.h
++++ gregkh-2.6/drivers/usb/input/hid.h
+@@ -41,6 +41,14 @@
+ #define USB_INTERFACE_CLASS_HID 3
+
+ /*
++ * USB HID interface subclass and protocol codes
++ */
++
++#define USB_INTERFACE_SUBCLASS_BOOT 1
++#define USB_INTERFACE_PROTOCOL_KEYBOARD 1
++#define USB_INTERFACE_PROTOCOL_MOUSE 2
++
++/*
+ * HID class requests
+ */
+