diff options
Diffstat (limited to 'target/linux/generic-2.6/patches-2.6.23/230-pps_support.patch')
-rw-r--r-- | target/linux/generic-2.6/patches-2.6.23/230-pps_support.patch | 2152 |
1 files changed, 0 insertions, 2152 deletions
diff --git a/target/linux/generic-2.6/patches-2.6.23/230-pps_support.patch b/target/linux/generic-2.6/patches-2.6.23/230-pps_support.patch deleted file mode 100644 index a532f4199f..0000000000 --- a/target/linux/generic-2.6/patches-2.6.23/230-pps_support.patch +++ /dev/null @@ -1,2152 +0,0 @@ ---- /dev/null -+++ b/Documentation/pps/Makefile -@@ -0,0 +1,27 @@ -+TARGETS = ppstest ppsctl -+ -+CFLAGS += -Wall -O2 -D_GNU_SOURCE -+CFLAGS += -I . -+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} ---- /dev/null -+++ b/Documentation/pps/pps.txt -@@ -0,0 +1,170 @@ -+ -+ PPS - Pulse Per Second -+ ---------------------- -+ -+(C) Copyright 2007 Rodolfo Giometti <giometti@enneenne.com> -+ -+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. -+ -+ -+ -+Overview -+-------- -+ -+LinuxPPS provides a programming interface (API) to define into the -+system several PPS sources. -+ -+PPS means "pulse per second" and a PPS source is just a device which -+provides a high precision signal each second so that an application -+can use it to adjust system clock time. -+ -+A PPS source can be connected to a serial port (usually to the Data -+Carrier Detect pin) or to a parallel port (ACK-pin) or to a special -+CPU's GPIOs (this is the common case in embedded systems) but in each -+case when a new pulse comes the system must apply to it a timestamp -+and record it for the userland. -+ -+Common use is the combination of the NTPD as userland program with a -+GPS receiver as PPS source to obtain a wallclock-time with -+sub-millisecond synchronisation to UTC. -+ -+ -+RFC considerations -+------------------ -+ -+While implementing a PPS API as RFC 2783 defines and using an embedded -+CPU GPIO-Pin as physical link to the signal, I encountered a deeper -+problem: -+ -+ At startup it needs a file descriptor as argument for the function -+ time_pps_create(). -+ -+This implies that the source has a /dev/... entry. This assumption is -+ok for the serial and parallel port, where you can do something -+useful besides(!) the gathering of timestamps as it is the central -+task for a PPS-API. But this assumption does not work for a single -+purpose GPIO line. In this case even basic file-related functionality -+(like read() and write()) makes no sense at all and should not be a -+precondition for the use of a PPS-API. -+ -+The problem can be simply solved if you consider that a PPS source is -+not always connected with a GPS data source. -+ -+So your programs should check if the GPS data source (the serial port -+for instance) is a PPS source too, otherwise they should provide the -+possibility to open another device as PPS source. -+ -+In LinuxPPS the PPS sources are simply char devices usually mapped -+into files /dev/pps0, /dev/pps1, etc.. -+ -+ -+Coding example -+-------------- -+ -+To register a PPS source into the kernel you should define a struct -+pps_source_info_s as follow: -+ -+ static struct pps_source_info_s 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, -+ }; -+ -+and then calling the function pps_register_source() in your -+intialization routine as follow: -+ -+ source = pps_register_source(&pps_ktimer_info, -+ PPS_CAPTUREASSERT | PPS_OFFSETASSERT); -+ -+The pps_register_source() prototype is: -+ -+ int pps_register_source(struct pps_source_info_s *info, int default_params) -+ -+where "info" is a pointer to a structure that describes a particular -+PPS source, "default_params" tells the system what the initial default -+parameters for the device should be (is obvious that these parameters -+must be a subset of ones defined into the struct -+pps_source_info_s which describe the capabilities of the driver). -+ -+Once you have registered a new PPS source into the system you can -+signal an assert event (for example in the interrupt handler routine) -+just using: -+ -+ pps_event(source, PPS_CAPTUREASSERT, ptr); -+ -+The same function may also run the defined echo function -+(pps_ktimer_echo(), passing to it the "ptr" pointer) if the user -+asked for that... etc.. -+ -+Please see the file drivers/pps/clients/ktimer.c for an example code. -+ -+ -+SYSFS support -+------------- -+ -+If the SYSFS filesystem is enabled in the kernel it provides a new class: -+ -+ $ ls /sys/class/pps/ -+ pps0/ pps1/ pps2/ -+ -+Every directory is the ID of a PPS sources defined into the system and -+inside you find several files: -+ -+ $ ls /sys/class/pps/pps0/ -+ assert clear echo mode name path subsystem@ uevent -+ -+Inside each "assert" and "clear" file you can find the timestamp and a -+sequence number: -+ -+ $ cat /sys/class/pps/pps0/assert -+ 1170026870.983207967#8 -+ -+Where before the "#" is the timestamp in seconds and after it is the -+sequence number. Other files are: -+ -+* echo: reports if the PPS source has an echo function or not; -+ -+* mode: reports available PPS functioning modes; -+ -+* name: reports the PPS source's name; -+ -+* path: reports the PPS source's device path, that is the device the -+ PPS source is connected to (if it exists). -+ -+ -+Testing the PPS support -+----------------------- -+ -+In order to test the PPS support even without specific hardware you can use -+the ktimer driver (see the client subsection in the PPS configuration menu) -+and the userland tools provided into Documentaion/pps/ directory. -+ -+Once you have enabled the compilation of ktimer just modprobe it (if -+not statically compiled): -+ -+ # modprobe ktimer -+ -+and the run ppstest as follow: -+ -+ $ ./ppstest /dev/pps0 -+ trying PPS source "/dev/pps1" -+ found PPS source "/dev/pps1" -+ ok, found 1 source(s), now start fetching data... -+ source 0 - assert 1186592699.388832443, sequence: 364 - clear 0.000000000, sequence: 0 -+ source 0 - assert 1186592700.388931295, sequence: 365 - clear 0.000000000, sequence: 0 -+ source 0 - assert 1186592701.389032765, sequence: 366 - clear 0.000000000, sequence: 0 -+ -+Please, note that to compile userland programs you need the file timepps.h -+(see Documentation/pps/). ---- /dev/null -+++ b/Documentation/pps/ppsctl.c -@@ -0,0 +1,62 @@ -+#include <stdio.h> -+#include <unistd.h> -+#include <stdlib.h> -+#include <errno.h> -+#include <sys/ioctl.h> -+#include <sys/types.h> -+#include <sys/stat.h> -+#include <fcntl.h> -+#include <string.h> -+#include <linux/serial.h> -+ -+void usage(char *name) -+{ -+ fprintf(stderr, "usage: %s <ttyS> [enable|disable]\n", name); -+ -+ exit(EXIT_FAILURE); -+} -+ -+int main(int argc, char *argv[]) -+{ -+ int fd; -+ int ret; -+ struct serial_struct ss; -+ -+ if (argc < 2) -+ usage(argv[0]); -+ -+ fd = open(argv[1], O_RDWR); -+ if (fd < 0) { -+ perror("open"); -+ exit(EXIT_FAILURE); -+ } -+ -+ ret = ioctl(fd, TIOCGSERIAL, &ss); -+ if (ret < 0) { -+ perror("ioctl(TIOCGSERIAL)"); -+ exit(EXIT_FAILURE); -+ } -+ -+ if (argc < 3) { /* just read PPS status */ -+ printf("PPS is %sabled\n", -+ ss.flags & ASYNC_HARDPPS_CD ? "en" : "dis"); -+ exit(EXIT_SUCCESS); -+ } -+ -+ if (argv[2][0] == 'e' || argv[2][0] == '1') -+ ss.flags |= ASYNC_HARDPPS_CD; -+ else if (argv[2][0] == 'd' || argv[2][0] == '0') -+ ss.flags &= ~ASYNC_HARDPPS_CD; -+ else { -+ fprintf(stderr, "invalid state argument \"%s\"\n", argv[2]); -+ exit(EXIT_FAILURE); -+ } -+ -+ ret = ioctl(fd, TIOCSSERIAL, &ss); -+ if (ret < 0) { -+ perror("ioctl(TIOCSSERIAL)"); -+ exit(EXIT_FAILURE); -+ } -+ -+ return 0; -+} ---- /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 ---- /dev/null -+++ b/Documentation/pps/ppstest.c -@@ -0,0 +1,151 @@ -+#include <stdio.h> -+#include <stdlib.h> -+#include <unistd.h> -+#include <errno.h> -+#include <string.h> -+#include <sys/types.h> -+#include <sys/stat.h> -+#include <fcntl.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; -+ 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; -+} ---- /dev/null -+++ b/Documentation/pps/timepps.h -@@ -0,0 +1,193 @@ -+/* -+ * timepps.h -- PPS API main header -+ * -+ * Copyright (C) 2005-2007 Rodolfo Giometti <giometti@linux.it> -+ * -+ * 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 <sys/syscall.h> -+#include <unistd.h> -+#include <errno.h> -+#include <linux/pps.h> -+ -+/* -+ * 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; -+ -+ if (!handle) { -+ errno = EINVAL; -+ return -1; -+ } -+ -+ /* First we check if current device is a PPS valid PPS one... -+ */ -+ ret = ioctl(source, PPS_CHECK); -+ 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_ */ ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -3011,6 +3011,13 @@ P: James Chapman - M: jchapman@katalix.com - S: Maintained - -+PPS SUPPORT -+P: Rodolfo Giometti -+M: giometti@enneenne.com -+W: http://wiki.enneenne.com/index.php/LinuxPPS_support -+L: linuxpps@ml.enneenne.com -+S: Maintained -+ - PREEMPTIBLE KERNEL - P: Robert Love - M: rml@tech9.net ---- a/drivers/Kconfig -+++ b/drivers/Kconfig -@@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig" - - source "drivers/spi/Kconfig" - -+source "drivers/pps/Kconfig" -+ - source "drivers/w1/Kconfig" - - source "drivers/power/Kconfig" ---- a/drivers/Makefile -+++ b/drivers/Makefile -@@ -63,6 +63,7 @@ obj-$(CONFIG_INPUT) += input/ - obj-$(CONFIG_I2O) += message/ - obj-$(CONFIG_RTC_LIB) += rtc/ - obj-y += i2c/ -+obj-$(CONFIG_PPS) += pps/ - obj-$(CONFIG_W1) += w1/ - obj-$(CONFIG_POWER_SUPPLY) += power/ - obj-$(CONFIG_HWMON) += hwmon/ ---- a/drivers/char/lp.c -+++ b/drivers/char/lp.c -@@ -746,6 +746,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 }; -@@ -817,6 +838,38 @@ static int lp_register(int nr, struct pa - } - #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 due port \"%s\" is " -+ "in polling mode\n", -+ port->pps_info.path); -+ } -+#endif -+ - return 0; - } - -@@ -860,6 +913,14 @@ static void lp_detach (struct parport *p - 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 = { ---- /dev/null -+++ b/drivers/pps/Kconfig -@@ -0,0 +1,34 @@ -+# -+# PPS support configuration -+# -+ -+menu "PPS support" -+ -+config PPS -+ tristate "PPS support" -+ depends on EXPERIMENTAL -+ ---help--- -+ PPS (Pulse Per Second) is a special pulse provided by some GPS -+ antennae. Userland can use it to get an high time reference. -+ -+ Some antennae's PPS signals are connected with the CD (Carrier -+ Detect) pin of the serial line they use to communicate with the -+ host. In this case use the SERIAL_LINE client support. -+ -+ Some antennae's PPS signals are connected with some special host -+ inputs so you have to enable the corresponding client support. -+ -+ To compile this driver as a module, choose M here: the module -+ will be called pps_core.ko. -+ -+config PPS_DEBUG -+ bool "PPS debugging messages" -+ depends on PPS -+ help -+ Say Y here if you want the PPS support to produce a bunch of 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 ---- /dev/null -+++ b/drivers/pps/Makefile -@@ -0,0 +1,11 @@ -+# -+# Makefile for the PPS core. -+# -+ -+pps_core-objs += pps.o kapi.o sysfs.o -+obj-$(CONFIG_PPS) += pps_core.o -+obj-y += clients/ -+ -+ifeq ($(CONFIG_PPS_DEBUG),y) -+EXTRA_CFLAGS += -DDEBUG -+endif ---- /dev/null -+++ b/drivers/pps/clients/Kconfig -@@ -0,0 +1,38 @@ -+# -+# 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. -+ -+comment "UART serial support (forced off)" -+ depends on ! (SERIAL_CORE != n && !(PPS = m && SERIAL_CORE = y)) -+ -+config PPS_CLIENT_UART -+ bool "UART serial support" -+ depends on SERIAL_CORE != n && !(PPS = m && SERIAL_CORE = y) -+ help -+ 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 ---- /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 ---- /dev/null -+++ b/drivers/pps/clients/ktimer.c -@@ -0,0 +1,114 @@ -+/* -+ * ktimer.c -- kernel timer test client -+ * -+ * -+ * Copyright (C) 2005-2006 Rodolfo Giometti <giometti@linux.it> -+ * -+ * 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.h> -+ -+/* -+ * Global variables -+ */ -+ -+static int source; -+static struct timer_list ktimer; -+ -+/* -+ * The kernel timer -+ */ -+ -+static void pps_ktimer_event(unsigned long ptr) -+{ -+ pr_info("PPS event at %lu\n", jiffies); -+ -+ pps_event(source, 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 <giometti@linux.it>"); -+MODULE_DESCRIPTION("dummy PPS source by using a kernel timer (just for debug)"); -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/drivers/pps/kapi.c -@@ -0,0 +1,271 @@ -+/* -+ * kapi.c -- kernel API -+ * -+ * -+ * Copyright (C) 2005-2007 Rodolfo Giometti <giometti@linux.it> -+ * -+ * 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/sched.h> -+#include <linux/time.h> -+#include <linux/spinlock.h> -+#include <linux/idr.h> -+#include <linux/fs.h> -+#include <linux/pps.h> -+ -+/* -+ * Local variables -+ */ -+ -+static spinlock_t idr_lock = SPIN_LOCK_UNLOCKED; -+static DEFINE_IDR(pps_idr); -+ -+/* -+ * Local functions -+ */ -+ -+static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime *offset) -+{ -+ ts->nsec += offset->nsec; -+ if (ts->nsec >= NSEC_PER_SEC) { -+ ts->nsec -= NSEC_PER_SEC; -+ ts->sec++; -+ } else if (ts->nsec < 0) { -+ ts->nsec += NSEC_PER_SEC; -+ ts->sec--; -+ } -+ ts->sec += offset->sec; -+} -+ -+/* -+ * Exported functions -+ */ -+ -+int pps_register_source(struct pps_source_info *info, int default_params) -+{ -+ struct pps_device *pps; -+ int id; -+ int err; -+ -+ /* Sanity checks */ -+ if ((info->mode & default_params) != default_params) { -+ printk(KERN_ERR "pps: %s: unsupported default parameters\n", -+ info->name); -+ err = -EINVAL; -+ goto pps_register_source_exit; -+ } -+ if ((info->mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) != 0 && -+ info->echo == NULL) { -+ printk(KERN_ERR "pps: %s: echo function is not defined\n", -+ info->name); -+ err = -EINVAL; -+ goto pps_register_source_exit; -+ } -+ if ((info->mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) { -+ printk(KERN_ERR "pps: %s: unspecified time format\n", -+ info->name); -+ err = -EINVAL; -+ goto pps_register_source_exit; -+ } -+ -+ /* Allocate memory for the new PPS source struct */ -+ pps = kzalloc(sizeof(struct pps_device), GFP_KERNEL); -+ if (pps == NULL) { -+ err = -ENOMEM; -+ goto pps_register_source_exit; -+ } -+ -+ /* Get new ID for the new PPS source */ -+ if (idr_pre_get(&pps_idr, GFP_KERNEL) == 0) { -+ err = -ENOMEM; -+ goto kfree_pps; -+ } -+ -+ spin_lock_irq(&idr_lock); -+ err = idr_get_new(&pps_idr, pps, &id); -+ spin_unlock_irq(&idr_lock); -+ -+ if (err < 0) -+ goto kfree_pps; -+ -+ id = id & MAX_ID_MASK; -+ if (id >= PPS_MAX_SOURCES) { -+ printk(KERN_ERR "pps: %s: too much PPS sources in the system\n", -+ info->name); -+ err = -EBUSY; -+ goto free_idr; -+ } -+ -+ pps->id = id; -+ pps->params.api_version = PPS_API_VERS; -+ pps->params.mode = default_params; -+ pps->info = *info; -+ -+ init_waitqueue_head(&pps->queue); -+ spin_lock_init(&pps->lock); -+ atomic_set(&pps->usage, 0); -+ init_waitqueue_head(&pps->usage_queue); -+ -+ /* Create the char device */ -+ err = pps_register_cdev(pps); -+ if (err < 0) { -+ printk(KERN_ERR "pps: %s: unable to create char device\n", -+ info->name); -+ goto free_idr; -+ } -+ -+ /* Create the sysfs entry */ -+ err = pps_sysfs_create_source_entry(pps); -+ if (err < 0) { -+ printk(KERN_ERR "pps: %s: unable to create sysfs entries\n", -+ info->name); -+ goto unregister_cdev; -+ } -+ -+ pr_info("new PPS source %s at ID %d\n", info->name, id); -+ -+ return id; -+ -+unregister_cdev: -+ pps_unregister_cdev(pps); -+ -+free_idr: -+ spin_lock_irq(&idr_lock); -+ idr_remove(&pps_idr, id); -+ spin_unlock_irq(&idr_lock); -+ -+kfree_pps: -+ kfree(pps); -+ -+pps_register_source_exit: -+ printk(KERN_ERR "pps: %s: unable to register source\n", info->name); -+ -+ return err; -+} -+EXPORT_SYMBOL(pps_register_source); -+ -+void pps_unregister_source(int source) -+{ -+ struct pps_device *pps; -+ -+ spin_lock_irq(&idr_lock); -+ pps = idr_find(&pps_idr, source); -+ -+ if (!pps) { -+ spin_unlock_irq(&idr_lock); -+ return; -+ } -+ -+ /* This should be done first in order to deny IRQ handlers -+ * to access PPS structs -+ */ -+ -+ idr_remove(&pps_idr, pps->id); -+ spin_unlock_irq(&idr_lock); -+ -+ wait_event(pps->usage_queue, atomic_read(&pps->usage) == 0); -+ -+ pps_sysfs_remove_source_entry(pps); -+ pps_unregister_cdev(pps); -+ kfree(pps); -+} -+EXPORT_SYMBOL(pps_unregister_source); -+ -+void pps_event(int source, int event, void *data) -+{ -+ struct pps_device *pps; -+ struct timespec __ts; -+ struct pps_ktime ts; -+ unsigned long flags; -+ -+ /* 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; -+ -+ if ((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0 ) { -+ printk(KERN_ERR "pps: unknown event (%x) for source %d\n", -+ event, source); -+ return; -+ } -+ -+ spin_lock_irqsave(&idr_lock, flags); -+ pps = idr_find(&pps_idr, source); -+ -+ /* If we find a valid PPS source we lock it before leaving -+ * the lock! -+ */ -+ if (pps) -+ atomic_inc(&pps->usage); -+ spin_unlock_irqrestore(&idr_lock, flags); -+ -+ if (!pps) -+ return; -+ -+ pr_debug("PPS event on source %d at at %llu.%06u\n", -+ pps->id, ts.sec, ts.nsec); -+ -+ spin_lock_irqsave(&pps->lock, flags); -+ -+ /* Must call the echo function? */ -+ if ((pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR))) -+ pps->info.echo(source, event, data); -+ -+ /* Check the event */ -+ pps->current_mode = pps->params.mode; -+ if (event & PPS_CAPTUREASSERT) { -+ /* We have to add an offset? */ -+ if (pps->params.mode & PPS_OFFSETASSERT) -+ pps_add_offset(&ts, &pps->params.assert_off_tu); -+ -+ /* Save the time stamp */ -+ pps->assert_tu = ts; -+ pps->assert_sequence++; -+ pr_debug("capture assert seq #%u for source %d\n", -+ pps->assert_sequence, source); -+ } -+ if (event & PPS_CAPTURECLEAR) { -+ /* We have to add an offset? */ -+ if (pps->params.mode & PPS_OFFSETCLEAR) -+ pps_add_offset(&ts, &pps->params.clear_off_tu); -+ -+ /* Save the time stamp */ -+ pps->clear_tu = ts; -+ pps->clear_sequence++; -+ pr_debug("capture clear seq #%u for source %d\n", -+ pps->clear_sequence, source); -+ } -+ -+ pps->go = ~0; -+ wake_up_interruptible(&pps->queue); -+ -+ kill_fasync(&pps->async_queue, SIGIO, POLL_IN); -+ -+ spin_unlock_irqrestore(&pps->lock, flags); -+ -+ /* Now we can release the PPS source for (possible) deregistration */ -+ spin_lock_irqsave(&idr_lock, flags); -+ atomic_dec(&pps->usage); -+ wake_up_all(&pps->usage_queue); -+ spin_unlock_irqrestore(&idr_lock, flags); -+} -+EXPORT_SYMBOL(pps_event); ---- /dev/null -+++ b/drivers/pps/pps.c -@@ -0,0 +1,332 @@ -+/* -+ * pps.c -- Main PPS support file -+ * -+ * -+ * Copyright (C) 2005-2007 Rodolfo Giometti <giometti@linux.it> -+ * -+ * 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/version.h> -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/sched.h> -+#include <linux/uaccess.h> -+#include <linux/cdev.h> -+#include <linux/poll.h> -+#include <linux/pps.h> -+ -+/* -+ * Local variables -+ */ -+ -+static dev_t pps_devt; -+static struct class *pps_class; -+ -+/* -+ * Char device methods -+ */ -+ -+static unsigned int pps_cdev_poll(struct file *file, poll_table *wait) -+{ -+ struct pps_device *pps = file->private_data; -+ -+ poll_wait(file, &pps->queue, wait); -+ -+ return POLLIN | POLLRDNORM; -+} -+ -+static int pps_cdev_fasync(int fd, struct file *file, int on) -+{ -+ struct pps_device *pps = file->private_data; -+ return fasync_helper(fd, file, on, &pps->async_queue); -+} -+ -+static int pps_cdev_ioctl(struct inode *inode, struct file *file, -+ unsigned int cmd, unsigned long arg) -+{ -+ struct pps_device *pps = file->private_data; -+ struct pps_kparams params; -+ struct pps_fdata fdata; -+ unsigned long ticks; -+ void __user *uarg = (void __user *) arg; -+ int __user *iuarg = (int __user *) arg; -+ int err; -+ -+ switch (cmd) { -+ case PPS_CHECK: -+ -+ /* This does nothing but signal we are a PPS source... */ -+ -+ return 0; -+ -+ case PPS_GETPARAMS: -+ pr_debug("PPS_GETPARAMS: source %d\n", pps->id); -+ -+ /* Sanity checks */ -+ if (!uarg) -+ return -EINVAL; -+ -+ /* Return current parameters */ -+ err = copy_to_user(uarg, &pps->params, -+ sizeof(struct pps_kparams)); -+ if (err) -+ return -EFAULT; -+ -+ break; -+ -+ case PPS_SETPARAMS: -+ pr_debug("PPS_SETPARAMS: source %d\n", pps->id); -+ -+ /* Check the capabilities */ -+ if (!capable(CAP_SYS_TIME)) -+ return -EPERM; -+ -+ /* Sanity checks */ -+ if (!uarg) -+ return -EINVAL; -+ err = copy_from_user(¶ms, uarg, sizeof(struct pps_kparams)); -+ if (err) -+ return -EFAULT; -+ if (!(params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) { -+ pr_debug("capture mode unspecified (%x)\n", -+ params.mode); -+ return -EINVAL; -+ } -+ -+ /* Check for supported capabilities */ -+ if ((params.mode & ~pps->info.mode) != 0) { -+ pr_debug("unsupported capabilities (%x)\n", -+ params.mode); -+ return -EINVAL; -+ } -+ -+ spin_lock_irq(&pps->lock); -+ -+ /* Save the new parameters */ -+ pps->params = params; -+ -+ /* Restore the read only parameters */ -+ if ((params.mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) { -+ /* section 3.3 of RFC 2783 interpreted */ -+ pr_debug("time format unspecified (%x)\n", -+ params.mode); -+ pps->params.mode |= PPS_TSFMT_TSPEC; -+ } -+ if (pps->info.mode & PPS_CANWAIT) -+ pps->params.mode |= PPS_CANWAIT; -+ pps->params.api_version = PPS_API_VERS; -+ -+ spin_unlock_irq(&pps->lock); -+ -+ break; -+ -+ case PPS_GETCAP: -+ pr_debug("PPS_GETCAP: source %d\n", pps->id); -+ -+ /* Sanity checks */ -+ if (!uarg) -+ return -EINVAL; -+ -+ err = put_user(pps->info.mode, iuarg); -+ if (err) -+ return -EFAULT; -+ -+ break; -+ -+ case PPS_FETCH: -+ pr_debug("PPS_FETCH: source %d\n", pps->id); -+ -+ if (!uarg) -+ return -EINVAL; -+ err = copy_from_user(&fdata, uarg, sizeof(struct pps_fdata)); -+ if (err) -+ return -EFAULT; -+ -+ pps->go = 0; -+ -+ /* Manage the timeout */ -+ if (fdata.timeout.flags & PPS_TIME_INVALID) -+ err = wait_event_interruptible(pps->queue, pps->go); -+ else { -+ pr_debug("timeout %lld.%09d\n", -+ fdata.timeout.sec, fdata.timeout.nsec); -+ ticks = fdata.timeout.sec * HZ; -+ ticks += fdata.timeout.nsec / (NSEC_PER_SEC / HZ); -+ -+ if (ticks != 0) { -+ err = wait_event_interruptible_timeout( -+ pps->queue, pps->go, ticks); -+ if (err == 0) -+ return -ETIMEDOUT; -+ } -+ } -+ -+ /* Check for pending signals */ -+ if (err == -ERESTARTSYS) { -+ pr_debug("pending signal caught\n"); -+ return -EINTR; -+ } -+ -+ /* Return the fetched timestamp */ -+ spin_lock_irq(&pps->lock); -+ -+ fdata.info.assert_sequence = pps->assert_sequence; -+ fdata.info.clear_sequence = pps->clear_sequence; -+ fdata.info.assert_tu = pps->assert_tu; -+ fdata.info.clear_tu = pps->clear_tu; -+ fdata.info.current_mode = pps->current_mode; -+ -+ spin_unlock_irq(&pps->lock); -+ -+ err = copy_to_user(uarg, &fdata, sizeof(struct pps_fdata)); -+ if (err) -+ return -EFAULT; -+ -+ break; -+ -+ default: -+ return -ENOTTY; -+ break; -+ } -+ -+ return 0; -+} -+ -+static int pps_cdev_open(struct inode *inode, struct file *file) -+{ -+ struct pps_device *pps = container_of(inode->i_cdev, -+ struct pps_device, cdev); -+ -+ /* Lock the PPS source against (possible) deregistration */ -+ atomic_inc(&pps->usage); -+ -+ file->private_data = pps; -+ -+ return 0; -+} -+ -+static int pps_cdev_release(struct inode *inode, struct file *file) -+{ -+ struct pps_device *pps = file->private_data; -+ -+ /* Free the PPS source and wake up (possible) deregistration */ -+ atomic_dec(&pps->usage); -+ wake_up_all(&pps->usage_queue); -+ -+ return 0; -+} -+ -+/* -+ * Char device stuff -+ */ -+ -+static const struct file_operations pps_cdev_fops = { -+ .owner = THIS_MODULE, -+ .llseek = no_llseek, -+ .poll = pps_cdev_poll, -+ .fasync = pps_cdev_fasync, -+ .ioctl = pps_cdev_ioctl, -+ .open = pps_cdev_open, -+ .release = pps_cdev_release, -+}; -+ -+int pps_register_cdev(struct pps_device *pps) -+{ -+ int err; -+ -+ pps->devno = MKDEV(MAJOR(pps_devt), pps->id); -+ cdev_init(&pps->cdev, &pps_cdev_fops); -+ pps->cdev.owner = pps->info.owner; -+ -+ err = cdev_add(&pps->cdev, pps->devno, 1); -+ if (err) { -+ printk(KERN_ERR "pps: %s: failed to add char device %d:%d\n", -+ pps->info.name, MAJOR(pps_devt), pps->id); -+ return err; -+ } -+ pps->dev = device_create(pps_class, pps->info.dev, pps->devno, -+ "pps%d", pps->id); -+ if (err) -+ goto del_cdev; -+ dev_set_drvdata(pps->dev, pps); -+ -+ pr_debug("source %s got cdev (%d:%d)\n", pps->info.name, -+ MAJOR(pps_devt), pps->id); -+ -+ return 0; -+ -+del_cdev: -+ cdev_del(&pps->cdev); -+ -+ return err; -+} -+ -+void pps_unregister_cdev(struct pps_device *pps) -+{ -+ device_destroy(pps_class, pps->devno); -+ cdev_del(&pps->cdev); -+} -+ -+/* -+ * Module staff -+ */ -+ -+static void __exit pps_exit(void) -+{ -+ class_destroy(pps_class); -+ -+ if (pps_devt) -+ unregister_chrdev_region(pps_devt, PPS_MAX_SOURCES); -+ -+ pr_info("LinuxPPS API ver. %d removed\n", PPS_API_VERS); -+} -+ -+static int __init pps_init(void) -+{ -+ int err; -+ -+ pps_class = class_create(THIS_MODULE, "pps"); -+ if (!pps_class) { -+ printk(KERN_ERR "pps: ailed to allocate class\n"); -+ return -ENOMEM; -+ } -+ -+ err = alloc_chrdev_region(&pps_devt, 0, PPS_MAX_SOURCES, "pps"); -+ if (err < 0) { -+ printk(KERN_ERR "pps: failed to allocate char device region\n"); -+ goto remove_class; -+ } -+ -+ pr_info("LinuxPPS API ver. %d registered\n", PPS_API_VERS); -+ pr_info("Software ver. %s - Copyright 2005-2007 Rodolfo Giometti " -+ "<giometti@linux.it>\n", PPS_VERSION); -+ -+ return 0; -+ -+remove_class: -+ class_destroy(pps_class); -+ -+ return err; -+} -+ -+subsys_initcall(pps_init); -+module_exit(pps_exit); -+ -+MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); -+MODULE_DESCRIPTION("LinuxPPS support (RFC 2783) - ver. " PPS_VERSION); -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/drivers/pps/sysfs.c -@@ -0,0 +1,124 @@ -+/* -+ * sysfs.c -- sysfs support -+ * -+ * -+ * Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it> -+ * -+ * 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/device.h> -+#include <linux/module.h> -+#include <linux/string.h> -+#include <linux/pps.h> -+ -+/* -+ * Attribute functions -+ */ -+ -+static ssize_t pps_show_assert(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct pps_device *pps = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%lld.%09d#%d\n", -+ pps->assert_tu.sec, pps->assert_tu.nsec, -+ pps->assert_sequence); -+} -+DEVICE_ATTR(assert, S_IRUGO, pps_show_assert, NULL); -+ -+static ssize_t pps_show_clear(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct pps_device *pps = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%lld.%09d#%d\n", -+ pps->clear_tu.sec, pps->clear_tu.nsec, -+ pps->clear_sequence); -+} -+DEVICE_ATTR(clear, S_IRUGO, pps_show_clear, NULL); -+ -+static ssize_t pps_show_mode(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct pps_device *pps = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%4x\n", pps->info.mode); -+} -+DEVICE_ATTR(mode, S_IRUGO, pps_show_mode, NULL); -+ -+static ssize_t pps_show_echo(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct pps_device *pps = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%d\n", !!pps->info.echo); -+} -+DEVICE_ATTR(echo, S_IRUGO, pps_show_echo, NULL); -+ -+static ssize_t pps_show_name(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct pps_device *pps = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%s\n", pps->info.name); -+} -+DEVICE_ATTR(name, S_IRUGO, pps_show_name, NULL); -+ -+static ssize_t pps_show_path(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct pps_device *pps = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%s\n", pps->info.path); -+} -+DEVICE_ATTR(path, S_IRUGO, pps_show_path, NULL); -+ -+/* -+ * Public functions -+ */ -+ -+void pps_sysfs_remove_source_entry(struct pps_device *pps) -+{ -+ /* Delete info files */ -+ if (pps->info.mode & PPS_CAPTUREASSERT) -+ device_remove_file(pps->dev, &dev_attr_assert); -+ -+ if (pps->info.mode & PPS_CAPTURECLEAR) -+ device_remove_file(pps->dev, &dev_attr_clear); -+ -+ device_remove_file(pps->dev, &dev_attr_mode); -+ device_remove_file(pps->dev, &dev_attr_echo); -+ device_remove_file(pps->dev, &dev_attr_name); -+ device_remove_file(pps->dev, &dev_attr_path); -+} -+ -+int pps_sysfs_create_source_entry(struct pps_device *pps) -+{ -+ /* Create file "assert" and "clear" according to source capability */ -+ if (pps->info.mode & PPS_CAPTUREASSERT) -+ device_create_file(pps->dev, &dev_attr_assert); -+ -+ if (pps->info.mode & PPS_CAPTURECLEAR) -+ device_create_file(pps->dev, &dev_attr_clear); -+ -+ device_create_file(pps->dev, &dev_attr_mode); -+ device_create_file(pps->dev, &dev_attr_echo); -+ device_create_file(pps->dev, &dev_attr_name); -+ device_create_file(pps->dev, &dev_attr_path); -+ -+ return 0; -+} ---- a/drivers/serial/8250.c -+++ b/drivers/serial/8250.c -@@ -2118,6 +2118,8 @@ serial8250_set_termios(struct uart_port - up->ier |= UART_IER_MSI; - if (up->capabilities & UART_CAP_UUE) - up->ier |= UART_IER_UUE | UART_IER_RTOIE; -+ if (up->port.flags & UPF_HARDPPS_CD) -+ up->ier |= UART_IER_MSI; /* enable interrupts */ - - serial_out(up, UART_IER, up->ier); - ---- a/drivers/serial/serial_core.c -+++ b/drivers/serial/serial_core.c -@@ -33,6 +33,7 @@ - #include <linux/serial.h> /* for serial_state and serial_icounter_struct */ - #include <linux/delay.h> - #include <linux/mutex.h> -+#include <linux/pps.h> - - #include <asm/irq.h> - #include <asm/uaccess.h> -@@ -633,6 +634,54 @@ static int uart_get_info(struct uart_sta - return 0; - } - -+#ifdef CONFIG_PPS_CLIENT_UART -+ -+static int -+uart_register_pps_port(struct uart_state *state, struct uart_port *port) -+{ -+ struct tty_driver *drv = port->info->tty->driver; -+ int ret; -+ -+ state->pps_info.owner = THIS_MODULE; -+ state->pps_info.dev = port->dev; -+ snprintf(state->pps_info.name, PPS_MAX_NAME_LEN, "%s%d", -+ drv->driver_name, port->line); -+ snprintf(state->pps_info.path, PPS_MAX_NAME_LEN, "/dev/%s%d", -+ drv->name, port->line); -+ -+ state->pps_info.mode = PPS_CAPTUREBOTH | \ -+ PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \ -+ PPS_CANWAIT | PPS_TSFMT_TSPEC; -+ -+ ret = pps_register_source(&state->pps_info, PPS_CAPTUREBOTH | \ -+ PPS_OFFSETASSERT | PPS_OFFSETCLEAR); -+ if (ret < 0) { -+ dev_err(port->dev, "cannot register PPS source \"%s\"\n", -+ state->pps_info.path); -+ return ret; -+ } -+ port->pps_source = ret; -+ dev_dbg(port->dev, "PPS source #%d \"%s\" added\n", -+ port->pps_source, state->pps_info.path); -+ -+ return 0; -+} -+ -+static void -+uart_unregister_pps_port(struct uart_state *state, struct uart_port *port) -+{ -+ pps_unregister_source(port->pps_source); -+ dev_dbg(port->dev, "PPS source #%d \"%s\" removed\n", -+ port->pps_source, state->pps_info.path); -+} -+ -+#else -+ -+#define uart_register_pps_port(state, port) do { } while (0) -+#define uart_unregister_pps_port(state, port) do { } while (0) -+ -+#endif /* CONFIG_PPS_CLIENT_UART */ -+ - static int uart_set_info(struct uart_state *state, - struct serial_struct __user *newinfo) - { -@@ -807,11 +856,19 @@ static int uart_set_info(struct uart_sta - (port->flags & UPF_LOW_LATENCY) ? 1 : 0; - - check_and_exit: -+ /* PPS support enabled/disabled? */ -+ if ((old_flags & UPF_HARDPPS_CD) != (new_flags & UPF_HARDPPS_CD)) { -+ if (new_flags & UPF_HARDPPS_CD) -+ uart_register_pps_port(state, port); -+ else -+ uart_unregister_pps_port(state, port); -+ } -+ - retval = 0; - if (port->type == PORT_UNKNOWN) - goto exit; - if (state->info->flags & UIF_INITIALIZED) { -- if (((old_flags ^ port->flags) & UPF_SPD_MASK) || -+ if (((old_flags ^ port->flags) & (UPF_SPD_MASK|UPF_HARDPPS_CD)) || - old_custom_divisor != port->custom_divisor) { - /* - * If they're setting up a custom divisor or speed, -@@ -2110,6 +2167,12 @@ uart_configure_port(struct uart_driver * - port->ops->config_port(port, flags); - } - -+ /* -+ * Add the PPS support for the current port. -+ */ -+ if (port->flags & UPF_HARDPPS_CD) -+ uart_register_pps_port(state, port); -+ - if (port->type != PORT_UNKNOWN) { - unsigned long flags; - -@@ -2359,6 +2422,12 @@ int uart_remove_one_port(struct uart_dri - mutex_unlock(&state->mutex); - - /* -+ * Remove PPS support from the current port. -+ */ -+ if (port->flags & UPF_HARDPPS_CD) -+ uart_unregister_pps_port(state, port); -+ -+ /* - * Remove the devices from the tty layer - */ - tty_unregister_device(drv->tty_driver, port->line); ---- a/include/linux/Kbuild -+++ b/include/linux/Kbuild -@@ -295,6 +295,7 @@ unifdef-y += pmu.h - unifdef-y += poll.h - unifdef-y += ppp_defs.h - unifdef-y += ppp-comp.h -+unifdef-y += pps.h - unifdef-y += ptrace.h - unifdef-y += qnx4_fs.h - unifdef-y += quota.h ---- a/include/linux/parport.h -+++ b/include/linux/parport.h -@@ -100,6 +100,7 @@ typedef enum { - #include <linux/proc_fs.h> - #include <linux/spinlock.h> - #include <linux/wait.h> -+#include <linux/pps.h> - #include <asm/system.h> - #include <asm/ptrace.h> - #include <asm/semaphore.h> -@@ -327,6 +328,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 */ -@@ -517,6 +523,12 @@ extern int parport_daisy_select (struct - /* Lowlevel drivers _can_ call this support function to handle irqs. */ - static __inline__ void parport_generic_irq(int irq, struct parport *port) - { -+#ifdef CONFIG_PPS_CLIENT_LP -+ pps_event(port->pps_source, PPS_CAPTUREASSERT, port); -+ dev_dbg(port->dev, "PPS assert at %lu on source #%d\n", -+ jiffies, port->pps_source); -+#endif -+ - parport_ieee1284_interrupt (irq, port); - read_lock(&port->cad_lock); - if (port->cad && port->cad->irq_func) ---- /dev/null -+++ b/include/linux/pps.h -@@ -0,0 +1,196 @@ -+/* -+ * pps.h -- PPS API kernel header. -+ * -+ * -+ * Copyright (C) 2005-2007 Rodolfo Giometti <giometti@linux.it> -+ * -+ * 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 _PPS_H_ -+#define _PPS_H_ -+ -+/* Implementation note: the logical states ``assert'' and ``clear'' -+ * are implemented in terms of the chip register, i.e. ``assert'' -+ * means the bit is set. */ -+ -+/* -+ * 3.2 New data structures -+ */ -+ -+#ifndef __KERNEL__ -+#include <linux/types.h> -+#include <sys/time.h> -+#include <sys/ioctl.h> -+#else -+#include <linux/time.h> -+#endif -+ -+#define PPS_API_VERS 1 -+#define PPS_MAX_NAME_LEN 32 -+ -+/* 32-bit vs. 64-bit compatibility. -+ * -+ * 0n i386, the alignment of a uint64_t is only 4 bytes, while on most other -+ * architectures it's 8 bytes. On i386, there will be no padding between the -+ * two consecutive 'struct pps_ktime' members of struct pps_kinfo and struct -+ * pps_kparams. But on most platforms there will be padding to ensure correct -+ * alignment. -+ * -+ * The simple fix is probably to add an explicit padding. -+ * [David Woodhouse] -+ */ -+struct pps_ktime { -+ __u64 sec; -+ __u32 nsec; -+ __u32 flags; -+}; -+#define PPS_TIME_INVALID (1<<0) /* used to specify timeout==NULL */ -+ -+struct pps_kinfo { -+ __u32 assert_sequence; /* seq. num. of assert event */ -+ __u32 clear_sequence; /* seq. num. of clear event */ -+ struct pps_ktime assert_tu; /* time of assert event */ -+ struct pps_ktime clear_tu; /* time of clear event */ -+ int current_mode; /* current mode bits */ -+}; -+ -+struct pps_kparams { -+ int api_version; /* API version # */ -+ int mode; /* mode bits */ -+ struct pps_ktime assert_off_tu; /* offset compensation for assert */ -+ struct pps_ktime clear_off_tu; /* offset compensation for clear */ -+}; -+ -+/* -+ * 3.3 Mode bit definitions -+ */ -+ -+/* Device/implementation parameters */ -+#define PPS_CAPTUREASSERT 0x01 /* capture assert events */ -+#define PPS_CAPTURECLEAR 0x02 /* capture clear events */ -+#define PPS_CAPTUREBOTH 0x03 /* capture assert and clear events */ -+ -+#define PPS_OFFSETASSERT 0x10 /* apply compensation for assert ev. */ -+#define PPS_OFFSETCLEAR 0x20 /* apply compensation for clear ev. */ -+ -+#define PPS_CANWAIT 0x100 /* can we wait for an event? */ -+#define PPS_CANPOLL 0x200 /* bit reserved for future use */ -+ -+/* Kernel actions */ -+#define PPS_ECHOASSERT 0x40 /* feed back assert event to output */ -+#define PPS_ECHOCLEAR 0x80 /* feed back clear event to output */ -+ -+/* Timestamp formats */ -+#define PPS_TSFMT_TSPEC 0x1000 /* select timespec format */ -+#define PPS_TSFMT_NTPFP 0x2000 /* select NTP format */ -+ -+/* -+ * 3.4.4 New functions: disciplining the kernel timebase -+ */ -+ -+/* Kernel consumers */ -+#define PPS_KC_HARDPPS 0 /* hardpps() (or equivalent) */ -+#define PPS_KC_HARDPPS_PLL 1 /* hardpps() constrained to -+ use a phase-locked loop */ -+#define PPS_KC_HARDPPS_FLL 2 /* hardpps() constrained to -+ use a frequency-locked loop */ -+/* -+ * Here begins the implementation-specific part! -+ */ -+ -+struct pps_fdata { -+ struct pps_kinfo info; -+ struct pps_ktime timeout; -+}; -+ -+#include <linux/ioctl.h> -+ -+#define PPS_CHECK _IO('P', 0) -+#define PPS_GETPARAMS _IOR('P', 1, struct pps_kparams *) -+#define PPS_SETPARAMS _IOW('P', 2, struct pps_kparams *) -+#define PPS_GETCAP _IOR('P', 3, int *) -+#define PPS_FETCH _IOWR('P', 4, struct pps_fdata *) -+ -+#ifdef __KERNEL__ -+ -+#include <linux/cdev.h> -+#include <linux/device.h> -+ -+#define PPS_VERSION "5.0.0-rc2" -+#define PPS_MAX_SOURCES 16 /* should be enought... */ -+ -+/* -+ * Global defines -+ */ -+ -+/* The specific PPS source info */ -+struct pps_source_info { -+ char name[PPS_MAX_NAME_LEN]; /* simbolic name */ -+ char path[PPS_MAX_NAME_LEN]; /* path of connected device */ -+ int mode; /* PPS's allowed mode */ -+ -+ void (*echo)(int source, int event, void *data); /* PPS echo function */ -+ -+ struct module *owner; -+ struct device *dev; -+}; -+ -+/* The main struct */ -+struct pps_device { -+ struct pps_source_info info; /* PSS source info */ -+ -+ struct pps_kparams params; /* PPS's current params */ -+ -+ __u32 assert_sequence; /* PPS' assert event seq # */ -+ __u32 clear_sequence; /* PPS' clear event seq # */ -+ struct pps_ktime assert_tu; -+ struct pps_ktime clear_tu; -+ int current_mode; /* PPS mode at event time */ -+ -+ int go; /* PPS event is arrived? */ -+ wait_queue_head_t queue; /* PPS event queue */ -+ -+ unsigned int id; /* PPS source unique ID */ -+ struct cdev cdev; -+ struct device *dev; -+ int devno; -+ struct fasync_struct *async_queue; /* fasync method */ -+ spinlock_t lock; -+ -+ atomic_t usage; /* usage count */ -+ wait_queue_head_t usage_queue; -+ -+ struct class_device class_dev; -+}; -+ -+/* -+ * Exported functions -+ */ -+ -+extern int pps_register_source(struct pps_source_info *info, -+ int default_params); -+extern void pps_unregister_source(int source); -+extern int pps_register_cdev(struct pps_device *pps); -+extern void pps_unregister_cdev(struct pps_device *pps); -+extern void pps_event(int source, int event, void *data); -+ -+extern int pps_sysfs_create_source_entry(struct pps_device *pps); -+extern void pps_sysfs_remove_source_entry(struct pps_device *pps); -+ -+#endif /* __KERNEL__ */ -+ -+#endif /* _PPS_H_ */ ---- a/include/linux/serial_core.h -+++ b/include/linux/serial_core.h -@@ -157,6 +157,7 @@ - #include <linux/tty.h> - #include <linux/mutex.h> - #include <linux/sysrq.h> -+#include <linux/pps.h> - - struct uart_port; - struct uart_info; -@@ -236,6 +237,9 @@ struct uart_port { - unsigned char regshift; /* reg offset shift */ - unsigned char iotype; /* io access style */ - unsigned char unused1; -+#ifdef CONFIG_PPS_CLIENT_UART -+ int pps_source; /* PPS source ID */ -+#endif - - #define UPIO_PORT (0) - #define UPIO_HUB6 (1) -@@ -280,7 +284,8 @@ struct uart_port { - #define UPF_IOREMAP ((__force upf_t) (1 << 31)) - - #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff)) --#define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY)) -+#define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY\ -+ |UPF_HARDPPS_CD)) - - unsigned int mctrl; /* current modem ctrl settings */ - unsigned int timeout; /* character-based timeout */ -@@ -312,6 +317,10 @@ struct uart_state { - struct uart_info *info; - struct uart_port *port; - -+#ifdef CONFIG_PPS_CLIENT_UART -+ struct pps_source_info pps_info; -+#endif -+ - struct mutex mutex; - }; - -@@ -476,13 +485,22 @@ uart_handle_dcd_change(struct uart_port - { - struct uart_info *info = port->info; - -- port->icount.dcd++; -- --#ifdef CONFIG_HARD_PPS -- if ((port->flags & UPF_HARDPPS_CD) && status) -- hardpps(); -+#ifdef CONFIG_PPS_CLIENT_UART -+ if (port->flags & UPF_HARDPPS_CD) { -+ if (status) { -+ pps_event(port->pps_source, PPS_CAPTUREASSERT, port); -+ dev_dbg(port->dev, "PPS assert at %lu on source #%d\n", -+ jiffies, port->pps_source); -+ } else { -+ pps_event(port->pps_source, PPS_CAPTURECLEAR, port); -+ dev_dbg(port->dev, "PPS clear at %lu on source #%d\n", -+ jiffies, port->pps_source); -+ } -+ } - #endif - -+ port->icount.dcd++; -+ - if (info->flags & UIF_CHECK_CD) { - if (status) - wake_up_interruptible(&info->open_wait); |