diff mbox series

[2/6] serial: core: add sysfs attribute to suppress raising DTR & RTS on open

Message ID 20220527222713.A369E3740211@freecalypso.org
State New
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
Add a manual_rtsdtr sysfs attribute to suppress automatic raising of
DTR and RTS modem control signals on serial port open.

This special mode can be used to prevent undesirable side effects on
open for applications where these lines are used for non-standard
purposes such as generating power-on and reset pulses.

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>
---
 Documentation/ABI/testing/sysfs-tty | 10 ++++++++++
 drivers/tty/serial/serial_core.c    | 27 +++++++++++++++++++++++++++
 2 files changed, 37 insertions(+)

Comments

Andy Shevchenko May 30, 2022, 1:34 p.m. UTC | #1
On Sat, May 28, 2022 at 2:22 AM Mychaela N. Falconia
<falcon@freecalypso.org> wrote:
>
> Add a manual_rtsdtr sysfs attribute to suppress automatic raising of
> DTR and RTS modem control signals on serial port open.
>
> This special mode can be used to prevent undesirable side effects on
> open for applications where these lines are used for non-standard
> purposes such as generating power-on and reset pulses.

As I read this I think of the pins used as GPIOs. So, sounds like what
we need instead is to list the pins that are used for (dedicated)
GPIOs and pass this information to serial mctrl layer and perhaps
prepare that layer to handle exclusions like these. The given (in this
series) approach sounds to me like a (hackish?) workaround of this
simple idea.
Mychaela Falconia May 30, 2022, 11:09 p.m. UTC | #2
Andy Shevchenko wrote:

> As I read this I think of the pins used as GPIOs.

Viewing my repurposed DTR & RTS outputs as GPIOs is a valid way to
look at them.  The target application has one 8-wire UART, a secondary
2-wire UART, and two GPIO (really GPO, output-only) controls.  Because
no major USB-to-X chip vendor makes a chip that goes from one USB to
this combination of two UARTs plus GPIO (there are some that may seem
to qualify at first glance, such as CP2105, but that one and others I
know of have other issues that preclude their use), I took the next
most practical route: I used FT2232D, and repurposed its otherwise
unused Channel B DTR & RTS outputs to be GPOs.

> So, sounds like what
> we need instead is to list the pins that are used for (dedicated)
> GPIOs and pass this information to serial mctrl layer and perhaps
> prepare that layer to handle exclusions like these.

What you are describing would be quite easy to implement for *my*
device, and the implementation would be entirely contained in the
ftdi_sio driver.  My particular FTDI chip (FT2232D) doesn't have any
pins that are officially regarded as GPIOs, but other FTDI chips do
(single-channel FT232H, FT232R and FT-X), and Linux ftdi_sio driver
already has support for presenting FT232H/R/FT-X CBUS pins to gpiolib
as a gpio_chip.  It would be fairly easy to implement a second kind of
gpio_chip within ftdi_sio, made out of an otherwise-serial channel's
DTR and RTS outputs - but here is the big BUT: I don't know how to
conditionalize enabling of this special mode (stealing DTR & RTS from
the ttyUSBx device and handing them over to gpiolib control instead)
other than by keying on specific USB VID:PID codes.  The latter
approach would work great for my application, but I keep being told
that the solution must work for "everyone", whatever that global
specifier could mean in this context.

However, this idea of moving an FTDI device's DTR & RTS outputs to
gpiolib control at the ftdi_sio driver level will not help anyone who
has an old-fashioned RS-232 (not USB-serial) device in which DTR
and/or RTS have been repurposed in a way that does not tolerate
automatic unconditional assertion of these signals on open.  I don't
know whether or not any such devices exist: there aren't any in _my_
world, but I keep being told to think generically, outside of my own
repertoire of use cases.

At this point I need to ask the maintainers with decision-making
powers: which approach would be more acceptable?  The two approaches
which I see as feasible are:

Option 1: implement a special mode for all serial ports, both "hard"
and USB-serial, that suppresses automatic assertion of DTR & RTS on
open - but allows them to be controlled with TIOCMBIS and TIOCMBIC.
Yes, the signals are really GPIOs in this case, but just how would one
go about turning UART signals into true Linux GPIOs on any arbitrary
serial port, whenever an end user decides to connect a particular
RS-232 device that needs special handling?

Option 2: implement a VID:PID-keyed solution in the ftdi_sio driver,
stealing DTR & RTS completely from the tty device and handing them
over to gpiolib control.  This solution would work for my specific USB
device, and if anyone else builds anything similar, they could reuse
the same ftdi_sio driver quirk - but this solution provides no help at
all to RS-232 tinkerers, if any exist.

M~
diff mbox series

Patch

diff --git a/Documentation/ABI/testing/sysfs-tty b/Documentation/ABI/testing/sysfs-tty
index 820e412d38a8..3e666538451b 100644
--- a/Documentation/ABI/testing/sysfs-tty
+++ b/Documentation/ABI/testing/sysfs-tty
@@ -161,3 +161,13 @@  Contact:	Andy Shevchenko <andriy.shevchenko@linux.intel.com>
 Description:
 		 Allows user to detach or attach back the given device as
 		 kernel console. It shows and accepts a boolean variable.
+
+What:		/sys/class/tty/tty<x>/manual_rtsdtr
+Date:		May 2022
+Contact:	Mychaela N. Falconia <falcon@freecalypso.org>
+Description:
+		 Writing 1 into this flag attribute suppresses automatic
+		 assertion of DTR & RTS on serial port open, putting these
+		 signals under manual control (TIOCMBIS & TIOCMBIC).
+		 Writing 0 restores standard POSIX/SUS behaviour of
+		 automatically asserting both DTR and RTS on open.
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 9a85b41caa0a..b47004a3fb77 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2870,6 +2870,31 @@  static ssize_t console_store(struct device *dev,
 	return ret < 0 ? ret : count;
 }
 
+static ssize_t manual_rtsdtr_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct tty_port *port = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", tty_port_manual_rtsdtr(port));
+}
+
+static ssize_t manual_rtsdtr_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	struct tty_port *port = dev_get_drvdata(dev);
+	bool val;
+	int ret;
+
+	ret = kstrtobool(buf, &val);
+	if (ret)
+		return ret;
+
+	tty_port_set_manual_rtsdtr(port, val);
+
+	return count;
+}
+
 static DEVICE_ATTR_RO(uartclk);
 static DEVICE_ATTR_RO(type);
 static DEVICE_ATTR_RO(line);
@@ -2884,6 +2909,7 @@  static DEVICE_ATTR_RO(io_type);
 static DEVICE_ATTR_RO(iomem_base);
 static DEVICE_ATTR_RO(iomem_reg_shift);
 static DEVICE_ATTR_RW(console);
+static DEVICE_ATTR_RW(manual_rtsdtr);
 
 static struct attribute *tty_dev_attrs[] = {
 	&dev_attr_uartclk.attr,
@@ -2900,6 +2926,7 @@  static struct attribute *tty_dev_attrs[] = {
 	&dev_attr_iomem_base.attr,
 	&dev_attr_iomem_reg_shift.attr,
 	&dev_attr_console.attr,
+	&dev_attr_manual_rtsdtr.attr,
 	NULL
 };