serial: core: Remove unsafe x_char optimization
[firefly-linux-kernel-4.4.55.git] / drivers / tty / serial / serial_core.c
index 3ce68f962c9238275c3d8b11aa12a14ea9561a32..bdc543caf069a6f4a4d1dfa18f7309d44f15e02b 100644 (file)
@@ -175,12 +175,8 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
                        if (tty->termios.c_cflag & CBAUD)
                                uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
                }
-               /*
-                * if hw support flow control without software intervention,
-                * then skip the below check
-                */
-               if (tty_port_cts_enabled(port) &&
-                   !(uport->flags & UPF_HARD_FLOW)) {
+
+               if (tty_port_cts_enabled(port)) {
                        spin_lock_irq(&uport->lock);
                        if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))
                                tty->hw_stopped = 1;
@@ -244,6 +240,9 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
                /*
                 * Turn off DTR and RTS early.
                 */
+               if (uart_console(uport) && tty)
+                       uport->cons->cflag = tty->termios.c_cflag;
+
                if (!tty || (tty->termios.c_cflag & HUPCL))
                        uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
 
@@ -448,6 +447,7 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
                return;
 
        termios = &tty->termios;
+       uport->ops->set_termios(uport, termios, old_termios);
 
        /*
         * Set flags based on termios cflag
@@ -461,8 +461,6 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
                clear_bit(ASYNCB_CHECK_CD, &port->flags);
        else
                set_bit(ASYNCB_CHECK_CD, &port->flags);
-
-       uport->ops->set_termios(uport, termios, old_termios);
 }
 
 static inline int __uart_put_char(struct uart_port *port,
@@ -602,12 +600,11 @@ static void uart_send_xchar(struct tty_struct *tty, char ch)
        if (port->ops->send_xchar)
                port->ops->send_xchar(port, ch);
        else {
+               spin_lock_irqsave(&port->lock, flags);
                port->x_char = ch;
-               if (ch) {
-                       spin_lock_irqsave(&port->lock, flags);
+               if (ch)
                        port->ops->start_tx(port);
-                       spin_unlock_irqrestore(&port->lock, flags);
-               }
+               spin_unlock_irqrestore(&port->lock, flags);
        }
 }
 
@@ -650,12 +647,8 @@ static void uart_unthrottle(struct tty_struct *tty)
                mask &= ~port->flags;
        }
 
-       if (mask & UPF_SOFT_FLOW) {
-               if (port->x_char)
-                       port->x_char = 0;
-               else
-                       uart_send_xchar(tty, START_CHAR(tty));
-       }
+       if (mask & UPF_SOFT_FLOW)
+               uart_send_xchar(tty, START_CHAR(tty));
 
        if (mask & UPF_HARD_FLOW)
                uart_set_mctrl(port, TIOCM_RTS);
@@ -890,10 +883,11 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
                         */
                        if (uport->flags & UPF_SPD_MASK) {
                                char buf[64];
-                               printk(KERN_NOTICE
-                                      "%s sets custom speed on %s. This "
-                                      "is deprecated.\n", current->comm,
-                                      tty_name(port->tty, buf));
+
+                               dev_notice(uport->dev,
+                                      "%s sets custom speed on %s. This is deprecated.\n",
+                                     current->comm,
+                                     tty_name(port->tty, buf));
                        }
                        uart_change_speed(tty, state, NULL);
                }
@@ -1051,6 +1045,15 @@ static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state)
        return ret;
 }
 
+static void uart_enable_ms(struct uart_port *uport)
+{
+       /*
+        * Force modem status interrupts on
+        */
+       if (uport->ops->enable_ms)
+               uport->ops->enable_ms(uport);
+}
+
 /*
  * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
  * - mask passed in arg for lines of interest
@@ -1074,11 +1077,7 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg)
         */
        spin_lock_irq(&uport->lock);
        memcpy(&cprev, &uport->icount, sizeof(struct uart_icount));
-
-       /*
-        * Force modem status interrupts on
-        */
-       uport->ops->enable_ms(uport);
+       uart_enable_ms(uport);
        spin_unlock_irq(&uport->lock);
 
        add_wait_queue(&port->delta_msr_wait, &wait);
@@ -1275,6 +1274,8 @@ static void uart_set_termios(struct tty_struct *tty,
        }
 
        uart_change_speed(tty, state, old_termios);
+       /* reload cflag from termios; port driver may have overriden flags */
+       cflag = tty->termios.c_cflag;
 
        /* Handle transition to B0 status */
        if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
@@ -1282,8 +1283,7 @@ static void uart_set_termios(struct tty_struct *tty,
        /* Handle transition away from B0 status */
        else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
                unsigned int mask = TIOCM_DTR;
-               if (!(cflag & CRTSCTS) ||
-                   !test_bit(TTY_THROTTLED, &tty->flags))
+               if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags))
                        mask |= TIOCM_RTS;
                uart_set_mctrl(uport, mask);
        }
@@ -1361,8 +1361,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
        tty_ldisc_flush(tty);
 
        tty_port_tty_set(port, NULL);
