diff mbox series

[1/6] tty: add port flag to suppress raising DTR & RTS on open

Message ID 20220527222709.1AC8837401F1@freecalypso.org
State Superseded
Headers show
Series [1/6] tty: add port flag to suppress raising DTR & RTS on open | expand

Commit Message

Mychaela N. Falconia May 27, 2022, 10:27 p.m. UTC
There exist special serial devices (either attaching to generic serial
ports or containing a built-in USB-serial chip) in which DTR and/or RTS
have been repurposed for non-standard uses.  Depending on exactly how
these signals are repurposed, standard POSIX/SUS behaviour of
unconditionally raising both signals on open may range from harmless
to undesirable to a total killer, precluding the use of Linux with
such custom hardware.

The newly added TTY_PORT_MANUAL_RTSDTR flag switches an individual
serial port from POSIX/SUS standard to non-standard behaviour: when
set, it suppresses the built-in action of raising DTR & RTS on serial
port open.

This flag can be exported through sysfs, and it can also be set by
USB-serial device drivers when they see a custom hw device (identified
by VID:PID) that is known to be wired in a way that requires this flag
to be set.

Co-developed-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Mychaela N. Falconia <falcon@freecalypso.org>
---
 drivers/tty/tty_port.c   |  2 +-
 include/linux/tty_port.h | 11 +++++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

Comments

Andy Shevchenko May 30, 2022, 1:31 p.m. UTC | #1
On Sat, May 28, 2022 at 8:51 PM Mychaela N. Falconia
<falcon@freecalypso.org> wrote:
>
> There exist special serial devices (either attaching to generic serial
> ports or containing a built-in USB-serial chip) in which DTR and/or RTS
> have been repurposed for non-standard uses.  Depending on exactly how
> these signals are repurposed, standard POSIX/SUS behaviour of

Which version of POSIX and SUS standards are in consideration?

> unconditionally raising both signals on open may range from harmless
> to undesirable to a total killer, precluding the use of Linux with
> such custom hardware.
>
> The newly added TTY_PORT_MANUAL_RTSDTR flag switches an individual
> serial port from POSIX/SUS standard to non-standard behaviour: when
> set, it suppresses the built-in action of raising DTR & RTS on serial
> port open.
>
> This flag can be exported through sysfs, and it can also be set by
> USB-serial device drivers when they see a custom hw device (identified
> by VID:PID) that is known to be wired in a way that requires this flag
> to be set.

Is it only the USB class of devices that are affected or do we have
examples on other buses?

...

>  void tty_port_raise_dtr_rts(struct tty_port *port)
>  {
> -       if (port->ops->dtr_rts)
> +       if (port->ops->dtr_rts && !tty_port_manual_rtsdtr(port))
>                 port->ops->dtr_rts(port, 1);

Logically I would put them otherwise, first to check a custom flag and
then the existence of the callback.

>  }
Mychaela Falconia May 30, 2022, 4:23 p.m. UTC | #2
Andy Shevchenko wrote:

> Which version of POSIX and SUS standards are in consideration?

I admit my ignorance - but I've always been told that the behaviour
of always unconditionally asserting both DTR & RTS on serial port open
is mandated by both POSIX and SUS, which is why all Unix-style OSes
have been following this behaviour up until 2021-04, when FreeBSD 13.0
became the world's first Unix-style OS to provide an *option* for
users to opt out (on a per-serial-device basis) of this standards-
mandated behaviour.

> Is it only the USB class of devices that are affected or do we have
> examples on other buses?

In my own use case, it is only USB: my hw device is one where a
USB-serial chip (FT2232D in my case) and the circuit that repurposes
DTR & RTS outputs from one of the UARTs (FT2232D Channel B in my case)
are inseparably integrated on the same PCB, with a custom USB VID:PID
identifying the device as a whole.  However, I have been told that in
order to be acceptable into Linux mainline, the proposed solution has
to work for all similarly affected parties and not just my device,
hence I am also considering a "generic" case where a custom hw device
would have an old-fashioned RS-232 electrical interface and could be
connected to "any" serial port.

> Logically I would put them otherwise, first to check a custom flag and
> then the existence of the callback.

I can make this change in the next version of my patch series, once I
get a clarification from Greg as to the correct way to denote the
chain of authorship and revision.

M~
diff mbox series

Patch

diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index 880608a65773..59f1c49bb23c 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -441,7 +441,7 @@  EXPORT_SYMBOL(tty_port_carrier_raised);
  */
 void tty_port_raise_dtr_rts(struct tty_port *port)
 {
-	if (port->ops->dtr_rts)
+	if (port->ops->dtr_rts && !tty_port_manual_rtsdtr(port))
 		port->ops->dtr_rts(port, 1);
 }
 EXPORT_SYMBOL(tty_port_raise_dtr_rts);
diff --git a/include/linux/tty_port.h b/include/linux/tty_port.h
index 58e9619116b7..9e5ad46d8a53 100644
--- a/include/linux/tty_port.h
+++ b/include/linux/tty_port.h
@@ -133,6 +133,7 @@  struct tty_port {
 #define TTY_PORT_CHECK_CD	4	/* carrier detect enabled */
 #define TTY_PORT_KOPENED	5	/* device exclusively opened by
 					   kernel */
+#define TTY_PORT_MANUAL_RTSDTR	6	/* do not raise DTR & RTS on open */
 
 void tty_port_init(struct tty_port *port);
 void tty_port_link_device(struct tty_port *port, struct tty_driver *driver,
@@ -226,6 +227,16 @@  static inline void tty_port_set_kopened(struct tty_port *port, bool val)
 	assign_bit(TTY_PORT_KOPENED, &port->iflags, val);
 }
 
+static inline bool tty_port_manual_rtsdtr(const struct tty_port *port)
+{
+	return test_bit(TTY_PORT_MANUAL_RTSDTR, &port->iflags);
+}
+
+static inline void tty_port_set_manual_rtsdtr(struct tty_port *port, bool val)
+{
+	assign_bit(TTY_PORT_MANUAL_RTSDTR, &port->iflags, val);
+}
+
 struct tty_struct *tty_port_tty_get(struct tty_port *port);
 void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty);
 int tty_port_carrier_raised(struct tty_port *port);