diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2006-05-16 15:07:29 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-05-16 15:07:29 -0700 |
commit | f0e3ae84732e61fe198a11197b7d0bef9952a6c5 (patch) | |
tree | 958882e94d5370ed4c96220724f3493f3d0256b1 /usb | |
parent | 91e341fcb2ec42630ceb6a9c5a38396f033116f6 (diff) | |
download | patches-f0e3ae84732e61fe198a11197b7d0bef9952a6c5.tar.gz |
usb patches added
Diffstat (limited to 'usb')
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, ®s->intrenable)) == 0) { + return IRQ_NOTMINE; +- } ++ } ++ ++ if (ints & OHCI_INTR_RHSC) { ++ ohci_vdbg (ohci, "rhsc\n"); ++ ohci_writel (ohci, OHCI_INTR_RHSC, ®s->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 + */ + |