2009-12-02 17:19:12

by Rodolfo Giometti

[permalink] [raw]
Subject: LinuxPPS new functionalities

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.

* Patches 07 and 10 add PPS serial support for specific hardware.

* Patch 08 adds PPS parallel support.

* Patch 09 is going to add a low level IRQ timestamps recording. Here
some notes:

1) This is just a proposal, so I'm quite sure it will be rejected,
but I decided to propose it anyway in order to have some
feedbacks.

2) Due previous point I didn't split up the patch into several (and
more logical) sub-parts in order to have a better review about
what I wish to do.

* Patch 11 shows how to add a low level IRQ timestamps recording for a
new platform once patch 09 has been added into main tree.

Even if patches 09 and 11 will be rejected I hope the others are
ok. :)

Ciao,

Rodolfo


2009-12-02 17:21:33

by Rodolfo Giometti

[permalink] [raw]
Subject: [PATCH 01/11] pps: userland header file for PPS API.

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

2009-12-02 17:19:16

by Rodolfo Giometti

[permalink] [raw]
Subject: [PATCH 02/11] pps: documentation programs and examples.

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, &params);
+ 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, &params);
+ 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

2009-12-02 17:19:23

by Rodolfo Giometti

[permalink] [raw]
Subject: [PATCH 03/11] pps: LinuxPPS clients support.

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

2009-12-02 17:19:58

by Rodolfo Giometti

[permalink] [raw]
Subject: [PATCH 04/11] ldisc: new dcd_change() method for line disciplines.

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

2009-12-02 17:19:20

by Rodolfo Giometti

[permalink] [raw]
Subject: [PATCH 05/11] ldisc n_tty: export all N_TTY ldisc methods.

Signed-off-by: Rodolfo Giometti <[email protected]>
---
drivers/char/n_tty.c | 31 +++++++++++++++++++++----------
include/linux/tty.h | 16 ++++++++++++++++
2 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index 2e50f4d..88733c5 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>

@@ -193,7 +194,7 @@ static void reset_buffer_flags(struct tty_struct *tty)
* Locking: ctrl_lock, read_lock.
*/

-static void n_tty_flush_buffer(struct tty_struct *tty)
+void n_tty_flush_buffer(struct tty_struct *tty)
{
unsigned long flags;
/* clear everything and unthrottle the driver */
@@ -209,6 +210,7 @@ static void n_tty_flush_buffer(struct tty_struct *tty)
}
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
}
+EXPORT_SYMBOL_GPL(n_tty_flush_buffer);

/**
* n_tty_chars_in_buffer - report available bytes
@@ -220,7 +222,7 @@ static void n_tty_flush_buffer(struct tty_struct *tty)
* Locking: read_lock
*/

-static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
+ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
{
unsigned long flags;
ssize_t n = 0;
@@ -236,6 +238,7 @@ static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
spin_unlock_irqrestore(&tty->read_lock, flags);
return n;
}
+EXPORT_SYMBOL_GPL(n_tty_chars_in_buffer);

/**
* is_utf8_continuation - utf8 multibyte check
@@ -1329,11 +1332,12 @@ handle_newline:
* IO must be woken up
*/

-static void n_tty_write_wakeup(struct tty_struct *tty)
+void n_tty_write_wakeup(struct tty_struct *tty)
{
if (tty->fasync && test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags))
kill_fasync(&tty->fasync, SIGIO, POLL_OUT);
}
+EXPORT_SYMBOL_GPL(n_tty_write_wakeup);

/**
* n_tty_receive_buf - data receive
@@ -1348,7 +1352,7 @@ static void n_tty_write_wakeup(struct tty_struct *tty)
* calls one at a time and in order (or using flush_to_ldisc)
*/

-static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
const unsigned char *p;
@@ -1422,6 +1426,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
if (tty->receive_room < TTY_THRESHOLD_THROTTLE)
tty_throttle(tty);
}
+EXPORT_SYMBOL_GPL(n_tty_receive_buf);

