[Please CC me on replies, I'm not subscribed]
Here's a generic version that tries not to be intrusive.
1. sysfs interface that allows setting per port
2. recycled an unused member of struct uart_port
Attached patch is against linux-4.13.
--
Andreas Steinmetz SPAMmers use robotrap@xxxxxxxx
diff -rNup linux-4.13.orig/drivers/tty/serial/serial_core.c linux-4.13/drivers/tty/serial/serial_core.c
--- linux-4.13.orig/drivers/tty/serial/serial_core.c 2017-09-03 22:56:17.000000000 +0200
+++ linux-4.13/drivers/tty/serial/serial_core.c 2017-10-10 11:58:02.259478128 +0200
@@ -2664,6 +2664,56 @@ static ssize_t uart_get_attr_iomem_reg_s
return snprintf(buf, PAGE_SIZE, "%d\n", tmp.iomem_reg_shift);
}
+static ssize_t uart_get_attr_pps_4wire(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tty_port *port = dev_get_drvdata(dev);
+ struct uart_state *state = container_of(port, struct uart_state, port);
+ struct uart_port *uport;
+ int mode = 0;
+
+ mutex_lock(&port->mutex);
+ uport = uart_port_check(state);
+ if (!uport)
+ goto out;
+
+ mode = uport->pps_4wire;
+
+out:
+ mutex_unlock(&port->mutex);
+ return snprintf(buf, PAGE_SIZE, mode ? "yes" : "no");
+}
+
+static ssize_t uart_set_attr_pps_4wire(struct device *dev,
+ struct device_attribute *attr, const char *buf,size_t count)
+{
+ struct tty_port *port = dev_get_drvdata(dev);
+ struct uart_state *state = container_of(port, struct uart_state, port);
+ struct uart_port *uport;
+ bool mode;
+ int ret;
+
+ if (!count)
+ return -EINVAL;
+
+ ret = kstrtobool(buf, &mode);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&port->mutex);
+ uport = uart_port_check(state);
+ if (!uport)
+ goto out;
+
+ spin_lock_irq(&uport->lock);
+ uport->pps_4wire = mode;
+ spin_unlock_irq(&uport->lock);
+
+out:
+ mutex_unlock(&port->mutex);
+ return count;
+}
+
static DEVICE_ATTR(type, S_IRUSR | S_IRGRP, uart_get_attr_type, NULL);
static DEVICE_ATTR(line, S_IRUSR | S_IRGRP, uart_get_attr_line, NULL);
static DEVICE_ATTR(port, S_IRUSR | S_IRGRP, uart_get_attr_port, NULL);
@@ -2677,6 +2727,7 @@ static DEVICE_ATTR(custom_divisor, S_IRU
static DEVICE_ATTR(io_type, S_IRUSR | S_IRGRP, uart_get_attr_io_type, NULL);
static DEVICE_ATTR(iomem_base, S_IRUSR | S_IRGRP, uart_get_attr_iomem_base, NULL);
static DEVICE_ATTR(iomem_reg_shift, S_IRUSR | S_IRGRP, uart_get_attr_iomem_reg_shift, NULL);
+static DEVICE_ATTR(pps_4wire, S_IRUSR | S_IWUSR | S_IRGRP, uart_get_attr_pps_4wire, uart_set_attr_pps_4wire);
static struct attribute *tty_dev_attrs[] = {
&dev_attr_type.attr,
@@ -2692,6 +2743,7 @@ static struct attribute *tty_dev_attrs[]
&dev_attr_io_type.attr,
&dev_attr_iomem_base.attr,
&dev_attr_iomem_reg_shift.attr,
+ &dev_attr_pps_4wire.attr,
NULL,
};
@@ -2748,6 +2800,9 @@ int uart_add_one_port(struct uart_driver
goto out;
}
+ /* assert that pps handling is done via DCD as default */
+ uport->pps_4wire = 0;
+
/*
* If this port is a console, then the spinlock is already
* initialised.
@@ -2923,7 +2978,7 @@ void uart_handle_dcd_change(struct uart_
lockdep_assert_held_once(&uport->lock);
- if (tty) {
+ if (tty && !uport->pps_4wire) {
ld = tty_ldisc_ref(tty);
if (ld) {
if (ld->ops->dcd_change)
@@ -2952,8 +3007,21 @@ EXPORT_SYMBOL_GPL(uart_handle_dcd_change
*/
void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
{
+ struct tty_port *port = &uport->state->port;
+ struct tty_struct *tty = port->tty;
+ struct tty_ldisc *ld;
+
lockdep_assert_held_once(&uport->lock);
+ if (tty && uport->pps_4wire) {
+ ld = tty_ldisc_ref(tty);
+ if (ld) {
+ if (ld->ops->dcd_change)
+ ld->ops->dcd_change(tty, status);
+ tty_ldisc_deref(ld);
+ }
+ }
+
uport->icount.cts++;
if (uart_softcts_mode(uport)) {
diff -rNup linux-4.13.orig/include/linux/serial_core.h linux-4.13/include/linux/serial_core.h
--- linux-4.13.orig/include/linux/serial_core.h 2017-09-03 22:56:17.000000000 +0200
+++ linux-4.13/include/linux/serial_core.h 2017-10-10 11:58:02.257478108 +0200
@@ -144,7 +144,7 @@ struct uart_port {
unsigned char x_char; /* xon/xoff char */
unsigned char regshift; /* reg offset shift */
unsigned char iotype; /* io access style */
- unsigned char unused1;
+ unsigned char pps_4wire; /* CTS instead of DCD */
#define UPIO_PORT (SERIAL_IO_PORT) /* 8b I/O port access */
#define UPIO_HUB6 (SERIAL_IO_HUB6) /* Hub6 ISA card */