--- /dev/null
+From 677fe555cbfb188af58cce105f4dae9505e58c31 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Thu, 14 Feb 2013 21:01:06 +0100
+Subject: serial: imx: Fix recursive locking bug
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit 677fe555cbfb188af58cce105f4dae9505e58c31 upstream.
+
+commit 9ec1882df2 (tty: serial: imx: console write routing is unsafe
+on SMP) introduced a recursive locking bug in imx_console_write().
+
+The callchain is:
+
+imx_rxint()
+ spin_lock_irqsave(&sport->port.lock,flags);
+ ...
+ uart_handle_sysrq_char();
+ sysrq_function();
+ printk();
+ imx_console_write();
+ spin_lock_irqsave(&sport->port.lock,flags); <--- DEAD
+
+The bad news is that the kernel debugging facilities can dectect the
+problem, but the printks never surface on the serial console for
+obvious reasons.
+
+There is a similar issue with oops_in_progress. If the kernel crashes
+we really don't want to be stuck on the lock and unable to tell what
+happened.
+
+In general most UP originated drivers miss these checks and nobody
+ever notices because CONFIG_PROVE_LOCKING seems to be still ignored by
+a large number of developers.
+
+The solution is to avoid locking in the sysrq case and trylock in the
+oops_in_progress case.
+
+This scheme is used in other drivers as well and it would be nice if
+we could move this to a common place, so the usual copy/paste/modify
+bugs can be avoided.
+
+Now there is another issue with this scheme:
+
+CPU0 CPU1
+printk()
+ rxint()
+ sysrq_detection() -> sets port->sysrq
+ return from interrupt
+ console_write()
+ if (port->sysrq)
+ avoid locking
+
+port->sysrq is reset with the next receive character. So as long as
+the port->sysrq is not reset and this can take an endless amount of
+time if after the break no futher receive character follows, all
+console writes happen unlocked.
+
+While the current writer is protected against other console writers by
+the console sem, it's unprotected against open/close or other
+operations which fiddle with the port. That's what the above mentioned
+commit tried to solve.
+
+That's an issue in all drivers which use that scheme and unfortunately
+there is no easy workaround. The only solution is to have a separate
+indicator port->sysrq_cpu. uart_handle_sysrq_char() then sets it to
+smp_processor_id() before calling into handle_sysrq() and resets it to
+-1 after that. Then change the locking check to:
+
+ if (port->sysrq_cpu == smp_processor_id())
+ locked = 0;
+ else if (oops_in_progress)
+ locked = spin_trylock_irqsave(port->lock, flags);
+ else
+ spin_lock_irqsave(port->lock, flags);
+
+That would force all other cpus into the spin_lock path. Problem
+solved, but that's way beyond the scope of this fix and really wants
+to be implemented in a common function which calls the uart specific
+write function to avoid another gazillion of hard to debug
+copy/paste/modify bugs.
+
+Reported-and-tested-by: Tim Sander <tim@krieglstein.org>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/tty/serial/imx.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+--- a/drivers/tty/serial/imx.c
++++ b/drivers/tty/serial/imx.c
+@@ -1213,8 +1213,14 @@ imx_console_write(struct console *co, co
+ struct imx_port_ucrs old_ucr;
+ unsigned int ucr1;
+ unsigned long flags;
++ int locked = 1;
+
+- spin_lock_irqsave(&sport->port.lock, flags);
++ if (sport->port.sysrq)
++ locked = 0;
++ else if (oops_in_progress)
++ locked = spin_trylock_irqsave(&sport->port.lock, flags);
++ else
++ spin_lock_irqsave(&sport->port.lock, flags);
+
+ /*
+ * First, save UCR1/2/3 and then disable interrupts
+@@ -1241,7 +1247,8 @@ imx_console_write(struct console *co, co
+
+ imx_port_ucrs_restore(&sport->port, &old_ucr);
+
+- spin_unlock_irqrestore(&sport->port.lock, flags);
++ if (locked)
++ spin_unlock_irqrestore(&sport->port.lock, flags);
+ }
+
+ /*
--- /dev/null
+From 85f024401bf80746ae08b7fd5809a9b16accf0b1 Mon Sep 17 00:00:00 2001
+From: Michael Chan <mchan@broadcom.com>
+Date: Tue, 29 Jan 2013 17:54:44 -0800
+Subject: serial_core: Fix type definition for PORT_BRCM_TRUMANAGE.
+
+From: Michael Chan <mchan@broadcom.com>
+
+commit 85f024401bf80746ae08b7fd5809a9b16accf0b1 upstream.
+
+It was mistakenly defined to be 24 instead of the next higher number 25.
+
+Reported-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
+Cc: Stephen Hurd <shurd@broadcom.com>
+Signed-off-by: Michael Chan <mchan@broadcom.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ include/uapi/linux/serial_core.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/include/uapi/linux/serial_core.h
++++ b/include/uapi/linux/serial_core.h
+@@ -50,7 +50,7 @@
+ #define PORT_LPC3220 22 /* NXP LPC32xx SoC "Standard" UART */
+ #define PORT_8250_CIR 23 /* CIR infrared port, has its own driver */
+ #define PORT_XR17V35X 24 /* Exar XR17V35x UARTs */
+-#define PORT_BRCM_TRUMANAGE 24
++#define PORT_BRCM_TRUMANAGE 25
+ #define PORT_MAX_8250 25 /* max port ID */
+
+ /*
alsa-ali5451-remove-irq-enabling-in-pointer-callback.patch
alsa-rme32.c-irq-enabling-after-spin_lock_irq.patch
alsa-aloop-fix-oops-while-pm-resume.patch
+tty-prevent-deadlock-in-n_gsm-driver.patch
+tty-set_termios-set_termiox-should-not-return-eintr.patch
+usb-serial-fix-null-pointer-dereferences-on-disconnect.patch
+serial-imx-fix-recursive-locking-bug.patch
+serial_core-fix-type-definition-for-port_brcm_trumanage.patch
--- /dev/null
+From 4d9b109060f690f5c835130ff54165ae157b3087 Mon Sep 17 00:00:00 2001
+From: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Wed, 30 Jan 2013 11:44:50 +0100
+Subject: tty: Prevent deadlock in n_gsm driver
+
+From: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit 4d9b109060f690f5c835130ff54165ae157b3087 upstream.
+
+This change fixes a deadlock when the multiplexer is closed while there
+are still client side ports open.
+
+When the multiplexer is closed and there are active tty's it tries to
+close them with tty_vhangup. This has a problem though, because
+tty_vhangup needs the tty_lock. This patch changes it to unlock the
+tty_lock before attempting the hangup and relocks afterwards. The
+additional call to tty_port_tty_set is needed because otherwise the
+port stays active because of the reference counter.
+
+This change also exposed another problem that other code paths don't
+expect that the multiplexer could have been closed. This patch also adds
+checks for these cases in the gsmtty_ class of function that could be
+called.
+
+The documentation explicitly states that "first close all virtual ports
+before closing the physical port" but we've found this to not always
+reality in our field situations. The GPRS / UTMS modem sometimes crashes
+and needs a power cycle in that case which means cleanly shutting down
+everything is not always possible. This change makes it much more robust
+for our situation where at least the system is recoverable with this patch
+and doesn't hang in a deadlock situation inside the kernel.
+
+The patch is against the long term support kernel (3.4.27) and should
+apply cleanly to more recent branches. Tested with a Telit GE864-QUADV2
+and Telit HE910 modem.
+
+Signed-off-by: Dirkjan Bussink <dirkjan.bussink@nedap.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/tty/n_gsm.c | 42 +++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 41 insertions(+), 1 deletion(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -1689,6 +1689,8 @@ static inline void dlci_put(struct gsm_d
+ tty_port_put(&dlci->port);
+ }
+
++static void gsm_destroy_network(struct gsm_dlci *dlci);
++
+ /**
+ * gsm_dlci_release - release DLCI
+ * @dlci: DLCI to destroy
+@@ -1702,9 +1704,19 @@ static void gsm_dlci_release(struct gsm_
+ {
+ struct tty_struct *tty = tty_port_tty_get(&dlci->port);
+ if (tty) {
++ mutex_lock(&dlci->mutex);
++ gsm_destroy_network(dlci);
++ mutex_unlock(&dlci->mutex);
++
++ /* tty_vhangup needs the tty_lock, so unlock and
++ relock after doing the hangup. */
++ tty_unlock(tty);
+ tty_vhangup(tty);
++ tty_lock(tty);
++ tty_port_tty_set(&dlci->port, NULL);
+ tty_kref_put(tty);
+ }
++ dlci->state = DLCI_CLOSED;
+ dlci_put(dlci);
+ }
+
+@@ -2947,6 +2959,8 @@ static void gsmtty_close(struct tty_stru
+
+ if (dlci == NULL)
+ return;
++ if (dlci->state == DLCI_CLOSED)
++ return;
+ mutex_lock(&dlci->mutex);
+ gsm_destroy_network(dlci);
+ mutex_unlock(&dlci->mutex);
+@@ -2965,6 +2979,8 @@ out:
+ static void gsmtty_hangup(struct tty_struct *tty)
+ {
+ struct gsm_dlci *dlci = tty->driver_data;
++ if (dlci->state == DLCI_CLOSED)
++ return;
+ tty_port_hangup(&dlci->port);
+ gsm_dlci_begin_close(dlci);
+ }
+@@ -2972,9 +2988,12 @@ static void gsmtty_hangup(struct tty_str
+ static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf,
+ int len)
+ {
++ int sent;
+ struct gsm_dlci *dlci = tty->driver_data;
++ if (dlci->state == DLCI_CLOSED)
++ return -EINVAL;
+ /* Stuff the bytes into the fifo queue */
+- int sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock);
++ sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock);
+ /* Need to kick the channel */
+ gsm_dlci_data_kick(dlci);
+ return sent;
+@@ -2983,18 +3002,24 @@ static int gsmtty_write(struct tty_struc
+ static int gsmtty_write_room(struct tty_struct *tty)
+ {
+ struct gsm_dlci *dlci = tty->driver_data;
++ if (dlci->state == DLCI_CLOSED)
++ return -EINVAL;
+ return TX_SIZE - kfifo_len(dlci->fifo);
+ }
+
+ static int gsmtty_chars_in_buffer(struct tty_struct *tty)
+ {
+ struct gsm_dlci *dlci = tty->driver_data;
++ if (dlci->state == DLCI_CLOSED)
++ return -EINVAL;
+ return kfifo_len(dlci->fifo);
+ }
+
+ static void gsmtty_flush_buffer(struct tty_struct *tty)
+ {
+ struct gsm_dlci *dlci = tty->driver_data;
++ if (dlci->state == DLCI_CLOSED)
++ return;
+ /* Caution needed: If we implement reliable transport classes
+ then the data being transmitted can't simply be junked once
+ it has first hit the stack. Until then we can just blow it
+@@ -3013,6 +3038,8 @@ static void gsmtty_wait_until_sent(struc
+ static int gsmtty_tiocmget(struct tty_struct *tty)
+ {
+ struct gsm_dlci *dlci = tty->driver_data;
++ if (dlci->state == DLCI_CLOSED)
++ return -EINVAL;
+ return dlci->modem_rx;
+ }
+
+@@ -3022,6 +3049,8 @@ static int gsmtty_tiocmset(struct tty_st
+ struct gsm_dlci *dlci = tty->driver_data;
+ unsigned int modem_tx = dlci->modem_tx;
+
++ if (dlci->state == DLCI_CLOSED)
++ return -EINVAL;
+ modem_tx &= ~clear;
+ modem_tx |= set;
+
+@@ -3040,6 +3069,8 @@ static int gsmtty_ioctl(struct tty_struc
+ struct gsm_netconfig nc;
+ int index;
+
++ if (dlci->state == DLCI_CLOSED)
++ return -EINVAL;
+ switch (cmd) {
+ case GSMIOC_ENABLE_NET:
+ if (copy_from_user(&nc, (void __user *)arg, sizeof(nc)))
+@@ -3066,6 +3097,9 @@ static int gsmtty_ioctl(struct tty_struc
+
+ static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old)
+ {
++ struct gsm_dlci *dlci = tty->driver_data;
++ if (dlci->state == DLCI_CLOSED)
++ return;
+ /* For the moment its fixed. In actual fact the speed information
+ for the virtual channel can be propogated in both directions by
+ the RPN control message. This however rapidly gets nasty as we
+@@ -3077,6 +3111,8 @@ static void gsmtty_set_termios(struct tt
+ static void gsmtty_throttle(struct tty_struct *tty)
+ {
+ struct gsm_dlci *dlci = tty->driver_data;
++ if (dlci->state == DLCI_CLOSED)
++ return;
+ if (tty->termios.c_cflag & CRTSCTS)
+ dlci->modem_tx &= ~TIOCM_DTR;
+ dlci->throttled = 1;
+@@ -3087,6 +3123,8 @@ static void gsmtty_throttle(struct tty_s
+ static void gsmtty_unthrottle(struct tty_struct *tty)
+ {
+ struct gsm_dlci *dlci = tty->driver_data;
++ if (dlci->state == DLCI_CLOSED)
++ return;
+ if (tty->termios.c_cflag & CRTSCTS)
+ dlci->modem_tx |= TIOCM_DTR;
+ dlci->throttled = 0;
+@@ -3098,6 +3136,8 @@ static int gsmtty_break_ctl(struct tty_s
+ {
+ struct gsm_dlci *dlci = tty->driver_data;
+ int encode = 0; /* Off */
++ if (dlci->state == DLCI_CLOSED)
++ return -EINVAL;
+
+ if (state == -1) /* "On indefinitely" - we can't encode this
+ properly */
--- /dev/null
+From 183d95cdd834381c594d3aa801c1f9f9c0c54fa9 Mon Sep 17 00:00:00 2001
+From: Oleg Nesterov <oleg@redhat.com>
+Date: Tue, 29 Jan 2013 20:07:41 +0100
+Subject: tty: set_termios/set_termiox should not return -EINTR
+
+From: Oleg Nesterov <oleg@redhat.com>
+
+commit 183d95cdd834381c594d3aa801c1f9f9c0c54fa9 upstream.
+
+See https://bugzilla.redhat.com/show_bug.cgi?id=904907
+read command causes bash to abort with double free or corruption (out).
+
+A simple test-case from Roman:
+
+ // Compile the reproducer and send sigchld ti that process.
+ // EINTR occurs even if SA_RESTART flag is set.
+
+ void handler(int sig)
+ {
+ }
+
+ main()
+ {
+ struct sigaction act;
+ act.sa_handler = handler;
+ act.sa_flags = SA_RESTART;
+ sigaction (SIGCHLD, &act, 0);
+ struct termio ttp;
+ ioctl(0, TCGETA, &ttp);
+ while(1)
+ {
+ if (ioctl(0, TCSETAW, ttp) < 0)
+ {
+ if (errno == EINTR)
+ {
+ fprintf(stderr, "BUG!"); return(1);
+ }
+ }
+ }
+ }
+
+Change set_termios/set_termiox to return -ERESTARTSYS to fix this
+particular problem.
+
+I didn't dare to change other EINTR's in drivers/tty/, but they look
+equally wrong.
+
+Reported-by: Roman Rakus <rrakus@redhat.com>
+Reported-by: Lingzhu Xiang <lxiang@redhat.com>
+Signed-off-by: Oleg Nesterov <oleg@redhat.com>
+Cc: Jiri Slaby <jslaby@suse.cz>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/tty/tty_ioctl.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/tty/tty_ioctl.c
++++ b/drivers/tty/tty_ioctl.c
+@@ -617,7 +617,7 @@ static int set_termios(struct tty_struct
+ if (opt & TERMIOS_WAIT) {
+ tty_wait_until_sent(tty, 0);
+ if (signal_pending(current))
+- return -EINTR;
++ return -ERESTARTSYS;
+ }
+
+ tty_set_termios(tty, &tmp_termios);
+@@ -684,7 +684,7 @@ static int set_termiox(struct tty_struct
+ if (opt & TERMIOS_WAIT) {
+ tty_wait_until_sent(tty, 0);
+ if (signal_pending(current))
+- return -EINTR;
++ return -ERESTARTSYS;
+ }
+
+ mutex_lock(&tty->termios_mutex);
--- /dev/null
+From b2ca699076573c94fee9a73cb0d8645383b602a0 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 13 Feb 2013 17:53:28 +0100
+Subject: USB: serial: fix null-pointer dereferences on disconnect
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit b2ca699076573c94fee9a73cb0d8645383b602a0 upstream.
+
+Make sure serial-driver dtr_rts is called with disc_mutex held after
+checking the disconnected flag.
+
+Due to a bug in the tty layer, dtr_rts may get called after a device has
+been disconnected and the tty-device unregistered. Some drivers have had
+individual checks for disconnect to make sure the disconnected interface
+was not accessed, but this should really be handled in usb-serial core
+(at least until the long-standing tty-bug has been fixed).
+
+Note that the problem has been made more acute with commit 0998d0631001
+("device-core: Ensure drvdata = NULL when no driver is bound") as the
+port data is now also NULL when dtr_rts is called resulting in further
+oopses.
+
+Reported-by: Chris Ruehl <chris.ruehl@gtsys.com.hk>
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/ftdi_sio.c | 20 +++++++++-----------
+ drivers/usb/serial/mct_u232.c | 22 +++++++++-------------
+ drivers/usb/serial/quatech2.c | 18 ++++++++----------
+ drivers/usb/serial/sierra.c | 8 +-------
+ drivers/usb/serial/ssu100.c | 19 ++++++++-----------
+ drivers/usb/serial/usb-serial.c | 14 ++++++++++++--
+ drivers/usb/serial/usb_wwan.c | 8 +++-----
+ 7 files changed, 50 insertions(+), 59 deletions(-)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -1886,24 +1886,22 @@ static void ftdi_dtr_rts(struct usb_seri
+ {
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+
+- mutex_lock(&port->serial->disc_mutex);
+- if (!port->serial->disconnected) {
+- /* Disable flow control */
+- if (!on && usb_control_msg(port->serial->dev,
++ /* Disable flow control */
++ if (!on) {
++ if (usb_control_msg(port->serial->dev,
+ usb_sndctrlpipe(port->serial->dev, 0),
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+ 0, priv->interface, NULL, 0,
+ WDR_TIMEOUT) < 0) {
+- dev_err(&port->dev, "error from flowcontrol urb\n");
++ dev_err(&port->dev, "error from flowcontrol urb\n");
+ }
+- /* drop RTS and DTR */
+- if (on)
+- set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+- else
+- clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+ }
+- mutex_unlock(&port->serial->disc_mutex);
++ /* drop RTS and DTR */
++ if (on)
++ set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
++ else
++ clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+ }
+
+ /*
+--- a/drivers/usb/serial/mct_u232.c
++++ b/drivers/usb/serial/mct_u232.c
+@@ -499,19 +499,15 @@ static void mct_u232_dtr_rts(struct usb_
+ unsigned int control_state;
+ struct mct_u232_private *priv = usb_get_serial_port_data(port);
+
+- mutex_lock(&port->serial->disc_mutex);
+- if (!port->serial->disconnected) {
+- /* drop DTR and RTS */
+- spin_lock_irq(&priv->lock);
+- if (on)
+- priv->control_state |= TIOCM_DTR | TIOCM_RTS;
+- else
+- priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+- control_state = priv->control_state;
+- spin_unlock_irq(&priv->lock);
+- mct_u232_set_modem_ctrl(port, control_state);
+- }
+- mutex_unlock(&port->serial->disc_mutex);
++ spin_lock_irq(&priv->lock);
++ if (on)
++ priv->control_state |= TIOCM_DTR | TIOCM_RTS;
++ else
++ priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
++ control_state = priv->control_state;
++ spin_unlock_irq(&priv->lock);
++
++ mct_u232_set_modem_ctrl(port, control_state);
+ }
+
+ static void mct_u232_close(struct usb_serial_port *port)
+--- a/drivers/usb/serial/quatech2.c
++++ b/drivers/usb/serial/quatech2.c
+@@ -945,19 +945,17 @@ static void qt2_dtr_rts(struct usb_seria
+ struct usb_device *dev = port->serial->dev;
+ struct qt2_port_private *port_priv = usb_get_serial_port_data(port);
+
+- mutex_lock(&port->serial->disc_mutex);
+- if (!port->serial->disconnected) {
+- /* Disable flow control */
+- if (!on && qt2_setregister(dev, port_priv->device_port,
++ /* Disable flow control */
++ if (!on) {
++ if (qt2_setregister(dev, port_priv->device_port,
+ UART_MCR, 0) < 0)
+ dev_warn(&port->dev, "error from flowcontrol urb\n");
+- /* drop RTS and DTR */
+- if (on)
+- update_mctrl(port_priv, TIOCM_DTR | TIOCM_RTS, 0);
+- else
+- update_mctrl(port_priv, 0, TIOCM_DTR | TIOCM_RTS);
+ }
+- mutex_unlock(&port->serial->disc_mutex);
++ /* drop RTS and DTR */
++ if (on)
++ update_mctrl(port_priv, TIOCM_DTR | TIOCM_RTS, 0);
++ else
++ update_mctrl(port_priv, 0, TIOCM_DTR | TIOCM_RTS);
+ }
+
+ static void qt2_update_msr(struct usb_serial_port *port, unsigned char *ch)
+--- a/drivers/usb/serial/sierra.c
++++ b/drivers/usb/serial/sierra.c
+@@ -861,19 +861,13 @@ static int sierra_open(struct tty_struct
+
+ static void sierra_dtr_rts(struct usb_serial_port *port, int on)
+ {
+- struct usb_serial *serial = port->serial;
+ struct sierra_port_private *portdata;
+
+ portdata = usb_get_serial_port_data(port);
+ portdata->rts_state = on;
+ portdata->dtr_state = on;
+
+- if (serial->dev) {
+- mutex_lock(&serial->disc_mutex);
+- if (!serial->disconnected)
+- sierra_send_setup(port);
+- mutex_unlock(&serial->disc_mutex);
+- }
++ sierra_send_setup(port);
+ }
+
+ static int sierra_startup(struct usb_serial *serial)
+--- a/drivers/usb/serial/ssu100.c
++++ b/drivers/usb/serial/ssu100.c
+@@ -506,19 +506,16 @@ static void ssu100_dtr_rts(struct usb_se
+ {
+ struct usb_device *dev = port->serial->dev;
+
+- mutex_lock(&port->serial->disc_mutex);
+- if (!port->serial->disconnected) {
+- /* Disable flow control */
+- if (!on &&
+- ssu100_setregister(dev, 0, UART_MCR, 0) < 0)
++ /* Disable flow control */
++ if (!on) {
++ if (ssu100_setregister(dev, 0, UART_MCR, 0) < 0)
+ dev_err(&port->dev, "error from flowcontrol urb\n");
+- /* drop RTS and DTR */
+- if (on)
+- set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
+- else
+- clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
+ }
+- mutex_unlock(&port->serial->disc_mutex);
++ /* drop RTS and DTR */
++ if (on)
++ set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
++ else
++ clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
+ }
+
+ static void ssu100_update_msr(struct usb_serial_port *port, u8 msr)
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -688,10 +688,20 @@ static int serial_carrier_raised(struct
+ static void serial_dtr_rts(struct tty_port *port, int on)
+ {
+ struct usb_serial_port *p = container_of(port, struct usb_serial_port, port);
+- struct usb_serial_driver *drv = p->serial->type;
++ struct usb_serial *serial = p->serial;
++ struct usb_serial_driver *drv = serial->type;
+
+- if (drv->dtr_rts)
++ if (!drv->dtr_rts)
++ return;
++ /*
++ * Work-around bug in the tty-layer which can result in dtr_rts
++ * being called after a disconnect (and tty_unregister_device
++ * has returned). Remove once bug has been squashed.
++ */
++ mutex_lock(&serial->disc_mutex);
++ if (!serial->disconnected)
+ drv->dtr_rts(p, on);
++ mutex_unlock(&serial->disc_mutex);
+ }
+
+ static const struct tty_port_operations serial_port_ops = {
+--- a/drivers/usb/serial/usb_wwan.c
++++ b/drivers/usb/serial/usb_wwan.c
+@@ -38,7 +38,6 @@
+
+ void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
+ {
+- struct usb_serial *serial = port->serial;
+ struct usb_wwan_port_private *portdata;
+ struct usb_wwan_intf_private *intfdata;
+
+@@ -48,12 +47,11 @@ void usb_wwan_dtr_rts(struct usb_serial_
+ return;
+
+ portdata = usb_get_serial_port_data(port);
+- mutex_lock(&serial->disc_mutex);
++ /* FIXME: locking */
+ portdata->rts_state = on;
+ portdata->dtr_state = on;
+- if (serial->dev)
+- intfdata->send_setup(port);
+- mutex_unlock(&serial->disc_mutex);
++
++ intfdata->send_setup(port);
+ }
+ EXPORT_SYMBOL(usb_wwan_dtr_rts);
+