aboutsummaryrefslogtreecommitdiffstats
path: root/usb.current
diff options
authorGreg Kroah-Hartman <gregkh@suse.de>2008-08-07 21:23:49 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2008-08-07 21:23:49 -0700
commit4e0781592b3976c117346ce46eb8fa5f4b7552ef (patch)
tree467b0dad426a03adf38cd6d0c68c35e53815cd48 /usb.current
parent9b11efecf242453bef9ffda66c50a74278538d16 (diff)
downloadpatches-4e0781592b3976c117346ce46eb8fa5f4b7552ef.tar.gz
usb patches actuall added this time...
Diffstat (limited to 'usb.current')
-rw-r--r--usb.current/usb-cdc-acm-bugfix-release.patch71
-rw-r--r--usb.current/usb-cdc-acm-drain-writes-on-close.patch156
-rw-r--r--usb.current/usb-cdc-acm-stop-dropping-tx-buffers.patch145
3 files changed, 372 insertions, 0 deletions
diff --git a/usb.current/usb-cdc-acm-bugfix-release.patch b/usb.current/usb-cdc-acm-bugfix-release.patch
new file mode 100644
index 00000000000000..f41e91c01993a0
--- /dev/null
+++ b/usb.current/usb-cdc-acm-bugfix-release.patch
@@ -0,0 +1,71 @@
+From david-b@pacbell.net Thu Aug 7 21:10:21 2008
+From: David Brownell <david-b@pacbell.net>
+Date: Wed, 6 Aug 2008 18:41:12 -0700
+Subject: usb: cdc-acm: bugfix release()
+To: Greg KH <greg@kroah.com>
+Cc: linux-usb@vger.kernel.org, Oliver Neukum <oneukum@suse.de>
+Message-ID: <200808061841.13144.david-b@pacbell.net>
+Content-Disposition: inline
+
+From: David Brownell <dbrownell@users.sourceforge.net>
+
+Bugfixes to the usb_driver_release_interface() usage;
+
+ (a) make sure releasing *either* interface first will release
+ the other, instead of insisting it be the control interface;
+
+ (b) remove the recently-added self-deadlock.
+
+(The "fix disconnect bug in cdc-acm" patch was incomplete and incorrect.)
+
+Plus a small "sparse" fix: rename a local variable so it doesn't
+shadow a function parameter.
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Acked-by: Oliver Neukum <oneukum@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/class/cdc-acm.c | 15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+--- a/drivers/usb/class/cdc-acm.c
++++ b/drivers/usb/class/cdc-acm.c
+@@ -1108,9 +1108,11 @@ skip_normal_probe:
+ rcv->instance = acm;
+ }
+ for (i = 0; i < num_rx_buf; i++) {
+- struct acm_rb *buf = &(acm->rb[i]);
++ struct acm_rb *rb = &(acm->rb[i]);
+
+- if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
++ rb->base = usb_buffer_alloc(acm->dev, readsize,
++ GFP_KERNEL, &rb->dma);
++ if (!rb->base) {
+ dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
+ goto alloc_fail7;
+ }
+@@ -1172,6 +1174,7 @@ skip_countries:
+ acm_set_line(acm, &acm->line);
+
+ usb_driver_claim_interface(&acm_driver, data_interface, acm);
++ usb_set_intfdata(data_interface, acm);
+
+ usb_get_intf(control_interface);
+ tty_register_device(acm_tty_driver, minor, &control_interface->dev);
+@@ -1221,11 +1224,11 @@ static void acm_disconnect(struct usb_in
+ struct acm *acm = usb_get_intfdata(intf);
+ struct usb_device *usb_dev = interface_to_usbdev(intf);
+
+- mutex_lock(&open_mutex);
+- if (!acm || !acm->dev) {
+- mutex_unlock(&open_mutex);
++ /* sibling interface is already cleaning up */
++ if (!acm)
+ return;
+- }
++
++ mutex_lock(&open_mutex);
+ if (acm->country_codes){
+ device_remove_file(&acm->control->dev,
+ &dev_attr_wCountryCodes);
diff --git a/usb.current/usb-cdc-acm-drain-writes-on-close.patch b/usb.current/usb-cdc-acm-drain-writes-on-close.patch
new file mode 100644
index 00000000000000..226361dc2c0d11
--- /dev/null
+++ b/usb.current/usb-cdc-acm-drain-writes-on-close.patch
@@ -0,0 +1,156 @@
+From david-b@pacbell.net Thu Aug 7 21:12:03 2008
+From: David Brownell <david-b@pacbell.net>
+Date: Wed, 6 Aug 2008 18:46:10 -0700
+Subject: usb: cdc-acm: drain writes on close
+To: Greg KH <greg@kroah.com>
+Cc: linux-usb@vger.kernel.org, Oliver Neukum <oneukum@suse.de>
+Message-ID: <200808061846.10641.david-b@pacbell.net>
+Content-Disposition: inline
+
+
+From: David Brownell <dbrownell@users.sourceforge.net>
+
+Add a mechanism to let the write queue drain naturally before
+closing the TTY, rather than always losing that data. There
+is a timeout, so it can't wait too long.
+
+Provide missing locking inside acm_wb_is_avail(); it matters
+more now. Note, this presumes an earlier patch was applied,
+removing a call to this routine where the lock was held.
+
+Slightly improved diagnostics on write URB completion, so we
+can tell when a write URB gets killed and, if so, how much
+data it wrote first ... and so that I/O path is normally
+silent (and can't much change timings).
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/class/cdc-acm.c | 39 ++++++++++++++++++++++++++++++++++-----
+ drivers/usb/class/cdc-acm.h | 1 +
+ 2 files changed, 35 insertions(+), 5 deletions(-)
+
+--- a/drivers/usb/class/cdc-acm.c
++++ b/drivers/usb/class/cdc-acm.c
+@@ -51,6 +51,7 @@
+ */
+
+ #undef DEBUG
++#undef VERBOSE_DEBUG
+
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+@@ -70,6 +71,9 @@
+
+ #include "cdc-acm.h"
+
++
++#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */
++
+ /*
+ * Version Information
+ */
+@@ -85,6 +89,12 @@ static DEFINE_MUTEX(open_mutex);
+
+ #define ACM_READY(acm) (acm && acm->dev && acm->used)
+
++#ifdef VERBOSE_DEBUG
++#define verbose 1
++#else
++#define verbose 0
++#endif
++
+ /*
+ * Functions for ACM control messages.
+ */
+@@ -136,11 +146,14 @@ static int acm_wb_alloc(struct acm *acm)
+ static int acm_wb_is_avail(struct acm *acm)
+ {
+ int i, n;
++ unsigned long flags;
+
+ n = ACM_NW;
++ spin_lock_irqsave(&acm->write_lock, flags);
+ for (i = 0; i < ACM_NW; i++) {
+ n -= acm->wb[i].use;
+ }
++ spin_unlock_irqrestore(&acm->write_lock, flags);
+ return n;
+ }
+
+@@ -467,22 +480,28 @@ urbs:
+ /* data interface wrote those outgoing bytes */
+ static void acm_write_bulk(struct urb *urb)
+ {
+- struct acm *acm;
+ struct acm_wb *wb = urb->context;
++ struct acm *acm = wb->instance;
+
+- dbg("Entering acm_write_bulk with status %d", urb->status);
++ if (verbose || urb->status
++ || (urb->actual_length != urb->transfer_buffer_length))
++ dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
++ urb->actual_length,
++ urb->transfer_buffer_length,
++ urb->status);
+
+- acm = wb->instance;
+ acm_write_done(acm, wb);
+ if (ACM_READY(acm))
+ schedule_work(&acm->work);
++ else
++ wake_up_interruptible(&acm->drain_wait);
+ }
+
+ static void acm_softint(struct work_struct *work)
+ {
+ struct acm *acm = container_of(work, struct acm, work);
+- dbg("Entering acm_softint.");
+-
++
++ dev_vdbg(&acm->data->dev, "tx work\n");
+ if (!ACM_READY(acm))
+ return;
+ tty_wakeup(acm->tty);
+@@ -603,6 +622,8 @@ static void acm_tty_unregister(struct ac
+ kfree(acm);
+ }
+
++static int acm_tty_chars_in_buffer(struct tty_struct *tty);
++
+ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
+ {
+ struct acm *acm = tty->driver_data;
+@@ -617,6 +638,13 @@ static void acm_tty_close(struct tty_str
+ if (acm->dev) {
+ usb_autopm_get_interface(acm->control);
+ acm_set_control(acm, acm->ctrlout = 0);
++
++ /* try letting the last writes drain naturally */
++ wait_event_interruptible_timeout(acm->drain_wait,
++ (ACM_NW == acm_wb_is_avail(acm))
++ || !acm->dev,
++ ACM_CLOSE_TIMEOUT * HZ);
++
+ usb_kill_urb(acm->ctrlurb);
+ for (i = 0; i < ACM_NW; i++)
+ usb_kill_urb(acm->wb[i].urb);
+@@ -1047,6 +1075,7 @@ skip_normal_probe:
+ acm->urb_task.data = (unsigned long) acm;
+ INIT_WORK(&acm->work, acm_softint);
+ INIT_WORK(&acm->waker, acm_waker);
++ init_waitqueue_head(&acm->drain_wait);
+ spin_lock_init(&acm->throttle_lock);
+ spin_lock_init(&acm->write_lock);
+ spin_lock_init(&acm->read_lock);
+--- a/drivers/usb/class/cdc-acm.h
++++ b/drivers/usb/class/cdc-acm.h
+@@ -113,6 +113,7 @@ struct acm {
+ struct usb_cdc_line_coding line; /* bits, stop, parity */
+ struct work_struct work; /* work queue entry for line discipline waking up */
+ struct work_struct waker;
++ wait_queue_head_t drain_wait; /* close processing */
+ struct tasklet_struct urb_task; /* rx processing */
+ spinlock_t throttle_lock; /* synchronize throtteling and read callback */
+ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
diff --git a/usb.current/usb-cdc-acm-stop-dropping-tx-buffers.patch b/usb.current/usb-cdc-acm-stop-dropping-tx-buffers.patch
new file mode 100644
index 00000000000000..7b7cc9c4979a40
--- /dev/null
+++ b/usb.current/usb-cdc-acm-stop-dropping-tx-buffers.patch
@@ -0,0 +1,145 @@
+From david-b@pacbell.net Thu Aug 7 21:10:47 2008
+From: David Brownell <david-b@pacbell.net>
+Date: Wed, 6 Aug 2008 18:44:12 -0700
+Subject: usb: cdc-acm: stop dropping tx buffers
+To: Greg KH <greg@kroah.com>
+Cc: linux-usb@vger.kernel.org, Oliver Neukum <oneukum@suse.de>, David Engraf <david.engraf@netcom.eu>
+Message-ID: <200808061844.12730.david-b@pacbell.net>
+Content-Disposition: inline
+
+
+From: David Brownell <dbrownell@users.sourceforge.net>
+
+The "increase cdc-acm write throughput" patch left in place two
+now-obsolete mechanisms, either of which can make the cdc-acm
+driver drop TX data (nasty!). This patch removes them:
+
+ - The write_ready flag ... if an URB and buffer were found,
+ they can (and should!) always be used.
+
+ - TX path acm_wb_is_used() ... used when the buffer was just
+ allocated, so that check is pointless.
+
+Also fix a won't-yet-matter leak of a write buffer on a disconnect path.
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Cc: David Engraf <david.engraf@netcom.eu>
+Acked-by: Oliver Neukum <oneukum@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/class/cdc-acm.c | 32 +++-----------------------------
+ drivers/usb/class/cdc-acm.h | 2 --
+ 2 files changed, 3 insertions(+), 31 deletions(-)
+
+--- a/drivers/usb/class/cdc-acm.c
++++ b/drivers/usb/class/cdc-acm.c
+@@ -144,11 +144,6 @@ static int acm_wb_is_avail(struct acm *a
+ return n;
+ }
+
+-static inline int acm_wb_is_used(struct acm *acm, int wbn)
+-{
+- return acm->wb[wbn].use;
+-}
+-
+ /*
+ * Finish write.
+ */
+@@ -157,7 +152,6 @@ static void acm_write_done(struct acm *a
+ unsigned long flags;
+
+ spin_lock_irqsave(&acm->write_lock, flags);
+- acm->write_ready = 1;
+ wb->use = 0;
+ acm->transmitting--;
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+@@ -190,40 +184,25 @@ static int acm_start_wb(struct acm *acm,
+ static int acm_write_start(struct acm *acm, int wbn)
+ {
+ unsigned long flags;
+- struct acm_wb *wb;
++ struct acm_wb *wb = &acm->wb[wbn];
+ int rc;
+
+ spin_lock_irqsave(&acm->write_lock, flags);
+ if (!acm->dev) {
++ wb->use = 0;
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+ return -ENODEV;
+ }
+
+- if (!acm->write_ready) {
+- spin_unlock_irqrestore(&acm->write_lock, flags);
+- return 0; /* A white lie */
+- }
+-
+- wb = &acm->wb[wbn];
+- if(acm_wb_is_avail(acm) <= 1)
+- acm->write_ready = 0;
+-
+ dbg("%s susp_count: %d", __func__, acm->susp_count);
+ if (acm->susp_count) {
+- acm->old_ready = acm->write_ready;
+ acm->delayed_wb = wb;
+- acm->write_ready = 0;
+ schedule_work(&acm->waker);
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+ return 0; /* A white lie */
+ }
+ usb_mark_last_busy(acm->dev);
+
+- if (!acm_wb_is_used(acm, wbn)) {
+- spin_unlock_irqrestore(&acm->write_lock, flags);
+- return 0;
+- }
+-
+ rc = acm_start_wb(acm, wb);
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+
+@@ -512,7 +491,6 @@ static void acm_softint(struct work_stru
+ static void acm_waker(struct work_struct *waker)
+ {
+ struct acm *acm = container_of(waker, struct acm, waker);
+- unsigned long flags;
+ int rv;
+
+ rv = usb_autopm_get_interface(acm->control);
+@@ -524,9 +502,6 @@ static void acm_waker(struct work_struct
+ acm_start_wb(acm, acm->delayed_wb);
+ acm->delayed_wb = NULL;
+ }
+- spin_lock_irqsave(&acm->write_lock, flags);
+- acm->write_ready = acm->old_ready;
+- spin_unlock_irqrestore(&acm->write_lock, flags);
+ usb_autopm_put_interface(acm->control);
+ }
+
+@@ -697,7 +672,7 @@ static int acm_tty_write_room(struct tty
+ * Do not let the line discipline to know that we have a reserve,
+ * or it might get too enthusiastic.
+ */
+- return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0;
++ return acm_wb_is_avail(acm) ? acm->writesize : 0;
+ }
+
+ static int acm_tty_chars_in_buffer(struct tty_struct *tty)
+@@ -1076,7 +1051,6 @@ skip_normal_probe:
+ spin_lock_init(&acm->write_lock);
+ spin_lock_init(&acm->read_lock);
+ mutex_init(&acm->mutex);
+- acm->write_ready = 1;
+ acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
+
+ buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
+--- a/drivers/usb/class/cdc-acm.h
++++ b/drivers/usb/class/cdc-acm.h
+@@ -106,8 +106,6 @@ struct acm {
+ struct list_head spare_read_bufs;
+ struct list_head filled_read_bufs;
+ int write_used; /* number of non-empty write buffers */
+- int write_ready; /* write urb is not running */
+- int old_ready;
+ int processing;
+ int transmitting;
+ spinlock_t write_lock;