int is_ignored(int sig)
{
@@ -1443,7 +1448,7 @@ int is_ignored(int sig)
* Locking: Caller holds tty->termios_mutex
*/

-static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
+void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
{
int canon_change = 1;
BUG_ON(!tty);
@@ -1522,6 +1527,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
wake_up_interruptible(&tty->write_wait);
wake_up_interruptible(&tty->read_wait);
}
+EXPORT_SYMBOL_GPL(n_tty_set_termios);

/**
* n_tty_close - close the ldisc for this tty
@@ -1533,7 +1539,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
* ldisc methods are in progress.
*/

-static void n_tty_close(struct tty_struct *tty)
+void n_tty_close(struct tty_struct *tty)
{
n_tty_flush_buffer(tty);
if (tty->read_buf) {
@@ -1545,6 +1551,7 @@ static void n_tty_close(struct tty_struct *tty)
tty->echo_buf = NULL;
}
}
+EXPORT_SYMBOL_GPL(n_tty_close);

/**
* n_tty_open - open an ldisc
@@ -1556,7 +1563,7 @@ static void n_tty_close(struct tty_struct *tty)
* until a close.
*/

-static int n_tty_open(struct tty_struct *tty)
+int n_tty_open(struct tty_struct *tty)
{
if (!tty)
return -EINVAL;
@@ -1580,6 +1587,7 @@ static int n_tty_open(struct tty_struct *tty)
tty->closing = 0;
return 0;
}
+EXPORT_SYMBOL_GPL(n_tty_open);

static inline int input_available_p(struct tty_struct *tty, int amt)
{
@@ -1695,7 +1703,7 @@ static int job_control(struct tty_struct *tty, struct file *file)
* This code must be sure never to sleep through a hangup.
*/

-static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
+ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
{
unsigned char __user *b = buf;
@@ -1895,6 +1903,7 @@ do_it_again:
n_tty_set_room(tty);
return retval;
}
+EXPORT_SYMBOL_GPL(n_tty_read);

/**
* n_tty_write - write function for tty
@@ -1918,7 +1927,7 @@ do_it_again:
* lock themselves)
*/

-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
+ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr)
{
const unsigned char *b = buf;
@@ -1995,6 +2004,7 @@ break_out:
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
return (b - buf) ? b - buf : retval;
}
+EXPORT_SYMBOL_GPL(n_tty_write);

/**
* n_tty_poll - poll method for N_TTY
@@ -2010,7 +2020,7 @@ break_out:
* Called without the kernel lock held - fine
*/

-static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
+unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
poll_table *wait)
{
unsigned int mask = 0;
@@ -2037,6 +2047,7 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
mask |= POLLOUT | POLLWRNORM;
return mask;
}
+EXPORT_SYMBOL_GPL(n_tty_poll);

static unsigned long inq_canon(struct tty_struct *tty)
{
diff --git a/include/linux/tty.h b/include/linux/tty.h
index f0f43d0..e5217db 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -13,6 +13,7 @@
#include <linux/tty_driver.h>
#include <linux/tty_ldisc.h>
#include <linux/mutex.h>
+#include <linux/poll.h>

#include <asm/system.h>

@@ -482,6 +483,21 @@ extern void tty_ldisc_begin(void);
/* This last one is just for the tty layer internals and shouldn't be used elsewhere */
extern void tty_ldisc_enable(struct tty_struct *tty);

+extern void n_tty_flush_buffer(struct tty_struct *tty);
+extern ssize_t n_tty_chars_in_buffer(struct tty_struct *tty);
+extern void n_tty_write_wakeup(struct tty_struct *tty);
+extern void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count);
+extern void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old);
+extern void n_tty_close(struct tty_struct *tty);
+extern int n_tty_open(struct tty_struct *tty);
+extern ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
+ unsigned char __user *buf, size_t nr);
+extern ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
+ const unsigned char *buf, size_t nr);
+extern unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
+ poll_table *wait);
+

