[PATCH] serial:serial_core: Allow use of CTS for PPS line discipline

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add a "pps_4wire" file to serial ports in sysfs in case the kernel is
configured with CONFIG_PPS_CLIENT_LDISC. Writing 1 to the file enables
the use of CTS instead of DCD for PPS signal input. This is necessary
in case a serial port is not completely wired.
Though this affects PPS processing the patch is against the serial core
as the source of the serial port PPS event dispatching has to be
modified. Furthermore it should be possible to modify the source of
serial port PPS event dispatching before changing the line discipline.

Signed-off-by: Andreas Steinmetz <ast@xxxxxxxx>
Signed-off-by: Steve Sakoman <steve@xxxxxxxxxxx>
Tested-by: Steve Sakoman <steve@xxxxxxxxxxx>
---
 Documentation/ABI/testing/sysfs-tty |  9 +++++
 drivers/tty/serial/serial_core.c    | 71 ++++++++++++++++++++++++++++++++++++-
 include/linux/serial_core.h         |  3 +-
 3 files changed, 81 insertions(+), 2 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-tty b/Documentation/ABI/testing/sysfs-tty
index 9eb3c2b..441105a 100644
--- a/Documentation/ABI/testing/sysfs-tty
+++ b/Documentation/ABI/testing/sysfs-tty
@@ -154,3 +154,12 @@ Description:
 		 device specification. For example, when user sets 7bytes on
 		 16550A, which has 1/4/8/14 bytes trigger, the RX trigger is
 		 automatically changed to 4 bytes.
+
+What:		/sys/class/tty/ttyS0/pps_4wire
+Date:		September 2018
+Contact:	Steve Sakoman <steve@xxxxxxxxxxx>
+Description:
+		 Shows/sets "4 wire" mode for the PPS input to the serial driver.
+		 For fully implemented serial ports PPS is normally provided
+		 on the DCD line. For partial "4 wire" implementations CTS is
+		 used instead of DCD.
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 80bb56f..5839bed 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2664,6 +2664,56 @@ static ssize_t uart_get_attr_iomem_reg_shift(struct device *dev,
 	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,8 @@ static DEVICE_ATTR(custom_divisor, S_IRUSR | S_IRGRP, uart_get_attr_custom_divis
 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 +2744,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 +2801,9 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
 		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 +2979,7 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
 
 	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 +3008,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 --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 406edae..079793e 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -255,7 +255,8 @@ struct uart_port {
 	struct device		*dev;			/* parent device */
 	unsigned char		hub6;			/* this should be in the 8250 driver */
 	unsigned char		suspended;
-	unsigned char		unused[2];
+	unsigned char		pps_4wire;		/* CTS instead of DCD */
+	unsigned char		unused;
 	const char		*name;			/* port name */
 	struct attribute_group	*attr_group;		/* port specific attributes */
 	const struct attribute_group **tty_groups;	/* all attributes (serial core use only) */
-- 
2.7.4




[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux