]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
.31 tty usb-serial mess
authorGreg Kroah-Hartman <gregkh@suse.de>
Thu, 1 Oct 2009 22:00:58 +0000 (15:00 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 1 Oct 2009 22:00:58 +0000 (15:00 -0700)
16 files changed:
queue-2.6.31/driver-core-add-new-device-to-bus-s-list-before-probing.patch [new file with mode: 0644]
queue-2.6.31/hwmon-add-maintainer-information.patch [new file with mode: 0644]
queue-2.6.31/series
queue-2.6.31/tty-add-a-full-port_close-function.patch [new file with mode: 0644]
queue-2.6.31/tty-usb-can-now-use-the-shutdown-method-for-kref-based-freeing-of-ports.patch [new file with mode: 0644]
queue-2.6.31/tty-usb-hangup-is-racy.patch [new file with mode: 0644]
queue-2.6.31/tty-usb-serial-termios-bits.patch [new file with mode: 0644]
queue-2.6.31/usb-serial-acquire-references-when-a-new-tty-is-installed.patch [new file with mode: 0644]
queue-2.6.31/usb-serial-add-missing-tests-and-debug-lines.patch [new file with mode: 0644]
queue-2.6.31/usb-serial-change-logic-of-serial-lookups.patch [new file with mode: 0644]
queue-2.6.31/usb-serial-change-referencing-of-port-and-serial-structures.patch [new file with mode: 0644]
queue-2.6.31/usb-serial-fix-termios-initialization-logic.patch [new file with mode: 0644]
queue-2.6.31/usb-serial-put-subroutines-in-logical-order.patch [new file with mode: 0644]
queue-2.6.31/usb-serial-rename-subroutines.patch [new file with mode: 0644]
queue-2.6.31/usb-serial-straighten-out-serial_open.patch [new file with mode: 0644]
queue-2.6.31/usb-serial-update-the-console-driver.patch [new file with mode: 0644]

diff --git a/queue-2.6.31/driver-core-add-new-device-to-bus-s-list-before-probing.patch b/queue-2.6.31/driver-core-add-new-device-to-bus-s-list-before-probing.patch
new file mode 100644 (file)
index 0000000..38b242a
--- /dev/null
@@ -0,0 +1,111 @@
+From 2023c610dc54a4f4130b0494309a9bd668ca3df8 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Thu, 30 Jul 2009 15:27:18 -0400
+Subject: Driver core: add new device to bus's list before probing
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit 2023c610dc54a4f4130b0494309a9bd668ca3df8 upstream.
+
+This patch (as1271) affects when new devices get linked into their
+bus's list of devices.  Currently this happens after probing, and it
+doesn't happen at all if probing fails.  Clearly this is wrong,
+because at that point quite a few symbolic links have already been
+created in sysfs.  We are committed to adding the device, so it should
+be linked into the bus's list regardless.
+
+In addition, this needs to happen before the uevent announcing the new
+device gets issued.  Otherwise user programs might try to access the
+device before it has been added to the bus.
+
+To fix both these problems, the patch moves the call to
+klist_add_tail() forward from bus_attach_device() to bus_add_device().
+Since bus_attach_device() now does nothing but probe for drivers, it
+has been renamed to bus_probe_device().  And lastly, the kerneldoc is
+updated.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+CC: Kay Sievers <kay.sievers@vrfy.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/base/base.h |    2 +-
+ drivers/base/bus.c  |   23 ++++++++++-------------
+ drivers/base/core.c |    2 +-
+ 3 files changed, 12 insertions(+), 15 deletions(-)
+
+--- a/drivers/base/base.h
++++ b/drivers/base/base.h
+@@ -104,7 +104,7 @@ extern int system_bus_init(void);
+ extern int cpu_dev_init(void);
+ extern int bus_add_device(struct device *dev);
+-extern void bus_attach_device(struct device *dev);
++extern void bus_probe_device(struct device *dev);
+ extern void bus_remove_device(struct device *dev);
+ extern int bus_add_driver(struct device_driver *drv);
+--- a/drivers/base/bus.c
++++ b/drivers/base/bus.c
+@@ -459,8 +459,9 @@ static inline void remove_deprecated_bus
+  * bus_add_device - add device to bus
+  * @dev: device being added
+  *
++ * - Add device's bus attributes.
++ * - Create links to device's bus.
+  * - Add the device to its bus's list of devices.
+- * - Create link to device's bus.
+  */
+ int bus_add_device(struct device *dev)
+ {
+@@ -483,6 +484,7 @@ int bus_add_device(struct device *dev)
+               error = make_deprecated_bus_links(dev);
+               if (error)
+                       goto out_deprecated;
++              klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
+       }
+       return 0;
+@@ -498,24 +500,19 @@ out_put:
+ }
+ /**
+- * bus_attach_device - add device to bus
+- * @dev: device tried to attach to a driver
++ * bus_probe_device - probe drivers for a new device
++ * @dev: device to probe
+  *
+- * - Add device to bus's list of devices.
+- * - Try to attach to driver.
++ * - Automatically probe for a driver if the bus allows it.
+  */
+-void bus_attach_device(struct device *dev)
++void bus_probe_device(struct device *dev)
+ {
+       struct bus_type *bus = dev->bus;
+-      int ret = 0;
++      int ret;
+-      if (bus) {
+-              if (bus->p->drivers_autoprobe)
+-                      ret = device_attach(dev);
++      if (bus && bus->p->drivers_autoprobe) {
++              ret = device_attach(dev);
+               WARN_ON(ret < 0);
+-              if (ret >= 0)
+-                      klist_add_tail(&dev->p->knode_bus,
+-                                     &bus->p->klist_devices);
+       }
+ }
+--- a/drivers/base/core.c
++++ b/drivers/base/core.c
+@@ -945,7 +945,7 @@ int device_add(struct device *dev)
+                                            BUS_NOTIFY_ADD_DEVICE, dev);
+       kobject_uevent(&dev->kobj, KOBJ_ADD);
+-      bus_attach_device(dev);
++      bus_probe_device(dev);
+       if (parent)
+               klist_add_tail(&dev->p->knode_parent,
+                              &parent->p->klist_children);
diff --git a/queue-2.6.31/hwmon-add-maintainer-information.patch b/queue-2.6.31/hwmon-add-maintainer-information.patch
new file mode 100644 (file)
index 0000000..54523e6
--- /dev/null
@@ -0,0 +1,34 @@
+From 2c2a6172afab7421f6af7e228cd3121f423ea932 Mon Sep 17 00:00:00 2001
+From: Luca Tettamanti <kronos.it@gmail.com>
+Date: Tue, 15 Sep 2009 17:18:10 +0200
+Subject: hwmon: (asus_atk0110) Add maintainer information
+
+From: Luca Tettamanti <kronos.it@gmail.com>
+
+commit 2c2a6172afab7421f6af7e228cd3121f423ea932 upstream.
+
+Add myself as asus_atk0110 maintainer.
+
+Signed-off-by: Luca Tettamanti <kronos.it@gmail.com>
+Signed-off-by: Jean Delvare <khali@linux-fr.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ MAINTAINERS |    6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -897,6 +897,12 @@ W:        http://wireless.kernel.org/en/users/D
+ S:    Maintained
+ F:    drivers/net/wireless/ath/ar9170/
++ATK0110 HWMON DRIVER
++M:    Luca Tettamanti <kronos.it@gmail.com>
++L:    lm-sensors@lm-sensors.org
++S:    Maintained
++F:    drivers/hwmon/asus_atk0110.c
++
+ ATI_REMOTE2 DRIVER
+ M:    Ville Syrjala <syrjala@sci.fi>
+ S:    Maintained
index b2eb8de86360c7e96fcb7804f12aad060a472e6d..5c42b8de5bfa15c261a823bb0d5a2826caab8767 100644 (file)
@@ -84,3 +84,18 @@ netfilter-bridge-refcount-fix.patch
 netfilter-ebt_ulog-fix-checkentry-return-value.patch
 ath5k-wakeup-fixes.patch
 ath5k-do-not-release-irq-across-suspend-resume.patch