/* n_tty.c */
extern struct tty_ldisc_ops tty_ldisc_N_TTY;
--
1.6.3.3

2009-12-02 17:19:35

by Rodolfo Giometti

[permalink] [raw]
Subject: [PATCH 06/11] pps: serial clients support.

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 | 155 +++++++++++++++++++++++++++++++++++++++
include/linux/serial_core.h | 11 +++-
4 files changed, 173 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..c2a7be6
--- /dev/null
+++ b/drivers/pps/clients/pps-ldisc.c
@@ -0,0 +1,155 @@
+/*
+ * 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 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 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);
+}
+
+struct tty_ldisc_ops pps_ldisc_ops = {
+ .owner = THIS_MODULE,
+ .magic = PPS_TTY_MAGIC,
+ .name = "pps_tty",
+ .dcd_change = pps_tty_dcd_change,
+ .open = pps_tty_open,
+ .close = pps_tty_close,
+
+ /* Now we should use N_TTY ldisc methods in order to have
+ * normal tty behaviour
+ */
+ .flush_buffer = n_tty_flush_buffer,
+ .chars_in_buffer = n_tty_chars_in_buffer,
+ .read = n_tty_read,
+ .write = n_tty_write,
+ .ioctl = n_tty_ioctl_helper,
+ .set_termios = n_tty_set_termios,
+ .poll = n_tty_poll,
+ .receive_buf = n_tty_receive_buf,
+ .write_wakeup = n_tty_write_wakeup
+};
+
+/*
+ * Module stuff
+ */
+
+static int __init pps_tty_init(void)
+{
+ int err;
+
+ 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

2009-12-02 17:19:13

by Rodolfo Giometti

[permalink] [raw]
Subject: [PATCH 07/11] serial 8250: enable PPS support.

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

2009-12-02 17:21:35

by Rodolfo Giometti

[permalink] [raw]
Subject: [PATCH 08/11] pps: parallel port clients support.

Adds support for the PPS sources connected with the interrupt pin of a
parallel port.

Signed-off-by: Rodolfo Giometti <[email protected]>
---
drivers/char/lp.c | 61 +++++++++++++++++++++++++++++++++++++++++++
drivers/pps/clients/Kconfig | 10 +++++++
include/linux/parport.h | 22 +++++++++++++++
3 files changed, 93 insertions(+), 0 deletions(-)

diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index e444c2d..dca8a39 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -760,6 +760,27 @@ static struct console lpcons = {

#endif /* console on line printer */

+/* Support for PPS signal on the line printer */
+
+#ifdef CONFIG_PPS_CLIENT_LP
+
+static void lp_pps_echo(int source, int event, void *data)
+{
+ struct parport *port = data;
+ unsigned char status = parport_read_status(port);
+
+ /* echo event via SEL bit */
+ parport_write_control(port,
+ parport_read_control(port) | PARPORT_CONTROL_SELECT);
+
+ /* signal no event */
+ if ((status & PARPORT_STATUS_ACK) != 0)
+ parport_write_control(port,
+ parport_read_control(port) & ~PARPORT_CONTROL_SELECT);
+}
+
+#endif
+
/* --- initialisation code ------------------------------------- */

static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };
@@ -831,6 +852,38 @@ static int lp_register(int nr, struct parport *port)
}
#endif

