]> www.infradead.org Git - linux.git/commitdiff
USB: serial: ch341: add hardware flow control RTS/CTS
authorLode Willems <me@lodewillems.com>
Wed, 18 Dec 2024 17:25:05 +0000 (18:25 +0100)
committerJohan Hovold <johan@kernel.org>
Wed, 8 Jan 2025 09:12:18 +0000 (10:12 +0100)
This adds support for enabling and disabling RTS/CTS hardware flow
control.

Tested using CH341A and CH340E.

Fixes part of the following bug report:
Link: https://bugzilla.kernel.org/show_bug.cgi?id=197109
Signed-off-by: Lode Willems <me@lodewillems.com>
[ johan: prepare index argument once, drop casts ]
Signed-off-by: Johan Hovold <johan@kernel.org>
drivers/usb/serial/ch341.c

index d10e4c4848a0ab9073c4c93638a8796ab61ce3a6..ac74b353b26d34b1fa1deab188163b543e48605e 100644 (file)
@@ -63,6 +63,7 @@
 #define CH341_REG_DIVISOR      0x13
 #define CH341_REG_LCR          0x18
 #define CH341_REG_LCR2         0x25
+#define CH341_REG_FLOW_CTL     0x27
 
 #define CH341_NBREAK_BITS      0x01
 
@@ -77,6 +78,9 @@
 #define CH341_LCR_CS6          0x01
 #define CH341_LCR_CS5          0x00
 
+#define CH341_FLOW_CTL_NONE    0x00
+#define CH341_FLOW_CTL_RTSCTS  0x01
+
 #define CH341_QUIRK_LIMITED_PRESCALER  BIT(0)
 #define CH341_QUIRK_SIMULATE_BREAK     BIT(1)
 
@@ -478,6 +482,28 @@ err_kill_interrupt_urb:
        return r;
 }
 
+static void ch341_set_flow_control(struct tty_struct *tty,
+                                  struct usb_serial_port *port,
+                                  const struct ktermios *old_termios)
+{
+       u16 flow_ctl;
+       int r;
+
+       if (C_CRTSCTS(tty))
+               flow_ctl = CH341_FLOW_CTL_RTSCTS;
+       else
+               flow_ctl = CH341_FLOW_CTL_NONE;
+
+       r = ch341_control_out(port->serial->dev,
+                             CH341_REQ_WRITE_REG,
+                             (CH341_REG_FLOW_CTL << 8) | CH341_REG_FLOW_CTL,
+                             (flow_ctl << 8) | flow_ctl);
+       if (r < 0 && old_termios) {
+               tty->termios.c_cflag &= ~CRTSCTS;
+               tty->termios.c_cflag |= (old_termios->c_cflag & CRTSCTS);
+       }
+}
+
 /* Old_termios contains the original termios settings and
  * tty->termios contains the new setting to be used.
  */
@@ -546,6 +572,8 @@ static void ch341_set_termios(struct tty_struct *tty,
        spin_unlock_irqrestore(&priv->lock, flags);
 
        ch341_set_handshake(port->serial->dev, priv->mcr);
+
+       ch341_set_flow_control(tty, port, old_termios);
 }
 
 /*