]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/tty/serial/serial_core.c
Merge remote-tracking branch 'tty/tty-next'
[karo-tx-linux.git] / drivers / tty / serial / serial_core.c
index d3dd4ad984f624e034b6554acd1c91ebf9e55ef6..31af2a488101a35b1abfd2de39bbde45d173e84a 100644 (file)
@@ -610,27 +610,50 @@ static void uart_send_xchar(struct tty_struct *tty, char ch)
 static void uart_throttle(struct tty_struct *tty)
 {
        struct uart_state *state = tty->driver_data;
+       struct uart_port *port = state->uart_port;
+       uint32_t mask = 0;
 
        if (I_IXOFF(tty))
+               mask |= UPF_SOFT_FLOW;
+       if (tty->termios.c_cflag & CRTSCTS)
+               mask |= UPF_HARD_FLOW;
+
+       if (port->flags & mask) {
+               port->ops->throttle(port);
+               mask &= ~port->flags;
+       }
+
+       if (mask & UPF_SOFT_FLOW)
                uart_send_xchar(tty, STOP_CHAR(tty));
 
-       if (tty->termios.c_cflag & CRTSCTS)
-               uart_clear_mctrl(state->uart_port, TIOCM_RTS);
+       if (mask & UPF_HARD_FLOW)
+               uart_clear_mctrl(port, TIOCM_RTS);
 }
 
 static void uart_unthrottle(struct tty_struct *tty)
 {
        struct uart_state *state = tty->driver_data;
        struct uart_port *port = state->uart_port;
+       uint32_t mask = 0;
 
-       if (I_IXOFF(tty)) {
+       if (I_IXOFF(tty))
+               mask |= UPF_SOFT_FLOW;
+       if (tty->termios.c_cflag & CRTSCTS)
+               mask |= UPF_HARD_FLOW;
+
+       if (port->flags & mask) {
+               port->ops->unthrottle(port);
+               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 (tty->termios.c_cflag & CRTSCTS)
+       if (mask & UPF_HARD_FLOW)
                uart_set_mctrl(port, TIOCM_RTS);
 }
 
@@ -1214,9 +1237,22 @@ static void uart_set_termios(struct tty_struct *tty,
                                                struct ktermios *old_termios)
 {
        struct uart_state *state = tty->driver_data;
+       struct uart_port *uport = state->uart_port;
        unsigned long flags;
        unsigned int cflag = tty->termios.c_cflag;
+       unsigned int iflag_mask = IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK;
+       bool sw_changed = false;
 
+       /*
+        * Drivers doing software flow control also need to know
+        * about changes to these input settings.
+        */
+       if (uport->flags & UPF_SOFT_FLOW) {
+               iflag_mask |= IXANY|IXON|IXOFF;
+               sw_changed =
+                  tty->termios.c_cc[VSTART] != old_termios->c_cc[VSTART] ||
+                  tty->termios.c_cc[VSTOP] != old_termios->c_cc[VSTOP];
+       }
 
        /*
         * These are the bits that are used to setup various
@@ -1224,11 +1260,11 @@ static void uart_set_termios(struct tty_struct *tty,
         * bits in c_cflag; c_[io]speed will always be set
         * appropriately by set_termios() in tty_ioctl.c
         */
-#define RELEVANT_IFLAG(iflag)  ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
        if ((cflag ^ old_termios->c_cflag) == 0 &&
            tty->termios.c_ospeed == old_termios->c_ospeed &&
            tty->termios.c_ispeed == old_termios->c_ispeed &&
-           RELEVANT_IFLAG(tty->termios.c_iflag ^ old_termios->c_iflag) == 0) {
+           ((tty->termios.c_iflag ^ old_termios->c_iflag) & iflag_mask) == 0 &&
+           !sw_changed) {
                return;
        }
 
@@ -1236,31 +1272,38 @@ static void uart_set_termios(struct tty_struct *tty,
 
        /* Handle transition to B0 status */
        if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
-               uart_clear_mctrl(state->uart_port, TIOCM_RTS | TIOCM_DTR);
+               uart_clear_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
        /* 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))
                        mask |= TIOCM_RTS;
-               uart_set_mctrl(state->uart_port, mask);
+               uart_set_mctrl(uport, mask);
        }
 
+       /*
+        * If the port is doing h/w assisted flow control, do nothing.
+        * We assume that tty->hw_stopped has never been set.
+        */
+       if (uport->flags & UPF_HARD_FLOW)
+               return;
+
        /* Handle turning off CRTSCTS */
        if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
-               spin_lock_irqsave(&state->uart_port->lock, flags);
+               spin_lock_irqsave(&uport->lock, flags);
                tty->hw_stopped = 0;
                __uart_start(tty);
-               spin_unlock_irqrestore(&state->uart_port->lock, flags);
+               spin_unlock_irqrestore(&uport->lock, flags);
        }
        /* Handle turning on CRTSCTS */
        else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
-               spin_lock_irqsave(&state->uart_port->lock, flags);
-               if (!(state->uart_port->ops->get_mctrl(state->uart_port) & TIOCM_CTS)) {
+               spin_lock_irqsave(&uport->lock, flags);
+               if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) {
                        tty->hw_stopped = 1;
-                       state->uart_port->ops->stop_tx(state->uart_port);
+                       uport->ops->stop_tx(uport);
                }
-               spin_unlock_irqrestore(&state->uart_port->lock, flags);
+               spin_unlock_irqrestore(&uport->lock, flags);
        }
 }