+#ifdef CONFIG_PPS_CLIENT_LP
+ port->pps_info.owner = THIS_MODULE;
+ port->pps_info.dev = port->dev;
+ snprintf(port->pps_info.path, PPS_MAX_NAME_LEN, "/dev/lp%d", nr);
+
+ /* No PPS support if lp port has no IRQ line */
+ if (port->irq != PARPORT_IRQ_NONE) {
+ strncpy(port->pps_info.name, port->name, PPS_MAX_NAME_LEN);
+
+ port->pps_info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT | \
+ PPS_ECHOASSERT | \
+ PPS_CANWAIT | PPS_TSFMT_TSPEC;
+
+ port->pps_info.echo = lp_pps_echo;
+
+ port->pps_source = pps_register_source(&(port->pps_info),
+ PPS_CAPTUREASSERT | PPS_OFFSETASSERT);
+ if (port->pps_source < 0)
+ dev_err(port->dev,
+ "cannot register PPS source \"%s\"\n",
+ port->pps_info.path);
+ else
+ dev_info(port->dev, "PPS source #%d \"%s\" added\n",
+ port->pps_source, port->pps_info.path);
+ } else {
+ port->pps_source = -1;
+ dev_err(port->dev, "PPS support disabled because port \"%s\" "
+ "is in polling mode\n",
+ port->pps_info.path);
+ }
+#endif
+
return 0;
}

@@ -873,6 +926,14 @@ static void lp_detach (struct parport *port)
console_registered = NULL;
}
#endif /* CONFIG_LP_CONSOLE */
+
+#ifdef CONFIG_PPS_CLIENT_LP
+ if (port->pps_source >= 0) {
+ pps_unregister_source(port->pps_source);
+ dev_dbg(port->dev, "PPS source #%d \"%s\" removed\n",
+ port->pps_source, port->pps_info.path);
+ }
+#endif
}

