[LinuxPPS] [PATCH 1/1] pps: pps_parport pps-echo implementation

Tom Burkart tom at aussec.com
Fri May 3 06:37:06 CEST 2019


This patch implements the pps echo functionality for pps_parport, that
sysfs claims is available already.

Configuration is via module parameters.  Any one of the four control
lines may be used as the ECHO output.

Signed-off-by: Tom Burkart <tom at aussec.com>
---
 drivers/pps/clients/pps_parport.c | 101 +++++++++++++++++++++++++++++++++++---
 1 file changed, 93 insertions(+), 8 deletions(-)

diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c
index 7226e39aae83..28f42e292901 100644
--- a/drivers/pps/clients/pps_parport.c
+++ b/drivers/pps/clients/pps_parport.c
@@ -20,11 +20,6 @@
  */
 
 
-/*
- * TODO:
- * implement echo over SEL pin
- */
-
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/kernel.h>
@@ -35,6 +30,8 @@
 #include <linux/slab.h>
 #include <linux/parport.h>
 #include <linux/pps_kernel.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
 
 #define DRVDESC "parallel port PPS client"
 
@@ -49,15 +46,33 @@ MODULE_PARM_DESC(clear_wait,
 	" zero turns clear edge capture off entirely");
 module_param(clear_wait, uint, 0);
 
+static unsigned int echo_active_ms = 0;
+MODULE_PARM_DESC(echo_active_ms,
+	"Active part of the echo pulse in ms, zero to disable (default)");
+module_param(echo_active_ms, uint, 0);
+static unsigned int invert_pps_echo = 0;
+MODULE_PARM_DESC(invert_pps_echo,
+	"Invert the PPS echo pulse, zero no inversion");
+module_param(invert_pps_echo, uint, 0);
+static unsigned int echo_pin = 1;
+MODULE_PARM_DESC(echo_pin,
+	"Pin number of echo pin to use: "
+	"1: STROBE (default), 14: LINEFEED, 16: RESET, 17: SELECT");
+module_param(echo_pin, uint, 0);
+
 static DEFINE_IDA(pps_client_index);
 
 /* internal per port structure */
 struct pps_client_pp {
 	struct pardevice *pardev;	/* parport device */
 	struct pps_device *pps;		/* PPS device */
+	struct timer_list echo_timer;	/* timer for resetting echo active edge */
+	unsigned long echo_timeout;	/* timer timeout value in jiffies */
 	unsigned int cw;		/* port clear timeout */
 	unsigned int cw_err;		/* number of timeouts */
 	int index;			/* device number */
+	unsigned int invert_pps_echo;	/* local copy of module parameters */
+	unsigned char echo_pin;
 };
 
 static inline int signal_is_set(struct parport *port)
@@ -123,19 +138,55 @@ static void parport_irq(void *handle)
 out_assert:
 	/* fire assert event */
 	pps_event(dev->pps, &ts_assert,
-			PPS_CAPTUREASSERT, NULL);
+			PPS_CAPTUREASSERT, handle);
 	return;
 
 out_both:
 	/* fire assert event */
 	pps_event(dev->pps, &ts_assert,
-			PPS_CAPTUREASSERT, NULL);
+			PPS_CAPTUREASSERT, handle);
 	/* fire clear event */
 	pps_event(dev->pps, &ts_clear,
-			PPS_CAPTURECLEAR, NULL);
+			PPS_CAPTURECLEAR, handle);
 	return;
 }
 
+/* echo function (only active if echo_active_ms is non-zero) */
+static void parport_echo(struct pps_device *pps, int event, void *handle)
+{
+	struct pps_client_pp *device = handle;
+	struct parport *port = device->pardev->port;
+
+	switch (event) {
+	case PPS_CAPTUREASSERT:
+		if (pps->params.mode & PPS_ECHOASSERT)
+			port->ops->write_control(port,
+				device->invert_pps_echo? device->echo_pin : 0);
+		break;
+
+	case PPS_CAPTURECLEAR:
+		if (pps->params.mode & PPS_ECHOCLEAR)
+			port->ops->write_control(port,
+				device->invert_pps_echo? device->echo_pin : 0);
+		break;
+	}
+	/* fire the timer */
+	if (pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) {
+		device->echo_timer.expires = jiffies + device->echo_timeout;
+		add_timer(&device->echo_timer);
+	}
+}
+
+/* Timer callback to reset the echo pin to the inactive state */
+static void parport_echo_timer_callback(struct timer_list *t)
+{
+	const struct pps_client_pp *device = from_timer(device, t, echo_timer);
+	struct parport *port = device->pardev->port;
+
+	port->ops->write_control(port,
+		device->invert_pps_echo? 0 : device->echo_pin);
+}
+
 static void parport_attach(struct parport *port)
 {
 	struct pardev_cb pps_client_cb;
@@ -158,6 +209,34 @@ static void parport_attach(struct parport *port)
 		return;
 	}
 
+	if (echo_active_ms > 999 ) {
+		pr_err("PPS ECHO active edge too long: %ums\n", echo_active_ms);
+		return;
+	}
+	if (echo_active_ms) {
+		switch (echo_pin) {
+		case 1:
+			device->echo_pin = PARPORT_CONTROL_STROBE;
+			break;
+		case 14:
+			device->echo_pin = PARPORT_CONTROL_AUTOFD;
+			break;
+		case 16:
+			device->echo_pin = PARPORT_CONTROL_INIT;
+			break;
+		case 17:
+			device->echo_pin = PARPORT_CONTROL_SELECT;
+			break;
+		default:
+			pr_err("Bad ECHO pin number: %u", echo_pin);
+			return;
+		}
+		device->invert_pps_echo = invert_pps_echo;
+		device->pps->info.echo = parport_echo;
+		device->echo_timeout = msecs_to_jiffies(echo_active_ms);
+		timer_setup(&device->echo_timer, parport_echo_timer_callback, 0);
+	}
+
 	index = ida_simple_get(&pps_client_index, 0, 0, GFP_KERNEL);
 	memset(&pps_client_cb, 0, sizeof(pps_client_cb));
 	pps_client_cb.private = device;
@@ -214,6 +293,12 @@ static void parport_detach(struct parport *port)
 
 	device = pardev->private;
 
+	if (device->pps->info.echo) {
+		del_timer_sync(&device->echo_timer);
+		/* reset echo pin in any case */
+		port->ops->write_control(port,
+			device->invert_pps_echo? 0 : device->echo_pin);
+	}
 	port->ops->disable_irq(port);
 	pps_unregister_source(device->pps);
 	parport_release(pardev);
-- 
2.12.3




More information about the discussions mailing list