This patchset adds several PPS functionalities.
All patches have a specific comment but I prefer say something more:
* Patches 01-02 are just documentation about PPS support.
* Patch 03 adds PPS client support skeleton.
* Patches 04-06 add generic PPS serial support (with specific
modifications requested by Alan Cox).
* Patches 07-08 add PPS serial support for specific hardware.
Ciao,
Rodolfo
Function pl010_enable_ms() is automagically called when PPS ldisc
is selected.
Signed-off-by: Rodolfo Giometti <[email protected]>
---
drivers/serial/amba-pl010.c | 15 +++++++++++++++
1 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c
index 429a8ae..e4b3c2c 100644
--- a/drivers/serial/amba-pl010.c
+++ b/drivers/serial/amba-pl010.c
@@ -471,6 +471,20 @@ pl010_set_termios(struct uart_port *port, struct ktermios *termios,
spin_unlock_irqrestore(&uap->port.lock, flags);
}
+static void pl010_set_ldisc(struct uart_port *port)
+{
+ int line = port->line;
+
+ if (line >= port->state->port.tty->driver->num)
+ return;
+
+ if (port->state->port.tty->ldisc->ops->num == N_PPS) {
+ port->flags |= UPF_HARDPPS_CD;
+ pl010_enable_ms(port);
+ } else
+ port->flags &= ~UPF_HARDPPS_CD;
+}
+
static const char *pl010_type(struct uart_port *port)
{
return port->type == PORT_AMBA ? "AMBA" : NULL;
@@ -531,6 +545,7 @@ static struct uart_ops amba_pl010_pops = {
.startup = pl010_startup,
.shutdown = pl010_shutdown,
.set_termios = pl010_set_termios,
+ .set_ldisc = pl010_set_ldisc,
.type = pl010_type,
.release_port = pl010_release_port,
.request_port = pl010_request_port,
--
1.6.3.3
Each PPS source can be registered/deregistered into the system by
using special modules called "clients". They simply define the PPS
sources' attributes and implement the time signal registartion
mechanism.
This patch adds a special directory for such clients and adds a dummy
client that can be useful to test system integrity on real systems.
Signed-off-by: Rodolfo Giometti <[email protected]>
---
drivers/pps/Kconfig | 2 +
drivers/pps/Makefile | 1 +
drivers/pps/clients/Kconfig | 18 ++++++
drivers/pps/clients/Makefile | 9 +++
drivers/pps/clients/ktimer.c | 123 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 153 insertions(+), 0 deletions(-)
create mode 100644 drivers/pps/clients/Kconfig
create mode 100644 drivers/pps/clients/Makefile
create mode 100644 drivers/pps/clients/ktimer.c
diff --git a/drivers/pps/Kconfig b/drivers/pps/Kconfig
index cc2eb8e..1afe4e0 100644
--- a/drivers/pps/Kconfig
+++ b/drivers/pps/Kconfig
@@ -30,4 +30,6 @@ config PPS_DEBUG
messages to the system log. Select this if you are having a
problem with PPS support and want to see more of what is going on.
+source drivers/pps/clients/Kconfig
+
endmenu
diff --git a/drivers/pps/Makefile b/drivers/pps/Makefile
index 19ea582..98960dd 100644
--- a/drivers/pps/Makefile
+++ b/drivers/pps/Makefile
@@ -4,5 +4,6 @@
pps_core-y := pps.o kapi.o sysfs.o
obj-$(CONFIG_PPS) := pps_core.o
+obj-y += clients/
ccflags-$(CONFIG_PPS_DEBUG) := -DDEBUG
diff --git a/drivers/pps/clients/Kconfig b/drivers/pps/clients/Kconfig
new file mode 100644
index 0000000..60b83be
--- /dev/null
+++ b/drivers/pps/clients/Kconfig
@@ -0,0 +1,18 @@
+#
+# PPS clients configuration
+#
+
+if PPS
+
+comment "PPS clients support"
+
+config PPS_CLIENT_KTIMER
+ tristate "Kernel timer client (Testing client, use for debug)"
+ help
+ If you say yes here you get support for a PPS debugging client
+ which uses a kernel timer to generate the PPS signal.
+
+ This driver can also be built as a module. If so, the module
+ will be called ktimer.ko.
+
+endif
diff --git a/drivers/pps/clients/Makefile b/drivers/pps/clients/Makefile
new file mode 100644
index 0000000..f3c1e39
--- /dev/null
+++ b/drivers/pps/clients/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for PPS clients.
+#
+
+obj-$(CONFIG_PPS_CLIENT_KTIMER) += ktimer.o
+
+ifeq ($(CONFIG_PPS_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff --git a/drivers/pps/clients/ktimer.c b/drivers/pps/clients/ktimer.c
new file mode 100644
index 0000000..6de5dfc
--- /dev/null
+++ b/drivers/pps/clients/ktimer.c
@@ -0,0 +1,123 @@
+/*
+ * ktimer.c -- kernel timer test client
+ *
+ *
+ * Copyright (C) 2005-2006 Rodolfo Giometti <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/pps_kernel.h>
+
+/*
+ * Global variables
+ */
+
+static int source;
+static struct timer_list ktimer;
+
+/*
+ * The kernel timer
+ */
+
+static void pps_ktimer_event(unsigned long ptr)
+{
+ struct timespec __ts;
+ struct pps_ktime ts;
+
+ /* First of all we get the time stamp... */
+ getnstimeofday(&__ts);
+
+ pr_info("PPS event at %lu\n", jiffies);
+
+ /* ... and translate it to PPS time data struct */
+ ts.sec = __ts.tv_sec;
+ ts.nsec = __ts.tv_nsec;
+
+ pps_event(source, &ts, PPS_CAPTUREASSERT, NULL);
+
+ mod_timer(&ktimer, jiffies + HZ);
+}
+
+/*
+ * The echo function
+ */
+
+static void pps_ktimer_echo(int source, int event, void *data)
+{
+ pr_info("echo %s %s for source %d\n",
+ event & PPS_CAPTUREASSERT ? "assert" : "",
+ event & PPS_CAPTURECLEAR ? "clear" : "",
+ source);
+}
+
+/*
+ * The PPS info struct
+ */
+
+static struct pps_source_info pps_ktimer_info = {
+ .name = "ktimer",
+ .path = "",
+ .mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT | \
+ PPS_ECHOASSERT | \
+ PPS_CANWAIT | PPS_TSFMT_TSPEC,
+ .echo = pps_ktimer_echo,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * Module staff
+ */
+
+static void __exit pps_ktimer_exit(void)
+{
+ del_timer_sync(&ktimer);
+ pps_unregister_source(source);
+
+ pr_info("ktimer PPS source unregistered\n");
+}
+
+static int __init pps_ktimer_init(void)
+{
+ int ret;
+
+ ret = pps_register_source(&pps_ktimer_info,
+ PPS_CAPTUREASSERT | PPS_OFFSETASSERT);
+ if (ret < 0) {
+ printk(KERN_ERR "cannot register ktimer source\n");
+ return ret;
+ }
+ source = ret;
+
+ setup_timer(&ktimer, pps_ktimer_event, 0);
+ mod_timer(&ktimer, jiffies + HZ);
+
+ pr_info("ktimer PPS source registered at %d\n", source);
+
+ return 0;
+}
+
+module_init(pps_ktimer_init);
+module_exit(pps_ktimer_exit);
+
+MODULE_AUTHOR("Rodolfo Giometti <[email protected]>");
+MODULE_DESCRIPTION("dummy PPS source by using a kernel timer (just for debug)");
+MODULE_LICENSE("GPL");
--
1.6.3.3
Here some utilities and examples about the PPS API and the LinuxPPS
support.
* ppstest.c implements an useful testing program, while
* ppsfind tries to help the user into finding a specific PPS source by
using its name or path.
Signed-off-by: Rodolfo Giometti <[email protected]>
---
Documentation/pps/Makefile | 27 ++++++++
Documentation/pps/ppsfind | 17 +++++
Documentation/pps/ppstest.c | 150 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 194 insertions(+), 0 deletions(-)
create mode 100644 Documentation/pps/Makefile
create mode 100644 Documentation/pps/ppsfind
create mode 100644 Documentation/pps/ppstest.c
diff --git a/Documentation/pps/Makefile b/Documentation/pps/Makefile
new file mode 100644
index 0000000..3848aba
--- /dev/null
+++ b/Documentation/pps/Makefile
@@ -0,0 +1,27 @@
+TARGETS = ppstest
+
+CFLAGS += -Wall -O2 -D_GNU_SOURCE
+CFLAGS += -I . -I ../../include/
+CFLAGS += -ggdb
+
+# -- Actions section --
+
+.PHONY : all depend dep
+
+all : .depend $(TARGETS)
+
+.depend depend dep :
+ $(CC) $(CFLAGS) -M $(TARGETS:=.c) > .depend
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+
+# -- Clean section --
+
+.PHONY : clean
+
+clean :
+ rm -f *.o *~ core .depend
+ rm -f ${TARGETS}
diff --git a/Documentation/pps/ppsfind b/Documentation/pps/ppsfind
new file mode 100644
index 0000000..93c0e17
--- /dev/null
+++ b/Documentation/pps/ppsfind
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+SYS="/sys/class/pps/"
+
+if [ $# -lt 1 ] ; then
+ echo "usage: ppsfind <name>" >&2
+ exit 1
+fi
+
+for d in $(ls $SYS) ; do
+ if grep $1 $SYS/$d/name >& /dev/null || \
+ grep $1 $SYS/$d/path >& /dev/null ; then
+ echo "$d: name=$(cat $SYS/$d/name) path=$(cat $SYS/$d/path)"
+ fi
+done
+
+exit 0
diff --git a/Documentation/pps/ppstest.c b/Documentation/pps/ppstest.c
new file mode 100644
index 0000000..30c8f6d
--- /dev/null
+++ b/Documentation/pps/ppstest.c
@@ -0,0 +1,150 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <timepps.h>
+
+int find_source(char *path, pps_handle_t *handle, int *avail_mode)
+{
+ pps_params_t params;
+ int ret;
+
+ printf("trying PPS source \"%s\"\n", path);
+
+ /* Try to find the source by using the supplied "path" name */
+ ret = open(path, O_RDWR);
+ if (ret < 0) {
+ fprintf(stderr, "unable to open device \"%s\" (%m)\n", path);
+ return ret;
+ }
+
+ /* Open the PPS source (and check the file descriptor) */
+ ret = time_pps_create(ret, handle);
+ if (ret < 0) {
+ fprintf(stderr, "cannot create a PPS source from device "
+ "\"%s\" (%m)\n", path);
+ return -1;
+ }
+ printf("found PPS source \"%s\"\n", path);
+
+ /* Find out what features are supported */
+ ret = time_pps_getcap(*handle, avail_mode);
+ if (ret < 0) {
+ fprintf(stderr, "cannot get capabilities (%m)\n");
+ return -1;
+ }
+ if ((*avail_mode & PPS_CAPTUREASSERT) == 0) {
+ fprintf(stderr, "cannot CAPTUREASSERT\n");
+ return -1;
+ }
+ if ((*avail_mode & PPS_OFFSETASSERT) == 0) {
+ fprintf(stderr, "cannot OFFSETASSERT\n");
+ return -1;
+ }
+
+ /* Capture assert timestamps, and compensate for a 675 nsec
+ * propagation delay */
+ ret = time_pps_getparams(*handle, ¶ms);
+ if (ret < 0) {
+ fprintf(stderr, "cannot get parameters (%m)\n");
+ return -1;
+ }
+ params.assert_offset.tv_sec = 0;
+ params.assert_offset.tv_nsec = 675;
+ params.mode |= PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
+ params.mode &= ~(PPS_CAPTURECLEAR | PPS_OFFSETCLEAR);
+ ret = time_pps_setparams(*handle, ¶ms);
+ if (ret < 0) {
+ fprintf(stderr, "cannot set parameters (%m)\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int fetch_source(int i, pps_handle_t *handle, int *avail_mode)
+{
+ struct timespec timeout;
+ pps_info_t infobuf;
+ int ret;
+
+ /* create a zero-valued timeout */
+ timeout.tv_sec = 3;
+ timeout.tv_nsec = 0;
+
+retry:
+ if (*avail_mode & PPS_CANWAIT) /* waits for the next event */
+ ret = time_pps_fetch(*handle, PPS_TSFMT_TSPEC, &infobuf,
+ &timeout);
+ else {
+ sleep(1);
+ ret = time_pps_fetch(*handle, PPS_TSFMT_TSPEC, &infobuf,
+ &timeout);
+ }
+ if (ret < 0) {
+ if (ret == -EINTR) {
+ fprintf(stderr, "time_pps_fetch() got a signal!\n");
+ goto retry;
+ }
+
+ fprintf(stderr, "time_pps_fetch() error %d (%m)\n", ret);
+ return -1;
+ }
+
+ printf("source %d - "
+ "assert %ld.%09ld, sequence: %ld - "
+ "clear %ld.%09ld, sequence: %ld\n",
+ i,
+ infobuf.assert_timestamp.tv_sec,
+ infobuf.assert_timestamp.tv_nsec,
+ infobuf.assert_sequence,
+ infobuf.clear_timestamp.tv_sec,
+ infobuf.clear_timestamp.tv_nsec, infobuf.clear_sequence);
+
+ return 0;
+}
+
+void usage(char *name)
+{
+ fprintf(stderr, "usage: %s <ppsdev> [<ppsdev> ...]\n", name);
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ int num;
+ pps_handle_t handle[4];
+ int avail_mode[4];
+ int i = 0;
+ int ret;
+
+ /* Check the command line */
+ if (argc < 2)
+ usage(argv[0]);
+
+ for (i = 1; i < argc && i <= 4; i++) {
+ ret = find_source(argv[i], &handle[i - 1], &avail_mode[i - 1]);
+ if (ret < 0)
+ exit(EXIT_FAILURE);
+ }
+
+ num = i - 1;
+ printf("ok, found %d source(s), now start fetching data...\n", num);
+
+ /* loop, printing the most recent timestamp every second or so */
+ while (1) {
+ for (i = 0; i < num; i++) {
+ ret = fetch_source(i, &handle[i], &avail_mode[i]);
+ if (ret < 0 && errno != ETIMEDOUT)
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ for (; i >= 0; i--)
+ time_pps_destroy(handle[i]);
+
+ return 0;
+}
--
1.6.3.3
Signed-off-by: Rodolfo Giometti <[email protected]>
---
Documentation/serial/tty.txt | 4 ++++
include/linux/tty_ldisc.h | 8 ++++++++
2 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/Documentation/serial/tty.txt b/Documentation/serial/tty.txt
index 8e65c44..3fc812a 100644
--- a/Documentation/serial/tty.txt
+++ b/Documentation/serial/tty.txt
@@ -100,6 +100,10 @@ write_wakeup() - May be called at any point between open and close.
is permitted to call the driver write method from
this function. In such a situation defer it.
+dcd_change() - Report to the tty line the current DCD pin status
+ changes and the relative timestamp. The timestamp
+ can be NULL.
+
Driver Access
diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h
index 0c4ee9b..526d66f 100644
--- a/include/linux/tty_ldisc.h
+++ b/include/linux/tty_ldisc.h
@@ -99,6 +99,12 @@
* cease I/O to the tty driver. Can sleep. The driver should
* seek to perform this action quickly but should wait until
* any pending driver I/O is completed.
+ *
+ * void (*dcd_change)(struct tty_struct *tty, unsigned int status,
+ * struct timespec *ts)
+ *
+ * Tells the discipline that the DCD pin has changed its status and
+ * the relative timestamp. Pointer ts can be NULL.
*/
#include <linux/fs.h>
@@ -136,6 +142,8 @@ struct tty_ldisc_ops {
void (*receive_buf)(struct tty_struct *, const unsigned char *cp,
char *fp, int count);
void (*write_wakeup)(struct tty_struct *);
+ void (*dcd_change)(struct tty_struct *, unsigned int,
+ struct timespec *);
struct module *owner;
--
1.6.3.3
This new method can be used to init a new struct tty_ldisc_ops as the
default tty_ldisc_N_TTY struct.
Signed-off-by: Rodolfo Giometti <[email protected]>
---
drivers/char/n_tty.c | 9 +++++++++
include/linux/tty.h | 1 +
2 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index 2e50f4d..0f11375 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -48,6 +48,7 @@
#include <linux/audit.h>
#include <linux/file.h>
#include <linux/uaccess.h>
+#include <linux/module.h>
#include <asm/system.h>
@@ -2091,3 +2092,11 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = {
.receive_buf = n_tty_receive_buf,
.write_wakeup = n_tty_write_wakeup
};
+
+void n_tty_inherit_ops(struct tty_ldisc_ops *ops)
+{
+ *ops = tty_ldisc_N_TTY;
+ ops->owner = NULL;
+ ops->refcount = ops->flags = 0;
+}
+EXPORT_SYMBOL_GPL(n_tty_inherit_ops);
diff --git a/include/linux/tty.h b/include/linux/tty.h
index f0f43d0..5091438 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -485,6 +485,7 @@ extern void tty_ldisc_enable(struct tty_struct *tty);
/* n_tty.c */
extern struct tty_ldisc_ops tty_ldisc_N_TTY;
+extern void n_tty_inherit_ops(struct tty_ldisc_ops *ops);
/* tty_audit.c */
#ifdef CONFIG_AUDIT
--
1.6.3.3
Adds support, by using the PPS line discipline, for the PPS sources
connected with the CD (Carrier Detect) pin of a serial port.
Signed-off-by: Rodolfo Giometti <[email protected]>
---
drivers/pps/clients/Kconfig | 7 ++
drivers/pps/clients/Makefile | 1 +
drivers/pps/clients/pps-ldisc.c | 154 +++++++++++++++++++++++++++++++++++++++
include/linux/serial_core.h | 11 +++-
4 files changed, 172 insertions(+), 1 deletions(-)
create mode 100644 drivers/pps/clients/pps-ldisc.c
diff --git a/drivers/pps/clients/Kconfig b/drivers/pps/clients/Kconfig
index 60b83be..487c1c8 100644
--- a/drivers/pps/clients/Kconfig
+++ b/drivers/pps/clients/Kconfig
@@ -15,4 +15,11 @@ config PPS_CLIENT_KTIMER
This driver can also be built as a module. If so, the module
will be called ktimer.ko.
+config PPS_CLIENT_LDISC
+ tristate "PPS line discipline"
+ depends on PPS
+ help
+ If you say yes here you get support for a PPS source connected
+ with the CD (Carrier Detect) pin of your serial port.
+
endif
diff --git a/drivers/pps/clients/Makefile b/drivers/pps/clients/Makefile
index f3c1e39..9f5b988 100644
--- a/drivers/pps/clients/Makefile
+++ b/drivers/pps/clients/Makefile
@@ -3,6 +3,7 @@
#
obj-$(CONFIG_PPS_CLIENT_KTIMER) += ktimer.o
+obj-$(CONFIG_PPS_CLIENT_LDISC) += pps-ldisc.o
ifeq ($(CONFIG_PPS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/pps/clients/pps-ldisc.c b/drivers/pps/clients/pps-ldisc.c
new file mode 100644
index 0000000..59e6301
--- /dev/null
+++ b/drivers/pps/clients/pps-ldisc.c
@@ -0,0 +1,154 @@
+/*
+ * pps-ldisc.c -- PPS line discipline
+ *
+ *
+ * Copyright (C) 2008 Rodolfo Giometti <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/pps_kernel.h>
+
+#define PPS_TTY_MAGIC 0x0001
+
+static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status,
+ struct timespec *ts)
+{
+ int id = (int) tty->disc_data;
+ struct timespec __ts;
+ struct pps_ktime pps_ts;
+
+ /* First of all we get the time stamp... */
+ getnstimeofday(&__ts);
+
+ /* Does caller give us a timestamp? */
+ if (ts) { /* Yes. Let's use it! */
+ pps_ts.sec = ts->tv_sec;
+ pps_ts.nsec = ts->tv_nsec;
+ } else { /* No. Do it ourself! */
+ pps_ts.sec = __ts.tv_sec;
+ pps_ts.nsec = __ts.tv_nsec;
+ }
+
+ /* Now do the PPS event report */
+ pps_event(id, &pps_ts, status ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR,
+ NULL);
+
+ pr_debug("PPS %s at %lu on source #%d\n",
+ status ? "assert" : "clear", jiffies, id);
+}
+
+static int (*n_tty_open)(struct tty_struct *tty);
+
+static int pps_tty_open(struct tty_struct *tty)
+{
+ struct pps_source_info info;
+ struct tty_driver *drv = tty->driver;
+ int index = tty->index + drv->name_base;
+ int ret;
+
+ info.owner = THIS_MODULE;
+ info.dev = NULL;
+ snprintf(info.name, PPS_MAX_NAME_LEN, "%s%d", drv->driver_name, index);
+ snprintf(info.path, PPS_MAX_NAME_LEN, "/dev/%s%d", drv->name, index);
+ info.mode = PPS_CAPTUREBOTH | \
+ PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \
+ PPS_CANWAIT | PPS_TSFMT_TSPEC;
+
+ ret = pps_register_source(&info, PPS_CAPTUREBOTH | \
+ PPS_OFFSETASSERT | PPS_OFFSETCLEAR);
+ if (ret < 0) {
+ pr_err("cannot register PPS source \"%s\"\n", info.path);
+ return ret;
+ }
+ tty->disc_data = (void *) ret;
+
+ /* Should open N_TTY ldisc too */
+ ret = n_tty_open(tty);
+ if (ret < 0)
+ pps_unregister_source((int) tty->disc_data);
+
+ pr_info("PPS source #%d \"%s\" added\n", ret, info.path);
+
+ return 0;
+}
+
+static void (*n_tty_close)(struct tty_struct *tty);
+
+static void pps_tty_close(struct tty_struct *tty)
+{
+ int id = (int) tty->disc_data;
+
+ pps_unregister_source(id);
+ n_tty_close(tty);
+
+ pr_info("PPS source #%d removed\n", id);
+}
+
+static struct tty_ldisc_ops pps_ldisc_ops;
+
+/*
+ * Module stuff
+ */
+
+static int __init pps_tty_init(void)
+{
+ int err;
+
+ /* Inherit the N_TTY's ops */
+ n_tty_inherit_ops(&pps_ldisc_ops);
+
+ /* Save N_TTY's open()/close() methods */
+ n_tty_open = pps_ldisc_ops.open;
+ n_tty_close = pps_ldisc_ops.close;
+
+ /* Init PPS_TTY data */
+ pps_ldisc_ops.owner = THIS_MODULE;
+ pps_ldisc_ops.magic = PPS_TTY_MAGIC;
+ pps_ldisc_ops.name = "pps_tty";
+ pps_ldisc_ops.dcd_change = pps_tty_dcd_change;
+ pps_ldisc_ops.open = pps_tty_open;
+ pps_ldisc_ops.close = pps_tty_close;
+
+ err = tty_register_ldisc(N_PPS, &pps_ldisc_ops);
+ if (err)
+ pr_err("can't register PPS line discipline\n");
+ else
+ pr_info("PPS line discipline registered\n");
+
+ return err;
+}
+
+static void __exit pps_tty_cleanup(void)
+{
+ int err;
+
+ err = tty_unregister_ldisc(N_PPS);
+ if (err)
+ pr_err("can't unregister PPS line discipline\n");
+ else
+ pr_info("PPS line discipline removed\n");
+}
+
+module_init(pps_tty_init);
+module_exit(pps_tty_cleanup);
+
+MODULE_ALIAS_LDISC(N_PPS);
+MODULE_AUTHOR("Rodolfo Giometti <[email protected]>");
+MODULE_DESCRIPTION("PPS TTY device driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index db532ce..4bfb046 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -488,9 +488,13 @@ uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
{
struct uart_state *state = uport->state;
struct tty_port *port = &state->port;
+ struct tty_ldisc *ld = tty_ldisc_ref(port->tty);
+ struct timespec ts;
- uport->icount.dcd++;
+ if (ld && ld->ops->dcd_change)
+ getnstimeofday(&ts);
+ uport->icount.dcd++;
#ifdef CONFIG_HARD_PPS
if ((uport->flags & UPF_HARDPPS_CD) && status)
hardpps();
@@ -502,6 +506,11 @@ uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
else if (port->tty)
tty_hangup(port->tty);
}
+
+ if (ld && ld->ops->dcd_change)
+ ld->ops->dcd_change(port->tty, status, &ts);
+ if (ld)
+ tty_ldisc_deref(ld);
}
/**
--
1.6.3.3
This patch adds into the PPS's documentation directory a possible
implementation of the PPS API (RFC 2783) by using the LinuxPPS's char
devices.
This file is not just an example but it can be used into real
systems. :)
Signed-off-by: Rodolfo Giometti <[email protected]>
---
Documentation/pps/timepps.h | 198 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 198 insertions(+), 0 deletions(-)
create mode 100644 Documentation/pps/timepps.h
diff --git a/Documentation/pps/timepps.h b/Documentation/pps/timepps.h
new file mode 100644
index 0000000..d2628d2
--- /dev/null
+++ b/Documentation/pps/timepps.h
@@ -0,0 +1,198 @@
+/*
+ * timepps.h -- PPS API main header
+ *
+ * Copyright (C) 2005-2007 Rodolfo Giometti <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _SYS_TIMEPPS_H_
+#define _SYS_TIMEPPS_H_
+
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/pps.h>
+
+#define LINUXPPS 1 /* signal we are using LinuxPPS */
+
+/*
+ * New data structures
+ */
+
+struct ntp_fp {
+ unsigned int integral;
+ unsigned int fractional;
+};
+
+union pps_timeu {
+ struct timespec tspec;
+ struct ntp_fp ntpfp;
+ unsigned long longpad[3];
+};
+
+struct pps_info {
+ unsigned long assert_sequence; /* seq. num. of assert event */
+ unsigned long clear_sequence; /* seq. num. of clear event */
+ union pps_timeu assert_tu; /* time of assert event */
+ union pps_timeu clear_tu; /* time of clear event */
+ int current_mode; /* current mode bits */
+};
+
+struct pps_params {
+ int api_version; /* API version # */
+ int mode; /* mode bits */
+ union pps_timeu assert_off_tu; /* offset compensation for assert */
+ union pps_timeu clear_off_tu; /* offset compensation for clear */
+};
+
+typedef int pps_handle_t; /* represents a PPS source */
+typedef unsigned long pps_seq_t; /* sequence number */
+typedef struct ntp_fp ntp_fp_t; /* NTP-compatible time stamp */
+typedef union pps_timeu pps_timeu_t; /* generic data type for time stamps */
+typedef struct pps_info pps_info_t;
+typedef struct pps_params pps_params_t;
+
+#define assert_timestamp assert_tu.tspec
+#define clear_timestamp clear_tu.tspec
+
+#define assert_timestamp_ntpfp assert_tu.ntpfp
+#define clear_timestamp_ntpfp clear_tu.ntpfp
+
+#define assert_offset assert_off_tu.tspec
+#define clear_offset clear_off_tu.tspec
+
+#define assert_offset_ntpfp assert_off_tu.ntpfp
+#define clear_offset_ntpfp clear_off_tu.ntpfp
+
+/*
+ * The PPS API
+ */
+
+static __inline int time_pps_create(int source, pps_handle_t *handle)
+{
+ int ret;
+ struct pps_kparams dummy;
+
+ if (!handle) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* First we check if current device is a valid PPS one by
+ * doing a dummy PPS_GETPARAMS...
+ */
+ ret = ioctl(source, PPS_GETPARAMS, &dummy);
+ if (ret) {
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+
+ /* ... then since in LinuxPPS there are no differences between a
+ * "PPS source" and a "PPS handle", we simply return the same value.
+ */
+ *handle = source;
+
+ return 0;
+}
+
+static __inline int time_pps_destroy(pps_handle_t handle)
+{
+ return close(handle);
+}
+
+static __inline int time_pps_getparams(pps_handle_t handle,
+ pps_params_t *ppsparams)
+{
+ int ret;
+ struct pps_kparams __ppsparams;
+
+ ret = ioctl(handle, PPS_GETPARAMS, &__ppsparams);
+
+ ppsparams->api_version = __ppsparams.api_version;
+ ppsparams->mode = __ppsparams.mode;
+ ppsparams->assert_off_tu.tspec.tv_sec = __ppsparams.assert_off_tu.sec;
+ ppsparams->assert_off_tu.tspec.tv_nsec = __ppsparams.assert_off_tu.nsec;
+ ppsparams->clear_off_tu.tspec.tv_sec = __ppsparams.clear_off_tu.sec;
+ ppsparams->clear_off_tu.tspec.tv_nsec = __ppsparams.clear_off_tu.nsec;
+
+ return ret;
+}
+
+static __inline int time_pps_setparams(pps_handle_t handle,
+ const pps_params_t *ppsparams)
+{
+ struct pps_kparams __ppsparams;
+
+ __ppsparams.api_version = ppsparams->api_version;
+ __ppsparams.mode = ppsparams->mode;
+ __ppsparams.assert_off_tu.sec = ppsparams->assert_off_tu.tspec.tv_sec;
+ __ppsparams.assert_off_tu.nsec = ppsparams->assert_off_tu.tspec.tv_nsec;
+ __ppsparams.clear_off_tu.sec = ppsparams->clear_off_tu.tspec.tv_sec;
+ __ppsparams.clear_off_tu.nsec = ppsparams->clear_off_tu.tspec.tv_nsec;
+
+ return ioctl(handle, PPS_SETPARAMS, &__ppsparams);
+}
+
+/* Get capabilities for handle */
+static __inline int time_pps_getcap(pps_handle_t handle, int *mode)
+{
+ return ioctl(handle, PPS_GETCAP, mode);
+}
+
+static __inline int time_pps_fetch(pps_handle_t handle, const int tsformat,
+ pps_info_t *ppsinfobuf,
+ const struct timespec *timeout)
+{
+ struct pps_fdata __fdata;
+ int ret;
+
+ /* Sanity checks */
+ if (tsformat != PPS_TSFMT_TSPEC) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (timeout) {
+ __fdata.timeout.sec = timeout->tv_sec;
+ __fdata.timeout.nsec = timeout->tv_nsec;
+ __fdata.timeout.flags = ~PPS_TIME_INVALID;
+ } else
+ __fdata.timeout.flags = PPS_TIME_INVALID;
+
+ ret = ioctl(handle, PPS_FETCH, &__fdata);
+
+ ppsinfobuf->assert_sequence = __fdata.info.assert_sequence;
+ ppsinfobuf->clear_sequence = __fdata.info.clear_sequence;
+ ppsinfobuf->assert_tu.tspec.tv_sec = __fdata.info.assert_tu.sec;
+ ppsinfobuf->assert_tu.tspec.tv_nsec = __fdata.info.assert_tu.nsec;
+ ppsinfobuf->clear_tu.tspec.tv_sec = __fdata.info.clear_tu.sec;
+ ppsinfobuf->clear_tu.tspec.tv_nsec = __fdata.info.clear_tu.nsec;
+ ppsinfobuf->current_mode = __fdata.info.current_mode;
+
+ return ret;
+}
+
+static __inline int time_pps_kcbind(pps_handle_t handle,
+ const int kernel_consumer,
+ const int edge, const int tsformat)
+{
+ /* LinuxPPS doesn't implement kernel consumer feature */
+ errno = EOPNOTSUPP;
+ return -1;
+}
+
+#endif /* _SYS_TIMEPPS_H_ */
--
1.6.3.3
Automagically function serial8250_enable_ms() is called when PPS ldisc
is selected.
Signed-off-by: Rodolfo Giometti <[email protected]>
---
drivers/serial/8250.c | 16 ++++++++++++++++
1 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index 737b4c9..e70187b 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -2415,6 +2415,21 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
}
static void
+serial8250_set_ldisc(struct uart_port *port)
+{
+ int line = port->line;
+
+ if (line >= port->state->port.tty->driver->num)
+ return;
+
+ if (port->state->port.tty->ldisc->ops->num == N_PPS) {
+ port->flags |= UPF_HARDPPS_CD;
+ serial8250_enable_ms(port);
+ } else
+ port->flags &= ~UPF_HARDPPS_CD;
+}
+
+static void
serial8250_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
@@ -2628,6 +2643,7 @@ static struct uart_ops serial8250_pops = {
.startup = serial8250_startup,
.shutdown = serial8250_shutdown,
.set_termios = serial8250_set_termios,
+ .set_ldisc = serial8250_set_ldisc,
.pm = serial8250_pm,
.type = serial8250_type,
.release_port = serial8250_release_port,
--
1.6.3.3
On Tue, 16 Feb 2010 10:51:17 +0100
Rodolfo Giometti <[email protected]> wrote:
> This patchset adds several PPS functionalities.
>
> All patches have a specific comment but I prefer say something more:
>
> * Patches 01-02 are just documentation about PPS support.
>
> * Patch 03 adds PPS client support skeleton.
>
> * Patches 04-06 add generic PPS serial support (with specific
> modifications requested by Alan Cox).
Thanks - the serial/tty stuff is much much cleaner in this patch set.
Hi Rodolfo,
On Tue, 16 Feb 2010 10:51:18 +0100
Rodolfo Giometti <[email protected]> wrote:
> This patch adds into the PPS's documentation directory a possible
> implementation of the PPS API (RFC 2783) by using the LinuxPPS's char
> devices.
>
> This file is not just an example but it can be used into real
> systems. :)
Isn't libc a proper place for this file? This header file provides a
generic standardized interface (PPSAPI) to the custom Linux
implementation (linux/pps.h). I think this is just what libc is for.
I wanted to file a bug in Debian BTS last week to add this header to
libc6-dev package and also push it upstream but something distracted
me. If you agree that the header should be in libc then I can do it now.
> Signed-off-by: Rodolfo Giometti <[email protected]>
> ---
> Documentation/pps/timepps.h | 198
> +++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 198
> insertions(+), 0 deletions(-) create mode 100644
> Documentation/pps/timepps.h
>
> diff --git a/Documentation/pps/timepps.h b/Documentation/pps/timepps.h
> new file mode 100644
> index 0000000..d2628d2
> --- /dev/null
> +++ b/Documentation/pps/timepps.h
> @@ -0,0 +1,198 @@
> +/*
> + * timepps.h -- PPS API main header
> + *
> + * Copyright (C) 2005-2007 Rodolfo Giometti <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> modify
> + * it under the terms of the GNU General Public License as
> published by
> + * the Free Software Foundation; either version 2 of the License,
> or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public
> License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#ifndef _SYS_TIMEPPS_H_
> +#define _SYS_TIMEPPS_H_
> +
> +#include <errno.h>
> +#include <sys/time.h>
> +#include <sys/ioctl.h>
> +#include <linux/types.h>
> +#include <linux/pps.h>
> +
> +#define LINUXPPS 1 /* signal we are using
> LinuxPPS */ +
> +/*
> + * New data structures
> + */
> +
> +struct ntp_fp {
> + unsigned int integral;
> + unsigned int fractional;
> +};
> +
> +union pps_timeu {
> + struct timespec tspec;
> + struct ntp_fp ntpfp;
> + unsigned long longpad[3];
> +};
> +
> +struct pps_info {
> + unsigned long assert_sequence; /* seq. num. of assert
> event */
> + unsigned long clear_sequence; /* seq. num. of clear
> event */
> + union pps_timeu assert_tu; /* time of assert event */
> + union pps_timeu clear_tu; /* time of clear event */
> + int current_mode; /* current mode bits */
> +};
> +
> +struct pps_params {
> + int api_version; /* API version # */
> + int mode; /* mode bits */
> + union pps_timeu assert_off_tu; /* offset compensation
> for assert */
> + union pps_timeu clear_off_tu; /* offset compensation
> for clear */ +};
> +
> +typedef int pps_handle_t; /* represents a PPS source
> */ +typedef unsigned long pps_seq_t; /* sequence number */
> +typedef struct ntp_fp ntp_fp_t; /* NTP-compatible
> time stamp */ +typedef union pps_timeu pps_timeu_t; /* generic
> data type for time stamps */ +typedef struct pps_info pps_info_t;
> +typedef struct pps_params pps_params_t;
> +
> +#define assert_timestamp assert_tu.tspec
> +#define clear_timestamp clear_tu.tspec
> +
> +#define assert_timestamp_ntpfp assert_tu.ntpfp
> +#define clear_timestamp_ntpfp clear_tu.ntpfp
> +
> +#define assert_offset assert_off_tu.tspec
> +#define clear_offset clear_off_tu.tspec
> +
> +#define assert_offset_ntpfp assert_off_tu.ntpfp
> +#define clear_offset_ntpfp clear_off_tu.ntpfp
> +
> +/*
> + * The PPS API
> + */
> +
> +static __inline int time_pps_create(int source, pps_handle_t *handle)
> +{
> + int ret;
> + struct pps_kparams dummy;
> +
> + if (!handle) {
> + errno = EINVAL;
> + return -1;
> + }
> +
> + /* First we check if current device is a valid PPS one by
> + * doing a dummy PPS_GETPARAMS...
> + */
> + ret = ioctl(source, PPS_GETPARAMS, &dummy);
> + if (ret) {
> + errno = EOPNOTSUPP;
> + return -1;
> + }
> +
> + /* ... then since in LinuxPPS there are no differences
> between a
> + * "PPS source" and a "PPS handle", we simply return the
> same value.
> + */
> + *handle = source;
> +
> + return 0;
> +}
> +
> +static __inline int time_pps_destroy(pps_handle_t handle)
> +{
> + return close(handle);
> +}
> +
> +static __inline int time_pps_getparams(pps_handle_t handle,
> + pps_params_t *ppsparams)
> +{
> + int ret;
> + struct pps_kparams __ppsparams;
> +
> + ret = ioctl(handle, PPS_GETPARAMS, &__ppsparams);
> +
> + ppsparams->api_version = __ppsparams.api_version;
> + ppsparams->mode = __ppsparams.mode;
> + ppsparams->assert_off_tu.tspec.tv_sec =
> __ppsparams.assert_off_tu.sec;
> + ppsparams->assert_off_tu.tspec.tv_nsec =
> __ppsparams.assert_off_tu.nsec;
> + ppsparams->clear_off_tu.tspec.tv_sec =
> __ppsparams.clear_off_tu.sec;
> + ppsparams->clear_off_tu.tspec.tv_nsec =
> __ppsparams.clear_off_tu.nsec; +
> + return ret;
> +}
> +
> +static __inline int time_pps_setparams(pps_handle_t handle,
> + const pps_params_t
> *ppsparams) +{
> + struct pps_kparams __ppsparams;
> +
> + __ppsparams.api_version = ppsparams->api_version;
> + __ppsparams.mode = ppsparams->mode;
> + __ppsparams.assert_off_tu.sec =
> ppsparams->assert_off_tu.tspec.tv_sec;
> + __ppsparams.assert_off_tu.nsec =
> ppsparams->assert_off_tu.tspec.tv_nsec;
> + __ppsparams.clear_off_tu.sec =
> ppsparams->clear_off_tu.tspec.tv_sec;
> + __ppsparams.clear_off_tu.nsec =
> ppsparams->clear_off_tu.tspec.tv_nsec; +
> + return ioctl(handle, PPS_SETPARAMS, &__ppsparams);
> +}
> +
> +/* Get capabilities for handle */
> +static __inline int time_pps_getcap(pps_handle_t handle, int *mode)
> +{
> + return ioctl(handle, PPS_GETCAP, mode);
> +}
> +
> +static __inline int time_pps_fetch(pps_handle_t handle, const int
> tsformat,
> + pps_info_t *ppsinfobuf,
> + const struct timespec
> *timeout) +{
> + struct pps_fdata __fdata;
> + int ret;
> +
> + /* Sanity checks */
> + if (tsformat != PPS_TSFMT_TSPEC) {
> + errno = EINVAL;
> + return -1;
> + }
> +
> + if (timeout) {
> + __fdata.timeout.sec = timeout->tv_sec;
> + __fdata.timeout.nsec = timeout->tv_nsec;
> + __fdata.timeout.flags = ~PPS_TIME_INVALID;
> + } else
> + __fdata.timeout.flags = PPS_TIME_INVALID;
> +
> + ret = ioctl(handle, PPS_FETCH, &__fdata);
> +
> + ppsinfobuf->assert_sequence = __fdata.info.assert_sequence;
> + ppsinfobuf->clear_sequence = __fdata.info.clear_sequence;
> + ppsinfobuf->assert_tu.tspec.tv_sec =
> __fdata.info.assert_tu.sec;
> + ppsinfobuf->assert_tu.tspec.tv_nsec =
> __fdata.info.assert_tu.nsec;
> + ppsinfobuf->clear_tu.tspec.tv_sec =
> __fdata.info.clear_tu.sec;
> + ppsinfobuf->clear_tu.tspec.tv_nsec =
> __fdata.info.clear_tu.nsec;
> + ppsinfobuf->current_mode = __fdata.info.current_mode;
> +
> + return ret;
> +}
> +
> +static __inline int time_pps_kcbind(pps_handle_t handle,
> + const int kernel_consumer,
> + const int edge, const int
> tsformat) +{
> + /* LinuxPPS doesn't implement kernel consumer feature */
> + errno = EOPNOTSUPP;
> + return -1;
> +}
> +
> +#endif /* _SYS_TIMEPPS_H_ */
--
Alexander
On Tue, 16 Feb 2010 10:51:19 +0100
Rodolfo Giometti <[email protected]> wrote:
> Here some utilities and examples about the PPS API and the LinuxPPS
> support.
>
> * ppstest.c implements an useful testing program, while
>
> * ppsfind tries to help the user into finding a specific PPS source by
> using its name or path.
Maybe these tools should be in a separate package (say pps-tools)? I've
written several tools too. They are based on your code. I think there
should be at least two of them:
* ppsctl to get/set params, get capabilities and bind/unbind kernel
consumer
* ppstest to fetch data and provide several representations for it
I can assist in this task too.
> Signed-off-by: Rodolfo Giometti <[email protected]>
> ---
> Documentation/pps/Makefile | 27 ++++++++
> Documentation/pps/ppsfind | 17 +++++
> Documentation/pps/ppstest.c | 150
> +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 194
> insertions(+), 0 deletions(-) create mode 100644
> Documentation/pps/Makefile create mode 100644
> Documentation/pps/ppsfind create mode 100644
> Documentation/pps/ppstest.c
>
> diff --git a/Documentation/pps/Makefile b/Documentation/pps/Makefile
> new file mode 100644
> index 0000000..3848aba
> --- /dev/null
> +++ b/Documentation/pps/Makefile
> @@ -0,0 +1,27 @@
> +TARGETS = ppstest
> +
> +CFLAGS += -Wall -O2 -D_GNU_SOURCE
> +CFLAGS += -I . -I ../../include/
> +CFLAGS += -ggdb
> +
> +# -- Actions section --
> +
> +.PHONY : all depend dep
> +
> +all : .depend $(TARGETS)
> +
> +.depend depend dep :
> + $(CC) $(CFLAGS) -M $(TARGETS:=.c) > .depend
> +
> +ifeq (.depend,$(wildcard .depend))
> +include .depend
> +endif
> +
> +
> +# -- Clean section --
> +
> +.PHONY : clean
> +
> +clean :
> + rm -f *.o *~ core .depend
> + rm -f ${TARGETS}
> diff --git a/Documentation/pps/ppsfind b/Documentation/pps/ppsfind
> new file mode 100644
> index 0000000..93c0e17
> --- /dev/null
> +++ b/Documentation/pps/ppsfind
> @@ -0,0 +1,17 @@
> +#!/bin/sh
> +
> +SYS="/sys/class/pps/"
> +
> +if [ $# -lt 1 ] ; then
> + echo "usage: ppsfind <name>" >&2
> + exit 1
> +fi
> +
> +for d in $(ls $SYS) ; do
> + if grep $1 $SYS/$d/name >& /dev/null || \
> + grep $1 $SYS/$d/path >& /dev/null ; then
> + echo "$d: name=$(cat $SYS/$d/name) path=$(cat
> $SYS/$d/path)"
> + fi
> +done
> +
> +exit 0
> diff --git a/Documentation/pps/ppstest.c b/Documentation/pps/ppstest.c
> new file mode 100644
> index 0000000..30c8f6d
> --- /dev/null
> +++ b/Documentation/pps/ppstest.c
> @@ -0,0 +1,150 @@
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +
> +#include <timepps.h>
> +
> +int find_source(char *path, pps_handle_t *handle, int *avail_mode)
> +{
> + pps_params_t params;
> + int ret;
> +
> + printf("trying PPS source \"%s\"\n", path);
> +
> + /* Try to find the source by using the supplied "path" name
> */
> + ret = open(path, O_RDWR);
> + if (ret < 0) {
> + fprintf(stderr, "unable to open device
> \"%s\" (%m)\n", path);
> + return ret;
> + }
> +
> + /* Open the PPS source (and check the file descriptor) */
> + ret = time_pps_create(ret, handle);
> + if (ret < 0) {
> + fprintf(stderr, "cannot create a PPS source from
> device "
> + "\"%s\" (%m)\n", path);
> + return -1;
> + }
> + printf("found PPS source \"%s\"\n", path);
> +
> + /* Find out what features are supported */
> + ret = time_pps_getcap(*handle, avail_mode);
> + if (ret < 0) {
> + fprintf(stderr, "cannot get capabilities (%m)\n");
> + return -1;
> + }
> + if ((*avail_mode & PPS_CAPTUREASSERT) == 0) {
> + fprintf(stderr, "cannot CAPTUREASSERT\n");
> + return -1;
> + }
> + if ((*avail_mode & PPS_OFFSETASSERT) == 0) {
> + fprintf(stderr, "cannot OFFSETASSERT\n");
> + return -1;
> + }
> +
> + /* Capture assert timestamps, and compensate for a 675 nsec
> + * propagation delay */
> + ret = time_pps_getparams(*handle, ¶ms);
> + if (ret < 0) {
> + fprintf(stderr, "cannot get parameters (%m)\n");
> + return -1;
> + }
> + params.assert_offset.tv_sec = 0;
> + params.assert_offset.tv_nsec = 675;
> + params.mode |= PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
> + params.mode &= ~(PPS_CAPTURECLEAR | PPS_OFFSETCLEAR);
> + ret = time_pps_setparams(*handle, ¶ms);
> + if (ret < 0) {
> + fprintf(stderr, "cannot set parameters (%m)\n");
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +int fetch_source(int i, pps_handle_t *handle, int *avail_mode)
> +{
> + struct timespec timeout;
> + pps_info_t infobuf;
> + int ret;
> +
> + /* create a zero-valued timeout */
> + timeout.tv_sec = 3;
> + timeout.tv_nsec = 0;
> +
> +retry:
> + if (*avail_mode & PPS_CANWAIT) /* waits for the next event */
> + ret = time_pps_fetch(*handle, PPS_TSFMT_TSPEC,
> &infobuf,
> + &timeout);
> + else {
> + sleep(1);
> + ret = time_pps_fetch(*handle, PPS_TSFMT_TSPEC,
> &infobuf,
> + &timeout);
> + }
> + if (ret < 0) {
> + if (ret == -EINTR) {
> + fprintf(stderr, "time_pps_fetch() got a
> signal!\n");
> + goto retry;
> + }
> +
> + fprintf(stderr, "time_pps_fetch() error %d (%m)\n",
> ret);
> + return -1;
> + }
> +
> + printf("source %d - "
> + "assert %ld.%09ld, sequence: %ld - "
> + "clear %ld.%09ld, sequence: %ld\n",
> + i,
> + infobuf.assert_timestamp.tv_sec,
> + infobuf.assert_timestamp.tv_nsec,
> + infobuf.assert_sequence,
> + infobuf.clear_timestamp.tv_sec,
> + infobuf.clear_timestamp.tv_nsec,
> infobuf.clear_sequence); +
> + return 0;
> +}
> +
> +void usage(char *name)
> +{
> + fprintf(stderr, "usage: %s <ppsdev> [<ppsdev> ...]\n", name);
> + exit(EXIT_FAILURE);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + int num;
> + pps_handle_t handle[4];
> + int avail_mode[4];
> + int i = 0;
> + int ret;
> +
> + /* Check the command line */
> + if (argc < 2)
> + usage(argv[0]);
> +
> + for (i = 1; i < argc && i <= 4; i++) {
> + ret = find_source(argv[i], &handle[i - 1],
> &avail_mode[i - 1]);
> + if (ret < 0)
> + exit(EXIT_FAILURE);
> + }
> +
> + num = i - 1;
> + printf("ok, found %d source(s), now start fetching
> data...\n", num); +
> + /* loop, printing the most recent timestamp every second or
> so */
> + while (1) {
> + for (i = 0; i < num; i++) {
> + ret = fetch_source(i, &handle[i],
> &avail_mode[i]);
> + if (ret < 0 && errno != ETIMEDOUT)
> + exit(EXIT_FAILURE);
> + }
> + }
> +
> + for (; i >= 0; i--)
> + time_pps_destroy(handle[i]);
> +
> + return 0;
> +}
--
Alexander
On Tue, Feb 16, 2010 at 03:33:01PM +0300, Alexander Gordeev wrote:
> Hi Rodolfo,
>
> On Tue, 16 Feb 2010 10:51:18 +0100
> Rodolfo Giometti <[email protected]> wrote:
>
> > This patch adds into the PPS's documentation directory a possible
> > implementation of the PPS API (RFC 2783) by using the LinuxPPS's char
> > devices.
> >
> > This file is not just an example but it can be used into real
> > systems. :)
>
> Isn't libc a proper place for this file? This header file provides a
> generic standardized interface (PPSAPI) to the custom Linux
> implementation (linux/pps.h). I think this is just what libc is for.
You are right, but I added that file for documentation purpose also.
Just to illustrate how to implement an high level API with my simple
char device interface.
> I wanted to file a bug in Debian BTS last week to add this header to
> libc6-dev package and also push it upstream but something distracted
> me. If you agree that the header should be in libc then I can do it now.
I agree. :)
Can you please put me in Cc in your letters with the Debian people?
Thanks a lot,
Rodolfo
--
GNU/Linux Solutions e-mail: [email protected]
Linux Device Driver [email protected]
Embedded Systems phone: +39 349 2432127
UNIX programming skype: rodolfo.giometti
Freelance ICT Italia - Consulente ICT Italia - http://www.consulenti-ict.it
On Tue, Feb 16, 2010 at 03:44:56PM +0300, Alexander Gordeev wrote:
> On Tue, 16 Feb 2010 10:51:19 +0100
> Rodolfo Giometti <[email protected]> wrote:
>
> > Here some utilities and examples about the PPS API and the LinuxPPS
> > support.
> >
> > * ppstest.c implements an useful testing program, while
> >
> > * ppsfind tries to help the user into finding a specific PPS source by
> > using its name or path.
>
> Maybe these tools should be in a separate package (say pps-tools)? I've
> written several tools too. They are based on your code. I think there
> should be at least two of them:
>
> * ppsctl to get/set params, get capabilities and bind/unbind kernel
> consumer
>
> * ppstest to fetch data and provide several representations for it
>
> I can assist in this task too.
Great! It's ok for me. Please, let me know how we can proceed.
Thanks a lot,
Rodolfo
--
GNU/Linux Solutions e-mail: [email protected]
Linux Device Driver [email protected]
Embedded Systems phone: +39 349 2432127
UNIX programming skype: rodolfo.giometti
Freelance ICT Italia - Consulente ICT Italia - http://www.consulenti-ict.it
On Tue, 16 Feb 2010 10:51:22 +0100
Rodolfo Giometti <[email protected]> wrote:
> };
> +
> +void n_tty_inherit_ops(struct tty_ldisc_ops *ops)
> +{
> + *ops = tty_ldisc_N_TTY;
> + ops->owner = NULL;
> + ops->refcount = ops->flags = 0;
> +}
> +EXPORT_SYMBOL_GPL(n_tty_inherit_ops);
Most (nearly all) functions in n_tty.c have been documented in
kerneldoc form. Let's continue that please.
n_tty_inherit_ops() is of course pretty simple, but OTOH it's a global,
export-to-modules interface.
On Tue, 16 Feb 2010 10:51:23 +0100
Rodolfo Giometti <[email protected]> wrote:
> Adds support, by using the PPS line discipline, for the PPS sources
> connected with the CD (Carrier Detect) pin of a serial port.
>
> ...
>
> +static int (*n_tty_open)(struct tty_struct *tty);
> +
> +static int pps_tty_open(struct tty_struct *tty)
> +{
> + struct pps_source_info info;
> + struct tty_driver *drv = tty->driver;
> + int index = tty->index + drv->name_base;
> + int ret;
> +
> + info.owner = THIS_MODULE;
> + info.dev = NULL;
> + snprintf(info.name, PPS_MAX_NAME_LEN, "%s%d", drv->driver_name, index);
> + snprintf(info.path, PPS_MAX_NAME_LEN, "/dev/%s%d", drv->name, index);
> + info.mode = PPS_CAPTUREBOTH | \
> + PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \
> + PPS_CANWAIT | PPS_TSFMT_TSPEC;
> +
> + ret = pps_register_source(&info, PPS_CAPTUREBOTH | \
> + PPS_OFFSETASSERT | PPS_OFFSETCLEAR);
> + if (ret < 0) {
> + pr_err("cannot register PPS source \"%s\"\n", info.path);
> + return ret;
> + }
> + tty->disc_data = (void *) ret;
> +
> + /* Should open N_TTY ldisc too */
> + ret = n_tty_open(tty);
> + if (ret < 0)
> + pps_unregister_source((int) tty->disc_data);
> +
> + pr_info("PPS source #%d \"%s\" added\n", ret, info.path);
> +
> + return 0;
> +}
hm. The n_tty_open/close layering is a bit grubby but I guess we don't
have a need to create anything more comprehensive at this stage.
A small stylistic thing: the code's a bit confusing at present because
the names n_tty_open/n_tty_close are identical to the global functions
over in n_tty.c. A user of ctags will be tricked. Also, the _way_ in
which these function pointers are called:
ret = n_tty_open(tty);
make it look like the code is calling
drivers/char/n_tty.c:n_tty_open(). Except it isn't.
So I think it's better to do it with
ret = (*n_tty_open)(tty);
to avoid misleading the reader.
And perhaps to rename these variables to avoid the duplication with the
global functions.
On Fri, Feb 19, 2010 at 01:53:59PM -0800, Andrew Morton wrote:
>
> hm. The n_tty_open/close layering is a bit grubby but I guess we don't
> have a need to create anything more comprehensive at this stage.
>
> A small stylistic thing: the code's a bit confusing at present because
> the names n_tty_open/n_tty_close are identical to the global functions
> over in n_tty.c. A user of ctags will be tricked. Also, the _way_ in
> which these function pointers are called:
>
> ret = n_tty_open(tty);
>
> make it look like the code is calling
> drivers/char/n_tty.c:n_tty_open(). Except it isn't.
>
> So I think it's better to do it with
>
> ret = (*n_tty_open)(tty);
>
> to avoid misleading the reader.
>
> And perhaps to rename these variables to avoid the duplication with the
> global functions.
It could be acceptable using functions
alias_n_tty_open()/alias_n_tty_close() modifying the code as follow?
diff --git a/drivers/pps/clients/pps-ldisc.c
b/drivers/pps/clients/pps-ldisc.c
index 59e6301..15c4622 100644
--- a/drivers/pps/clients/pps-ldisc.c
+++ b/drivers/pps/clients/pps-ldisc.c
@@ -53,7 +53,7 @@ static void pps_tty_dcd_change(struct tty_struct
*tty, unsigne
status ? "assert" : "clear", jiffies, id);
}
-static int (*n_tty_open)(struct tty_struct *tty);
+static int (*alias_n_tty_open)(struct tty_struct *tty);
static int pps_tty_open(struct tty_struct *tty)
{
@@ -79,7 +79,7 @@ static int pps_tty_open(struct tty_struct *tty)
tty->disc_data = (void *) ret;
/* Should open N_TTY ldisc too */
- ret = n_tty_open(tty);
+ ret = alias_n_tty_open(tty);
if (ret < 0)
pps_unregister_source((int) tty->disc_data);
@@ -88,14 +88,14 @@ static int pps_tty_open(struct tty_struct *tty)
return 0;
}
-static void (*n_tty_close)(struct tty_struct *tty);
+static void (*alias_n_tty_close)(struct tty_struct *tty);
static void pps_tty_close(struct tty_struct *tty)
{
int id = (int) tty->disc_data;
pps_unregister_source(id);
- n_tty_close(tty);
+ alias_n_tty_close(tty);
pr_info("PPS source #%d removed\n", id);
}
Ciao,
Rodolfo
--
GNU/Linux Solutions e-mail: [email protected]
Linux Device Driver [email protected]
Embedded Systems phone: +39 349 2432127
UNIX programming skype: rodolfo.giometti
Freelance ICT Italia - Consulente ICT Italia - http://www.consulenti-ict.it