static struct parport_driver lp_driver = {
diff --git a/drivers/pps/clients/Kconfig b/drivers/pps/clients/Kconfig
index 487c1c8..b4054cd 100644
--- a/drivers/pps/clients/Kconfig
+++ b/drivers/pps/clients/Kconfig
@@ -22,4 +22,14 @@ config PPS_CLIENT_LDISC
If you say yes here you get support for a PPS source connected
with the CD (Carrier Detect) pin of your serial port.

+comment "Parallel printer support (forced off)"
+ depends on !( PRINTER != n && !(PPS = m && PRINTER = y))
+
+config PPS_CLIENT_LP
+ bool "Parallel printer support"
+ depends on PRINTER != n && !(PPS = m && PRINTER = y)
+ help
+ If you say yes here you get support for a PPS source connected
+ with the interrupt pin of your parallel port.
+
endif
diff --git a/include/linux/parport.h b/include/linux/parport.h
index 38a423e..ce508a9 100644
--- a/include/linux/parport.h
+++ b/include/linux/parport.h
@@ -98,6 +98,7 @@ typedef enum {
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
+#include <linux/pps_kernel.h>
#include <linux/irqreturn.h>
#include <linux/semaphore.h>
#include <asm/system.h>
@@ -330,6 +331,11 @@ struct parport {

struct list_head full_list;
struct parport *slaves[3];
+
+#ifdef CONFIG_PPS_CLIENT_LP
+ struct pps_source_info pps_info;
+ int pps_source; /* PPS source ID */
+#endif
};

#define DEFAULT_SPIN_TIME 500 /* us */
@@ -518,6 +524,22 @@ extern int parport_daisy_select (struct parport *port, int daisy, int mode);
/* Lowlevel drivers _can_ call this support function to handle irqs. */
static inline void parport_generic_irq(struct parport *port)
{
+#ifdef CONFIG_PPS_CLIENT_LP
+ struct timespec __ts;
+ struct pps_ktime ts;
+
+ /* First of all we get the time stamp... */
+ getnstimeofday(&__ts);
+
+ /* ... and translate it to PPS time data struct */
+ ts.sec = __ts.tv_sec;
+ ts.nsec = __ts.tv_nsec;
+
+ pps_event(port->pps_source, &ts, PPS_CAPTUREASSERT, port);
+ dev_dbg(port->dev, "PPS assert at %lu on source #%d\n",
+ jiffies, port->pps_source);
+#endif
+
parport_ieee1284_interrupt (port);
read_lock(&port->cad_lock);
if (port->cad && port->cad->irq_func)
--
1.6.3.3

2009-12-02 17:21:13

by Rodolfo Giometti

[permalink] [raw]
Subject: [PATCH 09/11] pps: low level IRQ timestamps recording.

Add low level IRQ timestamps recording for x86 (32 and 64 bits)
platforms and enable UART clients in order to use it.

This improves PPS precision. :)

Signed-off-by: Rodolfo Giometti <[email protected]>
---
arch/x86/kernel/irq_32.c | 2 +
arch/x86/kernel/irq_64.c | 2 +
drivers/pps/Kconfig | 12 +++++++++++
include/linux/irqnr.h | 4 +++
include/linux/serial_core.h | 5 ++++
kernel/irq/handle.c | 46 +++++++++++++++++++++++++++++++++++++++++++
6 files changed, 71 insertions(+), 0 deletions(-)

diff --git a/arch/x86/kernel/irq_32.c b/arch/x86/kernel/irq_32.c
index 7d35d0f..0e34854 100644
--- a/arch/x86/kernel/irq_32.c
+++ b/arch/x86/kernel/irq_32.c
@@ -199,6 +199,8 @@ bool handle_irq(unsigned irq, struct pt_regs *regs)

overflow = check_stack_overflow();

+ irq_save_ts(irq);
+
desc = irq_to_desc(irq);
if (unlikely(!desc))
return false;
diff --git a/arch/x86/kernel/irq_64.c b/arch/x86/kernel/irq_64.c
index 977d8b4..afbdba0 100644
--- a/arch/x86/kernel/irq_64.c
+++ b/arch/x86/kernel/irq_64.c
@@ -54,6 +54,8 @@ bool handle_irq(unsigned irq, struct pt_regs *regs)

stack_overflow_check(regs);

+ irq_save_ts(irq);
+
desc = irq_to_desc(irq);
if (unlikely(!desc))
return false;
diff --git a/drivers/pps/Kconfig b/drivers/pps/Kconfig
index 1afe4e0..6e8a2aa 100644
--- a/drivers/pps/Kconfig
+++ b/drivers/pps/Kconfig
@@ -22,6 +22,18 @@ config PPS
To compile this driver as a module, choose M here: the module
will be called pps_core.ko.

+config PPS_IRQ_EVENTS
+ bool "Use low level IRQ timestamps"
+ depends on PPS && (X86_32 || X86_64)
+ default no
+ help
+ Say Y here if you wish using low level IRQ timestamps to register
+ PPS events.
+
+ This should improve PPS resolution but it delays echo functions
+ call. Note also that this function may not be supported by some
+ PPS clients!
+
config PPS_DEBUG
bool "PPS debugging messages"
depends on PPS
diff --git a/include/linux/irqnr.h b/include/linux/irqnr.h
index 7bf89bc..4a7f4e8 100644
--- a/include/linux/irqnr.h
+++ b/include/linux/irqnr.h
@@ -25,6 +25,7 @@

extern int nr_irqs;
extern struct irq_desc *irq_to_desc(unsigned int irq);
+extern struct timespec *irq_to_ts(unsigned int irq);

# define for_each_irq_desc(irq, desc) \
for (irq = 0, desc = irq_to_desc(irq); irq < nr_irqs; \
@@ -49,6 +50,9 @@ extern struct irq_desc *irq_to_desc(unsigned int irq);

#endif /* CONFIG_GENERIC_HARDIRQS */

+extern struct timespec *irq_to_ts(unsigned int irq);
+extern void irq_save_ts(unsigned int irq);
+
#define for_each_irq_nr(irq) \
for (irq = 0; irq < nr_irqs; irq++)

diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 4bfb046..5ffd720 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -189,6 +189,7 @@
#include <linux/tty.h>
#include <linux/mutex.h>
#include <linux/sysrq.h>
+#include <linux/irqnr.h>

struct uart_port;
struct serial_struct;
@@ -492,7 +493,11 @@ uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
struct timespec ts;

if (ld && ld->ops->dcd_change)
+#ifdef CONFIG_PPS_IRQ_EVENTS
+ ts = *(irq_to_ts(uport->irq));
+#else
getnstimeofday(&ts);
+#endif

uport->icount.dcd++;
#ifdef CONFIG_HARD_PPS
diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c
index 17c71bb..5c6d71b 100644
--- a/kernel/irq/handle.c
+++ b/kernel/irq/handle.c
@@ -133,6 +133,7 @@ static void init_one_irq_desc(int irq, struct irq_desc *desc, int node)
DEFINE_SPINLOCK(sparse_irq_lock);

struct irq_desc **irq_desc_ptrs __read_mostly;
+struct timespec *irq_ts __read_mostly;

static struct irq_desc irq_desc_legacy[NR_IRQS_LEGACY] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS_LEGACY-1] = {
@@ -167,6 +168,9 @@ int __init early_irq_init(void)
/* allocate irq_desc_ptrs array based on nr_irqs */
irq_desc_ptrs = kcalloc(nr_irqs, sizeof(void *), GFP_NOWAIT);

+ /* allocate irq_ts array based on nr_irqs */
+ irq_ts = kcalloc(nr_irqs, sizeof(struct timespec), GFP_NOWAIT);
+
/* allocate based on nr_cpu_ids */
kstat_irqs_legacy = kzalloc_node(NR_IRQS_LEGACY * nr_cpu_ids *
sizeof(int), GFP_NOWAIT, node);
@@ -197,6 +201,26 @@ struct irq_desc *irq_to_desc(unsigned int irq)
return NULL;
}

+void irq_save_ts(unsigned int irq)
+{
+#ifdef CONFIG_PPS_IRQ_EVENTS
+ if (irq_desc_ptrs && irq < nr_irqs)
+ getnstimeofday(&irq_ts[irq]);
+#endif
+}
+
+struct timespec *irq_to_ts(unsigned int irq)
+{
+ if (irq >= nr_irqs) {
+ WARN(1, "irq (%d) >= nr_irqs (%d) in irq_to_ts\n",
+ irq, nr_irqs);
+ return NULL;
+ }
+
+ return &irq_ts[irq];
+}
+EXPORT_SYMBOL(irq_to_ts);
+
struct irq_desc * __ref irq_to_desc_alloc_node(unsigned int irq, int node)
{
struct irq_desc *desc;
@@ -251,6 +275,8 @@ struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
}
};

+struct timespec irq_ts[NR_IRQS] __cacheline_aligned_in_smp;
+
static unsigned int kstat_irqs_all[NR_IRQS][NR_CPUS];
int __init early_irq_init(void)
{
@@ -279,6 +305,26 @@ struct irq_desc *irq_to_desc(unsigned int irq)
return (irq < NR_IRQS) ? irq_desc + irq : NULL;
}

+void irq_save_ts(unsigned int irq)
+{
+#ifdef CONFIG_PPS_IRQ_EVENTS
+ if (irq < NR_IRQS)
+ getnstimeofday(&irq_ts[irq]);
+#endif
+}
+
+struct timespec *irq_to_ts(unsigned int irq)
+{
+ if (irq >= NR_IRQS) {
+ WARN(1, "irq (%d) >= nr_irqs (%d) in irq_to_ts\n",
+ irq, nr_irqs);
+ return NULL;
+ }
+
+ return &irq_ts[irq];
+}
+EXPORT_SYMBOL(irq_to_ts);
+
struct irq_desc *irq_to_desc_alloc_node(unsigned int irq, int node)
{
return irq_to_desc(irq);
--
1.6.3.3

2009-12-02 17:19:17

by Rodolfo Giometti

[permalink] [raw]
Subject: [PATCH 10/11] serial amba-pl010: enable PPS support.

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

2009-12-02 17:20:56

by Rodolfo Giometti

[permalink] [raw]
Subject: [PATCH 11/11] arm: add low level IRQ timestamps recording for arm platforms.

Signed-off-by: Rodolfo Giometti <[email protected]>
---
arch/arm/kernel/irq.c | 2 ++
drivers/pps/Kconfig | 2 +-
2 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index c9a8619..4708db2 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -109,6 +109,8 @@ asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

irq_enter();

+ irq_save_ts(irq);
+
/*
* Some hardware gives randomly wrong interrupts. Rather
* than crashing, do something sensible.
diff --git a/drivers/pps/Kconfig b/drivers/pps/Kconfig
index 6e8a2aa..dcef32e 100644
--- a/drivers/pps/Kconfig
+++ b/drivers/pps/Kconfig
@@ -24,7 +24,7 @@ config PPS

config PPS_IRQ_EVENTS
bool "Use low level IRQ timestamps"
- depends on PPS && (X86_32 || X86_64)
+ depends on PPS && (X86_32 || X86_64 || ARM)
default no
help
Say Y here if you wish using low level IRQ timestamps to register
--
1.6.3.3

2009-12-02 18:22:49

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 05/11] ldisc n_tty: export all N_TTY ldisc methods.

On Wed, Dec 02, 2009 at 06:18:43PM +0100, Rodolfo Giometti wrote:
> Signed-off-by: Rodolfo Giometti <[email protected]>

Why make this change?

(hint, I think I know why, but you need to spell it out in the changelog
comments...)

thanks,

greg k-h

2009-12-02 18:58:03

by Alan

[permalink] [raw]
Subject: Re: [PATCH 06/11] pps: serial clients support.

On Wed, 2 Dec 2009 18:18:44 +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.

I would suggest one change here - but the pps-ldisc bits into the n_tty.c
file then you don't have to export everything. That or just export the
n_tty ops struct.

I think putting the pps ldisc (which is basically n_tty and a bit) into
n_tty is probably the right choice given how small and clean it is - what
do you think ?

2009-12-03 00:19:21

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 09/11] pps: low level IRQ timestamps recording.

On 12/02/2009 09:18 AM, Rodolfo Giometti wrote:
> Add low level IRQ timestamps recording for x86 (32 and 64 bits)
> platforms and enable UART clients in order to use it.
>
> This improves PPS precision. :)

It also invokes getnstimeofday on every single interrupt, including ones
which have absolutely nothing to do with the PPS, and are potentially
high volume. getnstimeofday can be a fairly expensive operation,
especially if the best available clock is away from the CPU, or it
requires extensive arithmetic in order to normalize the clock value...

-hpa


--
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel. I don't speak on their behalf.

2009-12-03 10:53:51

by Rodolfo Giometti

[permalink] [raw]
Subject: Re: [PATCH 06/11] pps: serial clients support.

On Wed, Dec 02, 2009 at 06:59:03PM +0000, Alan Cox wrote:
> On Wed, 2 Dec 2009 18:18:44 +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.
>
> I would suggest one change here - but the pps-ldisc bits into the n_tty.c
> file then you don't have to export everything. That or just export the
> n_tty ops struct.
>
> I think putting the pps ldisc (which is basically n_tty and a bit) into
> n_tty is probably the right choice given how small and clean it is - what
> do you think ?

I think it could be ok, but in this case where should I register
pps-ldisc? Into tty_ldisc_begin just after tty_ldisc_N_TTY?

Again, should I remove the possibility to compile pps-ldisc as module?

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

2009-12-03 11:45:46

by Alan

[permalink] [raw]
Subject: Re: [PATCH 06/11] pps: serial clients support.

> > I think putting the pps ldisc (which is basically n_tty and a bit) into
> > n_tty is probably the right choice given how small and clean it is - what
> > do you think ?
>
> I think it could be ok, but in this case where should I register
> pps-ldisc? Into tty_ldisc_begin just after tty_ldisc_N_TTY?

Yes.

> Again, should I remove the possibility to compile pps-ldisc as module?

It would do that yes. I hadn't considered that aspect of it - and that in
turn would force you to compile the core PPS support in to get pps-ldisc.

How about this then (I'm trying to see a way to avoid all thsoe exports
of functions)

/**
* n_tty_inherit_ops - initialise ldisc ops
* @ops: ops to initialise
*
* Allow a line discipline to inherit the basic operations
* from the n_tty line discipline. The caller must set up
* its own ldisc number, flags and name. It must use the
* inherited value of magic.
*/

void n_tty_init_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);

2009-12-03 12:01:32

by Rodolfo Giometti

[permalink] [raw]
Subject: Re: [PATCH 09/11] pps: low level IRQ timestamps recording.

On Wed, Dec 02, 2009 at 04:10:44PM -0800, H. Peter Anvin wrote:
> On 12/02/2009 09:18 AM, Rodolfo Giometti wrote:
> > Add low level IRQ timestamps recording for x86 (32 and 64 bits)
> > platforms and enable UART clients in order to use it.
> >
> > This improves PPS precision. :)
>
> It also invokes getnstimeofday on every single interrupt, including ones
> which have absolutely nothing to do with the PPS, and are potentially
> high volume. getnstimeofday can be a fairly expensive operation,
> especially if the best available clock is away from the CPU, or it
> requires extensive arithmetic in order to normalize the clock value...