-       spin_lock_irqsave(&port->lock, flags);
        tty->closing = 0;
+       spin_lock_irqsave(&port->lock, flags);
 
        if (port->blocked_open) {
                spin_unlock_irqrestore(&port->lock, flags);
@@ -1509,7 +1509,7 @@ static int uart_carrier_raised(struct tty_port *port)
        struct uart_port *uport = state->uart_port;
        int mctrl;
        spin_lock_irq(&uport->lock);
-       uport->ops->enable_ms(uport);
+       uart_enable_ms(uport);
        mctrl = uport->ops->get_mctrl(uport);
        spin_unlock_irq(&uport->lock);
        if (mctrl & TIOCM_CAR)
@@ -1576,14 +1576,6 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
                (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
        tty_port_tty_set(port, tty);
 
-       /*
-        * If the port is in the middle of closing, bail out now.
-        */
-       if (tty_hung_up_p(filp)) {
-               retval = -EAGAIN;
-               goto err_dec_count;
-       }
-
        /*
         * Start up the serial port.
         */
@@ -1974,12 +1966,9 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
                for (tries = 3; !ops->tx_empty(uport) && tries; tries--)
                        msleep(10);
                if (!tries)
-                       printk(KERN_ERR "%s%s%s%d: Unable to drain "
-                                       "transmitter\n",
-                              uport->dev ? dev_name(uport->dev) : "",
-                              uport->dev ? ": " : "",
-                              drv->dev_name,
-                              drv->tty_driver->name_base + uport->line);
+                       dev_err(uport->dev, "%s%d: Unable to drain transmitter\n",
+                               drv->dev_name,
+                               drv->tty_driver->name_base + uport->line);
 
                if (console_suspend_enabled || !uart_console(uport))
                        ops->shutdown(uport);
@@ -2108,9 +2097,7 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port)
                break;
        }
 
-       printk(KERN_INFO "%s%s%s%d at %s (irq = %d, base_baud = %d) is a %s\n",
-              port->dev ? dev_name(port->dev) : "",
-              port->dev ? ": " : "",
+       dev_info(port->dev, "%s%d at %s (irq = %d, base_baud = %d) is a %s\n",
               drv->dev_name,
               drv->tty_driver->name_base + port->line,
               address, port->irq, port->uartclk / 16, uart_type(port));
@@ -2564,12 +2551,6 @@ static const struct attribute_group tty_dev_attr_group = {
        .attrs = tty_dev_attrs,
        };
 
-static const struct attribute_group *tty_dev_attr_groups[] = {
-       &tty_dev_attr_group,
-       NULL
-       };
-
-
 /**
  *     uart_add_one_port - attach a driver-defined port structure
  *     @drv: pointer to the uart low level driver structure for this port
@@ -2586,6 +2567,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
        struct tty_port *port;
        int ret = 0;
        struct device *tty_dev;
+       int num_groups;
 
        BUG_ON(in_interrupt());
 
@@ -2621,16 +2603,30 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
 
        uart_configure_port(drv, state, uport);
 
+       num_groups = 2;
+       if (uport->attr_group)
+               num_groups++;
+
+       uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups),
+                                   GFP_KERNEL);
+       if (!uport->tty_groups) {
+               ret = -ENOMEM;
+               goto out;
+       }
+       uport->tty_groups[0] = &tty_dev_attr_group;
+       if (uport->attr_group)
+               uport->tty_groups[1] = uport->attr_group;
+
        /*
         * Register the port whether it's detected or not.  This allows
         * setserial to be used to alter this port's parameters.
         */
        tty_dev = tty_port_register_device_attr(port, drv->tty_driver,
-                       uport->line, uport->dev, port, tty_dev_attr_groups);
+                       uport->line, uport->dev, port, uport->tty_groups);
        if (likely(!IS_ERR(tty_dev))) {
                device_set_wakeup_capable(tty_dev, 1);
        } else {
-               printk(KERN_ERR "Cannot register tty device on line %d\n",
+               dev_err(uport->dev, "Cannot register tty device on line %d\n",
                       uport->line);
        }
 
@@ -2665,7 +2661,7 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
        BUG_ON(in_interrupt());
 
        if (state->uart_port != uport)
-               printk(KERN_ALERT "Removing wrong port: %p != %p\n",
+               dev_alert(uport->dev, "Removing wrong port: %p != %p\n",
                        state->uart_port, uport);
 
        mutex_lock(&port_mutex);
@@ -2705,6 +2701,7 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
         */
        if (uport->type != PORT_UNKNOWN)
                uport->ops->release_port(uport);
+       kfree(uport->tty_groups);
 
        /*
         * Indicate that there isn't a port here anymore.
@@ -2782,9 +2779,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
 
        uport->icount.cts++;
 
-       /* skip below code if the hw flow control is supported */
-       if (tty_port_cts_enabled(port) &&
-           !(uport->flags & UPF_HARD_FLOW)) {
+       if (tty_port_cts_enabled(port)) {
                if (tty->hw_stopped) {
                        if (status) {
                                tty->hw_stopped = 0;