serial: core: move RS485 configuration tasks from drivers into core
authorLino Sanfilippo <LinoSanfilippo@gmx.de>
Sun, 10 Apr 2022 10:46:34 +0000 (12:46 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 3 Nov 2022 14:59:20 +0000 (23:59 +0900)
commit 0ed12afa5655512ee418047fb3546d229df20aa1 upstream.

Several drivers that support setting the RS485 configuration via userspace
implement one or more of the following tasks:

- in case of an invalid RTS configuration (both RTS after send and RTS on
  send set or both unset) fall back to enable RTS on send and disable RTS
  after send

- nullify the padding field of the returned serial_rs485 struct

- copy the configuration into the uart port struct

- limit RTS delays to 100 ms

Move these tasks into the serial core to make them generic and to provide
a consistent behaviour among all drivers.

Signed-off-by: Lino Sanfilippo <LinoSanfilippo@gmx.de>
Link: https://lore.kernel.org/r/20220410104642.32195-2-LinoSanfilippo@gmx.de
Signed-off-by: Daisuke Mizobuchi <mizo@atmark-techno.com>
Signed-off-by: Dominique Martinet <dominique.martinet@atmark-techno.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/serial_core.c

index 82ddbb9..48dafd1 100644 (file)
@@ -42,6 +42,11 @@ static struct lock_class_key port_lock_key;
 
 #define HIGH_BITS_OFFSET       ((sizeof(long)-sizeof(int))*8)
 
+/*
+ * Max time with active RTS before/after data is sent.
+ */
+#define RS485_MAX_RTS_DELAY    100 /* msecs */
+
 static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
                                        struct ktermios *old_termios);
 static void uart_wait_until_sent(struct tty_struct *tty, int timeout);
@@ -1299,8 +1304,36 @@ static int uart_set_rs485_config(struct uart_port *port,
        if (copy_from_user(&rs485, rs485_user, sizeof(*rs485_user)))
                return -EFAULT;
 
+       /* pick sane settings if the user hasn't */
+       if (!(rs485.flags & SER_RS485_RTS_ON_SEND) ==
+           !(rs485.flags & SER_RS485_RTS_AFTER_SEND)) {
+               dev_warn_ratelimited(port->dev,
+                       "%s (%d): invalid RTS setting, using RTS_ON_SEND instead\n",
+                       port->name, port->line);
+               rs485.flags |= SER_RS485_RTS_ON_SEND;
+               rs485.flags &= ~SER_RS485_RTS_AFTER_SEND;
+       }
+
+       if (rs485.delay_rts_before_send > RS485_MAX_RTS_DELAY) {
+               rs485.delay_rts_before_send = RS485_MAX_RTS_DELAY;
+               dev_warn_ratelimited(port->dev,
+                       "%s (%d): RTS delay before sending clamped to %u ms\n",
+                       port->name, port->line, rs485.delay_rts_before_send);
+       }
+
+       if (rs485.delay_rts_after_send > RS485_MAX_RTS_DELAY) {
+               rs485.delay_rts_after_send = RS485_MAX_RTS_DELAY;
+               dev_warn_ratelimited(port->dev,
+                       "%s (%d): RTS delay after sending clamped to %u ms\n",
+                       port->name, port->line, rs485.delay_rts_after_send);
+       }
+       /* Return clean padding area to userspace */
+       memset(rs485.padding, 0, sizeof(rs485.padding));
+
        spin_lock_irqsave(&port->lock, flags);
        ret = port->rs485_config(port, &rs485);
+       if (!ret)
+               port->rs485 = rs485;
        spin_unlock_irqrestore(&port->lock, flags);
        if (ret)
                return ret;