I see, that's why I propose my patches to you: in order to have some
feedbacks for better solutions. :)

Can I add a IRQF_TIMESTAMP in order to ask to the system to record irq
timestamps for particular irq lines? The problem is that I cannot set
that flag at irq_register() time but I have to do it later... it can
be a problem?

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

2009-12-03 12:05:22

by Rodolfo Giometti

[permalink] [raw]
Subject: Re: [PATCH 06/11] pps: serial clients support.

On Thu, Dec 03, 2009 at 11:46:41AM +0000, Alan Cox wrote:

> > Again, should I remove the possibility to compile pps-ldisc as module?
>
> It would do that yes. I hadn't considered that aspect of it - and that in
> turn would force you to compile the core PPS support in to get pps-ldisc.
>
> How about this then (I'm trying to see a way to avoid all thsoe exports
> of functions)
>
> /**
> * n_tty_inherit_ops - initialise ldisc ops
> * @ops: ops to initialise
> *
> * Allow a line discipline to inherit the basic operations
> * from the n_tty line discipline. The caller must set up
> * its own ldisc number, flags and name. It must use the
> * inherited value of magic.
> */
>
> void n_tty_init_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);

The problem is that I have to do some operations into ldisc's open and
close methods (see linux/drivers/pps/clients/pps-ldisc.c).

