ehci-fix-lucid-nohandoff-pci-quirk-to-be-more-generic-with-bios-versions.patch
ehci-add-yet-another-lucid-nohandoff-pci-quirk.patch
usb-storage-add-unusual_devs-entry-for-casio-ex-n1-digital-camera.patch
+usb-usb-wwan-fix-multiple-memory-leaks-in-error-paths.patch
usb-hub-send-clear_tt_buffer_complete-events-when-canceling-tt-clear-work.patch
+usb-ch341-fix-port-data-memory-leak.patch
+usb-digi_acceleport-fix-port-data-memory-leak.patch
+usb-ipw-fix-interface-data-memory-leak-in-error-path.patch
+usb-mct_u232-fix-port-data-memory-leak.patch
+usb-mct_u232-fix-broken-close.patch
+usb-option-fix-interface-data-memory-leak-in-error-path.patch
--- /dev/null
+From 456c5be56ed070a4d883c60b587bcc1c97a8cf3e Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:03 +0200
+Subject: USB: ch341: fix port-data memory leak
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit 456c5be56ed070a4d883c60b587bcc1c97a8cf3e upstream.
+
+Fix port-data memory leak by moving port data allocation to port_probe
+and actually implementing deallocation.
+
+Note that this driver has never even bothered to try to deallocate it's
+port data...
+
+Compile-only tested.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/ch341.c | 23 ++++++++++++++++-------
+ 1 file changed, 16 insertions(+), 7 deletions(-)
+
+--- a/drivers/usb/serial/ch341.c
++++ b/drivers/usb/serial/ch341.c
+@@ -241,13 +241,11 @@ out: kfree(buffer);
+ return r;
+ }
+
+-/* allocate private data */
+-static int ch341_attach(struct usb_serial *serial)
++static int ch341_port_probe(struct usb_serial_port *port)
+ {
+ struct ch341_private *priv;
+ int r;
+
+- /* private data */
+ priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+@@ -257,17 +255,27 @@ static int ch341_attach(struct usb_seria
+ priv->baud_rate = DEFAULT_BAUD_RATE;
+ priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
+
+- r = ch341_configure(serial->dev, priv);
++ r = ch341_configure(port->serial->dev, priv);
+ if (r < 0)
+ goto error;
+
+- usb_set_serial_port_data(serial->port[0], priv);
++ usb_set_serial_port_data(port, priv);
+ return 0;
+
+ error: kfree(priv);
+ return r;
+ }
+
++static int ch341_port_remove(struct usb_serial_port *port)
++{
++ struct ch341_private *priv;
++
++ priv = usb_get_serial_port_data(port);
++ kfree(priv);
++
++ return 0;
++}
++
+ static int ch341_carrier_raised(struct usb_serial_port *port)
+ {
+ struct ch341_private *priv = usb_get_serial_port_data(port);
+@@ -303,7 +311,7 @@ static void ch341_close(struct usb_seria
+ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
+ {
+ struct usb_serial *serial = port->serial;
+- struct ch341_private *priv = usb_get_serial_port_data(serial->port[0]);
++ struct ch341_private *priv = usb_get_serial_port_data(port);
+ int r;
+
+ priv->baud_rate = DEFAULT_BAUD_RATE;
+@@ -606,7 +614,8 @@ static struct usb_serial_driver ch341_de
+ .tiocmget = ch341_tiocmget,
+ .tiocmset = ch341_tiocmset,
+ .read_int_callback = ch341_read_int_callback,
+- .attach = ch341_attach,
++ .port_probe = ch341_port_probe,
++ .port_remove = ch341_port_remove,
+ .reset_resume = ch341_reset_resume,
+ };
+
--- /dev/null
+From fb44ff854e148bc5c5982dad32da98b7a0989d2d Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:04 +0200
+Subject: USB: digi_acceleport: fix port-data memory leak
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit fb44ff854e148bc5c5982dad32da98b7a0989d2d upstream.
+
+Fix port-data memory leak by moving port data allocation and
+deallocation to port_probe and port_remove.
+
+Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no
+driver is bound) the port private data is no longer freed at release as
+it is no longer accessible.
+
+Note that the oob port is never registered as a port device and should
+thus be handled in attach and release.
+
+Compile-only tested.
+
+Cc: Peter Berger <pberger@brimson.com>
+Cc: Al Borchers <alborchers@steinerpoint.com>
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/digi_acceleport.c | 117 ++++++++++++++++++++---------------
+ 1 file changed, 67 insertions(+), 50 deletions(-)
+
+--- a/drivers/usb/serial/digi_acceleport.c
++++ b/drivers/usb/serial/digi_acceleport.c
+@@ -244,6 +244,8 @@ static int digi_startup_device(struct us
+ static int digi_startup(struct usb_serial *serial);
+ static void digi_disconnect(struct usb_serial *serial);
+ static void digi_release(struct usb_serial *serial);
++static int digi_port_probe(struct usb_serial_port *port);
++static int digi_port_remove(struct usb_serial_port *port);
+ 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);
+@@ -298,6 +300,8 @@ static struct usb_serial_driver digi_acc
+ .attach = digi_startup,
+ .disconnect = digi_disconnect,
+ .release = digi_release,
++ .port_probe = digi_port_probe,
++ .port_remove = digi_port_remove,
+ };
+
+ static struct usb_serial_driver digi_acceleport_4_device = {
+@@ -324,6 +328,8 @@ static struct usb_serial_driver digi_acc
+ .attach = digi_startup,
+ .disconnect = digi_disconnect,
+ .release = digi_release,
++ .port_probe = digi_port_probe,
++ .port_remove = digi_port_remove,
+ };
+
+ static struct usb_serial_driver * const serial_drivers[] = {
+@@ -1237,59 +1243,50 @@ static int digi_startup_device(struct us
+ return ret;
+ }
+
++static int digi_port_init(struct usb_serial_port *port, unsigned port_num)
++{
++ struct digi_port *priv;
++
++ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ spin_lock_init(&priv->dp_port_lock);
++ priv->dp_port_num = port_num;
++ init_waitqueue_head(&priv->dp_modem_change_wait);
++ init_waitqueue_head(&priv->dp_transmit_idle_wait);
++ init_waitqueue_head(&priv->dp_flush_wait);
++ init_waitqueue_head(&priv->dp_close_wait);
++ INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock);
++ priv->dp_port = port;
++
++ init_waitqueue_head(&port->write_wait);
++
++ usb_set_serial_port_data(port, priv);
++
++ return 0;
++}
+
+ static int digi_startup(struct usb_serial *serial)
+ {
+-
+- int i;
+- struct digi_port *priv;
+ struct digi_serial *serial_priv;
++ int ret;
+
+- /* allocate the private data structures for all ports */
+- /* number of regular ports + 1 for the out-of-band port */
+- for (i = 0; i < serial->type->num_ports + 1; i++) {
+- /* allocate port private structure */
+- priv = kmalloc(sizeof(struct digi_port), GFP_KERNEL);
+- if (priv == NULL) {
+- while (--i >= 0)
+- kfree(usb_get_serial_port_data(serial->port[i]));
+- return 1; /* error */
+- }
++ serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL);
++ if (!serial_priv)
++ return -ENOMEM;
+
+- /* initialize port private structure */
+- spin_lock_init(&priv->dp_port_lock);
+- priv->dp_port_num = i;
+- priv->dp_out_buf_len = 0;
+- priv->dp_write_urb_in_use = 0;
+- priv->dp_modem_signals = 0;
+- init_waitqueue_head(&priv->dp_modem_change_wait);
+- priv->dp_transmit_idle = 0;
+- init_waitqueue_head(&priv->dp_transmit_idle_wait);
+- priv->dp_throttled = 0;
+- priv->dp_throttle_restart = 0;
+- init_waitqueue_head(&priv->dp_flush_wait);
+- init_waitqueue_head(&priv->dp_close_wait);
+- INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock);
+- priv->dp_port = serial->port[i];
+- /* initialize write wait queue for this port */
+- init_waitqueue_head(&serial->port[i]->write_wait);
+-
+- usb_set_serial_port_data(serial->port[i], priv);
+- }
+-
+- /* allocate serial private structure */
+- serial_priv = kmalloc(sizeof(struct digi_serial), GFP_KERNEL);
+- if (serial_priv == NULL) {
+- for (i = 0; i < serial->type->num_ports + 1; i++)
+- kfree(usb_get_serial_port_data(serial->port[i]));
+- return 1; /* error */
+- }
+-
+- /* initialize serial private structure */
+ spin_lock_init(&serial_priv->ds_serial_lock);
+ serial_priv->ds_oob_port_num = serial->type->num_ports;
+ serial_priv->ds_oob_port = serial->port[serial_priv->ds_oob_port_num];
+- serial_priv->ds_device_started = 0;
++
++ ret = digi_port_init(serial_priv->ds_oob_port,
++ serial_priv->ds_oob_port_num);
++ if (ret) {
++ kfree(serial_priv);
++ return ret;
++ }
++
+ usb_set_serial_data(serial, serial_priv);
+
+ return 0;
+@@ -1310,15 +1307,35 @@ static void digi_disconnect(struct usb_s
+
+ static void digi_release(struct usb_serial *serial)
+ {
+- int i;
++ struct digi_serial *serial_priv;
++ struct digi_port *priv;
++
++ serial_priv = usb_get_serial_data(serial);
++
++ priv = usb_get_serial_port_data(serial_priv->ds_oob_port);
++ kfree(priv);
+
+- /* free the private data structures for all ports */
+- /* number of regular ports + 1 for the out-of-band port */
+- for (i = 0; i < serial->type->num_ports + 1; i++)
+- kfree(usb_get_serial_port_data(serial->port[i]));
+- kfree(usb_get_serial_data(serial));
++ kfree(serial_priv);
+ }
+
++static int digi_port_probe(struct usb_serial_port *port)
++{
++ unsigned port_num;
++
++ port_num = port->number - port->serial->minor;
++
++ return digi_port_init(port, port_num);
++}
++
++static int digi_port_remove(struct usb_serial_port *port)
++{
++ struct digi_port *priv;
++
++ priv = usb_get_serial_port_data(port);
++ kfree(priv);
++
++ return 0;
++}
+
+ static void digi_read_bulk_callback(struct urb *urb)
+ {
--- /dev/null
+From a997448c89905b80aa4022f734f03685e733d711 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 15:42:39 +0200
+Subject: USB: ipw: fix interface-data memory leak in error path
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit a997448c89905b80aa4022f734f03685e733d711 upstream.
+
+Move interface data allocation to attach so that it is deallocated
+should usb-serial probe fail.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/ipw.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/usb/serial/ipw.c
++++ b/drivers/usb/serial/ipw.c
+@@ -209,8 +209,7 @@ static int ipw_open(struct tty_struct *t
+ return 0;
+ }
+
+-/* fake probe - only to allocate data structures */
+-static int ipw_probe(struct usb_serial *serial, const struct usb_device_id *id)
++static int ipw_attach(struct usb_serial *serial)
+ {
+ struct usb_wwan_intf_private *data;
+
+@@ -310,7 +309,7 @@ static struct usb_serial_driver ipw_devi
+ .num_ports = 1,
+ .open = ipw_open,
+ .close = ipw_close,
+- .probe = ipw_probe,
++ .attach = ipw_attach,
+ .release = ipw_release,
+ .port_probe = usb_wwan_port_probe,
+ .port_remove = usb_wwan_port_remove,
--- /dev/null
+From 5260e458f5eff269a43e4f1e9c47186c57b88ddb Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:14 +0200
+Subject: USB: mct_u232: fix broken close
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit 5260e458f5eff269a43e4f1e9c47186c57b88ddb upstream.
+
+Make sure generic close is called at close.
+
+The driver relies on the generic write implementation but did not call
+generic close.
+
+Note that the call to kill the read urb is not redundant, as mct_u232
+uses an interrupt urb from the second port as the read urb and that
+generic close therefore fails to kill it.
+
+Compile-only tested.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/mct_u232.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+--- a/drivers/usb/serial/mct_u232.c
++++ b/drivers/usb/serial/mct_u232.c
+@@ -524,12 +524,14 @@ static void mct_u232_dtr_rts(struct usb_
+
+ static void mct_u232_close(struct usb_serial_port *port)
+ {
+- if (port->serial->dev) {
+- /* shutdown our urbs */
+- usb_kill_urb(port->write_urb);
+- usb_kill_urb(port->read_urb);
+- usb_kill_urb(port->interrupt_in_urb);
+- }
++ /*
++ * Must kill the read urb as it is actually an interrupt urb, which
++ * generic close thus fails to kill.
++ */
++ usb_kill_urb(port->read_urb);
++ usb_kill_urb(port->interrupt_in_urb);
++
++ usb_serial_generic_close(port);
+ } /* mct_u232_close */
+
+
--- /dev/null
+From a8f2ae7a3aa59079d7e7e1ddf5007f03532f458c Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:13 +0200
+Subject: USB: mct_u232: fix port-data memory leak
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit a8f2ae7a3aa59079d7e7e1ddf5007f03532f458c upstream.
+
+Fix port-data memory leak by moving port data allocation and
+deallocation to port_probe and port_remove.
+
+Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no
+driver is bound) the port private data is no longer freed at release as
+it is no longer accessible.
+
+Note that the write waitqueue was initialised but never used.
+
+Compile-only tested.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/mct_u232.c | 45 +++++++++++++++++++++++-------------------
+ 1 file changed, 25 insertions(+), 20 deletions(-)
+
+--- a/drivers/usb/serial/mct_u232.c
++++ b/drivers/usb/serial/mct_u232.c
+@@ -51,7 +51,8 @@ static bool debug;
+ * Function prototypes
+ */
+ static int mct_u232_startup(struct usb_serial *serial);
+-static void mct_u232_release(struct usb_serial *serial);
++static int mct_u232_port_probe(struct usb_serial_port *port);
++static int mct_u232_port_remove(struct usb_serial_port *remove);
+ static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port);
+ static void mct_u232_close(struct usb_serial_port *port);
+ static void mct_u232_dtr_rts(struct usb_serial_port *port, int on);
+@@ -101,7 +102,8 @@ static struct usb_serial_driver mct_u232
+ .tiocmget = mct_u232_tiocmget,
+ .tiocmset = mct_u232_tiocmset,
+ .attach = mct_u232_startup,
+- .release = mct_u232_release,
++ .port_probe = mct_u232_port_probe,
++ .port_remove = mct_u232_port_remove,
+ .ioctl = mct_u232_ioctl,
+ .get_icount = mct_u232_get_icount,
+ };
+@@ -392,18 +394,8 @@ static void mct_u232_msr_to_state(unsign
+
+ static int mct_u232_startup(struct usb_serial *serial)
+ {
+- struct mct_u232_private *priv;
+ struct usb_serial_port *port, *rport;
+
+- priv = kzalloc(sizeof(struct mct_u232_private), GFP_KERNEL);
+- if (!priv)
+- return -ENOMEM;
+- spin_lock_init(&priv->lock);
+- init_waitqueue_head(&priv->msr_wait);
+- usb_set_serial_port_data(serial->port[0], priv);
+-
+- init_waitqueue_head(&serial->port[0]->write_wait);
+-
+ /* Puh, that's dirty */
+ port = serial->port[0];
+ rport = serial->port[1];
+@@ -416,18 +408,31 @@ static int mct_u232_startup(struct usb_s
+ return 0;
+ } /* mct_u232_startup */
+
++static int mct_u232_port_probe(struct usb_serial_port *port)
++{
++ struct mct_u232_private *priv;
++
++ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
+
+-static void mct_u232_release(struct usb_serial *serial)
++ spin_lock_init(&priv->lock);
++ init_waitqueue_head(&priv->msr_wait);
++
++ usb_set_serial_port_data(port, priv);
++
++ return 0;
++}
++
++static int mct_u232_port_remove(struct usb_serial_port *port)
+ {
+ struct mct_u232_private *priv;
+- int i;
+
+- 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]);
+- kfree(priv);
+- }
+-} /* mct_u232_release */
++ priv = usb_get_serial_port_data(port);
++ kfree(priv);
++
++ return 0;
++}
+
+ static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port)
+ {
--- /dev/null
+From c2dd4a8eac7821fed2c2d19e4607d0986b53b0fe Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 15:42:40 +0200
+Subject: USB: option: fix interface-data memory leak in error path
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit c2dd4a8eac7821fed2c2d19e4607d0986b53b0fe upstream.
+
+Move interface data allocation to attach so that it is deallocated
+should usb-serial probe fail.
+
+Note that the usb device id is stored at probe so that it can be used
+in attach to determine send-setup blacklisting.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/option.c | 21 +++++++++++++++++++--
+ 1 file changed, 19 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -47,6 +47,7 @@
+ /* Function prototypes */
+ static int option_probe(struct usb_serial *serial,
+ const struct usb_device_id *id);
++static int option_attach(struct usb_serial *serial);
+ static void option_release(struct usb_serial *serial);
+ static int option_send_setup(struct usb_serial_port *port);
+ static void option_instat_callback(struct urb *urb);
+@@ -1244,6 +1245,7 @@ static struct usb_serial_driver option_1
+ .tiocmget = usb_wwan_tiocmget,
+ .tiocmset = usb_wwan_tiocmset,
+ .ioctl = usb_wwan_ioctl,
++ .attach = option_attach,
+ .release = option_release,
+ .port_probe = usb_wwan_port_probe,
+ .port_remove = usb_wwan_port_remove,
+@@ -1293,8 +1295,6 @@ static bool is_blacklisted(const u8 ifnu
+ static int option_probe(struct usb_serial *serial,
+ const struct usb_device_id *id)
+ {
+- struct usb_wwan_intf_private *data;
+- struct option_private *priv;
+ struct usb_interface_descriptor *iface_desc =
+ &serial->interface->cur_altsetting->desc;
+ struct usb_device_descriptor *dev_desc = &serial->dev->descriptor;
+@@ -1332,6 +1332,19 @@ static int option_probe(struct usb_seria
+ iface_desc->bInterfaceClass != USB_CLASS_CDC_DATA)
+ return -ENODEV;
+
++ /* Store device id so we can use it during attach. */
++ usb_set_serial_data(serial, (void *)id);
++
++ return 0;
++}
++
++static int option_attach(struct usb_serial *serial)
++{
++ struct usb_interface_descriptor *iface_desc;
++ const struct usb_device_id *id;
++ struct usb_wwan_intf_private *data;
++ struct option_private *priv;
++
+ data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+@@ -1342,6 +1355,10 @@ static int option_probe(struct usb_seria
+ return -ENOMEM;
+ }
+
++ /* Retrieve device id stored at probe. */
++ id = usb_get_serial_data(serial);
++ iface_desc = &serial->interface->cur_altsetting->desc;
++
+ priv->bInterfaceNumber = iface_desc->bInterfaceNumber;
+ data->private = priv;
+
--- /dev/null
+From b8f0e82044c9ba40e92340c8a6d47d6bd6d819bc Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:16 +0200
+Subject: USB: usb-wwan: fix multiple memory leaks in error paths
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit b8f0e82044c9ba40e92340c8a6d47d6bd6d819bc upstream.
+
+Fix port-data memory leak in usb-serial probe error path by moving port
+data allocation to port_probe.
+
+Since commit a1028f0abf ("usb: usb_wwan: replace release and disconnect
+with a port_remove hook") port data is deallocated in port_remove. This
+leaves a possibility for memory leaks if usb-serial probe fails after
+attach but before the port in question has been successfully registered.
+
+Note that this patch also fixes two additional memory leaks in the error
+path of attach should port initialisation fail for any port as the urbs
+were never freed and neither was the data of any of the successfully
+initialised ports.
+
+Compile-only tested.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/ipw.c | 2
+ drivers/usb/serial/option.c | 2
+ drivers/usb/serial/qcserial.c | 2
+ drivers/usb/serial/usb-wwan.h | 2
+ drivers/usb/serial/usb_wwan.c | 136 +++++++++++++++++-------------------------
+ 5 files changed, 60 insertions(+), 84 deletions(-)
+
+--- a/drivers/usb/serial/ipw.c
++++ b/drivers/usb/serial/ipw.c
+@@ -311,8 +311,8 @@ static struct usb_serial_driver ipw_devi
+ .open = ipw_open,
+ .close = ipw_close,
+ .probe = ipw_probe,
+- .attach = usb_wwan_startup,
+ .release = ipw_release,
++ .port_probe = usb_wwan_port_probe,
+ .port_remove = usb_wwan_port_remove,
+ .dtr_rts = ipw_dtr_rts,
+ .write = usb_wwan_write,
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -1244,8 +1244,8 @@ static struct usb_serial_driver option_1
+ .tiocmget = usb_wwan_tiocmget,
+ .tiocmset = usb_wwan_tiocmset,
+ .ioctl = usb_wwan_ioctl,
+- .attach = usb_wwan_startup,
+ .release = option_release,
++ .port_probe = usb_wwan_port_probe,
+ .port_remove = usb_wwan_port_remove,
+ .read_int_callback = option_instat_callback,
+ #ifdef CONFIG_PM
+--- a/drivers/usb/serial/qcserial.c
++++ b/drivers/usb/serial/qcserial.c
+@@ -287,8 +287,8 @@ static struct usb_serial_driver qcdevice
+ .write = usb_wwan_write,
+ .write_room = usb_wwan_write_room,
+ .chars_in_buffer = usb_wwan_chars_in_buffer,
+- .attach = usb_wwan_startup,
+ .release = qc_release,
++ .port_probe = usb_wwan_port_probe,
+ .port_remove = usb_wwan_port_remove,
+ #ifdef CONFIG_PM
+ .suspend = usb_wwan_suspend,
+--- a/drivers/usb/serial/usb-wwan.h
++++ b/drivers/usb/serial/usb-wwan.h
+@@ -8,7 +8,7 @@
+ extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on);
+ extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port);
+ extern void usb_wwan_close(struct usb_serial_port *port);
+-extern int usb_wwan_startup(struct usb_serial *serial);
++extern int usb_wwan_port_probe(struct usb_serial_port *port);
+ extern int usb_wwan_port_remove(struct usb_serial_port *port);
+ extern int usb_wwan_write_room(struct tty_struct *tty);
+ extern void usb_wwan_set_termios(struct tty_struct *tty,
+--- a/drivers/usb/serial/usb_wwan.c
++++ b/drivers/usb/serial/usb_wwan.c
+@@ -447,10 +447,12 @@ void usb_wwan_close(struct usb_serial_po
+ EXPORT_SYMBOL(usb_wwan_close);
+
+ /* Helper functions used by usb_wwan_setup_urbs */
+-static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint,
++static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port,
++ int endpoint,
+ int dir, void *ctx, char *buf, int len,
+ void (*callback) (struct urb *))
+ {
++ struct usb_serial *serial = port->serial;
+ struct urb *urb;
+
+ if (endpoint == -1)
+@@ -470,100 +472,74 @@ static struct urb *usb_wwan_setup_urb(st
+ return urb;
+ }
+
+-/* Setup urbs */
+-static void usb_wwan_setup_urbs(struct usb_serial *serial)
++int usb_wwan_port_probe(struct usb_serial_port *port)
+ {
+- int i, j;
+- struct usb_serial_port *port;
+- struct usb_wwan_port_private *portdata;
+-
+- for (i = 0; i < serial->num_ports; i++) {
+- port = serial->port[i];
+- portdata = usb_get_serial_port_data(port);
+-
+- /* Do indat endpoints first */
+- for (j = 0; j < N_IN_URB; ++j) {
+- portdata->in_urbs[j] = usb_wwan_setup_urb(serial,
+- port->
+- bulk_in_endpointAddress,
+- USB_DIR_IN,
+- port,
+- portdata->
+- in_buffer[j],
+- IN_BUFLEN,
+- usb_wwan_indat_callback);
+- }
+-
+- /* outdat endpoints */
+- for (j = 0; j < N_OUT_URB; ++j) {
+- portdata->out_urbs[j] = usb_wwan_setup_urb(serial,
+- port->
+- bulk_out_endpointAddress,
+- USB_DIR_OUT,
+- port,
+- portdata->
+- out_buffer
+- [j],
+- OUT_BUFLEN,
+- usb_wwan_outdat_callback);
+- }
+- }
+-}
+-
+-int usb_wwan_startup(struct usb_serial *serial)
+-{
+- int i, j, err;
+- struct usb_serial_port *port;
+ struct usb_wwan_port_private *portdata;
++ struct urb *urb;
+ u8 *buffer;
++ int err;
++ int i;
+
+- /* Now setup per port private data */
+- for (i = 0; i < serial->num_ports; i++) {
+- port = serial->port[i];
+- portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
+- if (!portdata) {
+- dbg("%s: kmalloc for usb_wwan_port_private (%d) failed!.",
+- __func__, i);
+- return 1;
+- }
+- init_usb_anchor(&portdata->delayed);
+-
+- for (j = 0; j < N_IN_URB; j++) {
+- buffer = (u8 *) __get_free_page(GFP_KERNEL);
+- if (!buffer)
+- goto bail_out_error;
+- portdata->in_buffer[j] = buffer;
+- }
+-
+- for (j = 0; j < N_OUT_URB; j++) {
+- buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
+- if (!buffer)
+- goto bail_out_error2;
+- portdata->out_buffer[j] = buffer;
+- }
++ portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
++ if (!portdata)
++ return -ENOMEM;
++
++ init_usb_anchor(&portdata->delayed);
++
++ for (i = 0; i < N_IN_URB; i++) {
++ buffer = (u8 *)__get_free_page(GFP_KERNEL);
++ if (!buffer)
++ goto bail_out_error;
++ portdata->in_buffer[i] = buffer;
++
++ urb = usb_wwan_setup_urb(port, port->bulk_in_endpointAddress,
++ USB_DIR_IN, port,
++ buffer, IN_BUFLEN,
++ usb_wwan_indat_callback);
++ portdata->in_urbs[i] = urb;
++ }
++ for (i = 0; i < N_OUT_URB; i++) {
++ if (port->bulk_out_endpointAddress == -1)
++ continue;
+
+- usb_set_serial_port_data(port, portdata);
++ buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
++ if (!buffer)
++ goto bail_out_error2;
++ portdata->out_buffer[i] = buffer;
++
++ urb = usb_wwan_setup_urb(port, port->bulk_out_endpointAddress,
++ USB_DIR_OUT, port,
++ buffer, OUT_BUFLEN,
++ usb_wwan_outdat_callback);
++ portdata->out_urbs[i] = urb;
++ }
+
+- if (!port->interrupt_in_urb)
+- continue;
++ usb_set_serial_port_data(port, portdata);
++
++ if (port->interrupt_in_urb) {
+ err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+ if (err)
+- dbg("%s: submit irq_in urb failed %d", __func__, err);
++ dev_dbg(&port->dev, "%s: submit irq_in urb failed %d\n",
++ __func__, err);
+ }
+- usb_wwan_setup_urbs(serial);
++
+ return 0;
+
+ bail_out_error2:
+- for (j = 0; j < N_OUT_URB; j++)
+- kfree(portdata->out_buffer[j]);
++ for (i = 0; i < N_OUT_URB; i++) {
++ usb_free_urb(portdata->out_urbs[i]);
++ kfree(portdata->out_buffer[i]);
++ }
+ bail_out_error:
+- for (j = 0; j < N_IN_URB; j++)
+- if (portdata->in_buffer[j])
+- free_page((unsigned long)portdata->in_buffer[j]);
++ for (i = 0; i < N_IN_URB; i++) {
++ usb_free_urb(portdata->in_urbs[i]);
++ free_page((unsigned long)portdata->in_buffer[i]);
++ }
+ kfree(portdata);
+- return 1;
++
++ return -ENOMEM;
+ }
+-EXPORT_SYMBOL(usb_wwan_startup);
++EXPORT_SYMBOL_GPL(usb_wwan_port_probe);
+
+ int usb_wwan_port_remove(struct usb_serial_port *port)
+ {