+driver-core-add-new-device-to-bus-s-list-before-probing.patch
+tty-add-a-full-port_close-function.patch
+tty-usb-hangup-is-racy.patch
+tty-usb-can-now-use-the-shutdown-method-for-kref-based-freeing-of-ports.patch
+hwmon-add-maintainer-information.patch
+tty-usb-serial-termios-bits.patch
+usb-serial-change-referencing-of-port-and-serial-structures.patch
+usb-serial-put-subroutines-in-logical-order.patch
+usb-serial-change-logic-of-serial-lookups.patch
+usb-serial-acquire-references-when-a-new-tty-is-installed.patch
+usb-serial-fix-termios-initialization-logic.patch
+usb-serial-rename-subroutines.patch
+usb-serial-add-missing-tests-and-debug-lines.patch
+usb-serial-straighten-out-serial_open.patch
+usb-serial-update-the-console-driver.patch
diff --git a/queue-2.6.31/tty-add-a-full-port_close-function.patch b/queue-2.6.31/tty-add-a-full-port_close-function.patch
new file mode 100644 (file)
index 0000000..5a765fb
--- /dev/null
@@ -0,0 +1,119 @@
+From 7ca0ff9ab3218ec443a7a9ad247e4650373ed41e Mon Sep 17 00:00:00 2001
+From: Alan Cox <alan@linux.intel.com>
+Date: Sat, 19 Sep 2009 13:13:20 -0700
+Subject: tty: Add a full port_close function
+
+From: Alan Cox <alan@linux.intel.com>
+
+commit 7ca0ff9ab3218ec443a7a9ad247e4650373ed41e upstream.
+
+Now we are extracting out methods for shutdown and the like we can add a
+proper tty_port_close method that knows all the innards of the tty closing
+process and hides the lot from the caller.
+
+At some point in the future this will be paired with a similar open()
+helper and the drivers can stick to hardware management.
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/char/tty_port.c |   29 +++++++++++++++++++++++++++--
+ include/linux/tty.h     |    8 +++++++-
+ 2 files changed, 34 insertions(+), 3 deletions(-)
+
+--- a/drivers/char/tty_port.c
++++ b/drivers/char/tty_port.c
+@@ -96,6 +96,14 @@ void tty_port_tty_set(struct tty_port *p
+ }
+ EXPORT_SYMBOL(tty_port_tty_set);
++static void tty_port_shutdown(struct tty_port *port)
++{
++      if (port->ops->shutdown &&
++              test_and_clear_bit(ASYNC_INITIALIZED, &port->flags))
++                      port->ops->shutdown(port);
++
++}
++
+ /**
+  *    tty_port_hangup         -       hangup helper
+  *    @port: tty port
+@@ -116,6 +124,7 @@ void tty_port_hangup(struct tty_port *po
+       port->tty = NULL;
+       spin_unlock_irqrestore(&port->lock, flags);
+       wake_up_interruptible(&port->open_wait);
++      tty_port_shutdown(port);
+ }
+ EXPORT_SYMBOL(tty_port_hangup);
+@@ -296,15 +305,17 @@ int tty_port_close_start(struct tty_port
+       if (port->count) {
+               spin_unlock_irqrestore(&port->lock, flags);
++              if (port->ops->drop)
++                      port->ops->drop(port);
+               return 0;
+       }
+-      port->flags |= ASYNC_CLOSING;
++      set_bit(ASYNC_CLOSING, &port->flags);
+       tty->closing = 1;
+       spin_unlock_irqrestore(&port->lock, flags);
+       /* Don't block on a stalled port, just pull the chain */
+       if (tty->flow_stopped)
+               tty_driver_flush_buffer(tty);
+-      if (port->flags & ASYNC_INITIALIZED &&
++      if (test_bit(ASYNCB_INITIALIZED, &port->flags) &&
+                       port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+               tty_wait_until_sent(tty, port->closing_wait);
+       if (port->drain_delay) {
+@@ -318,6 +329,9 @@ int tty_port_close_start(struct tty_port
+                       timeout = 2 * HZ;
+               schedule_timeout_interruptible(timeout);
+       }
++      /* Don't call port->drop for the last reference. Callers will want
++         to drop the last active reference in ->shutdown() or the tty
++         shutdown path */
+       return 1;
+ }
+ EXPORT_SYMBOL(tty_port_close_start);
+@@ -348,3 +362,14 @@ void tty_port_close_end(struct tty_port 
+       spin_unlock_irqrestore(&port->lock, flags);
+ }
+ EXPORT_SYMBOL(tty_port_close_end);
++
++void tty_port_close(struct tty_port *port, struct tty_struct *tty,
++                                                      struct file *filp)
++{
++      if (tty_port_close_start(port, tty, filp) == 0)
++              return;
++      tty_port_shutdown(port);
++      tty_port_close_end(port, tty);
++      tty_port_tty_set(port, NULL);
++}
++EXPORT_SYMBOL(tty_port_close);
+--- a/include/linux/tty.h
++++ b/include/linux/tty.h
+@@ -185,7 +185,12 @@ struct tty_port;
+ struct tty_port_operations {
+       /* Return 1 if the carrier is raised */
+       int (*carrier_raised)(struct tty_port *port);
++      /* Control the DTR line */
+       void (*dtr_rts)(struct tty_port *port, int raise);
++      /* Called when the last close completes or a hangup finishes
++         IFF the port was initialized. Do not use to free resources */
++      void (*shutdown)(struct tty_port *port);
++      void (*drop)(struct tty_port *port);
+ };
+       
+ struct tty_port {
+@@ -457,7 +462,8 @@ extern int tty_port_block_til_ready(stru
+ extern int tty_port_close_start(struct tty_port *port,
+                               struct tty_struct *tty, struct file *filp);
+ extern void tty_port_close_end(struct tty_port *port, struct tty_struct *tty);
+-
++extern void tty_port_close(struct tty_port *port,
++                              struct tty_struct *tty, struct file *filp);
+ extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc);
+ extern int tty_unregister_ldisc(int disc);
+ extern int tty_set_ldisc(struct tty_struct *tty, int ldisc);
diff --git a/queue-2.6.31/tty-usb-can-now-use-the-shutdown-method-for-kref-based-freeing-of-ports.patch b/queue-2.6.31/tty-usb-can-now-use-the-shutdown-method-for-kref-based-freeing-of-ports.patch
new file mode 100644 (file)
index 0000000..282261a
--- /dev/null
@@ -0,0 +1,80 @@
+From 4455e344959a217ffc28de2ab1af87541322b343 Mon Sep 17 00:00:00 2001
+From: Alan Cox <alan@linux.intel.com>
+Date: Sat, 19 Sep 2009 13:13:24 -0700
+Subject: tty: USB can now use the shutdown method for kref based freeing of ports
+
+From: Alan Cox <alan@linux.intel.com>
+
+commit 4455e344959a217ffc28de2ab1af87541322b343 upstream.
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/usb-serial.c |   28 +++++++---------------------
+ 1 file changed, 7 insertions(+), 21 deletions(-)
+
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -316,16 +316,19 @@ static void serial_do_down(struct usb_se
+  *
+  *    Do the resource freeing and refcount dropping for the port. We must
+  *    be careful about ordering and we must avoid freeing up the console.
++ *
++ *    Called when the last tty kref is dropped.
+  */
+-static void serial_do_free(struct usb_serial_port *port)
++static void serial_do_free(struct tty_struct *tty)
+ {
++      struct usb_serial_port *port = tty->driver_data;
+       struct usb_serial *serial;
+       struct module *owner;
+       /* The console is magical, do not hang up the console hardware
+          or there will be tears */
+-      if (port->console)
++      if (port == NULL || port->console)
+               return;
+       serial = port->serial;
+@@ -350,30 +353,12 @@ static void serial_close(struct tty_stru
+       dbg("%s - port %d", __func__, port->number);
+-      /* FIXME:
+-         This leaves a very narrow race. Really we should do the
+-         serial_do_free() on tty->shutdown(), but tty->shutdown can
+-         be called from IRQ context and serial_do_free can sleep.
+-
+-         The right fix is probably to make the tty free (which is rare)
+-         and thus tty->shutdown() occur via a work queue and simplify all
+-         the drivers that use it.
+-      */
+-      if (tty_hung_up_p(filp)) {
+-              /* serial_hangup already called serial_down at this point.
+-                 Another user may have already reopened the port but
+-                 serial_do_free is refcounted */
+-              serial_do_free(port);
+-              return;
+-      }
+-
+       if (tty_port_close_start(&port->port, tty, filp) == 0)
+               return;
+-
+       serial_do_down(port);           
+       tty_port_close_end(&port->port, tty);
+       tty_port_tty_set(&port->port, NULL);
+-      serial_do_free(port);
++
+ }
+ static void serial_hangup(struct tty_struct *tty)
+@@ -1243,6 +1228,7 @@ static const struct tty_operations seria
+       .chars_in_buffer =      serial_chars_in_buffer,
+       .tiocmget =             serial_tiocmget,
+       .tiocmset =             serial_tiocmset,
++      .shutdown =             serial_do_free,
+       .proc_fops =            &serial_proc_fops,
+ };
diff --git a/queue-2.6.31/tty-usb-hangup-is-racy.patch b/queue-2.6.31/tty-usb-hangup-is-racy.patch
new file mode 100644 (file)
index 0000000..60d1b1f
--- /dev/null
@@ -0,0 +1,35 @@
+From d2b391822a11302add9e46476f3da4e18e6de84c Mon Sep 17 00:00:00 2001
+From: Alan Cox <alan@linux.intel.com>
+Date: Sat, 19 Sep 2009 13:13:23 -0700
+Subject: tty: USB hangup is racy
+
+From: Alan Cox <alan@linux.intel.com>
+
+commit d2b391822a11302add9e46476f3da4e18e6de84c upstream.
+
+The USB layer uses tty_hangup to deal with unplugs of the physical hardware
+(analogous to loss of carrier) and then frees the resources. However the
+tty_hangup is asynchronous. As the hangup can sleep we can use tty_vhangup
+which is the non async version to avoid freeing resources too early.
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/usb-serial.c |    5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -1161,10 +1161,7 @@ void usb_serial_disconnect(struct usb_in
+               if (port) {
+                       struct tty_struct *tty = tty_port_tty_get(&port->port);
+                       if (tty) {
+-                              /* The hangup will occur asynchronously but
+-                                 the object refcounts will sort out all the
+-                                 cleanup */
+-                              tty_hangup(tty);
++                              tty_vhangup(tty);
+                               tty_kref_put(tty);
+                       }
+                       kill_traffic(port);
diff --git a/queue-2.6.31/tty-usb-serial-termios-bits.patch b/queue-2.6.31/tty-usb-serial-termios-bits.patch
new file mode 100644 (file)
index 0000000..376c975
--- /dev/null
@@ -0,0 +1,533 @@
+From fe1ae7fdd2ee603f2d95f04e09a68f7f79045127 Mon Sep 17 00:00:00 2001
+From: Alan Cox <alan@linux.intel.com>
+Date: Sat, 19 Sep 2009 13:13:33 -0700
+Subject: tty: USB serial termios bits
+
+From: Alan Cox <alan@linux.intel.com>
+
+commit fe1ae7fdd2ee603f2d95f04e09a68f7f79045127 upstream.
+
+Various drivers have hacks to mangle termios structures. This stems from
+the fact there is no nice setup hook for configuring the termios settings
+when the port is created
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/char/tty_io.c            |    1 
+ drivers/usb/serial/ark3116.c     |   46 +++++++++------------------------------
+ drivers/usb/serial/cypress_m8.c  |   12 ++--------
+ drivers/usb/serial/empeg.c       |   12 ++--------
+ drivers/usb/serial/iuu_phoenix.c |   31 +++++++++++---------------
+ drivers/usb/serial/kobil_sct.c   |   22 +++++++++---------
+ drivers/usb/serial/oti6858.c     |   21 ++++++++---------
+ drivers/usb/serial/spcp8x5.c     |   21 ++++++++---------
+ drivers/usb/serial/usb-serial.c  |   38 +++++++++++++++++++++++++++++++-
+ drivers/usb/serial/whiteheat.c   |    6 ++---
+ include/linux/usb/serial.h       |    3 ++
+ 11 files changed, 106 insertions(+), 107 deletions(-)
+
+--- a/drivers/char/tty_io.c
++++ b/drivers/char/tty_io.c
+@@ -1184,6 +1184,7 @@ int tty_init_termios(struct tty_struct *
+       tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
+       return 0;
+ }
++EXPORT_SYMBOL_GPL(tty_init_termios);
+ /**
+  *    tty_driver_install_tty() - install a tty entry in the driver
+--- a/drivers/usb/serial/ark3116.c
++++ b/drivers/usb/serial/ark3116.c
+@@ -35,11 +35,6 @@ static struct usb_device_id id_table [] 
+ };
+ MODULE_DEVICE_TABLE(usb, id_table);
+-struct ark3116_private {
+-      spinlock_t lock;
+-      u8 termios_initialized;
+-};
+-
+ static inline void ARK3116_SND(struct usb_serial *serial, int seq,
+                              __u8 request, __u8 requesttype,
+                              __u16 value, __u16 index)
+@@ -82,22 +77,11 @@ static inline void ARK3116_RCV_QUIET(str
+ static int ark3116_attach(struct usb_serial *serial)
+ {
+       char *buf;
+-      struct ark3116_private *priv;
+-      int i;
+-
+-      for (i = 0; i < serial->num_ports; ++i) {
+-              priv = kzalloc(sizeof(struct ark3116_private), GFP_KERNEL);
+-              if (!priv)
+-                      goto cleanup;
+-              spin_lock_init(&priv->lock);
+-
+-              usb_set_serial_port_data(serial->port[i], priv);
+-      }
+       buf = kmalloc(1, GFP_KERNEL);
+       if (!buf) {
+               dbg("error kmalloc -> out of mem?");
+-              goto cleanup;
++              return -ENOMEM;
+       }
+       /* 3 */
+@@ -149,13 +133,16 @@ static int ark3116_attach(struct usb_ser
+       kfree(buf);
+       return 0;
++}
+-cleanup:
+-      for (--i; i >= 0; --i) {
+-              kfree(usb_get_serial_port_data(serial->port[i]));
+-              usb_set_serial_port_data(serial->port[i], NULL);
+-      }
+-      return -ENOMEM;
++static void ark3116_init_termios(struct tty_struct *tty)
++{
++      struct ktermios *termios = tty->termios;
++      *termios = tty_std_termios;
++      termios->c_cflag = B9600 | CS8
++                                    | CREAD | HUPCL | CLOCAL;
++      termios->c_ispeed = 9600;
++      termios->c_ospeed = 9600;
+ }
+ static void ark3116_set_termios(struct tty_struct *tty,
+@@ -163,10 +150,8 @@ static void ark3116_set_termios(struct t
+                               struct ktermios *old_termios)
+ {
+       struct usb_serial *serial = port->serial;
+-      struct ark3116_private *priv = usb_get_serial_port_data(port);
+       struct ktermios *termios = tty->termios;
+       unsigned int cflag = termios->c_cflag;
+-      unsigned long flags;
+       int baud;
+       int ark3116_baud;
+       char *buf;
+@@ -176,16 +161,6 @@ static void ark3116_set_termios(struct t
+       dbg("%s - port %d", __func__, port->number);
+-      spin_lock_irqsave(&priv->lock, flags);
+-      if (!priv->termios_initialized) {
+-              *termios = tty_std_termios;
+-              termios->c_cflag = B9600 | CS8
+-                                            | CREAD | HUPCL | CLOCAL;
+-              termios->c_ispeed = 9600;
+-              termios->c_ospeed = 9600;
+-              priv->termios_initialized = 1;
+-      }
+-      spin_unlock_irqrestore(&priv->lock, flags);
+       cflag = termios->c_cflag;
+       termios->c_cflag &= ~(CMSPAR|CRTSCTS);
+@@ -455,6 +430,7 @@ static struct usb_serial_driver ark3116_
+       .num_ports =            1,
+       .attach =               ark3116_attach,
+       .set_termios =          ark3116_set_termios,
++      .init_termios =         ark3116_init_termios,
+       .ioctl =                ark3116_ioctl,
+       .tiocmget =             ark3116_tiocmget,
+       .open =                 ark3116_open,
+--- a/drivers/usb/serial/cypress_m8.c
++++ b/drivers/usb/serial/cypress_m8.c
+@@ -659,15 +659,7 @@ static int cypress_open(struct tty_struc
+       spin_unlock_irqrestore(&priv->lock, flags);
+       /* Set termios */
+-      result = cypress_write(tty, port, NULL, 0);
+-
+-      if (result) {
+-              dev_err(&port->dev,
+-                      "%s - failed setting the control lines - error %d\n",
+-                                                      __func__, result);
+-              return result;
+-      } else
+-              dbg("%s - success setting the control lines", __func__);
++      cypress_send(port);
+       if (tty)
+               cypress_set_termios(tty, port, &priv->tmp_termios);
+@@ -1005,6 +997,8 @@ static void cypress_set_termios(struct t
+       dbg("%s - port %d", __func__, port->number);
+       spin_lock_irqsave(&priv->lock, flags);
++      /* We can't clean this one up as we don't know the device type
++         early enough */
+       if (!priv->termios_initialized) {
+               if (priv->chiptype == CT_EARTHMATE) {
+                       *(tty->termios) = tty_std_termios;
+--- a/drivers/usb/serial/empeg.c
++++ b/drivers/usb/serial/empeg.c
+@@ -90,8 +90,7 @@ static int  empeg_chars_in_buffer(struct
+ static void empeg_throttle(struct tty_struct *tty);
+ static void empeg_unthrottle(struct tty_struct *tty);
+ static int  empeg_startup(struct usb_serial *serial);
+-static void empeg_set_termios(struct tty_struct *tty,
+-              struct usb_serial_port *port, struct ktermios *old_termios);
++static void empeg_init_termios(struct tty_struct *tty);
+ static void empeg_write_bulk_callback(struct urb *urb);
+ static void empeg_read_bulk_callback(struct urb *urb);
+@@ -123,7 +122,7 @@ static struct usb_serial_driver empeg_de
+       .throttle =             empeg_throttle,
+       .unthrottle =           empeg_unthrottle,
+       .attach =               empeg_startup,
+-      .set_termios =          empeg_set_termios,
++      .init_termios =         empeg_init_termios,
+       .write =                empeg_write,
+       .write_room =           empeg_write_room,
+       .chars_in_buffer =      empeg_chars_in_buffer,
+@@ -150,9 +149,6 @@ static int empeg_open(struct tty_struct 
+       dbg("%s - port %d", __func__, port->number);
+-      /* Force default termio settings */
+-      empeg_set_termios(tty, port, NULL) ;
+-
+       bytes_in = 0;
+       bytes_out = 0;
+@@ -425,11 +421,9 @@ static int  empeg_startup(struct usb_ser
+ }
+-static void empeg_set_termios(struct tty_struct *tty,
+-              struct usb_serial_port *port, struct ktermios *old_termios)
++static void empeg_init_termios(struct tty_struct *tty)
+ {
+       struct ktermios *termios = tty->termios;
+-      dbg("%s - port %d", __func__, port->number);
+       /*
+        * The empeg-car player wants these particular tty settings.
+--- a/drivers/usb/serial/iuu_phoenix.c
++++ b/drivers/usb/serial/iuu_phoenix.c
+@@ -71,7 +71,6 @@ struct iuu_private {
+       spinlock_t lock;        /* store irq state */
+       wait_queue_head_t delta_msr_wait;
+       u8 line_status;
+-      u8 termios_initialized;
+       int tiostatus;          /* store IUART SIGNAL for tiocmget call */
+       u8 reset;               /* if 1 reset is needed */
+       int poll;               /* number of poll */
+@@ -1018,6 +1017,18 @@ static void iuu_close(struct usb_serial_
+       }
+ }
++static void iuu_init_termios(struct tty_struct *tty)
++{
++      *(tty->termios) = tty_std_termios;
++      tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600
++                              | TIOCM_CTS | CSTOPB | PARENB;
++      tty->termios->c_ispeed = 9600;
++      tty->termios->c_ospeed = 9600;
++      tty->termios->c_lflag = 0;
++      tty->termios->c_oflag = 0;
++      tty->termios->c_iflag = 0;
++}
++
+ static int iuu_open(struct tty_struct *tty,
+                       struct usb_serial_port *port, struct file *filp)
+ {
+@@ -1025,7 +1036,6 @@ static int iuu_open(struct tty_struct *t
+       u8 *buf;
+       int result;
+       u32 actual;
+-      unsigned long flags;
+       struct iuu_private *priv = usb_get_serial_port_data(port);
+       dbg("%s -  port %d", __func__, port->number);
+@@ -1064,21 +1074,7 @@ static int iuu_open(struct tty_struct *t
+                         port->bulk_in_buffer, 512,
+                         NULL, NULL);
+-      /* set the termios structure */
+-      spin_lock_irqsave(&priv->lock, flags);
+-      if (tty && !priv->termios_initialized) {
+-              *(tty->termios) = tty_std_termios;
+-              tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600
+-                                      | TIOCM_CTS | CSTOPB | PARENB;
+-              tty->termios->c_ispeed = 9600;
+-              tty->termios->c_ospeed = 9600;
+-              tty->termios->c_lflag = 0;
+-              tty->termios->c_oflag = 0;
+-              tty->termios->c_iflag = 0;
+-              priv->termios_initialized = 1;
+-              priv->poll = 0;
+-       }
+-      spin_unlock_irqrestore(&priv->lock, flags);
++      priv->poll = 0;
+       /* initialize writebuf */
+ #define FISH(a, b, c, d) do { \
+@@ -1201,6 +1197,7 @@ static struct usb_serial_driver iuu_devi
+       .tiocmget = iuu_tiocmget,
+       .tiocmset = iuu_tiocmset,
+       .set_termios = iuu_set_termios,
++      .init_termios = iuu_init_termios,
+       .attach = iuu_startup,
+       .release = iuu_release,
+ };
+--- a/drivers/usb/serial/kobil_sct.c
++++ b/drivers/usb/serial/kobil_sct.c
+@@ -85,7 +85,7 @@ static void kobil_read_int_callback(stru
+ static void kobil_write_callback(struct urb *purb);
+ static void kobil_set_termios(struct tty_struct *tty,
+                       struct usb_serial_port *port, struct ktermios *old);
+-
++static void kobil_init_termios(struct tty_struct *tty);
+ static struct usb_device_id id_table [] = {
+       { USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_B_PRODUCT_ID) },
+@@ -120,6 +120,7 @@ static struct usb_serial_driver kobil_de
+       .release =              kobil_release,
+       .ioctl =                kobil_ioctl,
+       .set_termios =          kobil_set_termios,
++      .init_termios =         kobil_init_termios,
+       .tiocmget =             kobil_tiocmget,
+       .tiocmset =             kobil_tiocmset,
+       .open =                 kobil_open,
+@@ -210,6 +211,15 @@ static void kobil_release(struct usb_ser
+               kfree(usb_get_serial_port_data(serial->port[i]));
+ }
++static void kobil_init_termios(struct tty_struct *tty)
++{
++      /* Default to echo off and other sane device settings */
++      tty->termios->c_lflag = 0;
++      tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE);
++      tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF;
++      /* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */
++      tty->termios->c_oflag &= ~ONLCR;
++}
+ static int kobil_open(struct tty_struct *tty,
+                       struct usb_serial_port *port, struct file *filp)
+@@ -226,16 +236,6 @@ static int kobil_open(struct tty_struct 
+       /* someone sets the dev to 0 if the close method has been called */
+       port->interrupt_in_urb->dev = port->serial->dev;
+-      if (tty) {
+-
+-              /* Default to echo off and other sane device settings */
+-              tty->termios->c_lflag = 0;
+-              tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN |
+-                                                               XCASE);
+-              tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF;
+-              /* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */
+-              tty->termios->c_oflag &= ~ONLCR;
+-      }
+       /* allocate memory for transfer buffer */
+       transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
+       if (!transfer_buffer)
+--- a/drivers/usb/serial/oti6858.c
++++ b/drivers/usb/serial/oti6858.c
+@@ -146,6 +146,7 @@ static int oti6858_open(struct tty_struc
+ static void oti6858_close(struct usb_serial_port *port);
+ static void oti6858_set_termios(struct tty_struct *tty,
+                       struct usb_serial_port *port, struct ktermios *old);
++static void oti6858_init_termios(struct tty_struct *tty);
+ static int oti6858_ioctl(struct tty_struct *tty, struct file *file,
+                       unsigned int cmd, unsigned long arg);
+ static void oti6858_read_int_callback(struct urb *urb);
+@@ -186,6 +187,7 @@ static struct usb_serial_driver oti6858_
+       .write =                oti6858_write,
+       .ioctl =                oti6858_ioctl,
+       .set_termios =          oti6858_set_termios,
++      .init_termios =         oti6858_init_termios,
+       .tiocmget =             oti6858_tiocmget,
+       .tiocmset =             oti6858_tiocmset,
+       .read_bulk_callback =   oti6858_read_bulk_callback,
+@@ -206,7 +208,6 @@ struct oti6858_private {
+       struct {
+               u8 read_urb_in_use;
+               u8 write_urb_in_use;
+-              u8 termios_initialized;
+       } flags;
+       struct delayed_work delayed_write_work;
+@@ -447,6 +448,14 @@ static int oti6858_chars_in_buffer(struc
+       return chars;
+ }
++static void oti6858_init_termios(struct tty_struct *tty)
++{
++      *(tty->termios) = tty_std_termios;
++      tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL;
++      tty->termios->c_ispeed = 38400;
++      tty->termios->c_ospeed = 38400;
++}
++
+ static void oti6858_set_termios(struct tty_struct *tty,
+               struct usb_serial_port *port, struct ktermios *old_termios)
+ {
+@@ -464,16 +473,6 @@ static void oti6858_set_termios(struct t
+               return;
+       }
+-      spin_lock_irqsave(&priv->lock, flags);
+-      if (!priv->flags.termios_initialized) {
+-              *(tty->termios) = tty_std_termios;
+-              tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL;
+-              tty->termios->c_ispeed = 38400;
+-              tty->termios->c_ospeed = 38400;
+-              priv->flags.termios_initialized = 1;
+-      }
+-      spin_unlock_irqrestore(&priv->lock, flags);
+-
+       cflag = tty->termios->c_cflag;
+       spin_lock_irqsave(&priv->lock, flags);
+--- a/drivers/usb/serial/spcp8x5.c
++++ b/drivers/usb/serial/spcp8x5.c
+@@ -299,7 +299,6 @@ struct spcp8x5_private {
+       wait_queue_head_t       delta_msr_wait;
+       u8                      line_control;
+       u8                      line_status;
+-      u8                      termios_initialized;
+ };
+ /* desc : when device plug in,this function would be called.
+@@ -498,6 +497,15 @@ static void spcp8x5_close(struct usb_ser
+               dev_dbg(&port->dev, "usb_unlink_urb(read_urb) = %d\n", result);
+ }
++static void spcp8x5_init_termios(struct tty_struct *tty)
++{
++      /* for the 1st time call this function */
++      *(tty->termios) = tty_std_termios;
++      tty->termios->c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
++      tty->termios->c_ispeed = 115200;
++      tty->termios->c_ospeed = 115200;
++}
++
+ /* set the serial param for transfer. we should check if we really need to
+  * transfer. if we set flow control we should do this too. */
+ static void spcp8x5_set_termios(struct tty_struct *tty,
+@@ -514,16 +522,6 @@ static void spcp8x5_set_termios(struct t
+       int i;
+       u8 control;
+-      /* for the 1st time call this function */
+-      spin_lock_irqsave(&priv->lock, flags);
+-      if (!priv->termios_initialized) {
+-              *(tty->termios) = tty_std_termios;
+-              tty->termios->c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
+-              tty->termios->c_ispeed = 115200;
+-              tty->termios->c_ospeed = 115200;
+-              priv->termios_initialized = 1;
+-      }
+-      spin_unlock_irqrestore(&priv->lock, flags);
+       /* check that they really want us to change something */
+       if (!tty_termios_hw_change(tty->termios, old_termios))
+@@ -1011,6 +1009,7 @@ static struct usb_serial_driver spcp8x5_
+       .carrier_raised         = spcp8x5_carrier_raised,
+       .write                  = spcp8x5_write,
+       .set_termios            = spcp8x5_set_termios,
++      .init_termios           = spcp8x5_init_termios,
+       .ioctl                  = spcp8x5_ioctl,
+       .tiocmget               = spcp8x5_tiocmget,
+       .tiocmset               = spcp8x5_tiocmset,
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -721,6 +721,41 @@ static const struct tty_port_operations 
+       .dtr_rts = serial_dtr_rts,
+ };
++/**
++ *    serial_install          -       install tty
++ *    @driver: the driver (USB in our case)
++ *    @tty: the tty being created
++ *
++ *    Create the termios objects for this tty. We use the default USB
++ *    serial ones but permit them to be overriddenby serial->type->termios.
++ *    This lets us remove all the ugly hackery
++ */
++
++static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
++{
++      int idx = tty->index;
++      struct usb_serial *serial;
++      int retval;
++
++      /* If the termios setup has yet to be done */
++      if (tty->driver->termios[idx] == NULL) {
++              /* perform the standard setup */
++              retval = tty_init_termios(tty);
++              if (retval)
++                      return retval;
++              /* allow the driver to update it */
++              serial = usb_serial_get_by_index(tty->index);
++              if (serial->type->init_termios)
++                      serial->type->init_termios(tty);
++              usb_serial_put(serial);
++      }
++      /* Final install (we use the default method) */
++      tty_driver_kref_get(driver);
++      tty->count++;
++      driver->ttys[idx] = tty;
++      return 0;
++}
++
+ int usb_serial_probe(struct usb_interface *interface,
+                              const struct usb_device_id *id)
+ {
+@@ -1228,7 +1263,8 @@ static const struct tty_operations seria
+       .chars_in_buffer =      serial_chars_in_buffer,
+       .tiocmget =             serial_tiocmget,
+       .tiocmset =             serial_tiocmset,
+-      .shutdown =             serial_do_free,
++      .shutdown =             serial_do_free,
++      .install =              serial_install,
+       .proc_fops =            &serial_proc_fops,
+ };
+--- a/drivers/usb/serial/whiteheat.c
++++ b/drivers/usb/serial/whiteheat.c
+@@ -259,7 +259,7 @@ static int firm_send_command(struct usb_
+                                               __u8 *data, __u8 datasize);
+ static int firm_open(struct usb_serial_port *port);
+ static int firm_close(struct usb_serial_port *port);
+-static int firm_setup_port(struct tty_struct *tty);
++static void firm_setup_port(struct tty_struct *tty);
+ static int firm_set_rts(struct usb_serial_port *port, __u8 onoff);
+ static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff);
+ static int firm_set_break(struct usb_serial_port *port, __u8 onoff);
+@@ -1211,7 +1211,7 @@ static int firm_close(struct usb_serial_
+ }
+-static int firm_setup_port(struct tty_struct *tty)
++static void firm_setup_port(struct tty_struct *tty)
+ {
+       struct usb_serial_port *port = tty->driver_data;
+       struct whiteheat_port_settings port_settings;
+@@ -1286,7 +1286,7 @@ static int firm_setup_port(struct tty_st
+       port_settings.lloop = 0;
+       /* now send the message to the device */
+-      return firm_send_command(port, WHITEHEAT_SETUP_PORT,
++      firm_send_command(port, WHITEHEAT_SETUP_PORT,
+                       (__u8 *)&port_settings, sizeof(port_settings));
+ }
+--- a/include/linux/usb/serial.h
++++ b/include/linux/usb/serial.h
+@@ -261,6 +261,9 @@ struct usb_serial_driver {
+          be an attached tty at this point */
+       void (*dtr_rts)(struct usb_serial_port *port, int on);
+       int  (*carrier_raised)(struct usb_serial_port *port);
++      /* Called by the usb serial hooks to allow the user to rework the
++         termios state */
++      void (*init_termios)(struct tty_struct *tty);
+       /* USB events */
+       void (*read_int_callback)(struct urb *urb);
+       void (*write_int_callback)(struct urb *urb);
diff --git a/queue-2.6.31/usb-serial-acquire-references-when-a-new-tty-is-installed.patch b/queue-2.6.31/usb-serial-acquire-references-when-a-new-tty-is-installed.patch
new file mode 100644 (file)
index 0000000..b6e3047
--- /dev/null
@@ -0,0 +1,223 @@
+From cc56cd0157753c04a987888a2f793803df661a40 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Tue, 1 Sep 2009 11:39:13 -0400
+Subject: usb-serial: acquire references when a new tty is installed
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit cc56cd0157753c04a987888a2f793803df661a40 upstream.
+
+This patch (as1287) makes serial_install() be reponsible for acquiring
+references to the usb_serial structure and the driver module when a
+tty is first used.  This is more sensible than having serial_open() do
+it, because a tty can be opened many times whereas it is installed
+only once, when it is created.  (Not to mention that these actions are
+reversed when the tty is released, not when it is closed.)  Finally,
+it is at install time that the TTY core takes its own reference to the
+usb_serial module, so it is only fitting that we should act the same
+way in regard to the lower-level serial driver.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/usb-serial.c |  111 ++++++++++++++++------------------------
+ 1 file changed, 47 insertions(+), 64 deletions(-)
+
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -187,100 +187,92 @@ void usb_serial_put(struct usb_serial *s
+  * Create the termios objects for this tty.  We use the default
+  * USB serial settings but permit them to be overridden by
+  * serial->type->init_termios.
++ *
++ * This is the first place a new tty gets used.  Hence this is where we
++ * acquire references to the usb_serial structure and the driver module,
++ * where we store a pointer to the port, and where we do an autoresume.
++ * All these actions are reversed in serial_do_free().
+  */
+ static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
+ {
+       int idx = tty->index;
+       struct usb_serial *serial;
+-      int retval;
++      struct usb_serial_port *port;
++      int retval = -ENODEV;
++
++      serial = usb_serial_get_by_index(idx);
++      if (!serial)
++              return retval;
++
++      port = serial->port[idx - serial->minor];
++      if (!port)
++              goto error_no_port;
++      if (!try_module_get(serial->type->driver.owner))
++              goto error_module_get;
++
++      retval = usb_autopm_get_interface(serial->interface);
++      if (retval)
++              goto error_get_interface;
+       /* If the termios setup has yet to be done */
+       if (tty->driver->termios[idx] == NULL) {
+               /* perform the standard setup */
+               retval = tty_init_termios(tty);
+               if (retval)
+-                      return retval;
++                      goto error_init_termios;
+               /* allow the driver to update it */
+-              serial = usb_serial_get_by_index(tty->index);
+-              if (serial) {
+-                      if (serial->type->init_termios)
+-                              serial->type->init_termios(tty);
+-                      usb_serial_put(serial);
+-                      mutex_unlock(&serial->disc_mutex);
+-              }
++              if (serial->type->init_termios)
++                      serial->type->init_termios(tty);
+       }
++      mutex_unlock(&serial->disc_mutex);
++
++      tty->driver_data = port;
++
+       /* Final install (we use the default method) */
+       tty_driver_kref_get(driver);
+       tty->count++;
+       driver->ttys[idx] = tty;
+-      return 0;
++      return retval;
++
++ error_init_termios:
++      usb_autopm_put_interface(serial->interface);
++ error_get_interface:
++      module_put(serial->type->driver.owner);
++ error_module_get:
++ error_no_port:
++      usb_serial_put(serial);
++      mutex_unlock(&serial->disc_mutex);
++      return retval;
+ }
+ static int serial_open (struct tty_struct *tty, struct file *filp)
+ {
+       struct usb_serial *serial;
+       struct usb_serial_port *port;
+-      unsigned int portNumber;
+       int retval = 0;
+       int first = 0;
+       dbg("%s", __func__);
+-      /* get the serial object associated with this tty pointer */
+-      serial = usb_serial_get_by_index(tty->index);
+-      if (!serial) {
+-              tty->driver_data = NULL;
+-              return -ENODEV;
+-      }
+-
+-      portNumber = tty->index - serial->minor;
+-      port = serial->port[portNumber];
+-      if (!port || serial->disconnected)
+-              retval = -ENODEV;
+-      /*
+-       * Note: Our locking order requirement does not allow port->mutex
+-       * to be acquired while serial->disc_mutex is held.
+-       */
+-      mutex_unlock(&serial->disc_mutex);
+-      if (retval)
+-              goto bailout_serial_put;
++      port = tty->driver_data;
++      serial = port->serial;
+-      if (mutex_lock_interruptible(&port->mutex)) {
+-              retval = -ERESTARTSYS;
+-              goto bailout_serial_put;
+-      }
++      if (mutex_lock_interruptible(&port->mutex))
++              return -ERESTARTSYS;
+       ++port->port.count;
+-
+-      /* set up our port structure making the tty driver
+-       * remember our port object, and us it */
+-      tty->driver_data = port;
+       tty_port_tty_set(&port->port, tty);
+       /* If the console is attached, the device is already open */
+       if (port->port.count == 1 && !port->console) {
+               first = 1;
+-              /* lock this module before we call it
+-               * this may fail, which means we must bail out,
+-               * safe because we are called with BKL held */
+-              if (!try_module_get(serial->type->driver.owner)) {
+-                      retval = -ENODEV;
+-                      goto bailout_mutex_unlock;
+-              }
+-
+               mutex_lock(&serial->disc_mutex);
+-              if (serial->disconnected)
+-                      retval = -ENODEV;
+-              else
+-                      retval = usb_autopm_get_interface(serial->interface);
+-              if (retval)
+-                      goto bailout_module_put;
+               /* only call the device specific open if this
+                * is the first time the port is opened */
+               retval = serial->type->open(tty, port, filp);
+               if (retval)
+-                      goto bailout_interface_put;
++                      goto bailout_module_put;
+               mutex_unlock(&serial->disc_mutex);
+               set_bit(ASYNCB_INITIALIZED, &port->port.flags);
+       }
+@@ -297,18 +289,11 @@ static int serial_open (struct tty_struc
+               goto bailout_mutex_unlock;
+       /* Undo the initial port actions */
+       mutex_lock(&serial->disc_mutex);
+-bailout_interface_put:
+-      usb_autopm_put_interface(serial->interface);
+ bailout_module_put:
+       mutex_unlock(&serial->disc_mutex);
+-      module_put(serial->type->driver.owner);
+ bailout_mutex_unlock:
+       port->port.count = 0;
+-      tty->driver_data = NULL;
+-      tty_port_tty_set(&port->port, NULL);
+       mutex_unlock(&port->mutex);
+-bailout_serial_put:
+-      usb_serial_put(serial);
+       return retval;
+ }
+@@ -355,9 +340,6 @@ static void serial_close(struct tty_stru
+ {
+       struct usb_serial_port *port = tty->driver_data;
+-      if (!port)
+-              return;
+-
+       dbg("%s - port %d", __func__, port->number);
+       if (tty_port_close_start(&port->port, tty, filp) == 0)
+@@ -365,7 +347,6 @@ static void serial_close(struct tty_stru
+       serial_do_down(port);
+       tty_port_close_end(&port->port, tty);
+       tty_port_tty_set(&port->port, NULL);
+-
+ }
+ /**
+@@ -386,9 +367,11 @@ static void serial_do_free(struct tty_st
+       /* The console is magical.  Do not hang up the console hardware
+        * or there will be tears.
+        */
+-      if (port == NULL || port->console)
++      if (port->console)
+               return;
++      tty->driver_data = NULL;
++
+       serial = port->serial;
+       owner = serial->type->driver.owner;
diff --git a/queue-2.6.31/usb-serial-add-missing-tests-and-debug-lines.patch b/queue-2.6.31/usb-serial-add-missing-tests-and-debug-lines.patch
new file mode 100644 (file)
index 0000000..127da37
--- /dev/null
@@ -0,0 +1,94 @@
+From ff8324df1187b7280e507c976777df76c73a1ef1 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Tue, 1 Sep 2009 11:39:51 -0400
+Subject: usb-serial: add missing tests and debug lines
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit ff8324df1187b7280e507c976777df76c73a1ef1 upstream.
+
+This patch (as1290) adds some missing tests.  serial_down() isn't
+supposed to do anything if the hardware hasn't been initialized, and
+serial_close() isn't supposed to do anything if the tty has gotten a
+hangup (because serial_hangup() takes care of shutting down the
+hardware).
+
+The patch also updates and adds a few debugging lines.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/usb-serial.c |   21 +++++++++++++++++----
+ 1 file changed, 17 insertions(+), 4 deletions(-)
+
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -200,6 +200,8 @@ static int serial_install(struct tty_dri
+       struct usb_serial_port *port;
+       int retval = -ENODEV;
++      dbg("%s", __func__);
++
+       serial = usb_serial_get_by_index(idx);
+       if (!serial)
+               return retval;
+@@ -250,11 +252,11 @@ static int serial_open (struct tty_struc
+       int retval = 0;
+       int first = 0;
+-      dbg("%s", __func__);
+-
+       port = tty->driver_data;
+       serial = port->serial;
++      dbg("%s - port %d", __func__, port->number);
++
+       if (mutex_lock_interruptible(&port->mutex))
+               return -ERESTARTSYS;
+@@ -315,6 +317,12 @@ static void serial_down(struct usb_seria
+       if (port->console)
+               return;
++      /* Don't call the close method if the hardware hasn't been
++       * initialized.
++       */
++      if (!test_and_clear_bit(ASYNCB_INITIALIZED, &port->port.flags))
++              return;
++
+       mutex_lock(&port->mutex);
+       serial = port->serial;
+       owner = serial->type->driver.owner;
+@@ -328,10 +336,11 @@ static void serial_down(struct usb_seria
+ static void serial_hangup(struct tty_struct *tty)
+ {
+       struct usb_serial_port *port = tty->driver_data;
++
++      dbg("%s - port %d", __func__, port->number);
++
+       serial_down(port);
+       tty_port_hangup(&port->port);
+-      /* We must not free port yet - the USB serial layer depends on it's
+-         continued existence */
+ }
+ static void serial_close(struct tty_struct *tty, struct file *filp)
+@@ -340,6 +349,8 @@ static void serial_close(struct tty_stru
+       dbg("%s - port %d", __func__, port->number);
++      if (tty_hung_up_p(filp))
++              return;
+       if (tty_port_close_start(&port->port, tty, filp) == 0)
+               return;
+       serial_down(port);
+@@ -368,6 +379,8 @@ static void serial_release(struct tty_st
+       if (port->console)
+               return;
++      dbg("%s - port %d", __func__, port->number);
++
+       /* Standard shutdown processing */
+       tty_shutdown(tty);
diff --git a/queue-2.6.31/usb-serial-change-logic-of-serial-lookups.patch b/queue-2.6.31/usb-serial-change-logic-of-serial-lookups.patch
new file mode 100644 (file)
index 0000000..b614a9c
--- /dev/null
@@ -0,0 +1,119 @@
+From 8bc2c1b2daf95029658868cb1427baea2da87139 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Tue, 1 Sep 2009 11:38:59 -0400
+Subject: usb-serial: change logic of serial lookups
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit 8bc2c1b2daf95029658868cb1427baea2da87139 upstream.
+
+This patch (as1286) changes usb_serial_get_by_index().  Now the
+routine will check whether the serial device has been disconnected; if
+it has then the return value will be NULL.  If the device hasn't been
+disconnected then the routine will return with serial->disc_mutex
+held, so that the caller can use the structure without fear of racing
+against driver unloads.
+
+This permits the scope of table_mutex in destroy_serial() to be
+reduced.  Instead of protecting the entire function, it suffices to
+protect the part that actually uses serial_table[], i.e., the call to
+return_serial().  There's no longer any danger of the refcount being
+incremented after it reaches 0 (which was the reason for having the
+large scope previously), because it can't reach 0 until the serial
+device has been disconnected.
+
+Also, the patch makes serial_install() check that serial is non-NULL
+before attempting to use it.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/usb-serial.c |   31 +++++++++++++++++++++++--------
+ 1 file changed, 23 insertions(+), 8 deletions(-)
+
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -66,6 +66,11 @@ static struct usb_serial *serial_table[S
+ static DEFINE_MUTEX(table_lock);
+ static LIST_HEAD(usb_serial_driver_list);
++/*
++ * Look up the serial structure.  If it is found and it hasn't been
++ * disconnected, return with its disc_mutex held and its refcount
++ * incremented.  Otherwise return NULL.
++ */
+ struct usb_serial *usb_serial_get_by_index(unsigned index)
+ {
+       struct usb_serial *serial;
+@@ -73,8 +78,15 @@ struct usb_serial *usb_serial_get_by_ind
+       mutex_lock(&table_lock);
+       serial = serial_table[index];
+-      if (serial)
+-              kref_get(&serial->kref);
++      if (serial) {
++              mutex_lock(&serial->disc_mutex);
++              if (serial->disconnected) {
++                      mutex_unlock(&serial->disc_mutex);
++                      serial = NULL;
++              } else {
++                      kref_get(&serial->kref);
++              }
++      }
+       mutex_unlock(&table_lock);
+       return serial;
+ }
+@@ -123,8 +135,10 @@ static void return_serial(struct usb_ser
+       dbg("%s", __func__);
++      mutex_lock(&table_lock);
+       for (i = 0; i < serial->num_ports; ++i)
+               serial_table[serial->minor + i] = NULL;
++      mutex_unlock(&table_lock);
+ }
+ static void destroy_serial(struct kref *kref)
+@@ -158,9 +172,7 @@ static void destroy_serial(struct kref *
+ void usb_serial_put(struct usb_serial *serial)
+ {
+-      mutex_lock(&table_lock);
+       kref_put(&serial->kref, destroy_serial);
+-      mutex_unlock(&table_lock);
+ }
+ /*****************************************************************************
+@@ -190,9 +202,12 @@ static int serial_install(struct tty_dri
+                       return retval;
+               /* allow the driver to update it */
+               serial = usb_serial_get_by_index(tty->index);
+-              if (serial->type->init_termios)
+-                      serial->type->init_termios(tty);
+-              usb_serial_put(serial);
++              if (serial) {
++                      if (serial->type->init_termios)
++                              serial->type->init_termios(tty);
++                      usb_serial_put(serial);
++                      mutex_unlock(&serial->disc_mutex);
++              }
+       }
+       /* Final install (we use the default method) */
+       tty_driver_kref_get(driver);
+@@ -218,7 +233,6 @@ static int serial_open (struct tty_struc
+               return -ENODEV;
+       }
+-      mutex_lock(&serial->disc_mutex);
+       portNumber = tty->index - serial->minor;
+       port = serial->port[portNumber];
+       if (!port || serial->disconnected)
+@@ -529,6 +543,7 @@ static int serial_proc_show(struct seq_f
+               seq_putc(m, '\n');
+               usb_serial_put(serial);
++              mutex_unlock(&serial->disc_mutex);
+       }
+       return 0;
+ }
diff --git a/queue-2.6.31/usb-serial-change-referencing-of-port-and-serial-structures.patch b/queue-2.6.31/usb-serial-change-referencing-of-port-and-serial-structures.patch
new file mode 100644 (file)
index 0000000..964bef1
--- /dev/null
@@ -0,0 +1,235 @@
+From 41bd34ddd7aa46dbc03b5bb33896e0fa8100fe7b Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Tue, 1 Sep 2009 11:38:34 -0400
+Subject: usb-serial: change referencing of port and serial structures
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit 41bd34ddd7aa46dbc03b5bb33896e0fa8100fe7b upstream.
+
+This patch (as1284) changes the referencing of the usb_serial and
+usb_serial_port structures in usb-serial.c.  It's not feasible to make
+the port structures keep a reference to the serial structure, because
+the ports need to remain in existence when serial is released -- quite
+a few of the drivers expect this.  Consequently taking a reference
+to the port when the device file is open is insufficient; such a
+reference would not pin serial.
+
+To fix this, we now take a reference to serial when the device file is
+opened.  The final put_device() for the ports occurs in
+destroy_serial(), so that the ports will last as long as they are
+needed.
+
+The patch initializes all the port devices, including those in the
+unused "fake" ports.  This makes the code more uniform because they
+can all be released in the same way.  The error handling code in
+usb_serial_probe() is much simplified by this approach; instead of
+freeing everything by hand we can use a single usb_serial_put() call.
+
+Also simplified is the port-release mechanism.  Instead of being two
+separate routines, port_release() and port_free() can be combined into
+one.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/usb-serial.c |   95 ++++++++--------------------------------
+ 1 file changed, 20 insertions(+), 75 deletions(-)
+
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -43,8 +43,6 @@
+ #define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux/"
+ #define DRIVER_DESC "USB Serial Driver core"
+-static void port_free(struct usb_serial_port *port);
+-
+ /* Driver structure we register with the USB core */
+ static struct usb_driver usb_serial_driver = {
+       .name =         "usbserial",
+@@ -145,27 +143,16 @@ static void destroy_serial(struct kref *
+       serial->type->release(serial);
+-      for (i = 0; i < serial->num_ports; ++i) {
++      /* Now that nothing is using the ports, they can be freed */
++      for (i = 0; i < serial->num_port_pointers; ++i) {
+               port = serial->port[i];
+-              if (port)
++              if (port) {
++                      port->serial = NULL;
+                       put_device(&port->dev);
+-      }
+-
+-      /* If this is a "fake" port, we have to clean it up here, as it will
+-       * not get cleaned up in port_release() as it was never registered with
+-       * the driver core */
+-      if (serial->num_ports < serial->num_port_pointers) {
+-              for (i = serial->num_ports;
+-                                      i < serial->num_port_pointers; ++i) {
+-                      port = serial->port[i];
+-                      if (port)
+-                              port_free(port);
+               }
+       }
+       usb_put_dev(serial->dev);
+-
+-      /* free up any memory that we allocated */
+       kfree(serial);
+ }
+@@ -201,8 +188,6 @@ static int serial_open (struct tty_struc
+       port = serial->port[portNumber];
+       if (!port || serial->disconnected)
+               retval = -ENODEV;
+-      else
+-              get_device(&port->dev);
+       /*
+        * Note: Our locking order requirement does not allow port->mutex
+        * to be acquired while serial->disc_mutex is held.
+@@ -213,7 +198,7 @@ static int serial_open (struct tty_struc
+       if (mutex_lock_interruptible(&port->mutex)) {
+               retval = -ERESTARTSYS;
+-              goto bailout_port_put;
++              goto bailout_serial_put;
+       }
+       ++port->port.count;
+@@ -273,8 +258,6 @@ bailout_mutex_unlock:
+       tty->driver_data = NULL;
+       tty_port_tty_set(&port->port, NULL);
+       mutex_unlock(&port->mutex);
+-bailout_port_put:
+-      put_device(&port->dev);
+ bailout_serial_put:
+       usb_serial_put(serial);
+       return retval;
+@@ -333,14 +316,13 @@ static void serial_do_free(struct tty_st
+       serial = port->serial;
+       owner = serial->type->driver.owner;
+-      put_device(&port->dev);
+-      /* Mustn't dereference port any more */
++
+       mutex_lock(&serial->disc_mutex);
+       if (!serial->disconnected)
+               usb_autopm_put_interface(serial->interface);
+       mutex_unlock(&serial->disc_mutex);
++
+       usb_serial_put(serial);
+-      /* Mustn't dereference serial any more */
+       module_put(owner);
+ }
+@@ -581,14 +563,6 @@ static void usb_serial_port_work(struct 
+       tty_kref_put(tty);
+ }
+-static void port_release(struct device *dev)
+-{
+-      struct usb_serial_port *port = to_usb_serial_port(dev);
+-
+-      dbg ("%s - %s", __func__, dev_name(dev));
+-      port_free(port);
+-}
+-
+ static void kill_traffic(struct usb_serial_port *port)
+ {
+       usb_kill_urb(port->read_urb);
+@@ -608,8 +582,12 @@ static void kill_traffic(struct usb_seri
+       usb_kill_urb(port->interrupt_out_urb);
+ }
+-static void port_free(struct usb_serial_port *port)
++static void port_release(struct device *dev)
+ {
++      struct usb_serial_port *port = to_usb_serial_port(dev);
++
++      dbg ("%s - %s", __func__, dev_name(dev));
++
+       /*
+        * Stop all the traffic before cancelling the work, so that
+        * nobody will restart it by calling usb_serial_port_softint.
+@@ -955,6 +933,11 @@ int usb_serial_probe(struct usb_interfac
+               mutex_init(&port->mutex);
+               INIT_WORK(&port->work, usb_serial_port_work);
+               serial->port[i] = port;
++              port->dev.parent = &interface->dev;
++              port->dev.driver = NULL;
++              port->dev.bus = &usb_serial_bus_type;
++              port->dev.release = &port_release;
++              device_initialize(&port->dev);
+       }
+       /* set up the endpoint information */
+@@ -1097,15 +1080,10 @@ int usb_serial_probe(struct usb_interfac
+       /* register all of the individual ports with the driver core */
+       for (i = 0; i < num_ports; ++i) {
+               port = serial->port[i];
+-              port->dev.parent = &interface->dev;
+-              port->dev.driver = NULL;
+-              port->dev.bus = &usb_serial_bus_type;
+-              port->dev.release = &port_release;
+-
+               dev_set_name(&port->dev, "ttyUSB%d", port->number);
+               dbg ("%s - registering %s", __func__, dev_name(&port->dev));
+               port->dev_state = PORT_REGISTERING;
+-              retval = device_register(&port->dev);
++              retval = device_add(&port->dev);
+               if (retval) {
+                       dev_err(&port->dev, "Error registering port device, "
+                               "continuing\n");
+@@ -1123,39 +1101,7 @@ exit:
+       return 0;
+ probe_error:
+-      for (i = 0; i < num_bulk_in; ++i) {
+-              port = serial->port[i];
+-              if (!port)
+-                      continue;
+-              usb_free_urb(port->read_urb);
+-              kfree(port->bulk_in_buffer);
+-      }
+-      for (i = 0; i < num_bulk_out; ++i) {
+-              port = serial->port[i];
+-              if (!port)
+-                      continue;
+-              usb_free_urb(port->write_urb);
+-              kfree(port->bulk_out_buffer);
+-      }
+-      for (i = 0; i < num_interrupt_in; ++i) {
+-              port = serial->port[i];
+-              if (!port)
+-                      continue;
+-              usb_free_urb(port->interrupt_in_urb);
+-              kfree(port->interrupt_in_buffer);
+-      }
+-      for (i = 0; i < num_interrupt_out; ++i) {
+-              port = serial->port[i];
+-              if (!port)
+-                      continue;
+-              usb_free_urb(port->interrupt_out_urb);
+-              kfree(port->interrupt_out_buffer);
+-      }
+-
+-      /* free up any memory that we allocated */
+-      for (i = 0; i < serial->num_port_pointers; ++i)
+-              kfree(serial->port[i]);
+-      kfree(serial);
++      usb_serial_put(serial);
+       return -EIO;
+ }
+ EXPORT_SYMBOL_GPL(usb_serial_probe);
+@@ -1206,8 +1152,7 @@ void usb_serial_disconnect(struct usb_in
+       }
+       serial->type->disconnect(serial);
+-      /* let the last holder of this object
+-       * cause it to be cleaned up */
++      /* let the last holder of this object cause it to be cleaned up */
+       usb_serial_put(serial);
+       dev_info(dev, "device disconnected\n");
+ }
diff --git a/queue-2.6.31/usb-serial-fix-termios-initialization-logic.patch b/queue-2.6.31/usb-serial-fix-termios-initialization-logic.patch
new file mode 100644 (file)
index 0000000..9df954f
--- /dev/null
@@ -0,0 +1,66 @@
+From 7e29bb4b779f4f35385e6f21994758845bf14d23 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Tue, 1 Sep 2009 11:39:22 -0400
+Subject: usb-serial: fix termios initialization logic
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit 7e29bb4b779f4f35385e6f21994758845bf14d23 upstream.
+
+This patch (as1288) fixes the initialization logic in
+serial_install().  A new tty always needs to have a termios
+initialized no matter what, not just in the case where the lower
+driver will override the termios settings.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/usb-serial.c |   22 ++++++++++------------
+ 1 file changed, 10 insertions(+), 12 deletions(-)
+
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -210,22 +210,21 @@ static int serial_install(struct tty_dri
+       if (!try_module_get(serial->type->driver.owner))
+               goto error_module_get;
++      /* perform the standard setup */
++      retval = tty_init_termios(tty);
++      if (retval)
++              goto error_init_termios;
++
+       retval = usb_autopm_get_interface(serial->interface);
+       if (retval)
+               goto error_get_interface;
+-      /* If the termios setup has yet to be done */
+-      if (tty->driver->termios[idx] == NULL) {
+-              /* perform the standard setup */
+-              retval = tty_init_termios(tty);
+-              if (retval)
+-                      goto error_init_termios;
+-              /* allow the driver to update it */
+-              if (serial->type->init_termios)
+-                      serial->type->init_termios(tty);
+-      }
+       mutex_unlock(&serial->disc_mutex);
++      /* allow the driver to update the settings */
++      if (serial->type->init_termios)
++              serial->type->init_termios(tty);
++
+       tty->driver_data = port;
+       /* Final install (we use the default method) */
+@@ -234,9 +233,8 @@ static int serial_install(struct tty_dri
+       driver->ttys[idx] = tty;
+       return retval;
+- error_init_termios:
+-      usb_autopm_put_interface(serial->interface);
+  error_get_interface:
++ error_init_termios:
+       module_put(serial->type->driver.owner);
+  error_module_get:
+  error_no_port:
diff --git a/queue-2.6.31/usb-serial-put-subroutines-in-logical-order.patch b/queue-2.6.31/usb-serial-put-subroutines-in-logical-order.patch
new file mode 100644 (file)
index 0000000..70afcaf
--- /dev/null
@@ -0,0 +1,230 @@
+From f5b0953a89fa3407fb293cc54ead7d8feec489e4 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Tue, 1 Sep 2009 11:38:44 -0400
+Subject: usb-serial: put subroutines in logical order
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit f5b0953a89fa3407fb293cc54ead7d8feec489e4 upstream.
+
+This patch (as1285) rearranges the subroutines in usb-serial.c
+concerned with tty lifetimes into a more logical order: install, open,
+hangup, close, release.  It also updates the formatting of the
+kerneldoc comments.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/usb-serial.c |  154 ++++++++++++++++++++--------------------
+ 1 file changed, 77 insertions(+), 77 deletions(-)
+
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -166,6 +166,41 @@ void usb_serial_put(struct usb_serial *s
+ /*****************************************************************************
+  * Driver tty interface functions
+  *****************************************************************************/
++
++/**
++ * serial_install - install tty
++ * @driver: the driver (USB in our case)
++ * @tty: the tty being created
++ *
++ * Create the termios objects for this tty.  We use the default
++ * USB serial settings but permit them to be overridden by
++ * serial->type->init_termios.
++ */
++static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
++{
++      int idx = tty->index;
++      struct usb_serial *serial;
++      int retval;
++
++      /* If the termios setup has yet to be done */
++      if (tty->driver->termios[idx] == NULL) {
++              /* perform the standard setup */
++              retval = tty_init_termios(tty);
++              if (retval)
++                      return retval;
++              /* allow the driver to update it */
++              serial = usb_serial_get_by_index(tty->index);
++              if (serial->type->init_termios)
++                      serial->type->init_termios(tty);
++              usb_serial_put(serial);
++      }
++      /* Final install (we use the default method) */
++      tty_driver_kref_get(driver);
++      tty->count++;
++      driver->ttys[idx] = tty;
++      return 0;
++}
++
+ static int serial_open (struct tty_struct *tty, struct file *filp)
+ {
+       struct usb_serial *serial;
+@@ -264,13 +299,11 @@ bailout_serial_put:
+ }
+ /**
+- *    serial_do_down          -       shut down hardware
+- *    @port: port to shut down
++ * serial_do_down - shut down hardware
++ * @port: port to shut down
+  *
+- *    Shut down a USB port unless it is the console. We never shut down the
+- *    console hardware as it will always be in use.
+- *
+- *    Don't free any resources at this point
++ * Shut down a USB serial port unless it is the console.  We never
++ * shut down the console hardware as it will always be in use.
+  */
+ static void serial_do_down(struct usb_serial_port *port)
+ {
+@@ -278,8 +311,10 @@ static void serial_do_down(struct usb_se
+       struct usb_serial *serial;
+       struct module *owner;
+-      /* The console is magical, do not hang up the console hardware
+-         or there will be tears */
++      /*
++       * The console is magical.  Do not hang up the console hardware
++       * or there will be tears.
++       */
+       if (port->console)
+               return;
+@@ -293,24 +328,50 @@ static void serial_do_down(struct usb_se
+       mutex_unlock(&port->mutex);
+ }
++static void serial_hangup(struct tty_struct *tty)
++{
++      struct usb_serial_port *port = tty->driver_data;
++      serial_do_down(port);
++      tty_port_hangup(&port->port);
++      /* We must not free port yet - the USB serial layer depends on it's
++         continued existence */
++}
++
++static void serial_close(struct tty_struct *tty, struct file *filp)
++{
++      struct usb_serial_port *port = tty->driver_data;
++
++      if (!port)
++              return;
++
++      dbg("%s - port %d", __func__, port->number);
++
++      if (tty_port_close_start(&port->port, tty, filp) == 0)
++              return;
++      serial_do_down(port);
++      tty_port_close_end(&port->port, tty);
++      tty_port_tty_set(&port->port, NULL);
++
++}
++
+ /**
+- *    serial_do_free          -       free resources post close/hangup
+- *    @port: port to free up
++ * serial_do_free - free resources post close/hangup
++ * @port: port to free up
+  *
+- *    Do the resource freeing and refcount dropping for the port. We must
+- *    be careful about ordering and we must avoid freeing up the console.
++ * Do the resource freeing and refcount dropping for the port.
++ * Avoid freeing the console.
+  *
+- *    Called when the last tty kref is dropped.
++ * Called when the last tty kref is dropped.
+  */
+-
+ static void serial_do_free(struct tty_struct *tty)
+ {
+       struct usb_serial_port *port = tty->driver_data;
+       struct usb_serial *serial;
+       struct module *owner;
+-      /* The console is magical, do not hang up the console hardware
+-         or there will be tears */
++      /* The console is magical.  Do not hang up the console hardware
++       * or there will be tears.
++       */
+       if (port == NULL || port->console)
+               return;
+@@ -326,32 +387,6 @@ static void serial_do_free(struct tty_st
+       module_put(owner);
+ }
+-static void serial_close(struct tty_struct *tty, struct file *filp)
+-{
+-      struct usb_serial_port *port = tty->driver_data;
+-
+-      if (!port)
+-              return;
+-
+-      dbg("%s - port %d", __func__, port->number);
+-
+-      if (tty_port_close_start(&port->port, tty, filp) == 0)
+-              return;
+-      serial_do_down(port);           
+-      tty_port_close_end(&port->port, tty);
+-      tty_port_tty_set(&port->port, NULL);
+-
+-}
+-
+-static void serial_hangup(struct tty_struct *tty)
+-{
+-      struct usb_serial_port *port = tty->driver_data;
+-      serial_do_down(port);
+-      tty_port_hangup(&port->port);
+-      /* We must not free port yet - the USB serial layer depends on it's
+-         continued existence */
+-}
+-
+ static int serial_write(struct tty_struct *tty, const unsigned char *buf,
+                                                               int count)
+ {
+@@ -699,41 +734,6 @@ static const struct tty_port_operations 
+       .dtr_rts = serial_dtr_rts,
+ };
+-/**
+- *    serial_install          -       install tty
+- *    @driver: the driver (USB in our case)
+- *    @tty: the tty being created
+- *
+- *    Create the termios objects for this tty. We use the default USB
+- *    serial ones but permit them to be overriddenby serial->type->termios.
+- *    This lets us remove all the ugly hackery
+- */
+-
+-static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
+-{
+-      int idx = tty->index;
+-      struct usb_serial *serial;
+-      int retval;
+-
+-      /* If the termios setup has yet to be done */
+-      if (tty->driver->termios[idx] == NULL) {
+-              /* perform the standard setup */
+-              retval = tty_init_termios(tty);
+-              if (retval)
+-                      return retval;
+-              /* allow the driver to update it */
+-              serial = usb_serial_get_by_index(tty->index);
+-              if (serial->type->init_termios)
+-                      serial->type->init_termios(tty);
+-              usb_serial_put(serial);
+-      }
+-      /* Final install (we use the default method) */
+-      tty_driver_kref_get(driver);
+-      tty->count++;
+-      driver->ttys[idx] = tty;
+-      return 0;
+-}
+-
+ int usb_serial_probe(struct usb_interface *interface,
+                              const struct usb_device_id *id)
+ {
diff --git a/queue-2.6.31/usb-serial-rename-subroutines.patch b/queue-2.6.31/usb-serial-rename-subroutines.patch
new file mode 100644 (file)
index 0000000..391132c
--- /dev/null
@@ -0,0 +1,100 @@
+From 74556123e034c8337b69a3ebac2f3a5fc0a97032 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Tue, 1 Sep 2009 11:39:40 -0400
+Subject: usb-serial: rename subroutines
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit 74556123e034c8337b69a3ebac2f3a5fc0a97032 upstream.
+
+This patch (as1289) renames serial_do_down() to serial_down() and
+serial_do_free() to serial_release().  It also adds a missing call to
+tty_shutdown() in serial_release().
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/usb-serial.c |   19 +++++++++++--------
+ 1 file changed, 11 insertions(+), 8 deletions(-)
+
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -191,7 +191,7 @@ void usb_serial_put(struct usb_serial *s
+  * This is the first place a new tty gets used.  Hence this is where we
+  * acquire references to the usb_serial structure and the driver module,
+  * where we store a pointer to the port, and where we do an autoresume.
+- * All these actions are reversed in serial_do_free().
++ * All these actions are reversed in serial_release().
+  */
+ static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
+ {
+@@ -296,13 +296,13 @@ bailout_mutex_unlock:
+ }
+ /**
+- * serial_do_down - shut down hardware
++ * serial_down - shut down hardware
+  * @port: port to shut down
+  *
+  * Shut down a USB serial port unless it is the console.  We never
+  * shut down the console hardware as it will always be in use.
+  */
+-static void serial_do_down(struct usb_serial_port *port)
++static void serial_down(struct usb_serial_port *port)
+ {
+       struct usb_serial_driver *drv = port->serial->type;
+       struct usb_serial *serial;
+@@ -328,7 +328,7 @@ static void serial_do_down(struct usb_se
+ static void serial_hangup(struct tty_struct *tty)
+ {
+       struct usb_serial_port *port = tty->driver_data;
+-      serial_do_down(port);
++      serial_down(port);
+       tty_port_hangup(&port->port);
+       /* We must not free port yet - the USB serial layer depends on it's
+          continued existence */
+@@ -342,13 +342,13 @@ static void serial_close(struct tty_stru
+       if (tty_port_close_start(&port->port, tty, filp) == 0)
+               return;
+-      serial_do_down(port);
++      serial_down(port);
+       tty_port_close_end(&port->port, tty);
+       tty_port_tty_set(&port->port, NULL);
+ }
+ /**
+- * serial_do_free - free resources post close/hangup
++ * serial_release - free resources post close/hangup
+  * @port: port to free up
+  *
+  * Do the resource freeing and refcount dropping for the port.
+@@ -356,7 +356,7 @@ static void serial_close(struct tty_stru
+  *
+  * Called when the last tty kref is dropped.
+  */
+-static void serial_do_free(struct tty_struct *tty)
++static void serial_release(struct tty_struct *tty)
+ {
+       struct usb_serial_port *port = tty->driver_data;
+       struct usb_serial *serial;
+@@ -368,6 +368,9 @@ static void serial_do_free(struct tty_st
+       if (port->console)
+               return;
++      /* Standard shutdown processing */
++      tty_shutdown(tty);
++
+       tty->driver_data = NULL;
+       serial = port->serial;
+@@ -1204,7 +1207,7 @@ static const struct tty_operations seria
+       .chars_in_buffer =      serial_chars_in_buffer,
+       .tiocmget =             serial_tiocmget,
+       .tiocmset =             serial_tiocmset,
+-      .shutdown =             serial_do_free,
++      .shutdown =             serial_release,
+       .install =              serial_install,
+       .proc_fops =            &serial_proc_fops,
+ };
diff --git a/queue-2.6.31/usb-serial-straighten-out-serial_open.patch b/queue-2.6.31/usb-serial-straighten-out-serial_open.patch
new file mode 100644 (file)
index 0000000..24921bc
--- /dev/null
@@ -0,0 +1,104 @@
+From 320348c8d5c9b591282633ddb8959b42f7fc7a1c Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Tue, 1 Sep 2009 11:39:59 -0400
+Subject: usb-serial: straighten out serial_open
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit 320348c8d5c9b591282633ddb8959b42f7fc7a1c upstream.
+
+This patch (as1291) removes a bunch of code from serial_open(), things
+that were rendered unnecessary by earlier patches.  A missing spinlock
+is added to protect port->port.count, which needs to be incremented
+even if the open fails but not if the tty has gotten a hangup.  The
+test for whether the hardware has been initialized, based on the use
+count, is replaced by a more transparent test of the
+ASYNCB_INITIALIZED bit in the port flags.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/usb-serial.c |   59 ++++++++++++++--------------------------
+ 1 file changed, 22 insertions(+), 37 deletions(-)
+
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -245,55 +245,40 @@ static int serial_install(struct tty_dri
+       return retval;
+ }
+-static int serial_open (struct tty_struct *tty, struct file *filp)
++static int serial_open(struct tty_struct *tty, struct file *filp)
+ {
+-      struct usb_serial *serial;
+-      struct usb_serial_port *port;
+-      int retval = 0;
+-      int first = 0;
+-
+-      port = tty->driver_data;
+-      serial = port->serial;
++      struct usb_serial_port *port = tty->driver_data;
++      struct usb_serial *serial = port->serial;
++      int retval;
+       dbg("%s - port %d", __func__, port->number);
+-      if (mutex_lock_interruptible(&port->mutex))
+-              return -ERESTARTSYS;
+-
+-      ++port->port.count;
++      spin_lock_irq(&port->port.lock);
++      if (!tty_hung_up_p(filp))
++              ++port->port.count;
++      spin_unlock_irq(&port->port.lock);
+       tty_port_tty_set(&port->port, tty);
+-      /* If the console is attached, the device is already open */
+-      if (port->port.count == 1 && !port->console) {
+-              first = 1;
++      /* Do the device-specific open only if the hardware isn't
++       * already initialized.
++       */
++      if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) {
++              if (mutex_lock_interruptible(&port->mutex))
++                      return -ERESTARTSYS;
+               mutex_lock(&serial->disc_mutex);
+-
+-              /* only call the device specific open if this
+-               * is the first time the port is opened */
+-              retval = serial->type->open(tty, port, filp);
+-              if (retval)
+-                      goto bailout_module_put;
++              if (serial->disconnected)
++                      retval = -ENODEV;
++              else
++                      retval = port->serial->type->open(tty, port, flip);
+               mutex_unlock(&serial->disc_mutex);
++              mutex_unlock(&port->mutex);
++              if (retval)
++                      return retval;
+               set_bit(ASYNCB_INITIALIZED, &port->port.flags);
+       }
+-      mutex_unlock(&port->mutex);
++
+       /* Now do the correct tty layer semantics */
+       retval = tty_port_block_til_ready(&port->port, tty, filp);
+-      if (retval == 0) {
+-              if (!first)
+-                      usb_serial_put(serial);
+-              return 0;
+-      }
+-      mutex_lock(&port->mutex);
+-      if (first == 0)
+-              goto bailout_mutex_unlock;
+-      /* Undo the initial port actions */
+-      mutex_lock(&serial->disc_mutex);
+-bailout_module_put:
+-      mutex_unlock(&serial->disc_mutex);
+-bailout_mutex_unlock:
+-      port->port.count = 0;
+-      mutex_unlock(&port->mutex);
+       return retval;
+ }
diff --git a/queue-2.6.31/usb-serial-update-the-console-driver.patch b/queue-2.6.31/usb-serial-update-the-console-driver.patch
new file mode 100644 (file)
index 0000000..bd35b11
--- /dev/null
@@ -0,0 +1,109 @@
+From 7bd032dc2793afcbaf4a350056768da84cdbd89b Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Fri, 4 Sep 2009 15:29:59 -0400
+Subject: USB serial: update the console driver
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit 7bd032dc2793afcbaf4a350056768da84cdbd89b upstream.
+
+This patch (as1292) modifies the USB serial console driver, to make it
+compatible with the recent changes to the USB serial core.  The most
+important change is that serial->disc_mutex now has to be unlocked
+following a successful call to usb_serial_get_by_index().
+
+Other less notable changes include:
+
+       Use the requested port number instead of port 0 always.
+
+       Prevent the serial device from being autosuspended.
+
+       Use the ASYNCB_INITIALIZED flag bit to indicate when the
+       port hardware has been initialized.
+
+In spite of these changes, there's no question that the USB serial
+console code is still a big hack.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/console.c |   28 +++++++++++++++++++---------
+ 1 file changed, 19 insertions(+), 9 deletions(-)
+
+--- a/drivers/usb/serial/console.c
++++ b/drivers/usb/serial/console.c
+@@ -16,6 +16,7 @@
+ #include <linux/slab.h>
+ #include <linux/tty.h>
+ #include <linux/console.h>
++#include <linux/serial.h>
+ #include <linux/usb.h>
+ #include <linux/usb/serial.h>
+@@ -63,7 +64,7 @@ static int usb_console_setup(struct cons
+       char *s;
+       struct usb_serial *serial;
+       struct usb_serial_port *port;
+-      int retval = 0;
++      int retval;
+       struct tty_struct *tty = NULL;
+       struct ktermios *termios = NULL, dummy;
+@@ -116,13 +117,17 @@ static int usb_console_setup(struct cons
+               return -ENODEV;
+       }
+-      port = serial->port[0];
++      retval = usb_autopm_get_interface(serial->interface);
++      if (retval)
++              goto error_get_interface;
++
++      port = serial->port[co->index - serial->minor];
+       tty_port_tty_set(&port->port, NULL);
+       info->port = port;
+       ++port->port.count;
+-      if (port->port.count == 1) {
++      if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) {
+               if (serial->type->set_termios) {
+                       /*
+                        * allocate a fake tty so the driver can initialize
+@@ -168,6 +173,7 @@ static int usb_console_setup(struct cons
+                       kfree(termios);
+                       kfree(tty);
+               }
++              set_bit(ASYNCB_INITIALIZED, &port->port.flags);
+       }
+       /* Now that any required fake tty operations are completed restore
+        * the tty port count */
+@@ -175,18 +181,22 @@ static int usb_console_setup(struct cons
+       /* The console is special in terms of closing the device so
+        * indicate this port is now acting as a system console. */
+       port->console = 1;
+-      retval = 0;
+-out:
++      mutex_unlock(&serial->disc_mutex);
+       return retval;
+-free_termios:
++
++ free_termios:
+       kfree(termios);
+       tty_port_tty_set(&port->port, NULL);
+-free_tty:
++ free_tty:
+       kfree(tty);
+-reset_open_count:
++ reset_open_count:
+       port->port.count = 0;
+-      goto out;
++      usb_autopm_put_interface(serial->interface);
++ error_get_interface:
++      usb_serial_put(serial);
++      mutex_unlock(&serial->disc_mutex);
++      return retval;
+ }
+ static void usb_console_write(struct console *co,