By using your solution I inherit such methods but I cannot modify
their behaviour in a manner suitable for register/deregister PPS
support at runtime.

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

2009-12-03 12:23:46

by Alan

[permalink] [raw]
Subject: Re: [PATCH 06/11] pps: serial clients support.

> > void n_tty_init_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);
>
> The problem is that I have to do some operations into ldisc's open and
> close methods (see linux/drivers/pps/clients/pps-ldisc.c).
>
> By using your solution I inherit such methods but I cannot modify
> their behaviour in a manner suitable for register/deregister PPS
> support at runtime.

You can

n_tty_init_ops(&my_ops);
my_ops.num = whatever;
old_open = my_ops.open;
my_ops.open = pps_ntty_open;

etc

While not as pretty in C as C++ it works fine.

2009-12-05 06:51:50

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 09/11] pps: low level IRQ timestamps recording.

On 12/03/2009 04:01 AM, Rodolfo Giometti wrote:
>
> I see, that's why I propose my patches to you: in order to have some
> feedbacks for better solutions. :)
>
> Can I add a IRQF_TIMESTAMP in order to ask to the system to record irq
> timestamps for particular irq lines? The problem is that I cannot set
> that flag at irq_register() time but I have to do it later... it can
> be a problem?
>

Shouldn't be an issue, I don't think. Deregistration might be an
(minor) issue in the case of shared IRQs -- just requires some care.

-hpa