--- /dev/null
+From stern@rowland.harvard.edu Tue Jun 30 09:54:21 2009
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Tue, 30 Jun 2009 11:26:47 -0400 (EDT)
+Subject: usb-serial: replace shutdown with disconnect, release
+To: stable@kernel.org
+Message-ID: <Pine.LNX.4.44L0.0906301125070.3049-100000@iolanthe.rowland.org>
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit f9c99bb8b3a1ec81af68d484a551307326c2e933 upstream
+
+This patch splits up the shutdown method of usb_serial_driver into a
+disconnect and a release method.
+
+The problem is that the usb-serial core was calling shutdown during
+disconnect handling, but drivers didn't expect it to be called until
+after all the open file references had been closed. The result was an
+oops when the close method tried to use memory that had been
+deallocated by shutdown.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/staging/uc2322/aten2011.c | 4 +--
+ drivers/usb/serial/aircable.c | 5 +---
+ drivers/usb/serial/belkin_sa.c | 7 ++---
+ drivers/usb/serial/cp210x.c | 6 ++--
+ drivers/usb/serial/cyberjack.c | 20 ++++++++++++----
+ drivers/usb/serial/cypress_m8.c | 11 ++++----
+ drivers/usb/serial/digi_acceleport.c | 20 ++++++++++++----
+ drivers/usb/serial/empeg.c | 8 ------
+ drivers/usb/serial/ftdi_sio.c | 14 -----------
+ drivers/usb/serial/garmin_gps.c | 16 ++++++++++--
+ drivers/usb/serial/generic.c | 9 +++++--
+ drivers/usb/serial/io_edgeport.c | 29 ++++++++++++++++-------
+ drivers/usb/serial/io_tables.h | 12 ++++++---
+ drivers/usb/serial/io_ti.c | 22 +++++++++++++----
+ drivers/usb/serial/ipaq.c | 7 -----
+ drivers/usb/serial/iuu_phoenix.c | 6 ++--
+ drivers/usb/serial/keyspan.c | 13 +++++++++-
+ drivers/usb/serial/keyspan.h | 12 ++++++---
+ drivers/usb/serial/keyspan_pda.c | 4 +--
+ drivers/usb/serial/kl5kusb105.c | 39 +++++++++++++++++--------------
+ drivers/usb/serial/kobil_sct.c | 12 +++------
+ drivers/usb/serial/mct_u232.c | 13 ++++------
+ drivers/usb/serial/mos7720.c | 9 ++-----
+ drivers/usb/serial/mos7840.c | 42 +++++++++++++++++++++++++++++-----
+ drivers/usb/serial/omninet.c | 19 ++++++++++++---
+ drivers/usb/serial/opticon.c | 14 ++++++++---
+ drivers/usb/serial/option.c | 17 +++++++++----
+ drivers/usb/serial/oti6858.c | 7 ++---
+ drivers/usb/serial/pl2303.c | 5 +---
+ drivers/usb/serial/sierra.c | 28 +++++++++++++++++++---
+ drivers/usb/serial/spcp8x5.c | 5 +---
+ drivers/usb/serial/symbolserial.c | 14 ++++++++---
+ drivers/usb/serial/ti_usb_3410_5052.c | 10 +++-----
+ drivers/usb/serial/usb-serial.c | 29 ++++++++++-------------
+ drivers/usb/serial/visor.c | 13 ++++------
+ drivers/usb/serial/whiteheat.c | 6 ++--
+ include/linux/usb/serial.h | 12 ++++++---
+ 37 files changed, 319 insertions(+), 200 deletions(-)
+
+--- a/drivers/staging/uc2322/aten2011.c
++++ b/drivers/staging/uc2322/aten2011.c
+@@ -2336,7 +2336,7 @@ static int ATEN2011_startup(struct usb_s
+ return 0;
+ }
+
+-static void ATEN2011_shutdown(struct usb_serial *serial)
++static void ATEN2011_release(struct usb_serial *serial)
+ {
+ int i;
+ struct ATENINTL_port *ATEN2011_port;
+@@ -2382,7 +2382,7 @@ static struct usb_serial_driver aten_ser
+ .tiocmget = ATEN2011_tiocmget,
+ .tiocmset = ATEN2011_tiocmset,
+ .attach = ATEN2011_startup,
+- .shutdown = ATEN2011_shutdown,
++ .release = ATEN2011_release,
+ .read_bulk_callback = ATEN2011_bulk_in_callback,
+ .read_int_callback = ATEN2011_interrupt_callback,
+ };
+--- a/drivers/usb/serial/aircable.c
++++ b/drivers/usb/serial/aircable.c
+@@ -364,7 +364,7 @@ static int aircable_attach(struct usb_se
+ return 0;
+ }
+
+-static void aircable_shutdown(struct usb_serial *serial)
++static void aircable_release(struct usb_serial *serial)
+ {
+
+ struct usb_serial_port *port = serial->port[0];
+@@ -375,7 +375,6 @@ static void aircable_shutdown(struct usb
+ if (priv) {
+ serial_buf_free(priv->tx_buf);
+ serial_buf_free(priv->rx_buf);
+- usb_set_serial_port_data(port, NULL);
+ kfree(priv);
+ }
+ }
+@@ -601,7 +600,7 @@ static struct usb_serial_driver aircable
+ .num_ports = 1,
+ .attach = aircable_attach,
+ .probe = aircable_probe,
+- .shutdown = aircable_shutdown,
++ .release = aircable_release,
+ .write = aircable_write,
+ .write_room = aircable_write_room,
+ .write_bulk_callback = aircable_write_bulk_callback,
+--- a/drivers/usb/serial/belkin_sa.c
++++ b/drivers/usb/serial/belkin_sa.c
+@@ -90,7 +90,7 @@ static int debug;
+
+ /* function prototypes for a Belkin USB Serial Adapter F5U103 */
+ static int belkin_sa_startup(struct usb_serial *serial);
+-static void belkin_sa_shutdown(struct usb_serial *serial);
++static void belkin_sa_release(struct usb_serial *serial);
+ static int belkin_sa_open(struct tty_struct *tty,
+ struct usb_serial_port *port, struct file *filp);
+ static void belkin_sa_close(struct tty_struct *tty,
+@@ -143,7 +143,7 @@ static struct usb_serial_driver belkin_d
+ .tiocmget = belkin_sa_tiocmget,
+ .tiocmset = belkin_sa_tiocmset,
+ .attach = belkin_sa_startup,
+- .shutdown = belkin_sa_shutdown,
++ .release = belkin_sa_release,
+ };
+
+
+@@ -198,14 +198,13 @@ static int belkin_sa_startup(struct usb_
+ }
+
+
+-static void belkin_sa_shutdown(struct usb_serial *serial)
++static void belkin_sa_release(struct usb_serial *serial)
+ {
+ struct belkin_sa_private *priv;
+ int i;
+
+ dbg("%s", __func__);
+
+- /* stop reads and writes on all ports */
+ for (i = 0; i < serial->num_ports; ++i) {
+ /* My special items, the standard routines free my urbs */
+ priv = usb_get_serial_port_data(serial->port[i]);
+--- a/drivers/usb/serial/cp210x.c
++++ b/drivers/usb/serial/cp210x.c
+@@ -51,7 +51,7 @@ static int cp2101_tiocmset_port(struct u
+ unsigned int, unsigned int);
+ static void cp2101_break_ctl(struct tty_struct *, int);
+ static int cp2101_startup(struct usb_serial *);
+-static void cp2101_shutdown(struct usb_serial *);
++static void cp2101_disconnect(struct usb_serial *);
+
+ static int debug;
+
+@@ -131,7 +131,7 @@ static struct usb_serial_driver cp2101_d
+ .tiocmget = cp2101_tiocmget,
+ .tiocmset = cp2101_tiocmset,
+ .attach = cp2101_startup,
+- .shutdown = cp2101_shutdown,
++ .disconnect = cp2101_disconnect,
+ };
+
+ /* Config request types */
+@@ -773,7 +773,7 @@ static int cp2101_startup(struct usb_ser
+ return 0;
+ }
+
+-static void cp2101_shutdown(struct usb_serial *serial)
++static void cp2101_disconnect(struct usb_serial *serial)
+ {
+ int i;
+
+--- a/drivers/usb/serial/cyberjack.c
++++ b/drivers/usb/serial/cyberjack.c
+@@ -58,7 +58,8 @@ static int debug;
+
+ /* Function prototypes */
+ static int cyberjack_startup(struct usb_serial *serial);
+-static void cyberjack_shutdown(struct usb_serial *serial);
++static void cyberjack_disconnect(struct usb_serial *serial);
++static void cyberjack_release(struct usb_serial *serial);
+ static int cyberjack_open(struct tty_struct *tty,
+ struct usb_serial_port *port, struct file *filp);
+ static void cyberjack_close(struct tty_struct *tty,
+@@ -95,7 +96,8 @@ static struct usb_serial_driver cyberjac
+ .id_table = id_table,
+ .num_ports = 1,
+ .attach = cyberjack_startup,
+- .shutdown = cyberjack_shutdown,
++ .disconnect = cyberjack_disconnect,
++ .release = cyberjack_release,
+ .open = cyberjack_open,
+ .close = cyberjack_close,
+ .write = cyberjack_write,
+@@ -149,17 +151,25 @@ static int cyberjack_startup(struct usb_
+ return 0;
+ }
+
+-static void cyberjack_shutdown(struct usb_serial *serial)
++static void cyberjack_disconnect(struct usb_serial *serial)
+ {
+ int i;
+
+ dbg("%s", __func__);
+
+- for (i = 0; i < serial->num_ports; ++i) {
++ for (i = 0; i < serial->num_ports; ++i)
+ usb_kill_urb(serial->port[i]->interrupt_in_urb);
++}
++
++static void cyberjack_release(struct usb_serial *serial)
++{
++ int i;
++
++ dbg("%s", __func__);
++
++ for (i = 0; i < serial->num_ports; ++i) {
+ /* My special items, the standard routines free my urbs */
+ kfree(usb_get_serial_port_data(serial->port[i]));
+- usb_set_serial_port_data(serial->port[i], NULL);
+ }
+ }
+
+--- a/drivers/usb/serial/cypress_m8.c
++++ b/drivers/usb/serial/cypress_m8.c
+@@ -171,7 +171,7 @@ struct cypress_buf {
+ static int cypress_earthmate_startup(struct usb_serial *serial);
+ static int cypress_hidcom_startup(struct usb_serial *serial);
+ static int cypress_ca42v2_startup(struct usb_serial *serial);
+-static void cypress_shutdown(struct usb_serial *serial);
++static void cypress_release(struct usb_serial *serial);
+ static int cypress_open(struct tty_struct *tty,
+ struct usb_serial_port *port, struct file *filp);
+ static void cypress_close(struct tty_struct *tty,
+@@ -215,7 +215,7 @@ static struct usb_serial_driver cypress_
+ .id_table = id_table_earthmate,
+ .num_ports = 1,
+ .attach = cypress_earthmate_startup,
+- .shutdown = cypress_shutdown,
++ .release = cypress_release,
+ .open = cypress_open,
+ .close = cypress_close,
+ .write = cypress_write,
+@@ -241,7 +241,7 @@ static struct usb_serial_driver cypress_
+ .id_table = id_table_cyphidcomrs232,
+ .num_ports = 1,
+ .attach = cypress_hidcom_startup,
+- .shutdown = cypress_shutdown,
++ .release = cypress_release,
+ .open = cypress_open,
+ .close = cypress_close,
+ .write = cypress_write,
+@@ -267,7 +267,7 @@ static struct usb_serial_driver cypress_
+ .id_table = id_table_nokiaca42v2,
+ .num_ports = 1,
+ .attach = cypress_ca42v2_startup,
+- .shutdown = cypress_shutdown,
++ .release = cypress_release,
+ .open = cypress_open,
+ .close = cypress_close,
+ .write = cypress_write,
+@@ -613,7 +613,7 @@ static int cypress_ca42v2_startup(struct
+ } /* cypress_ca42v2_startup */
+
+
+-static void cypress_shutdown(struct usb_serial *serial)
++static void cypress_release(struct usb_serial *serial)
+ {
+ struct cypress_private *priv;
+
+@@ -626,7 +626,6 @@ static void cypress_shutdown(struct usb_
+ if (priv) {
+ cypress_buf_free(priv->buf);
+ kfree(priv);
+- usb_set_serial_port_data(serial->port[0], NULL);
+ }
+ }
+
+--- a/drivers/usb/serial/digi_acceleport.c
++++ b/drivers/usb/serial/digi_acceleport.c
+@@ -460,7 +460,8 @@ static void digi_close(struct tty_struct
+ struct file *filp);
+ static int digi_startup_device(struct usb_serial *serial);
+ static int digi_startup(struct usb_serial *serial);
+-static void digi_shutdown(struct usb_serial *serial);
++static void digi_disconnect(struct usb_serial *serial);
++static void digi_release(struct usb_serial *serial);
+ static void digi_read_bulk_callback(struct urb *urb);
+ static int digi_read_inb_callback(struct urb *urb);
+ static int digi_read_oob_callback(struct urb *urb);
+@@ -522,7 +523,8 @@ static struct usb_serial_driver digi_acc
+ .tiocmget = digi_tiocmget,
+ .tiocmset = digi_tiocmset,
+ .attach = digi_startup,
+- .shutdown = digi_shutdown,
++ .disconnect = digi_disconnect,
++ .release = digi_release,
+ };
+
+ static struct usb_serial_driver digi_acceleport_4_device = {
+@@ -548,7 +550,8 @@ static struct usb_serial_driver digi_acc
+ .tiocmget = digi_tiocmget,
+ .tiocmset = digi_tiocmset,
+ .attach = digi_startup,
+- .shutdown = digi_shutdown,
++ .disconnect = digi_disconnect,
++ .release = digi_release,
+ };
+
+
+@@ -1589,16 +1592,23 @@ static int digi_startup(struct usb_seria
+ }
+
+
+-static void digi_shutdown(struct usb_serial *serial)
++static void digi_disconnect(struct usb_serial *serial)
+ {
+ int i;
+- dbg("digi_shutdown: TOP, in_interrupt()=%ld", in_interrupt());
++ dbg("digi_disconnect: TOP, in_interrupt()=%ld", in_interrupt());
+
+ /* stop reads and writes on all ports */
+ for (i = 0; i < serial->type->num_ports + 1; i++) {
+ usb_kill_urb(serial->port[i]->read_urb);
+ usb_kill_urb(serial->port[i]->write_urb);
+ }
++}
++
++
++static void digi_release(struct usb_serial *serial)
++{
++ int i;
++ dbg("digi_release: TOP, in_interrupt()=%ld", in_interrupt());
+
+ /* free the private data structures for all ports */
+ /* number of regular ports + 1 for the out-of-band port */
+--- a/drivers/usb/serial/empeg.c
++++ b/drivers/usb/serial/empeg.c
+@@ -91,7 +91,6 @@ 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_shutdown(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_write_bulk_callback(struct urb *urb);
+@@ -125,7 +124,6 @@ static struct usb_serial_driver empeg_de
+ .throttle = empeg_throttle,
+ .unthrottle = empeg_unthrottle,
+ .attach = empeg_startup,
+- .shutdown = empeg_shutdown,
+ .set_termios = empeg_set_termios,
+ .write = empeg_write,
+ .write_room = empeg_write_room,
+@@ -429,12 +427,6 @@ static int empeg_startup(struct usb_ser
+ }
+
+
+-static void empeg_shutdown(struct usb_serial *serial)
+-{
+- dbg("%s", __func__);
+-}
+-
+-
+ static void empeg_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port, struct ktermios *old_termios)
+ {
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -714,7 +714,6 @@ static const char *ftdi_chip_name[] = {
+ /* function prototypes for a FTDI serial converter */
+ static int ftdi_sio_probe(struct usb_serial *serial,
+ const struct usb_device_id *id);
+-static void ftdi_shutdown(struct usb_serial *serial);
+ static int ftdi_sio_port_probe(struct usb_serial_port *port);
+ static int ftdi_sio_port_remove(struct usb_serial_port *port);
+ static int ftdi_open(struct tty_struct *tty,
+@@ -770,7 +769,6 @@ static struct usb_serial_driver ftdi_sio
+ .ioctl = ftdi_ioctl,
+ .set_termios = ftdi_set_termios,
+ .break_ctl = ftdi_break_ctl,
+- .shutdown = ftdi_shutdown,
+ };
+
+
+@@ -1460,18 +1458,6 @@ static int ftdi_mtxorb_hack_setup(struct
+ return 0;
+ }
+
+-/* ftdi_shutdown is called from usbserial:usb_serial_disconnect
+- * it is called when the usb device is disconnected
+- *
+- * usbserial:usb_serial_disconnect
+- * calls __serial_close for each open of the port
+- * shutdown is called then (ie ftdi_shutdown)
+- */
+-static void ftdi_shutdown(struct usb_serial *serial)
+-{
+- dbg("%s", __func__);
+-}
+-
+ static void ftdi_sio_priv_release(struct kref *k)
+ {
+ struct ftdi_private *priv = container_of(k, struct ftdi_private, kref);
+--- a/drivers/usb/serial/garmin_gps.c
++++ b/drivers/usb/serial/garmin_gps.c
+@@ -1528,7 +1528,7 @@ static int garmin_attach(struct usb_seri
+ }
+
+
+-static void garmin_shutdown(struct usb_serial *serial)
++static void garmin_disconnect(struct usb_serial *serial)
+ {
+ struct usb_serial_port *port = serial->port[0];
+ struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
+@@ -1537,8 +1537,17 @@ static void garmin_shutdown(struct usb_s
+
+ usb_kill_urb(port->interrupt_in_urb);
+ del_timer_sync(&garmin_data_p->timer);
++}
++
++
++static void garmin_release(struct usb_serial *serial)
++{
++ struct usb_serial_port *port = serial->port[0];
++ struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
++
++ dbg("%s", __func__);
++
+ kfree(garmin_data_p);
+- usb_set_serial_port_data(port, NULL);
+ }
+
+
+@@ -1557,7 +1566,8 @@ static struct usb_serial_driver garmin_d
+ .throttle = garmin_throttle,
+ .unthrottle = garmin_unthrottle,
+ .attach = garmin_attach,
+- .shutdown = garmin_shutdown,
++ .disconnect = garmin_disconnect,
++ .release = garmin_release,
+ .write = garmin_write,
+ .write_room = garmin_write_room,
+ .write_bulk_callback = garmin_write_bulk_callback,
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -63,7 +63,8 @@ struct usb_serial_driver usb_serial_gene
+ .id_table = generic_device_ids,
+ .usb_driver = &generic_driver,
+ .num_ports = 1,
+- .shutdown = usb_serial_generic_shutdown,
++ .disconnect = usb_serial_generic_disconnect,
++ .release = usb_serial_generic_release,
+ .throttle = usb_serial_generic_throttle,
+ .unthrottle = usb_serial_generic_unthrottle,
+ .resume = usb_serial_generic_resume,
+@@ -413,7 +414,7 @@ void usb_serial_generic_unthrottle(struc
+ }
+ }
+
+-void usb_serial_generic_shutdown(struct usb_serial *serial)
++void usb_serial_generic_disconnect(struct usb_serial *serial)
+ {
+ int i;
+
+@@ -424,3 +425,7 @@ void usb_serial_generic_shutdown(struct
+ generic_cleanup(serial->port[i]);
+ }
+
++void usb_serial_generic_release(struct usb_serial *serial)
++{
++ dbg("%s", __func__);
++}
+--- a/drivers/usb/serial/io_edgeport.c
++++ b/drivers/usb/serial/io_edgeport.c
+@@ -225,7 +225,8 @@ static int edge_tiocmget(struct tty_str
+ static int edge_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear);
+ static int edge_startup(struct usb_serial *serial);
+-static void edge_shutdown(struct usb_serial *serial);
++static void edge_disconnect(struct usb_serial *serial);
++static void edge_release(struct usb_serial *serial);
+
+ #include "io_tables.h" /* all of the devices that this driver supports */
+
+@@ -3195,21 +3196,16 @@ static int edge_startup(struct usb_seria
+
+
+ /****************************************************************************
+- * edge_shutdown
++ * edge_disconnect
+ * This function is called whenever the device is removed from the usb bus.
+ ****************************************************************************/
+-static void edge_shutdown(struct usb_serial *serial)
++static void edge_disconnect(struct usb_serial *serial)
+ {
+ struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+- int i;
+
+ dbg("%s", __func__);
+
+ /* stop reads and writes on all ports */
+- for (i = 0; i < serial->num_ports; ++i) {
+- kfree(usb_get_serial_port_data(serial->port[i]));
+- usb_set_serial_port_data(serial->port[i], NULL);
+- }
+ /* free up our endpoint stuff */
+ if (edge_serial->is_epic) {
+ usb_kill_urb(edge_serial->interrupt_read_urb);
+@@ -3220,9 +3216,24 @@ static void edge_shutdown(struct usb_ser
+ usb_free_urb(edge_serial->read_urb);
+ kfree(edge_serial->bulk_in_buffer);
+ }
++}
++
++
++/****************************************************************************
++ * edge_release
++ * This function is called when the device structure is deallocated.
++ ****************************************************************************/
++static void edge_release(struct usb_serial *serial)
++{
++ struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
++ int i;
++
++ dbg("%s", __func__);
++
++ for (i = 0; i < serial->num_ports; ++i)
++ kfree(usb_get_serial_port_data(serial->port[i]));
+
+ kfree(edge_serial);
+- usb_set_serial_data(serial, NULL);
+ }
+
+
+--- a/drivers/usb/serial/io_tables.h
++++ b/drivers/usb/serial/io_tables.h
+@@ -117,7 +117,8 @@ static struct usb_serial_driver edgeport
+ .throttle = edge_throttle,
+ .unthrottle = edge_unthrottle,
+ .attach = edge_startup,
+- .shutdown = edge_shutdown,
++ .disconnect = edge_disconnect,
++ .release = edge_release,
+ .ioctl = edge_ioctl,
+ .set_termios = edge_set_termios,
+ .tiocmget = edge_tiocmget,
+@@ -145,7 +146,8 @@ static struct usb_serial_driver edgeport
+ .throttle = edge_throttle,
+ .unthrottle = edge_unthrottle,
+ .attach = edge_startup,
+- .shutdown = edge_shutdown,
++ .disconnect = edge_disconnect,
++ .release = edge_release,
+ .ioctl = edge_ioctl,
+ .set_termios = edge_set_termios,
+ .tiocmget = edge_tiocmget,
+@@ -173,7 +175,8 @@ static struct usb_serial_driver edgeport
+ .throttle = edge_throttle,
+ .unthrottle = edge_unthrottle,
+ .attach = edge_startup,
+- .shutdown = edge_shutdown,
++ .disconnect = edge_disconnect,
++ .release = edge_release,
+ .ioctl = edge_ioctl,
+ .set_termios = edge_set_termios,
+ .tiocmget = edge_tiocmget,
+@@ -200,7 +203,8 @@ static struct usb_serial_driver epic_dev
+ .throttle = edge_throttle,
+ .unthrottle = edge_unthrottle,
+ .attach = edge_startup,
+- .shutdown = edge_shutdown,
++ .disconnect = edge_disconnect,
++ .release = edge_release,
+ .ioctl = edge_ioctl,
+ .set_termios = edge_set_termios,
+ .tiocmget = edge_tiocmget,
+--- a/drivers/usb/serial/io_ti.c
++++ b/drivers/usb/serial/io_ti.c
+@@ -2664,7 +2664,7 @@ cleanup:
+ return -ENOMEM;
+ }
+
+-static void edge_shutdown(struct usb_serial *serial)
++static void edge_disconnect(struct usb_serial *serial)
+ {
+ int i;
+ struct edgeport_port *edge_port;
+@@ -2674,12 +2674,22 @@ static void edge_shutdown(struct usb_ser
+ for (i = 0; i < serial->num_ports; ++i) {
+ edge_port = usb_get_serial_port_data(serial->port[i]);
+ edge_remove_sysfs_attrs(edge_port->port);
++ }
++}
++
++static void edge_release(struct usb_serial *serial)
++{
++ int i;
++ struct edgeport_port *edge_port;
++
++ dbg("%s", __func__);
++
++ for (i = 0; i < serial->num_ports; ++i) {
++ edge_port = usb_get_serial_port_data(serial->port[i]);
+ edge_buf_free(edge_port->ep_out_buf);
+ kfree(edge_port);
+- usb_set_serial_port_data(serial->port[i], NULL);
+ }
+ kfree(usb_get_serial_data(serial));
+- usb_set_serial_data(serial, NULL);
+ }
+
+
+@@ -2916,7 +2926,8 @@ static struct usb_serial_driver edgeport
+ .throttle = edge_throttle,
+ .unthrottle = edge_unthrottle,
+ .attach = edge_startup,
+- .shutdown = edge_shutdown,
++ .disconnect = edge_disconnect,
++ .release = edge_release,
+ .port_probe = edge_create_sysfs_attrs,
+ .ioctl = edge_ioctl,
+ .set_termios = edge_set_termios,
+@@ -2945,7 +2956,8 @@ static struct usb_serial_driver edgeport
+ .throttle = edge_throttle,
+ .unthrottle = edge_unthrottle,
+ .attach = edge_startup,
+- .shutdown = edge_shutdown,
++ .disconnect = edge_disconnect,
++ .release = edge_release,
+ .port_probe = edge_create_sysfs_attrs,
+ .ioctl = edge_ioctl,
+ .set_termios = edge_set_termios,
+--- a/drivers/usb/serial/ipaq.c
++++ b/drivers/usb/serial/ipaq.c
+@@ -80,7 +80,6 @@ static void ipaq_close(struct tty_struct
+ struct usb_serial_port *port, struct file *filp);
+ static int ipaq_calc_num_ports(struct usb_serial *serial);
+ static int ipaq_startup(struct usb_serial *serial);
+-static void ipaq_shutdown(struct usb_serial *serial);
+ static int ipaq_write(struct tty_struct *tty, struct usb_serial_port *port,
+ const unsigned char *buf, int count);
+ static int ipaq_write_bulk(struct usb_serial_port *port,
+@@ -577,7 +576,6 @@ static struct usb_serial_driver ipaq_dev
+ .close = ipaq_close,
+ .attach = ipaq_startup,
+ .calc_num_ports = ipaq_calc_num_ports,
+- .shutdown = ipaq_shutdown,
+ .write = ipaq_write,
+ .write_room = ipaq_write_room,
+ .chars_in_buffer = ipaq_chars_in_buffer,
+@@ -992,11 +990,6 @@ static int ipaq_startup(struct usb_seria
+ return usb_reset_configuration(serial->dev);
+ }
+
+-static void ipaq_shutdown(struct usb_serial *serial)
+-{
+- dbg("%s", __func__);
+-}
+-
+ static int __init ipaq_init(void)
+ {
+ int retval;
+--- a/drivers/usb/serial/iuu_phoenix.c
++++ b/drivers/usb/serial/iuu_phoenix.c
+@@ -122,8 +122,8 @@ static int iuu_startup(struct usb_serial
+ return 0;
+ }
+
+-/* Shutdown function */
+-static void iuu_shutdown(struct usb_serial *serial)
++/* Release function */
++static void iuu_release(struct usb_serial *serial)
+ {
+ struct usb_serial_port *port = serial->port[0];
+ struct iuu_private *priv = usb_get_serial_port_data(port);
+@@ -1176,7 +1176,7 @@ static struct usb_serial_driver iuu_devi
+ .tiocmget = iuu_tiocmget,
+ .tiocmset = iuu_tiocmset,
+ .attach = iuu_startup,
+- .shutdown = iuu_shutdown,
++ .release = iuu_release,
+ };
+
+ static int __init iuu_init(void)
+--- a/drivers/usb/serial/keyspan.c
++++ b/drivers/usb/serial/keyspan.c
+@@ -2682,7 +2682,7 @@ static int keyspan_startup(struct usb_se
+ return 0;
+ }
+
+-static void keyspan_shutdown(struct usb_serial *serial)
++static void keyspan_disconnect(struct usb_serial *serial)
+ {
+ int i, j;
+ struct usb_serial_port *port;
+@@ -2722,6 +2722,17 @@ static void keyspan_shutdown(struct usb_
+ usb_free_urb(p_priv->out_urbs[j]);
+ }
+ }
++}
++
++static void keyspan_release(struct usb_serial *serial)
++{
++ int i;
++ struct usb_serial_port *port;
++ struct keyspan_serial_private *s_priv;
++
++ dbg("%s", __func__);
++
++ s_priv = usb_get_serial_data(serial);
+
+ /* dbg("Freeing serial->private."); */
+ kfree(s_priv);
+--- a/drivers/usb/serial/keyspan.h
++++ b/drivers/usb/serial/keyspan.h
+@@ -42,7 +42,8 @@ static void keyspan_close (struct tty_s
+ struct usb_serial_port *port,
+ struct file *filp);
+ static int keyspan_startup (struct usb_serial *serial);
+-static void keyspan_shutdown (struct usb_serial *serial);
++static void keyspan_disconnect (struct usb_serial *serial);
++static void keyspan_release (struct usb_serial *serial);
+ static int keyspan_write_room (struct tty_struct *tty);
+
+ static int keyspan_write (struct tty_struct *tty,
+@@ -569,7 +570,8 @@ static struct usb_serial_driver keyspan_
+ .tiocmget = keyspan_tiocmget,
+ .tiocmset = keyspan_tiocmset,
+ .attach = keyspan_startup,
+- .shutdown = keyspan_shutdown,
++ .disconnect = keyspan_disconnect,
++ .release = keyspan_release,
+ };
+
+ static struct usb_serial_driver keyspan_2port_device = {
+@@ -589,7 +591,8 @@ static struct usb_serial_driver keyspan_
+ .tiocmget = keyspan_tiocmget,
+ .tiocmset = keyspan_tiocmset,
+ .attach = keyspan_startup,
+- .shutdown = keyspan_shutdown,
++ .disconnect = keyspan_disconnect,
++ .release = keyspan_release,
+ };
+
+ static struct usb_serial_driver keyspan_4port_device = {
+@@ -609,7 +612,8 @@ static struct usb_serial_driver keyspan_
+ .tiocmget = keyspan_tiocmget,
+ .tiocmset = keyspan_tiocmset,
+ .attach = keyspan_startup,
+- .shutdown = keyspan_shutdown,
++ .disconnect = keyspan_disconnect,
++ .release = keyspan_release,
+ };
+
+ #endif
+--- a/drivers/usb/serial/keyspan_pda.c
++++ b/drivers/usb/serial/keyspan_pda.c
+@@ -795,7 +795,7 @@ static int keyspan_pda_startup(struct us
+ return 0;
+ }
+
+-static void keyspan_pda_shutdown(struct usb_serial *serial)
++static void keyspan_pda_release(struct usb_serial *serial)
+ {
+ dbg("%s", __func__);
+
+@@ -853,7 +853,7 @@ static struct usb_serial_driver keyspan_
+ .tiocmget = keyspan_pda_tiocmget,
+ .tiocmset = keyspan_pda_tiocmset,
+ .attach = keyspan_pda_startup,
+- .shutdown = keyspan_pda_shutdown,
++ .release = keyspan_pda_release,
+ };
+
+
+--- a/drivers/usb/serial/kl5kusb105.c
++++ b/drivers/usb/serial/kl5kusb105.c
+@@ -73,7 +73,8 @@ static int debug;
+ * Function prototypes
+ */
+ static int klsi_105_startup(struct usb_serial *serial);
+-static void klsi_105_shutdown(struct usb_serial *serial);
++static void klsi_105_disconnect(struct usb_serial *serial);
++static void klsi_105_release(struct usb_serial *serial);
+ static int klsi_105_open(struct tty_struct *tty,
+ struct usb_serial_port *port, struct file *filp);
+ static void klsi_105_close(struct tty_struct *tty,
+@@ -132,7 +133,8 @@ static struct usb_serial_driver kl5kusb1
+ .tiocmget = klsi_105_tiocmget,
+ .tiocmset = klsi_105_tiocmset,
+ .attach = klsi_105_startup,
+- .shutdown = klsi_105_shutdown,
++ .disconnect = klsi_105_disconnect,
++ .release = klsi_105_release,
+ .throttle = klsi_105_throttle,
+ .unthrottle = klsi_105_unthrottle,
+ };
+@@ -316,7 +318,7 @@ err_cleanup:
+ } /* klsi_105_startup */
+
+
+-static void klsi_105_shutdown(struct usb_serial *serial)
++static void klsi_105_disconnect(struct usb_serial *serial)
+ {
+ int i;
+
+@@ -326,33 +328,36 @@ static void klsi_105_shutdown(struct usb
+ for (i = 0; i < serial->num_ports; ++i) {
+ struct klsi_105_private *priv =
+ usb_get_serial_port_data(serial->port[i]);
+- unsigned long flags;
+
+ if (priv) {
+ /* kill our write urb pool */
+ int j;
+ struct urb **write_urbs = priv->write_urb_pool;
+- spin_lock_irqsave(&priv->lock, flags);
+
+ for (j = 0; j < NUM_URBS; j++) {
+ if (write_urbs[j]) {
+- /* FIXME - uncomment the following
+- * usb_kill_urb call when the host
+- * controllers get fixed to set
+- * urb->dev = NULL after the urb is
+- * finished. Otherwise this call
+- * oopses. */
+- /* usb_kill_urb(write_urbs[j]); */
+- kfree(write_urbs[j]->transfer_buffer);
++ usb_kill_urb(write_urbs[j]);
+ usb_free_urb(write_urbs[j]);
+ }
+ }
+- spin_unlock_irqrestore(&priv->lock, flags);
+- kfree(priv);
+- usb_set_serial_port_data(serial->port[i], NULL);
+ }
+ }
+-} /* klsi_105_shutdown */
++} /* klsi_105_disconnect */
++
++
++static void klsi_105_release(struct usb_serial *serial)
++{
++ int i;
++
++ dbg("%s", __func__);
++
++ for (i = 0; i < serial->num_ports; ++i) {
++ struct klsi_105_private *priv =
++ usb_get_serial_port_data(serial->port[i]);
++
++ kfree(priv);
++ }
++} /* klsi_105_release */
+
+ static int klsi_105_open(struct tty_struct *tty,
+ struct usb_serial_port *port, struct file *filp)
+--- a/drivers/usb/serial/kobil_sct.c
++++ b/drivers/usb/serial/kobil_sct.c
+@@ -69,7 +69,7 @@ static int debug;
+
+ /* Function prototypes */
+ static int kobil_startup(struct usb_serial *serial);
+-static void kobil_shutdown(struct usb_serial *serial);
++static void kobil_release(struct usb_serial *serial);
+ static int kobil_open(struct tty_struct *tty,
+ struct usb_serial_port *port, struct file *filp);
+ static void kobil_close(struct tty_struct *tty, struct usb_serial_port *port,
+@@ -118,7 +118,7 @@ static struct usb_serial_driver kobil_de
+ .id_table = id_table,
+ .num_ports = 1,
+ .attach = kobil_startup,
+- .shutdown = kobil_shutdown,
++ .release = kobil_release,
+ .ioctl = kobil_ioctl,
+ .set_termios = kobil_set_termios,
+ .tiocmget = kobil_tiocmget,
+@@ -202,17 +202,13 @@ static int kobil_startup(struct usb_seri
+ }
+
+
+-static void kobil_shutdown(struct usb_serial *serial)
++static void kobil_release(struct usb_serial *serial)
+ {
+ int i;
+ dbg("%s - port %d", __func__, serial->port[0]->number);
+
+- for (i = 0; i < serial->num_ports; ++i) {
+- while (serial->port[i]->port.count > 0)
+- kobil_close(NULL, serial->port[i], NULL);
++ for (i = 0; i < serial->num_ports; ++i)
+ kfree(usb_get_serial_port_data(serial->port[i]));
+- usb_set_serial_port_data(serial->port[i], NULL);
+- }
+ }
+
+
+--- a/drivers/usb/serial/mct_u232.c
++++ b/drivers/usb/serial/mct_u232.c
+@@ -92,7 +92,7 @@ static int debug;
+ * Function prototypes
+ */
+ static int mct_u232_startup(struct usb_serial *serial);
+-static void mct_u232_shutdown(struct usb_serial *serial);
++static void mct_u232_release(struct usb_serial *serial);
+ static int mct_u232_open(struct tty_struct *tty,
+ struct usb_serial_port *port, struct file *filp);
+ static void mct_u232_close(struct tty_struct *tty,
+@@ -148,7 +148,7 @@ static struct usb_serial_driver mct_u232
+ .tiocmget = mct_u232_tiocmget,
+ .tiocmset = mct_u232_tiocmset,
+ .attach = mct_u232_startup,
+- .shutdown = mct_u232_shutdown,
++ .release = mct_u232_release,
+ };
+
+
+@@ -406,7 +406,7 @@ static int mct_u232_startup(struct usb_s
+ } /* mct_u232_startup */
+
+
+-static void mct_u232_shutdown(struct usb_serial *serial)
++static void mct_u232_release(struct usb_serial *serial)
+ {
+ struct mct_u232_private *priv;
+ int i;
+@@ -416,12 +416,9 @@ static void mct_u232_shutdown(struct usb
+ for (i = 0; i < serial->num_ports; ++i) {
+ /* My special items, the standard routines free my urbs */
+ priv = usb_get_serial_port_data(serial->port[i]);
+- if (priv) {
+- usb_set_serial_port_data(serial->port[i], NULL);
+- kfree(priv);
+- }
++ kfree(priv);
+ }
+-} /* mct_u232_shutdown */
++} /* mct_u232_release */
+
+ static int mct_u232_open(struct tty_struct *tty,
+ struct usb_serial_port *port, struct file *filp)
+--- a/drivers/usb/serial/mos7720.c
++++ b/drivers/usb/serial/mos7720.c
+@@ -1522,19 +1522,16 @@ static int mos7720_startup(struct usb_se
+ return 0;
+ }
+
+-static void mos7720_shutdown(struct usb_serial *serial)
++static void mos7720_release(struct usb_serial *serial)
+ {
+ int i;
+
+ /* free private structure allocated for serial port */
+- for (i = 0; i < serial->num_ports; ++i) {
++ for (i = 0; i < serial->num_ports; ++i)
+ kfree(usb_get_serial_port_data(serial->port[i]));
+- usb_set_serial_port_data(serial->port[i], NULL);
+- }
+
+ /* free private structure allocated for serial device */
+ kfree(usb_get_serial_data(serial));
+- usb_set_serial_data(serial, NULL);
+ }
+
+ static struct usb_driver usb_driver = {
+@@ -1559,7 +1556,7 @@ static struct usb_serial_driver moschip7
+ .throttle = mos7720_throttle,
+ .unthrottle = mos7720_unthrottle,
+ .attach = mos7720_startup,
+- .shutdown = mos7720_shutdown,
++ .release = mos7720_release,
+ .ioctl = mos7720_ioctl,
+ .set_termios = mos7720_set_termios,
+ .write = mos7720_write,
+--- a/drivers/usb/serial/mos7840.c
++++ b/drivers/usb/serial/mos7840.c
+@@ -2673,16 +2673,16 @@ error:
+ }
+
+ /****************************************************************************
+- * mos7840_shutdown
++ * mos7840_disconnect
+ * This function is called whenever the device is removed from the usb bus.
+ ****************************************************************************/
+
+-static void mos7840_shutdown(struct usb_serial *serial)
++static void mos7840_disconnect(struct usb_serial *serial)
+ {
+ int i;
+ unsigned long flags;
+ struct moschip_port *mos7840_port;
+- dbg("%s \n", " shutdown :entering..........");
++ dbg("%s \n", " disconnect :entering..........");
+
+ if (!serial) {
+ dbg("%s", "Invalid Handler \n");
+@@ -2702,11 +2702,42 @@ static void mos7840_shutdown(struct usb_
+ mos7840_port->zombie = 1;
+ spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
+ usb_kill_urb(mos7840_port->control_urb);
++ }
++ }
++
++ dbg("%s", "Thank u :: ");
++
++}
++
++/****************************************************************************
++ * mos7840_release
++ * This function is called when the usb_serial structure is freed.
++ ****************************************************************************/
++
++static void mos7840_release(struct usb_serial *serial)
++{
++ int i;
++ struct moschip_port *mos7840_port;
++ dbg("%s", " release :entering..........");
++
++ if (!serial) {
++ dbg("%s", "Invalid Handler");
++ return;
++ }
++
++ /* check for the ports to be closed,close the ports and disconnect */
++
++ /* free private structure allocated for serial port *
++ * stop reads and writes on all ports */
++
++ for (i = 0; i < serial->num_ports; ++i) {
++ mos7840_port = mos7840_get_port_private(serial->port[i]);
++ dbg("mos7840_port %d = %p", i, mos7840_port);
++ if (mos7840_port) {
+ kfree(mos7840_port->ctrl_buf);
+ kfree(mos7840_port->dr);
+ kfree(mos7840_port);
+ }
+- mos7840_set_port_private(serial->port[i], NULL);
+ }
+
+ dbg("%s\n", "Thank u :: ");
+@@ -2747,7 +2778,8 @@ static struct usb_serial_driver moschip7
+ .tiocmget = mos7840_tiocmget,
+ .tiocmset = mos7840_tiocmset,
+ .attach = mos7840_startup,
+- .shutdown = mos7840_shutdown,
++ .disconnect = mos7840_disconnect,
++ .release = mos7840_release,
+ .read_bulk_callback = mos7840_bulk_in_callback,
+ .read_int_callback = mos7840_interrupt_callback,
+ };
+--- a/drivers/usb/serial/omninet.c
++++ b/drivers/usb/serial/omninet.c
+@@ -73,7 +73,8 @@ static void omninet_write_bulk_callback(
+ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
+ const unsigned char *buf, int count);
+ static int omninet_write_room(struct tty_struct *tty);
+-static void omninet_shutdown(struct usb_serial *serial);
++static void omninet_disconnect(struct usb_serial *serial);
++static void omninet_release(struct usb_serial *serial);
+ static int omninet_attach(struct usb_serial *serial);
+
+ static struct usb_device_id id_table[] = {
+@@ -109,7 +110,8 @@ static struct usb_serial_driver zyxel_om
+ .write_room = omninet_write_room,
+ .read_bulk_callback = omninet_read_bulk_callback,
+ .write_bulk_callback = omninet_write_bulk_callback,
+- .shutdown = omninet_shutdown,
++ .disconnect = omninet_disconnect,
++ .release = omninet_release,
+ };
+
+
+@@ -347,13 +349,22 @@ static void omninet_write_bulk_callback(
+ }
+
+
+-static void omninet_shutdown(struct usb_serial *serial)
++static void omninet_disconnect(struct usb_serial *serial)
+ {
+ struct usb_serial_port *wport = serial->port[1];
+- struct usb_serial_port *port = serial->port[0];
++
+ dbg("%s", __func__);
+
+ usb_kill_urb(wport->write_urb);
++}
++
++
++static void omninet_release(struct usb_serial *serial)
++{
++ struct usb_serial_port *port = serial->port[0];
++
++ dbg("%s", __func__);
++
+ kfree(usb_get_serial_port_data(port));
+ }
+
+--- a/drivers/usb/serial/opticon.c
++++ b/drivers/usb/serial/opticon.c
+@@ -464,7 +464,7 @@ error:
+ return retval;
+ }
+
+-static void opticon_shutdown(struct usb_serial *serial)
++static void opticon_disconnect(struct usb_serial *serial)
+ {
+ struct opticon_private *priv = usb_get_serial_data(serial);
+
+@@ -472,9 +472,16 @@ static void opticon_shutdown(struct usb_
+
+ usb_kill_urb(priv->bulk_read_urb);
+ usb_free_urb(priv->bulk_read_urb);
++}
++
++static void opticon_release(struct usb_serial *serial)
++{
++ struct opticon_private *priv = usb_get_serial_data(serial);
++
++ dbg("%s", __func__);
++
+ kfree(priv->bulk_in_buffer);
+ kfree(priv);
+- usb_set_serial_data(serial, NULL);
+ }
+
+ static int opticon_suspend(struct usb_interface *intf, pm_message_t message)
+@@ -525,7 +532,8 @@ static struct usb_serial_driver opticon_
+ .close = opticon_close,
+ .write = opticon_write,
+ .write_room = opticon_write_room,
+- .shutdown = opticon_shutdown,
++ .disconnect = opticon_disconnect,
++ .release = opticon_release,
+ .throttle = opticon_throttle,
+ .unthrottle = opticon_unthrottle,
+ .ioctl = opticon_ioctl,
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -48,7 +48,8 @@ static int option_open(struct tty_struc
+ static void option_close(struct tty_struct *tty, struct usb_serial_port *port,
+ struct file *filp);
+ static int option_startup(struct usb_serial *serial);
+-static void option_shutdown(struct usb_serial *serial);
++static void option_disconnect(struct usb_serial *serial);
++static void option_release(struct usb_serial *serial);
+ static int option_write_room(struct tty_struct *tty);
+
+ static void option_instat_callback(struct urb *urb);
+@@ -558,7 +559,8 @@ static struct usb_serial_driver option_1
+ .tiocmget = option_tiocmget,
+ .tiocmset = option_tiocmset,
+ .attach = option_startup,
+- .shutdown = option_shutdown,
++ .disconnect = option_disconnect,
++ .release = option_release,
+ .read_int_callback = option_instat_callback,
+ .suspend = option_suspend,
+ .resume = option_resume,
+@@ -1129,7 +1131,14 @@ static void stop_read_write_urbs(struct
+ }
+ }
+
+-static void option_shutdown(struct usb_serial *serial)
++static void option_disconnect(struct usb_serial *serial)
++{
++ dbg("%s", __func__);
++
++ stop_read_write_urbs(serial);
++}
++
++static void option_release(struct usb_serial *serial)
+ {
+ int i, j;
+ struct usb_serial_port *port;
+@@ -1137,8 +1146,6 @@ static void option_shutdown(struct usb_s
+
+ dbg("%s", __func__);
+
+- stop_read_write_urbs(serial);
+-
+ /* Now free them */
+ for (i = 0; i < serial->num_ports; ++i) {
+ port = serial->port[i];
+--- a/drivers/usb/serial/oti6858.c
++++ b/drivers/usb/serial/oti6858.c
+@@ -160,7 +160,7 @@ static int oti6858_tiocmget(struct tty_s
+ static int oti6858_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear);
+ static int oti6858_startup(struct usb_serial *serial);
+-static void oti6858_shutdown(struct usb_serial *serial);
++static void oti6858_release(struct usb_serial *serial);
+
+ /* functions operating on buffers */
+ static struct oti6858_buf *oti6858_buf_alloc(unsigned int size);
+@@ -195,7 +195,7 @@ static struct usb_serial_driver oti6858_
+ .write_room = oti6858_write_room,
+ .chars_in_buffer = oti6858_chars_in_buffer,
+ .attach = oti6858_startup,
+- .shutdown = oti6858_shutdown,
++ .release = oti6858_release,
+ };
+
+ struct oti6858_private {
+@@ -829,7 +829,7 @@ static int oti6858_ioctl(struct tty_stru
+ }
+
+
+-static void oti6858_shutdown(struct usb_serial *serial)
++static void oti6858_release(struct usb_serial *serial)
+ {
+ struct oti6858_private *priv;
+ int i;
+@@ -841,7 +841,6 @@ static void oti6858_shutdown(struct usb_
+ if (priv) {
+ oti6858_buf_free(priv->buf);
+ kfree(priv);
+- usb_set_serial_port_data(serial->port[i], NULL);
+ }
+ }
+ }
+--- a/drivers/usb/serial/pl2303.c
++++ b/drivers/usb/serial/pl2303.c
+@@ -897,7 +897,7 @@ static void pl2303_break_ctl(struct tty_
+ dbg("%s - error sending break = %d", __func__, result);
+ }
+
+-static void pl2303_shutdown(struct usb_serial *serial)
++static void pl2303_release(struct usb_serial *serial)
+ {
+ int i;
+ struct pl2303_private *priv;
+@@ -909,7 +909,6 @@ static void pl2303_shutdown(struct usb_s
+ if (priv) {
+ pl2303_buf_free(priv->buf);
+ kfree(priv);
+- usb_set_serial_port_data(serial->port[i], NULL);
+ }
+ }
+ }
+@@ -1137,7 +1136,7 @@ static struct usb_serial_driver pl2303_d
+ .write_room = pl2303_write_room,
+ .chars_in_buffer = pl2303_chars_in_buffer,
+ .attach = pl2303_startup,
+- .shutdown = pl2303_shutdown,
++ .release = pl2303_release,
+ };
+
+ static int __init pl2303_init(void)
+--- a/drivers/usb/serial/sierra.c
++++ b/drivers/usb/serial/sierra.c
+@@ -699,7 +699,7 @@ static int sierra_startup(struct usb_ser
+ return 0;
+ }
+
+-static void sierra_shutdown(struct usb_serial *serial)
++static void sierra_disconnect(struct usb_serial *serial)
+ {
+ int i, j;
+ struct usb_serial_port *port;
+@@ -718,10 +718,29 @@ static void sierra_shutdown(struct usb_s
+ for (j = 0; j < N_IN_URB; j++) {
+ usb_kill_urb(portdata->in_urbs[j]);
+ usb_free_urb(portdata->in_urbs[j]);
+- kfree(portdata->in_buffer[j]);
+ }
++ }
++}
++
++static void sierra_release(struct usb_serial *serial)
++{
++ int i, j;
++ struct usb_serial_port *port;
++ struct sierra_port_private *portdata;
++
++ dev_dbg(&serial->dev->dev, "%s\n", __func__);
++
++ for (i = 0; i < serial->num_ports; ++i) {
++ port = serial->port[i];
++ if (!port)
++ continue;
++ portdata = usb_get_serial_port_data(port);
++ if (!portdata)
++ continue;
++
++ for (j = 0; j < N_IN_URB; j++)
++ kfree(portdata->in_buffer[j]);
+ kfree(portdata);
+- usb_set_serial_port_data(port, NULL);
+ }
+ }
+
+@@ -743,7 +762,8 @@ static struct usb_serial_driver sierra_d
+ .tiocmget = sierra_tiocmget,
+ .tiocmset = sierra_tiocmset,
+ .attach = sierra_startup,
+- .shutdown = sierra_shutdown,
++ .disconnect = sierra_disconnect,
++ .release = sierra_release,
+ .read_int_callback = sierra_instat_callback,
+ };
+
+--- a/drivers/usb/serial/spcp8x5.c
++++ b/drivers/usb/serial/spcp8x5.c
+@@ -356,7 +356,7 @@ cleanup:
+ }
+
+ /* call when the device plug out. free all the memory alloced by probe */
+-static void spcp8x5_shutdown(struct usb_serial *serial)
++static void spcp8x5_release(struct usb_serial *serial)
+ {
+ int i;
+ struct spcp8x5_private *priv;
+@@ -366,7 +366,6 @@ static void spcp8x5_shutdown(struct usb_
+ if (priv) {
+ free_ringbuf(priv->buf);
+ kfree(priv);
+- usb_set_serial_port_data(serial->port[i] , NULL);
+ }
+ }
+ }
+@@ -1043,7 +1042,7 @@ static struct usb_serial_driver spcp8x5_
+ .write_bulk_callback = spcp8x5_write_bulk_callback,
+ .chars_in_buffer = spcp8x5_chars_in_buffer,
+ .attach = spcp8x5_startup,
+- .shutdown = spcp8x5_shutdown,
++ .release = spcp8x5_release,
+ };
+
+ static int __init spcp8x5_init(void)
+--- a/drivers/usb/serial/symbolserial.c
++++ b/drivers/usb/serial/symbolserial.c
+@@ -268,7 +268,7 @@ error:
+ return retval;
+ }
+
+-static void symbol_shutdown(struct usb_serial *serial)
++static void symbol_disconnect(struct usb_serial *serial)
+ {
+ struct symbol_private *priv = usb_get_serial_data(serial);
+
+@@ -276,9 +276,16 @@ static void symbol_shutdown(struct usb_s
+
+ usb_kill_urb(priv->int_urb);
+ usb_free_urb(priv->int_urb);
++}
++
++static void symbol_release(struct usb_serial *serial)
++{
++ struct symbol_private *priv = usb_get_serial_data(serial);
++
++ dbg("%s", __func__);
++
+ kfree(priv->int_buffer);
+ kfree(priv);
+- usb_set_serial_data(serial, NULL);
+ }
+
+ static struct usb_driver symbol_driver = {
+@@ -300,7 +307,8 @@ static struct usb_serial_driver symbol_d
+ .attach = symbol_startup,
+ .open = symbol_open,
+ .close = symbol_close,
+- .shutdown = symbol_shutdown,
++ .disconnect = symbol_disconnect,
++ .release = symbol_release,
+ .throttle = symbol_throttle,
+ .unthrottle = symbol_unthrottle,
+ };
+--- a/drivers/usb/serial/ti_usb_3410_5052.c
++++ b/drivers/usb/serial/ti_usb_3410_5052.c
+@@ -97,7 +97,7 @@ struct ti_device {
+ /* Function Declarations */
+
+ static int ti_startup(struct usb_serial *serial);
+-static void ti_shutdown(struct usb_serial *serial);
++static void ti_release(struct usb_serial *serial);
+ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port,
+ struct file *file);
+ static void ti_close(struct tty_struct *tty, struct usb_serial_port *port,
+@@ -231,7 +231,7 @@ static struct usb_serial_driver ti_1port
+ .id_table = ti_id_table_3410,
+ .num_ports = 1,
+ .attach = ti_startup,
+- .shutdown = ti_shutdown,
++ .release = ti_release,
+ .open = ti_open,
+ .close = ti_close,
+ .write = ti_write,
+@@ -259,7 +259,7 @@ static struct usb_serial_driver ti_2port
+ .id_table = ti_id_table_5052,
+ .num_ports = 2,
+ .attach = ti_startup,
+- .shutdown = ti_shutdown,
++ .release = ti_release,
+ .open = ti_open,
+ .close = ti_close,
+ .write = ti_write,
+@@ -474,7 +474,7 @@ free_tdev:
+ }
+
+
+-static void ti_shutdown(struct usb_serial *serial)
++static void ti_release(struct usb_serial *serial)
+ {
+ int i;
+ struct ti_device *tdev = usb_get_serial_data(serial);
+@@ -487,12 +487,10 @@ static void ti_shutdown(struct usb_seria
+ if (tport) {
+ ti_buf_free(tport->tp_write_buf);
+ kfree(tport);
+- usb_set_serial_port_data(serial->port[i], NULL);
+ }
+ }
+
+ kfree(tdev);
+- usb_set_serial_data(serial, NULL);
+ }
+
+
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -141,6 +141,14 @@ static void destroy_serial(struct kref *
+ if (serial->minor != SERIAL_TTY_NO_MINOR)
+ return_serial(serial);
+
++ serial->type->release(serial);
++
++ for (i = 0; i < serial->num_ports; ++i) {
++ port = serial->port[i];
++ if (port)
++ 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 */
+@@ -148,9 +156,8 @@ static void destroy_serial(struct kref *
+ for (i = serial->num_ports;
+ i < serial->num_port_pointers; ++i) {
+ port = serial->port[i];
+- if (!port)
+- continue;
+- port_free(port);
++ if (port)
++ port_free(port);
+ }
+ }
+
+@@ -1062,10 +1069,6 @@ void usb_serial_disconnect(struct usb_in
+ serial->disconnected = 1;
+ mutex_unlock(&serial->disc_mutex);
+
+- /* Unfortunately, many of the sub-drivers expect the port structures
+- * to exist when their shutdown method is called, so we have to go
+- * through this awkward two-step unregistration procedure.
+- */
+ for (i = 0; i < serial->num_ports; ++i) {
+ port = serial->port[i];
+ if (port) {
+@@ -1079,14 +1082,7 @@ void usb_serial_disconnect(struct usb_in
+ device_del(&port->dev);
+ }
+ }
+- serial->type->shutdown(serial);
+- for (i = 0; i < serial->num_ports; ++i) {
+- port = serial->port[i];
+- if (port) {
+- put_device(&port->dev);
+- serial->port[i] = NULL;
+- }
+- }
++ serial->type->disconnect(serial);
+
+ /* let the last holder of this object
+ * cause it to be cleaned up */
+@@ -1262,7 +1258,8 @@ static void fixup_generic(struct usb_ser
+ set_to_generic_if_null(device, chars_in_buffer);
+ set_to_generic_if_null(device, read_bulk_callback);
+ set_to_generic_if_null(device, write_bulk_callback);
+- set_to_generic_if_null(device, shutdown);
++ set_to_generic_if_null(device, disconnect);
++ set_to_generic_if_null(device, release);
+ }
+
+ int usb_serial_register(struct usb_serial_driver *driver)
+--- a/drivers/usb/serial/visor.c
++++ b/drivers/usb/serial/visor.c
+@@ -48,7 +48,7 @@ static void visor_unthrottle(struct tty_
+ static int visor_probe(struct usb_serial *serial,
+ const struct usb_device_id *id);
+ static int visor_calc_num_ports(struct usb_serial *serial);
+-static void visor_shutdown(struct usb_serial *serial);
++static void visor_release(struct usb_serial *serial);
+ static void visor_write_bulk_callback(struct urb *urb);
+ static void visor_read_bulk_callback(struct urb *urb);
+ static void visor_read_int_callback(struct urb *urb);
+@@ -203,7 +203,7 @@ static struct usb_serial_driver handspri
+ .attach = treo_attach,
+ .probe = visor_probe,
+ .calc_num_ports = visor_calc_num_ports,
+- .shutdown = visor_shutdown,
++ .release = visor_release,
+ .write = visor_write,
+ .write_room = visor_write_room,
+ .write_bulk_callback = visor_write_bulk_callback,
+@@ -228,7 +228,7 @@ static struct usb_serial_driver clie_5_d
+ .attach = clie_5_attach,
+ .probe = visor_probe,
+ .calc_num_ports = visor_calc_num_ports,
+- .shutdown = visor_shutdown,
++ .release = visor_release,
+ .write = visor_write,
+ .write_room = visor_write_room,
+ .write_bulk_callback = visor_write_bulk_callback,
+@@ -920,7 +920,7 @@ static int clie_5_attach(struct usb_seri
+ return generic_startup(serial);
+ }
+
+-static void visor_shutdown(struct usb_serial *serial)
++static void visor_release(struct usb_serial *serial)
+ {
+ struct visor_private *priv;
+ int i;
+@@ -929,10 +929,7 @@ static void visor_shutdown(struct usb_se
+
+ for (i = 0; i < serial->num_ports; i++) {
+ priv = usb_get_serial_port_data(serial->port[i]);
+- if (priv) {
+- usb_set_serial_port_data(serial->port[i], NULL);
+- kfree(priv);
+- }
++ kfree(priv);
+ }
+ }
+
+--- a/drivers/usb/serial/whiteheat.c
++++ b/drivers/usb/serial/whiteheat.c
+@@ -144,7 +144,7 @@ static int whiteheat_firmware_attach(st
+
+ /* function prototypes for the Connect Tech WhiteHEAT serial converter */
+ static int whiteheat_attach(struct usb_serial *serial);
+-static void whiteheat_shutdown(struct usb_serial *serial);
++static void whiteheat_release(struct usb_serial *serial);
+ static int whiteheat_open(struct tty_struct *tty,
+ struct usb_serial_port *port, struct file *filp);
+ static void whiteheat_close(struct tty_struct *tty,
+@@ -190,7 +190,7 @@ static struct usb_serial_driver whitehea
+ .id_table = id_table_std,
+ .num_ports = 4,
+ .attach = whiteheat_attach,
+- .shutdown = whiteheat_shutdown,
++ .release = whiteheat_release,
+ .open = whiteheat_open,
+ .close = whiteheat_close,
+ .write = whiteheat_write,
+@@ -618,7 +618,7 @@ no_command_buffer:
+ }
+
+
+-static void whiteheat_shutdown(struct usb_serial *serial)
++static void whiteheat_release(struct usb_serial *serial)
+ {
+ struct usb_serial_port *command_port;
+ struct usb_serial_port *port;
+--- a/include/linux/usb/serial.h
++++ b/include/linux/usb/serial.h
+@@ -181,8 +181,10 @@ static inline void usb_set_serial_data(s
+ * This will be called when the struct usb_serial structure is fully set
+ * set up. Do any local initialization of the device, or any private
+ * memory structure allocation at this point in time.
+- * @shutdown: pointer to the driver's shutdown function. This will be
+- * called when the device is removed from the system.
++ * @disconnect: pointer to the driver's disconnect function. This will be
++ * called when the device is unplugged or unbound from the driver.
++ * @release: pointer to the driver's release function. This will be called
++ * when the usb_serial data structure is about to be destroyed.
+ * @usb_driver: pointer to the struct usb_driver that controls this
+ * device. This is necessary to allow dynamic ids to be added to
+ * the driver from sysfs.
+@@ -212,7 +214,8 @@ struct usb_serial_driver {
+ int (*attach)(struct usb_serial *serial);
+ int (*calc_num_ports) (struct usb_serial *serial);
+
+- void (*shutdown)(struct usb_serial *serial);
++ void (*disconnect)(struct usb_serial *serial);
++ void (*release)(struct usb_serial *serial);
+
+ int (*port_probe)(struct usb_serial_port *port);
+ int (*port_remove)(struct usb_serial_port *port);
+@@ -292,7 +295,8 @@ extern void usb_serial_generic_read_bulk
+ extern void usb_serial_generic_write_bulk_callback(struct urb *urb);
+ extern void usb_serial_generic_throttle(struct tty_struct *tty);
+ extern void usb_serial_generic_unthrottle(struct tty_struct *tty);
+-extern void usb_serial_generic_shutdown(struct usb_serial *serial);
++extern void usb_serial_generic_disconnect(struct usb_serial *serial);
++extern void usb_serial_generic_release(struct usb_serial *serial);
+ extern int usb_serial_generic_register(int debug);
+ extern void usb_serial_generic_deregister(void);
+