]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
serial: core: Fix initializing and restoring termios speed
authorPali Rohár <pali@kernel.org>
Sat, 2 Oct 2021 13:09:00 +0000 (15:09 +0200)
committerAndrea Righi <andrea.righi@canonical.com>
Tue, 4 Jan 2022 08:48:11 +0000 (09:48 +0100)
BugLink: https://bugs.launchpad.net/bugs/1951822
commit 027b57170bf8bb6999a28e4a5f3d78bf1db0f90c upstream.

Since commit edc6afc54968 ("tty: switch to ktermios and new framework")
termios speed is no longer stored only in c_cflag member but also in new
additional c_ispeed and c_ospeed members. If BOTHER flag is set in c_cflag
then termios speed is stored only in these new members.

Therefore to correctly restore termios speed it is required to store also
ispeed and ospeed members, not only cflag member.

In case only cflag member with BOTHER flag is restored then functions
tty_termios_baud_rate() and tty_termios_input_baud_rate() returns baudrate
stored in c_ospeed / c_ispeed member, which is zero as it was not restored
too. If reported baudrate is invalid (e.g. zero) then serial core functions
report fallback baudrate value 9600. So it means that in this case original
baudrate is lost and kernel changes it to value 9600.

Simple reproducer of this issue is to boot kernel with following command
line argument: "console=ttyXXX,86400" (where ttyXXX is the device name).
For speed 86400 there is no Bnnn constant and therefore kernel has to
represent this speed via BOTHER c_cflag. Which means that speed is stored
only in c_ospeed and c_ispeed members, not in c_cflag anymore.

If bootloader correctly configures serial device to speed 86400 then kernel
prints boot log to early console at speed speed 86400 without any issue.
But after kernel starts initializing real console device ttyXXX then speed
is changed to fallback value 9600 because information about speed was lost.

This patch fixes above issue by storing and restoring also ispeed and
ospeed members, which are required for BOTHER flag.

Fixes: edc6afc54968 ("[PATCH] tty: switch to ktermios and new framework")
Cc: stable@vger.kernel.org
Signed-off-by: Pali Rohár <pali@kernel.org>
Link: https://lore.kernel.org/r/20211002130900.9518-1-pali@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
drivers/tty/serial/serial_core.c
include/linux/console.h

index 0e2e35ab64c794fd6939753d302753ebdf910045..1e738f265eeaa210626df63cb124145f34d90751 100644 (file)
@@ -222,7 +222,11 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
        if (retval == 0) {
                if (uart_console(uport) && uport->cons->cflag) {
                        tty->termios.c_cflag = uport->cons->cflag;
+                       tty->termios.c_ispeed = uport->cons->ispeed;
+                       tty->termios.c_ospeed = uport->cons->ospeed;
                        uport->cons->cflag = 0;
+                       uport->cons->ispeed = 0;
+                       uport->cons->ospeed = 0;
                }
                /*
                 * Initialise the hardware port settings.
@@ -290,8 +294,11 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
                /*
                 * Turn off DTR and RTS early.
                 */
-               if (uport && uart_console(uport) && tty)
+               if (uport && uart_console(uport) && tty) {
                        uport->cons->cflag = tty->termios.c_cflag;
+                       uport->cons->ispeed = tty->termios.c_ispeed;
+                       uport->cons->ospeed = tty->termios.c_ospeed;
+               }
 
                if (!tty || C_HUPCL(tty))
                        uart_port_dtr_rts(uport, 0);
@@ -2094,8 +2101,11 @@ uart_set_options(struct uart_port *port, struct console *co,
         * Allow the setting of the UART parameters with a NULL console
         * too:
         */
-       if (co)
+       if (co) {
                co->cflag = termios.c_cflag;
+               co->ispeed = termios.c_ispeed;
+               co->ospeed = termios.c_ospeed;
+       }
 
        return 0;
 }
@@ -2229,6 +2239,8 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
                 */
                memset(&termios, 0, sizeof(struct ktermios));
                termios.c_cflag = uport->cons->cflag;
+               termios.c_ispeed = uport->cons->ispeed;
+               termios.c_ospeed = uport->cons->ospeed;
 
                /*
                 * If that's unset, use the tty termios setting.
index 20874db50bc8a5ad054bdc607e44d842fe5f7274..a97f277cfdfa36cced9954edc19f47224e75b3cc 100644 (file)
@@ -149,6 +149,8 @@ struct console {
        short   flags;
        short   index;
        int     cflag;
+       uint    ispeed;
+       uint    ospeed;
        void    *data;
        struct   console *next;
 };