]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/tty/tty_port.c
PCI: Delay final fixups until resources are assigned
[karo-tx-linux.git] / drivers / tty / tty_port.c
index 8bb757c62ee2044746cd9e93a052325bc28907db..121aeb9393e1172e75fe5972a7c4b8ab4640557f 100644 (file)
@@ -132,6 +132,7 @@ EXPORT_SYMBOL(tty_port_free_xmit_buf);
  */
 void tty_port_destroy(struct tty_port *port)
 {
+       cancel_work_sync(&port->buf.work);
        tty_buffer_free_all(port);
 }
 EXPORT_SYMBOL(tty_port_destroy);
@@ -196,12 +197,24 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty)
 }
 EXPORT_SYMBOL(tty_port_tty_set);
 
-static void tty_port_shutdown(struct tty_port *port)
+static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty)
 {
        mutex_lock(&port->mutex);
-       if (port->ops->shutdown && !port->console &&
-               test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags))
+       if (port->console)
+               goto out;
+
+       if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) {
+               /*
+                * Drop DTR/RTS if HUPCL is set. This causes any attached
+                * modem to hang up the line.
+                */
+               if (tty && C_HUPCL(tty))
+                       tty_port_lower_dtr_rts(port);
+
+               if (port->ops->shutdown)
                        port->ops->shutdown(port);
+       }
+out:
        mutex_unlock(&port->mutex);
 }
 
@@ -215,23 +228,41 @@ static void tty_port_shutdown(struct tty_port *port)
 
 void tty_port_hangup(struct tty_port *port)
 {
+       struct tty_struct *tty;
        unsigned long flags;
 
        spin_lock_irqsave(&port->lock, flags);
        port->count = 0;
        port->flags &= ~ASYNC_NORMAL_ACTIVE;
-       if (port->tty) {
-               set_bit(TTY_IO_ERROR, &port->tty->flags);
-               tty_kref_put(port->tty);
-       }
+       tty = port->tty;
+       if (tty)
+               set_bit(TTY_IO_ERROR, &tty->flags);
        port->tty = NULL;
        spin_unlock_irqrestore(&port->lock, flags);
+       tty_port_shutdown(port, tty);
+       tty_kref_put(tty);
        wake_up_interruptible(&port->open_wait);
        wake_up_interruptible(&port->delta_msr_wait);
-       tty_port_shutdown(port);
 }
 EXPORT_SYMBOL(tty_port_hangup);
 
+/**
+ * tty_port_tty_hangup - helper to hang up a tty
+ *
+ * @port: tty port
+ * @check_clocal: hang only ttys with CLOCAL unset?
+ */
+void tty_port_tty_hangup(struct tty_port *port, bool check_clocal)
+{
+       struct tty_struct *tty = tty_port_tty_get(port);
+
+       if (tty && (!check_clocal || !C_CLOCAL(tty))) {
+               tty_hangup(tty);
+               tty_kref_put(tty);
+       }
+}
+EXPORT_SYMBOL_GPL(tty_port_tty_hangup);
+
 /**
  * tty_port_tty_wakeup - helper to wake up a tty
  *
@@ -366,7 +397,7 @@ int tty_port_block_til_ready(struct tty_port *port,
 
        while (1) {
                /* Indicate we are open */
-               if (tty->termios.c_cflag & CBAUD)
+               if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags))
                        tty_port_raise_dtr_rts(port);
 
                prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE);
@@ -411,6 +442,20 @@ int tty_port_block_til_ready(struct tty_port *port,
 }
 EXPORT_SYMBOL(tty_port_block_til_ready);
 
+static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty)
+{
+       unsigned int bps = tty_get_baud_rate(tty);
+       long timeout;
+
+       if (bps > 1200) {
+               timeout = (HZ * 10 * port->drain_delay) / bps;
+               timeout = max_t(long, timeout, HZ / 10);
+       } else {
+               timeout = 2 * HZ;
+       }
+       schedule_timeout_interruptible(timeout);
+}
+
 int tty_port_close_start(struct tty_port *port,
                                struct tty_struct *tty, struct file *filp)
 {
@@ -443,31 +488,19 @@ int tty_port_close_start(struct tty_port *port,
        set_bit(ASYNCB_CLOSING, &port->flags);
        tty->closing = 1;
        spin_unlock_irqrestore(&port->lock, flags);
-       /* Don't block on a stalled port, just pull the chain */
-       if (tty->flow_stopped)
-               tty_driver_flush_buffer(tty);
-       if (test_bit(ASYNCB_INITIALIZED, &port->flags) &&
-                       port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
-               tty_wait_until_sent_from_close(tty, port->closing_wait);
-       if (port->drain_delay) {
-               unsigned int bps = tty_get_baud_rate(tty);
-               long timeout;
-
-               if (bps > 1200)
-                       timeout = max_t(long,
-                               (HZ * 10 * port->drain_delay) / bps, HZ / 10);
-               else
-                       timeout = 2 * HZ;
-               schedule_timeout_interruptible(timeout);
+
+       if (test_bit(ASYNCB_INITIALIZED, &port->flags)) {
+               /* Don't block on a stalled port, just pull the chain */
+               if (tty->flow_stopped)
+                       tty_driver_flush_buffer(tty);
+               if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+                       tty_wait_until_sent_from_close(tty, port->closing_wait);
+               if (port->drain_delay)
+                       tty_port_drain_delay(port, tty);
        }
        /* Flush the ldisc buffering */
        tty_ldisc_flush(tty);
 
-       /* Drop DTR/RTS if HUPCL is set. This causes any attached modem to
-          hang up the line */
-       if (tty->termios.c_cflag & HUPCL)
-               tty_port_lower_dtr_rts(port);
-
        /* Don't call port->drop for the last reference. Callers will want
           to drop the last active reference in ->shutdown() or the tty
           shutdown path */
@@ -502,7 +535,7 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty,
 {
        if (tty_port_close_start(port, tty, filp) == 0)
                return;
-       tty_port_shutdown(port);
+       tty_port_shutdown(port, tty);
        set_bit(TTY_IO_ERROR, &tty->flags);
        tty_port_close_end(port, tty);
        tty_port_tty_set(port, NULL);