diff -pruN linux-2.6.13-copy/drivers/serial/8250.c linux-2.6.13/drivers/serial/8250.c
--- linux-2.6.13-copy/drivers/serial/8250.c	2005-08-29 01:41:01.000000000 +0200
+++ linux-2.6.13/drivers/serial/8250.c	2005-09-19 09:09:59.000000000 +0200
@@ -44,6 +44,11 @@
 #include <asm/io.h>
 #include <asm/irq.h>
 
+#ifdef CONFIG_SERIAL_PPS
+#include <linux/timepps.h>
+#include <linux/linuxpps.h>
+#endif
+
 #include "8250.h"
 
 /*
@@ -118,6 +123,10 @@ struct uart_8250_port {
 	struct uart_port	port;
 	struct timer_list	timer;		/* "no irq" timer */
 	struct list_head	list;		/* ports on this IRQ */
+#ifdef CONFIG_SERIAL_PPS
+	struct linuxpps_source_info_s linuxpps_8250_info;
+	int source;
+#endif
 	unsigned short		capabilities;	/* port capabilities */
 	unsigned short		bugs;		/* port bugs */
 	unsigned int		tx_loadsz;	/* transmit fifo load size */
@@ -251,6 +260,7 @@ static const struct serial8250_config ua
 	},
 };
 
+
 static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset)
 {
 	offset <<= up->port.regshift;
@@ -1194,8 +1204,13 @@ static _INLINE_ void check_modem_status(
 		up->port.icount.rng++;
 	if (status & UART_MSR_DDSR)
 		up->port.icount.dsr++;
-	if (status & UART_MSR_DDCD)
+	if (status & UART_MSR_DDCD) {
+#ifdef CONFIG_SERIAL_PPS
+		linuxpps_event(up->source, PPS_CAPTUREASSERT);
+		PINFO("serial8250: PPS event at %lu", jiffies);
+#endif
 		uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
+	}
 	if (status & UART_MSR_DCTS)
 		uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
 
@@ -1774,6 +1789,9 @@ 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;
+#ifdef CONFIG_SERIAL_PPS
+	up->ier |= UART_IER_MSI;	/* enable interrupts */
+#endif
 
 	serial_out(up, UART_IER, up->ier);
 
@@ -2081,6 +2099,7 @@ static void __init
 serial8250_register_ports(struct uart_driver *drv, struct device *dev)
 {
 	int i;
+	int ret;
 
 	serial8250_isa_init_ports();
 
@@ -2089,6 +2108,25 @@ serial8250_register_ports(struct uart_dr
 
 		up->port.dev = dev;
 		uart_add_one_port(drv, &up->port);
+#ifdef CONFIG_SERIAL_PPS		
+		snprintf(&(up->linuxpps_8250_info.name), 
+			LINUXPPS_MAX_NAME_LEN, "pps_8250_%d",
+			up->port.line);
+		snprintf(&(up->linuxpps_8250_info.path),
+			LINUXPPS_MAX_NAME_LEN, "/dev/ttyS%d",
+			up->port.line);
+		up->linuxpps_8250_info.mode = 
+			  PPS_CAPTUREASSERT|PPS_OFFSETASSERT|
+            		  PPS_CANWAIT|PPS_TSFMT_TSPEC;
+		ret = linuxpps_register_source(
+			&(up->linuxpps_8250_info),
+    			PPS_CAPTUREASSERT|PPS_OFFSETASSERT,
+			-1 /* is up to the system */);
+		if (ret >= 0)
+			up->source = ret;
+		else
+			PDEBUG("cannot register 8250 source");
+#endif
 	}
 }
 
@@ -2482,8 +2520,30 @@ int serial8250_register_port(struct uart
 			uart->port.dev = port->dev;
 
 		ret = uart_add_one_port(&serial8250_reg, &uart->port);
-		if (ret == 0)
+		if (ret == 0) {
+#ifdef CONFIG_SERIAL_PPS		
+			snprintf(&(uart->linuxpps_8250_info.name), 
+				LINUXPPS_MAX_NAME_LEN, "pps_8250_%d",
+				uart->port.line);
+			snprintf(&(uart->linuxpps_8250_info.path),
+				LINUXPPS_MAX_NAME_LEN, "/dev/ttyS%d",
+				uart->port.line);
+			uart->linuxpps_8250_info.mode = 
+				  PPS_CAPTUREASSERT|PPS_OFFSETASSERT|
+                		  PPS_CANWAIT|PPS_TSFMT_TSPEC;
+			ret = linuxpps_register_source(
+				&(uart->linuxpps_8250_info),
+	    			PPS_CAPTUREASSERT|PPS_OFFSETASSERT,
+				-1 /* is up to the system */);
+			if (ret >= 0) {
+				uart->source = ret;
+				ret = uart->port.line;
+			} else
+				PDEBUG("cannot register 8250 source");
+#else
 			ret = uart->port.line;
+#endif
+		}
 	}
 	up(&serial_sem);
 
@@ -2502,6 +2562,10 @@ void serial8250_unregister_port(int line
 {
 	struct uart_8250_port *uart = &serial8250_ports[line];
 
+#ifdef CONFIG_SERIAL_PPS		
+	linuxpps_unregister_source(&(uart->linuxpps_8250_info));
+	PINFO("8250 PPS source unregistered");
+#endif
 	down(&serial_sem);
 	uart_remove_one_port(&serial8250_reg, &uart->port);
 	if (serial8250_isa_devs) {
diff -pruN linux-2.6.13-copy/drivers/serial/Kconfig linux-2.6.13/drivers/serial/Kconfig
--- linux-2.6.13-copy/drivers/serial/Kconfig	2005-08-29 01:41:01.000000000 +0200
+++ linux-2.6.13/drivers/serial/Kconfig	2005-09-08 13:38:57.000000000 +0200
@@ -888,4 +888,14 @@ config SERIAL_SGI_IOC4
 		and wish to use the serial ports on this card, say Y.
 		Otherwise, say N.
 
+config SERIAL_PPS
+	tristate "LinuxPPS"
+	help
+		Eneables LinuxPPS
+
+config SERIAL_PPSTEST
+	tristate "LinuxPPS - Test Client"
+	help
+		Eneables LinuxPPS Text Client (ktimer.ko)
+
 endmenu
diff -pruN linux-2.6.13-copy/drivers/serial/linuxpps/kapi.c linux-2.6.13/drivers/serial/linuxpps/kapi.c
--- linux-2.6.13-copy/drivers/serial/linuxpps/kapi.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.13/drivers/serial/linuxpps/kapi.c	2005-09-08 10:19:31.000000000 +0200
@@ -0,0 +1,178 @@
+/*
+ * kapi.c -- kernel API
+ *
+ *
+ * Copyright (C) 2005   Rodolfo Giometti <giometti@linux.it>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are
+ *  met:
+ *  
+ *     1. Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *  
+ *     2. Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials provided
+ *        with the distribution.
+ *  
+ *     3. The name of the author may not be used to endorse or promote
+ *        products derived from this software without specific prior
+ *        written permission.
+ *  
+ *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/time.h>
+
+#include <linux/timepps.h>
+#include <linux/linuxpps.h>
+
+/* --- Local functions ----------------------------------------------------- */
+
+#ifndef NSEC_PER_SEC
+#define	NSEC_PER_SEC		1000000000
+#endif
+static void linuxpps_add_offeset(struct timespec *ts, struct timespec *offset)
+{
+   ts->tv_nsec += offset->tv_nsec;
+   if (ts->tv_nsec >= NSEC_PER_SEC) {
+      ts->tv_nsec -= NSEC_PER_SEC;
+      ts->tv_sec++;
+   } else if (ts->tv_nsec < 0) {
+      ts->tv_nsec += NSEC_PER_SEC;
+      ts->tv_sec--;
+   }
+   ts->tv_sec += offset->tv_sec;
+}
+
+/* --- Exported functions -------------------------------------------------- */
+
+int linuxpps_register_source(struct linuxpps_source_info_s *info, int default_params, int try_id)
+{
+   int i;
+
+   if (try_id >= 0) {
+      if (linuxpps_is_allocated(try_id)) {
+	 PERR("source id %d busy", try_id);
+	 return -EBUSY;
+      }
+      i = try_id;
+   }
+   else {
+      for (i = 0; i < LINUXPPS_MAX_SOURCES; i++)
+	 if (!linuxpps_is_allocated(i))
+	    break;
+      if (i >= LINUXPPS_MAX_SOURCES) {
+	 PERR("no free source ids");
+	 return -ENOMEM;
+      }
+   }
+
+   /* Sanity checks */
+   if ((info->mode&default_params) != default_params) {
+      PERR("unsupported default parameters");
+      return -EINVAL;
+   }
+   if ((info->mode&(PPS_ECHOASSERT|PPS_ECHOCLEAR)) != 0 && info->echo == NULL) {
+      PERR("echo function is not defined");
+      return -EINVAL;
+   }
+   if ((info->mode&(PPS_TSFMT_TSPEC|PPS_TSFMT_NTPFP)) == 0) {
+      PERR("unspecified time format");
+      return -EINVAL;
+   }
+
+   /* Allocate the PPS source */
+   memset(&linuxpps_source[i], 0, sizeof(struct linuxpps_s));
+   linuxpps_source[i].info = info;
+   linuxpps_source[i].params.api_version = PPS_API_VERS_1;
+   linuxpps_source[i].params.mode = default_params;
+   init_waitqueue_head(&linuxpps_source[i].queue);
+
+   return i;
+}
+
+void linuxpps_unregister_source(struct linuxpps_source_info_s *info)
+{  
+   int i;
+
+   for (i = 0; i < LINUXPPS_MAX_SOURCES; i++)
+      if (linuxpps_is_allocated(i) && linuxpps_source[i].info == info)
+	 break;
+
+   if (i >= LINUXPPS_MAX_SOURCES) {
+      PERR("warning! Try to unregister an unknow PPS source");
+      return;
+   }
+
+   /* Deallocate the PPS source */
+   linuxpps_source[i].info = NULL; 
+} 
+
+void linuxpps_event(int source, int event)
+{
+   struct timespec ts;
+
+   /* Sanity checks */
+   if (!linuxpps_is_allocated(source)) {
+      PERR("unknow source for event!");
+      return;
+   }
+   if ((event&(PPS_CAPTUREASSERT|PPS_CAPTURECLEAR)) == 0 ) {
+      PERR("unknow event (%x) for source %d", event, source);
+      return;
+   }
+
+   /* Get the time stamp */
+   do_gettimeofday((struct timeval *) &ts);
+   ts.tv_nsec *= 1000;	  /* microseconds to nanoseconds */
+
+   /* Must call the echo function? */
+   if ((linuxpps_source[source].params.mode&(PPS_ECHOASSERT|PPS_ECHOCLEAR)) != 0)
+      linuxpps_source[source].info->echo(source, event);
+
+   /* Check the event */
+   linuxpps_source[source].current_mode = linuxpps_source[source].params.mode;
+   if (event&PPS_CAPTUREASSERT) {
+      linuxpps_add_offeset(&ts, &linuxpps_source[source].params.assert_off_tu.tspec);
+
+      /* Save the time stamp */
+      linuxpps_source[source].assert_tu.tspec = ts;
+      linuxpps_source[source].assert_sequence++;
+      PDEBUG("capture assert seq #%lu for source %d", 
+	     linuxpps_source[source].assert_sequence, source);
+   }
+   if (event&PPS_CAPTURECLEAR) {
+      linuxpps_add_offeset(&ts, &linuxpps_source[source].params.clear_off_tu.tspec);
+
+      /* Save the time stamp */
+      linuxpps_source[source].clear_tu.tspec = ts;
+      linuxpps_source[source].clear_sequence++;
+      PDEBUG("capture clear seq #%lu for source %d", 
+	     linuxpps_source[source].clear_sequence, source);
+   }
+
+   wake_up_interruptible(&linuxpps_source[source].queue);
+}
+
+/* --- Exported functions -------------------------------------------------- */
+
+EXPORT_SYMBOL(linuxpps_register_source);
+EXPORT_SYMBOL(linuxpps_unregister_source);
+EXPORT_SYMBOL(linuxpps_event);
diff -pruN linux-2.6.13-copy/drivers/serial/linuxpps/ktimer.c linux-2.6.13/drivers/serial/linuxpps/ktimer.c
--- linux-2.6.13-copy/drivers/serial/linuxpps/ktimer.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.13/drivers/serial/linuxpps/ktimer.c	2005-09-13 10:24:30.000000000 +0200
@@ -0,0 +1,122 @@
+/*
+ * ktimer.c -- kernel timer test client
+ *
+ *
+ * Copyright (C) 2005   Rodolfo Giometti <giometti@linux.it>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are
+ *  met:
+ *  
+ *     1. Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *  
+ *     2. Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials provided
+ *        with the distribution.
+ *  
+ *     3. The name of the author may not be used to endorse or promote
+ *        products derived from this software without specific prior
+ *        written permission.
+ *  
+ *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+
+#include <linux/timepps.h>
+#include <linux/linuxpps.h>
+
+/* --- Global variables ----------------------------------------------------- */
+
+static int source;
+static struct timer_list ktimer;
+
+/* --- The kernel timer ----------------------------------------------------- */
+
+static void linuxpps_ktimer_event(unsigned long ptr) {
+   PINFO("ktimer: PPS event at %lu", jiffies);
+
+   linuxpps_event(source, PPS_CAPTUREASSERT);
+
+   /* Rescheduling */
+   ktimer.expires = jiffies+HZ;   /* 1 second */
+   add_timer(&ktimer);
+}
+
+/* --- The echo function ---------------------------------------------------- */
+
+static void linuxpps_ktimer_echo(int source, int event)
+{
+   PINFO("ktimer: echo %s %s for source %d",
+	 event&PPS_CAPTUREASSERT ? "assert" : "",
+	 event&PPS_CAPTURECLEAR ? "clear" : "",
+	 source);
+}
+
+/* --- The PPS info struct -------------------------------------------------- */
+
+static struct linuxpps_source_info_s linuxpps_ktimer_info = {
+   name		: "ktimer",
+   path		: "/dev/gps0",
+   mode		: PPS_CAPTUREASSERT|PPS_OFFSETASSERT|PPS_ECHOASSERT| \
+                  PPS_CANWAIT|PPS_TSFMT_TSPEC,
+   echo 	: linuxpps_ktimer_echo,
+};
+
+/* --- Module staff -------------------------------------------------------- */
+
+void __exit linuxpps_ktimer_exit(void)
+{
+   del_timer_sync(&ktimer);
+   linuxpps_unregister_source(&linuxpps_ktimer_info);
+
+   PINFO("ktimer PPS source unregistered");
+}
+
+int __init linuxpps_ktimer_init(void)
+{
+   int ret;
+
+   ret = linuxpps_register_source(&linuxpps_ktimer_info,
+	                          PPS_CAPTUREASSERT|PPS_OFFSETASSERT,
+				  -1 /* is up to the system */);
+   if (ret < 0) {
+      PDEBUG("cannot register ktimer source");
+      return ret;
+   }
+   source = ret;
+
+   init_timer(&ktimer);
+   ktimer.function = linuxpps_ktimer_event;
+   ktimer.expires = jiffies+HZ;   /* 1 second */
+   add_timer(&ktimer);
+
+   PINFO("ktimer PPS source registered at %d", source);
+
+   return  0;
+}
+
+module_init(linuxpps_ktimer_init);
+module_exit(linuxpps_ktimer_exit);
+
+MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
+MODULE_DESCRIPTION("testing PPS source by using a kernel timer (just for debug)");
+MODULE_LICENSE("GPL");
diff -pruN linux-2.6.13-copy/drivers/serial/linuxpps/main.c linux-2.6.13/drivers/serial/linuxpps/main.c
--- linux-2.6.13-copy/drivers/serial/linuxpps/main.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.13/drivers/serial/linuxpps/main.c	2005-09-08 10:20:25.000000000 +0200
@@ -0,0 +1,343 @@
+/*
+ * main.c -- Main driver file
+ *
+ *
+ * Copyright (C) 2005   Rodolfo Giometti <giometti@linux.it>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are
+ *  met:
+ *  
+ *     1. Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *  
+ *     2. Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials provided
+ *        with the distribution.
+ *  
+ *     3. The name of the author may not be used to endorse or promote
+ *        products derived from this software without specific prior
+ *        written permission.
+ *  
+ *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+
+#include <linux/timepps.h>
+#include <linux/linuxpps.h>
+
+/* --- Global variables ----------------------------------------------------- */
+
+struct linuxpps_s linuxpps_source[LINUXPPS_MAX_SOURCES];
+static struct sock *nl_sk = NULL;
+
+/* --- Misc functions ------------------------------------------------------ */
+
+static inline int linuxpps_find_source(int source)
+{
+   int i;
+
+   if (source >= 0) {
+      if (!linuxpps_is_allocated(source))
+	 return -EINVAL;
+      else
+	 return source;
+   }
+
+   for (i = 0; i < LINUXPPS_MAX_SOURCES; i++)
+      if (linuxpps_is_allocated(i))
+	 break;
+
+   if (i >= LINUXPPS_MAX_SOURCES)
+      return -EINVAL;
+   return i;
+}
+
+/* --- Input function ------------------------------------------------------ */
+
+static void linuxpps_nl_data_ready(struct sock *sk, int len)
+{
+   struct sk_buff *skb;
+   struct nlmsghdr *nlh;
+   struct pps_netlink_msg *nlpps;
+
+   int cmd, source;
+   wait_queue_head_t *queue;
+   unsigned long timeout;
+
+   int ret;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+   while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
+#else
+   while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+#endif
+      nlh = (struct nlmsghdr *) skb->data;
+      PDEBUG("New message from PID %d (flags %x)",
+	     nlh->nlmsg_pid, nlh->nlmsg_flags);
+
+      /* Decode the userland command */
+      nlpps = (struct pps_netlink_msg*) NLMSG_DATA(nlh);
+      cmd = nlpps->cmd;
+      source = nlpps->source;
+      switch (cmd) {
+	 case PPS_CREATE : {
+            PDEBUG("PPS_CREATE: source %d", source);
+
+	    /* Check if the requested source is allocated */
+	    ret = linuxpps_find_source(source);
+	    if (ret < 0) {
+               nlpps->ret = ret;
+	       break;
+	    }
+
+	    nlpps->source = ret;
+            nlpps->ret = 0;
+
+	    break;
+	 }
+
+	 case PPS_DESTROY : {
+            PDEBUG("PPS_DESTROY: source %d", source);
+
+	    /* Nothing to do here! Just answer ok... */
+            nlpps->ret = 0;
+
+	    break;
+	 }
+
+	 case PPS_SETPARMS : {
+            PDEBUG("PPS_SETPARMS: source %d", source);
+
+	    /* Check the capabilities */
+	    if (!capable(CAP_SYS_TIME)) {
+               nlpps->ret = -EPERM;
+	       break;
+	    }
+
+	    /* Sanity checks */
+	    if (nlpps->source < 0 || !linuxpps_is_allocated(source)) {
+               nlpps->ret = -EINVAL;
+	       break;
+	    }
+            if ((nlpps->params.mode&~linuxpps_source[source].info->mode) != 0) {
+	       PDEBUG("unsupported capabilities");
+               nlpps->ret = -EINVAL;
+	       break;
+	    }
+            if ((nlpps->params.mode&(PPS_CAPTUREASSERT|PPS_CAPTURECLEAR)) == 0) {
+	       PDEBUG("capture mode unspecified");
+               nlpps->ret = -EINVAL;
+	       break;
+	    }
+            if ((nlpps->params.mode&(PPS_TSFMT_TSPEC|PPS_TSFMT_NTPFP)) == 0) {
+               /* section 3.3 of RFC 2783 interpreted */
+	       PDEBUG("time format unspecified");
+	       nlpps->params.mode |= PPS_TSFMT_TSPEC;
+	    }
+
+	    /* Save the new parameters */
+	    linuxpps_source[source].params = nlpps->params;
+
+	    /* Restore the read only parameters */
+	    if (linuxpps_source[source].info->mode&PPS_CANWAIT)
+	       linuxpps_source[source].params.mode |= PPS_CANWAIT;
+	    linuxpps_source[source].params.api_version = PPS_API_VERS_1;
+
+            nlpps->ret = 0;
+
+	    break;
+	 }
+
+	 case PPS_GETPARMS : {
+            PDEBUG("PPS_GETPARMS: source %d", source);
+
+	    /* Sanity checks */
+	    if (nlpps->source < 0 || !linuxpps_is_allocated(source)) {
+               nlpps->ret = -EINVAL;
+	       break;
+	    }
+
+	    nlpps->params = linuxpps_source[source].params;
+            nlpps->ret = 0;
+
+	    break;
+	 }
+
+	 case PPS_GETCAP : {
+            PDEBUG("PPS_GETCAP: source %d", source);
+
+	    /* Sanity checks */
+	    if (nlpps->source < 0 || !linuxpps_is_allocated(source)) {
+               nlpps->ret = -EINVAL;
+	       break;
+	    }
+
+	    nlpps->mode = linuxpps_source[source].info->mode;
+            nlpps->ret = 0;
+
+	    break;
+	 }
+
+	 case PPS_FETCH : {
+            PDEBUG("PPS_FETCH: source %d", source);
+            queue = &linuxpps_source[source].queue;
+
+	    /* Sanity checks */
+	    if (nlpps->source < 0 || !linuxpps_is_allocated(source)) {
+               nlpps->ret = -EINVAL;
+	       break;
+	    }
+            if ((nlpps->tsformat != PPS_TSFMT_TSPEC) != 0 ) {
+	       PDEBUG("unsupported time format");
+               nlpps->ret = -EINVAL;
+	       break;
+	    }
+
+	    /* Manage the timeout */
+            if (nlpps->timeout.tv_sec != -1) {
+               timeout = nlpps->timeout.tv_sec*HZ;
+               timeout += nlpps->timeout.tv_nsec/(1000000000/HZ);
+
+               if (timeout != 0) {
+                  timeout = interruptible_sleep_on_timeout(queue, timeout);
+		  if (timeout <= 0) {
+		     PDEBUG("timeout expired");
+		     nlpps->ret = -ETIMEDOUT;
+                     break;
+                  }
+	       }
+	    }
+	    else
+	       interruptible_sleep_on(queue);
+
+	    /* Return the fetched timestamp */
+            nlpps->info.assert_sequence = linuxpps_source[source].assert_sequence;
+            nlpps->info.clear_sequence = linuxpps_source[source].clear_sequence;
+            nlpps->info.assert_tu = linuxpps_source[source].assert_tu;
+            nlpps->info.clear_tu = linuxpps_source[source].clear_tu;
+            nlpps->info.current_mode = linuxpps_source[source].current_mode;
+
+            nlpps->ret = 0;
+
+	    break;
+	 }
+
+	 case PPS_KC_BIND : {
+	    /* Feature currently not supported */
+            nlpps->ret = -EOPNOTSUPP;
+
+	    break;
+	 }
+
+	 case PPS_FIND_SRC : {
+            nlpps->source = linuxpps_find_source(source);
+	    if (nlpps->source < 0) {
+	       PDEBUG("no PPS devices found");
+               nlpps->ret = -ENODEV;
+	       break;
+	    }
+
+            /* Found! So copy the info */
+	    source = nlpps->source;
+	    strncpy(nlpps->name, linuxpps_source[source].info->name, LINUXPPS_MAX_NAME_LEN);
+	    strncpy(nlpps->path, linuxpps_source[source].info->path, LINUXPPS_MAX_NAME_LEN);
+            nlpps->ret = 0;
+
+	    break;
+	 }
+
+	 default : {
+	    /* Unknow command */
+	    PDEBUG("unknow command %d", nlpps->cmd);
+
+            nlpps->ret = -EINVAL;
+	 }
+      }
+
+      /* Send an answer to the userland */
+      NETLINK_CB(skb).groups = 0;		/* not in mcast groups */
+      NETLINK_CB(skb).pid = 0;			/* from the kernel */
+      NETLINK_CB(skb).dst_pid = nlh->nlmsg_pid;
+      NETLINK_CB(skb).dst_groups = 0;		/* not in mcast groups */
+
+      netlink_unicast(nl_sk, skb, nlh->nlmsg_pid, MSG_DONTWAIT);
+   }
+}
+
+/* --- Module staff -------------------------------------------------------- */
+
+void __exit linuxpps_exit(void)
+{
+   linuxpps_procfs_unregister();
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+   sock_release(nl_sk->sk_socket);
+#else
+   sock_release(nl_sk->socket);
+#endif
+
+   PINFO("PPSAPI ver. %d based on netlink layer removed", PPS_API_VERS_1);
+}
+
+int __init linuxpps_init(void)
+{
+   int ret;
+
+   nl_sk = netlink_kernel_create(NETLINK_PPSAPI, linuxpps_nl_data_ready);
+   if (nl_sk == NULL) {
+      PERR("unable to create netlink kernel socket");
+      return -EBUSY;
+   }
+   PDEBUG("netlink protocol %d created", NETLINK_PPSAPI);
+
+   /* Init the main struct */
+   memset(linuxpps_source, 0, sizeof(struct linuxpps_s)*LINUXPPS_MAX_SOURCES);
+
+   /* Register to procfs */
+   ret = linuxpps_procfs_register();
+   if (ret < 0) {
+      PERR("unable to register procfs");
+      goto linuxpps_procfs_register_error;
+   }
+   PDEBUG("netlink protocol %d created", NETLINK_PPSAPI);
+
+   PINFO("Linux PPSAPI ver. %d based on netlink layer registered", PPS_API_VERS_1);
+   PINFO("Software ver. %s - Copyright 2005 Rodolfo Giometti <giometti@linux.it>", PPS_VERSION);
+
+   return  0;
+
+linuxpps_procfs_register_error :
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+   sock_release(nl_sk->sk_socket);
+#else
+   sock_release(nl_sk->socket);
+#endif
+
+   return ret;
+}
+
+module_init(linuxpps_init);
+module_exit(linuxpps_exit);
+
+MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
+MODULE_DESCRIPTION("Linux PPS support (RFC 2783) - ver. " PPS_VERSION);
+MODULE_LICENSE("GPL");
diff -pruN linux-2.6.13-copy/drivers/serial/linuxpps/Makefile linux-2.6.13/drivers/serial/linuxpps/Makefile
--- linux-2.6.13-copy/drivers/serial/linuxpps/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.13/drivers/serial/linuxpps/Makefile	2005-09-08 13:37:40.000000000 +0200
@@ -0,0 +1,8 @@
+#
+# Makefile for LinuxPPS
+#
+
+obj-$(CONFIG_SERIAL_PPS) += linuxpps.o 
+linuxpps-objs := main.o kapi.o procfs.o
+
+obj-$(CONFIG_SERIAL_PPSTEST) += ktimer.o 
diff -pruN linux-2.6.13-copy/drivers/serial/linuxpps/procfs.c linux-2.6.13/drivers/serial/linuxpps/procfs.c
--- linux-2.6.13-copy/drivers/serial/linuxpps/procfs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.13/drivers/serial/linuxpps/procfs.c	2005-09-08 14:51:08.000000000 +0200
@@ -0,0 +1,112 @@
+/*
+ * procfs.c -- procfs support
+ *
+ *
+ * Copyright (C) 2005   Rodolfo Giometti <giometti@linux.it>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are
+ *  met:
+ *  
+ *     1. Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *  
+ *     2. Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials provided
+ *        with the distribution.
+ *  
+ *     3. The name of the author may not be used to endorse or promote
+ *        products derived from this software without specific prior
+ *        written permission.
+ *  
+ *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include <linux/config.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+
+#include <linux/timepps.h>
+#include <linux/linuxpps.h>
+
+#ifdef CONFIG_PROC_FS
+/* ----- Private functions -------------------------------------------- */
+
+static int linuxpps_sources_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+   int i;
+   int len = 0;
+
+   len += sprintf(page+len, "id\tmode\techo\tname\n");
+   len += sprintf(page+len, "----\t------\t----\t----------------\n");
+   for (i = 0; i < LINUXPPS_MAX_SOURCES; i++)
+      if (linuxpps_is_allocated(i))
+         len += sprintf(page+len, "%2d\t%4x\t%s\t%s\n",
+	                i,
+			linuxpps_source[i].info->mode,
+			linuxpps_source[i].info->echo ? "yes" : "no",
+		       	linuxpps_source[i].info->name);
+   
+   *eof = 1;
+   return len;
+}
+
+/* ----- Public functions --------------------------------------------- */
+
+void linuxpps_procfs_unregister(void)
+{
+   remove_proc_entry("pps/sources", NULL);
+
+   /* The root dir in /proc/pps */
+   remove_proc_entry("pps", NULL);
+}
+
+int linuxpps_procfs_register(void)
+{
+   struct proc_dir_entry *root_dir, *procfs_file;
+
+   /* The root dir in /proc/driver/fedctrl */
+   root_dir = proc_mkdir("pps", NULL);
+   if (root_dir == NULL)
+      return -ENOMEM;
+
+   /* The file "sources" */
+   procfs_file = create_proc_entry("sources", S_IRUGO|S_IWUSR, root_dir);
+   if (procfs_file == NULL) {
+      linuxpps_procfs_unregister();
+      return -ENOMEM;
+   }
+   procfs_file->owner = THIS_MODULE;
+   procfs_file->read_proc = linuxpps_sources_read;
+   procfs_file->write_proc = NULL;   /* no write method */
+
+   PINFO("procfs enabled");
+   return 0;
+}
+
+#else   /* LINUXPPS_PROCFS */
+
+void linuxpps_procfs_unregister(void)
+{
+   /* Nothing to do here... */
+   return;
+}
+
+int linuxpps_procfs_register(void)
+{
+   PINFO("procfs not enabled");
+   return 0;
+}
+#endif   /* LINUXPPS_PROCFS */
diff -pruN linux-2.6.13-copy/drivers/serial/Makefile linux-2.6.13/drivers/serial/Makefile
--- linux-2.6.13-copy/drivers/serial/Makefile	2005-08-29 01:41:01.000000000 +0200
+++ linux-2.6.13/drivers/serial/Makefile	2005-09-13 10:28:39.000000000 +0200
@@ -4,6 +4,8 @@
 #  $Id: ntp-pps-2.6.13.diff,v 1.1 2005/09/26 12:36:30 giometti Exp $
 #
 
+obj-$(CONFIG_SERIAL_PPS) += linuxpps/
+
 serial-8250-y :=
 serial-8250-$(CONFIG_SERIAL_8250_ACPI) += 8250_acpi.o
 serial-8250-$(CONFIG_PNP) += 8250_pnp.o
diff -pruN linux-2.6.13-copy/include/linux/linuxpps.h linux-2.6.13/include/linux/linuxpps.h
--- linux-2.6.13-copy/include/linux/linuxpps.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.13/include/linux/linuxpps.h	2005-09-08 14:49:54.000000000 +0200
@@ -0,0 +1,92 @@
+/*
+ * linuxpps.h -- PPS API kernel header.
+ *
+ *
+ * Copyright (C) 2005   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.
+ */
+
+
+/* ----- Misc macros -------------------------------------------------- */
+
+#define PPS_NAME	"linuxpps"
+#define PPS_VERSION	"1.0.0"
+
+#undef PDEBUG           /* undef it, just in case */
+#undef PINFO            /* undef it, just in case */
+#ifdef LINUXPPS_DEBUG
+   #define PDEBUG(fmt, args...) printk(KERN_DEBUG "%s:%s[%d]: " fmt "\n", \
+                                     PPS_NAME,  __FILE__, __LINE__ , ## args)
+   #define PINFO(fmt, args...) printk(KERN_INFO "%s:%s[%d]: " fmt "\n", \
+	                             PPS_NAME, __FILE__, __LINE__ , ## args)
+   #define PERR(fmt, args...) printk(KERN_ERR "%s:%s[%d]: " fmt "\n", \
+	                             PPS_NAME, __FILE__, __LINE__ , ## args)
+#else   /* LINUXPPSL_DEBUG */
+   #define PDEBUG(fmt, args...) /* do nothing! */
+   #define PINFO(fmt, args...) printk(KERN_INFO "%s: " fmt "\n", \
+                                     PPS_NAME , ## args)
+   #define PERR(fmt, args...) printk(KERN_ERR "%s: " fmt "\n", \
+	                             PPS_NAME  , ## args)
+#endif   /* LINUXPPS_DEBUG */
+
+/* --- Global defines ------------------------------------------------------ */
+
+#define LINUXPPS_MAX_SOURCES		16
+
+/* --- Global variables ---------------------------------------------------- */
+
+/* The specific PPS source info */
+struct linuxpps_source_info_s {
+   char name[LINUXPPS_MAX_NAME_LEN];		/* simbolic name */
+   char path[LINUXPPS_MAX_NAME_LEN];		/* path of connected device */
+   int mode;					/* PPS's allowed mode */
+
+   void (*echo)(int source, int event);		/* the PPS echo function */
+};
+
+/* The main struct */
+struct linuxpps_s {
+   struct linuxpps_source_info_s *info;		/* PSS source info */
+
+   pps_params_t params;				/* PPS's current params */
+
+   volatile pps_seq_t assert_sequence;		/* PPS' assert event seq # */
+   volatile pps_seq_t clear_sequence;		/* PPS' clear event seq # */
+   volatile pps_timeu_t assert_tu;
+   volatile pps_timeu_t clear_tu;
+   int current_mode;				/* PPS mode at event time */
+
+   wait_queue_head_t queue;			/* PPS event queue */
+};
+
+/* --- Global variables ---------------------------------------------------- */
+
+extern struct linuxpps_s linuxpps_source[LINUXPPS_MAX_SOURCES];
+
+/* --- Global functions ---------------------------------------------------- */
+
+static inline int linuxpps_is_allocated(int source) {
+      return linuxpps_source[source].info != NULL;
+}
+
+/* --- Exported functions -------------------------------------------------- */
+
+extern int linuxpps_register_source(struct linuxpps_source_info_s *info, int default_params, int try_id);
+extern void linuxpps_unregister_source(struct linuxpps_source_info_s *info);
+extern void linuxpps_event(int source, int event);
+
+extern int linuxpps_procfs_register(void);
+extern void linuxpps_procfs_unregister(void);
diff -pruN linux-2.6.13-copy/include/linux/timepps.h linux-2.6.13/include/linux/timepps.h
--- linux-2.6.13-copy/include/linux/timepps.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.13/include/linux/timepps.h	2005-09-08 10:00:09.000000000 +0200
@@ -0,0 +1,452 @@
+/*
+ * timepps.h -- PPS API main header
+ *
+ *
+ * Copyright (C) 2005   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.
+ *
+ * NOTE: this file is *strongly* based on a previous job by Ulrich Windl.
+ *       The original copyright note follows:
+ *
+ *    Interface to the PPS API described in RFC 2783 (March 2000)
+ *
+ *    Copyright (c) 1999, 2001, 2004 by Ulrich Windl,
+ * 	   based on code by Reg Clemens <reg@dwf.com>
+ *	   based on code by Poul-Henning Kamp <phk@FreeBSD.org>
+ *
+ *    ----------------------------------------------------------------------
+ *    "THE BEER-WARE LICENSE" (Revision 42):
+ *    <phk@FreeBSD.org> wrote this file.  As long as you retain this notice
+ *    you can do whatever you want with this stuff. If we meet some day, and
+ *    you think this stuff is worth it, you can buy me a beer in return.
+ *       Poul-Henning Kamp
+ *    ----------------------------------------------------------------------
+ */
+
+#ifndef _SYS_TIMEPPS_H_
+#define _SYS_TIMEPPS_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 --------------------------------------------- */
+
+#define PPS_API_VERS_1		1	/* draft-05, dated 1999-08 */
+#define NETLINK_PPSAPI		17	/* we use just one free number... */
+
+typedef struct pps_handle_s {
+   int source;
+   int socket;
+} pps_handle_t;				/* represents a PPS source */
+
+typedef unsigned long pps_seq_t;	/* sequence number */
+
+typedef struct ntp_fp {
+   unsigned int	integral;
+   unsigned int	fractional;
+} ntp_fp_t;				/* NTP-compatible time stamp */
+
+typedef union pps_timeu {
+   struct timespec tspec;
+   ntp_fp_t ntpfp;
+   unsigned long longpad[3];
+} pps_timeu_t;				/* generic data type to represent time stamps */
+
+typedef struct pps_info {
+   pps_seq_t	assert_sequence;	/* seq. num. of assert event */
+   pps_seq_t	clear_sequence;		/* seq. num. of clear event */
+   pps_timeu_t	assert_tu;		/* time of assert event */
+   pps_timeu_t	clear_tu;		/* time of clear event */
+   int		current_mode;		/* current mode bits */
+} pps_info_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
+
+typedef struct pps_params {
+   int		api_version;	/* API version # */
+   int		mode;		/* mode bits */
+   pps_timeu_t assert_off_tu;	/* offset compensation for assert */
+   pps_timeu_t clear_off_tu;	/* offset compensation for clear */
+} pps_params_t;
+
+#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
+
+/* --- 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	/* This bit is 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! ----------------------- */
+
+#define LINUXPPS_MAX_NAME_LEN           32
+struct pps_netlink_msg {
+   int cmd;				/* the command to execute */
+   int source;
+   char name[LINUXPPS_MAX_NAME_LEN];	/* symbolic name */
+   char path[LINUXPPS_MAX_NAME_LEN];	/* path of the connected device */
+   int consumer;			/* selected kernel consumer */
+   pps_params_t params;
+   int mode;   /* edge */
+   int tsformat;			/* format of time stamps */
+   pps_info_t info;
+   struct timespec timeout;
+   int ret;
+};
+#define PPSAPI_MAX_PAYLOAD	sizeof(struct pps_netlink_msg)
+
+/* check Documentation/ioctl-number.txt! */
+#define PPS_CREATE		1
+#define PPS_DESTROY		2
+#define PPS_SETPARMS		3
+#define PPS_GETPARMS		4
+#define PPS_GETCAP		5
+#define PPS_FETCH		6
+#define PPS_KC_BIND		7
+#define PPS_FIND_SRC		8
+
+#ifdef __KERNEL__
+
+#include <linux/socket.h>
+#include <net/sock.h>
+#include <linux/netlink.h>
+
+struct pps_state {
+   pps_params_t	parm;			/* PPS parameters */
+   pps_info_t info;			/* PPS information */
+   int cap;				/* PPS capabilities */
+   long ecount;				/* interpolation offset of event */
+   struct timespec etime;		/* kernel time of event */
+   wait_queue_head_t ewait;		/* wait queue for event */
+};
+
+/* State variables to bind kernel consumer */
+/* PPS API (RFC 2783): current source and mode for ``kernel consumer'' */
+extern const struct pps *pps_kc_hardpps_dev; /* some unique pointer to device */
+extern int pps_kc_hardpps_mode;		     /* mode bits for kernel consumer */
+
+/* Return allowed mode bits for given pps struct, file's mode, and user.
+ * Bits set in `*obligatory' must be set.  Returned bits may be set. */
+extern int pps_allowed_mode(const struct pps *pps, mode_t fmode, int *obligatory);
+
+#else /* !__KERNEL__ */
+
+/* --- 3.4 Functions ------------------------------------------------------- */
+
+#include <asm/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+
+/* Private functions */
+
+static int netlink_msg(int socket, struct pps_netlink_msg *nlpps)
+{
+   struct sockaddr_nl dest_addr;
+   struct nlmsghdr *nlh;
+   struct iovec iov;
+   struct msghdr msg;
+
+   int ret;
+
+   memset(&msg, 0, sizeof(msg));
+
+   /* Create the destination address */
+   memset(&dest_addr, 0, sizeof(dest_addr));
+   dest_addr.nl_family = AF_NETLINK;
+   dest_addr.nl_pid = 0;          /* for the kernel */
+   dest_addr.nl_groups = 0;       /* not in mcast groups */
+
+   nlh = (struct nlmsghdr *) alloca(NLMSG_SPACE(PPSAPI_MAX_PAYLOAD));
+   if (nlh == NULL)
+      return -1;
+
+   /* Fill the netlink message header */
+   nlh->nlmsg_len = NLMSG_SPACE(PPSAPI_MAX_PAYLOAD);
+   nlh->nlmsg_pid = getpid();
+   nlh->nlmsg_flags = 0;
+   memcpy(NLMSG_DATA(nlh), nlpps, sizeof(struct pps_netlink_msg));
+
+   iov.iov_base = (void *) nlh;
+   iov.iov_len = nlh->nlmsg_len;
+   msg.msg_name = (void *) &dest_addr;
+   msg.msg_namelen = sizeof(dest_addr);
+   msg.msg_iov = &iov;
+   msg.msg_iovlen = 1;
+
+   /* Send the message */
+   ret = sendmsg(socket, &msg, 0);
+   if (ret < 0)
+      return ret;
+
+   /* Wait for the answer */
+   memset(nlh, 0, NLMSG_SPACE(PPSAPI_MAX_PAYLOAD));
+   ret = recvmsg(socket, &msg, 0);
+   if (ret < 0)
+      return ret;
+
+   /* Check the return value */
+   memcpy(nlpps, NLMSG_DATA(nlh), sizeof(struct pps_netlink_msg));
+   if (nlpps->ret < 0) {
+      errno = -nlpps->ret;
+      return -1;
+   }
+
+   return 0;
+}
+
+/* The PPSAPI functions */
+
+/* Create PPS handle from file descriptor */
+/* We simply ignore "filedes" */
+static __inline int time_pps_create(int filedes, pps_handle_t *handle)
+{
+   struct sockaddr_nl src_addr, dest_addr;
+   struct pps_netlink_msg nlpps;
+
+   int ret;
+
+   /* Create the netlink socket */
+   ret = socket(PF_NETLINK, SOCK_RAW, NETLINK_PPSAPI);
+   if (ret < 0)
+      return ret;
+   handle->socket = ret;
+
+   /* Bind the socket with the source address */
+   memset(&src_addr, 0, sizeof(src_addr));
+   src_addr.nl_family = AF_NETLINK;
+   src_addr.nl_pid = getpid();   /* self PID as unique ID */
+   src_addr.nl_groups = 0;       /* not in mcast groups */
+   ret = bind(handle->socket, (struct sockaddr *) &src_addr, sizeof(src_addr));
+   if (ret < 0) {
+      close(handle->socket);
+      return ret;
+   }
+
+   /* Now ask the kernel to create the PPS source */
+   nlpps.cmd = PPS_CREATE;
+   nlpps.source = filedes;
+   ret = netlink_msg(handle->socket, &nlpps);
+   if (ret < 0)
+      return ret;
+
+   /* Save the PPS source returned by the kernel */
+   handle->source = nlpps.source;
+
+   return 0;
+}
+
+/* Release PPS handle */
+static __inline int time_pps_destroy(pps_handle_t handle)
+{
+   struct pps_netlink_msg nlpps;
+
+   int ret;
+
+   /* Ask the kernel to destroy the PPS source */
+   nlpps.cmd = PPS_DESTROY;
+   nlpps.source = handle.source;
+   ret = netlink_msg(handle.socket, &nlpps);
+   if (ret < 0)
+      return ret;
+
+   /* Now we can destroy the netlink socket */
+   close(handle.socket);
+
+   return 0;
+}
+
+/* Set parameters for handle */
+static __inline int time_pps_setparams(pps_handle_t handle, const pps_params_t *ppsparams)
+{
+   struct pps_netlink_msg nlpps;
+
+   int ret;
+
+   /* Ask the kernel to set the new PPS source's parameters */
+   nlpps.cmd = PPS_SETPARMS;
+   nlpps.source = handle.source;
+   nlpps.params = *ppsparams;
+   ret = netlink_msg(handle.socket, &nlpps);
+   if (ret < 0)
+      return ret;
+
+   return 0;
+}
+
+static __inline int time_pps_getparams(pps_handle_t handle, pps_params_t *ppsparams)
+{
+   struct pps_netlink_msg nlpps;
+
+   int ret;
+
+   /* Ask the kernel to return the PPS source's parameters */
+   nlpps.cmd = PPS_GETPARMS;
+   nlpps.source = handle.source;
+   ret = netlink_msg(handle.socket, &nlpps);
+   if (ret < 0)
+      return ret;
+
+   /* Return the parameters */
+   *ppsparams = nlpps.params; 
+
+   return 0;
+}
+
+/* Get capabilities for handle */
+static __inline int time_pps_getcap(pps_handle_t handle, int *mode)
+{
+   struct pps_netlink_msg nlpps;
+
+   int ret;
+
+   /* Ask the kernel to return the PPS source's capabilities */
+   nlpps.cmd = PPS_GETCAP;
+   nlpps.source = handle.source;
+   ret = netlink_msg(handle.socket, &nlpps);
+   if (ret < 0)
+      return ret;
+
+   /* Return the capabilities */
+   *mode = nlpps.mode; 
+
+   return 0;
+}
+
+/* current event for handle */
+static __inline int time_pps_fetch(pps_handle_t handle, const int tsformat, pps_info_t *ppsinfobuf, const struct timespec *timeout)
+{
+   struct pps_netlink_msg nlpps;
+
+   int ret;
+
+   /* Ask the kernel to return the PPS source's capabilities */
+   nlpps.cmd = PPS_FETCH;
+   nlpps.source = handle.source;
+   nlpps.tsformat = tsformat;
+   if (timeout)
+      nlpps.timeout = *timeout;
+   else    /* wait forever */
+      nlpps.timeout.tv_sec = nlpps.timeout.tv_nsec = -1;
+
+   ret = netlink_msg(handle.socket, &nlpps);
+   if (ret < 0)
+      return ret;
+
+   /* Return the timestamps */
+   *ppsinfobuf = nlpps.info; 
+
+   return 0;
+}
+
+/* Specify kernel consumer */
+static __inline int time_pps_kcbind(pps_handle_t handle, const int kernel_consumer, const int edge, const int tsformat)
+{
+   struct pps_netlink_msg nlpps;
+
+   int ret;
+
+   /* Ask the kernel to destroy the PPS source */
+   nlpps.cmd = PPS_KC_BIND;
+   nlpps.source = handle.source;
+   nlpps.consumer = kernel_consumer;
+   nlpps.mode = edge;
+   nlpps.tsformat = tsformat;
+   ret = netlink_msg(handle.socket, &nlpps);
+   if (ret < 0)
+      return ret;
+
+   return 0;
+}
+
+/* Find a PPS source */
+#define PPS_HAVE_FINDSOURCE	1
+static __inline int time_pps_findsource(int index, char *path, int pathlen, char *idstring, int idlen)
+{
+   int sock;
+   struct sockaddr_nl src_addr, dest_addr;
+   struct pps_netlink_msg nlpps;
+
+   int ret;
+
+   /* Create the netlink socket */
+   ret = socket(PF_NETLINK, SOCK_RAW, NETLINK_PPSAPI);
+   if (ret < 0)
+      return ret;
+   sock = ret;
+
+   /* Bind the socket with the source address */
+   memset(&src_addr, 0, sizeof(src_addr));
+   src_addr.nl_family = AF_NETLINK;
+   src_addr.nl_pid = getpid();   /* self PID as unique ID */
+   src_addr.nl_groups = 0;       /* not in mcast groups */
+   ret = bind(sock, (struct sockaddr *) &src_addr, sizeof(src_addr));
+   if (ret < 0) {
+      close(sock);
+      return ret;
+   }
+
+   /* Ask the kernel to destroy the PPS source */
+   nlpps.cmd = PPS_FIND_SRC;
+   nlpps.source = index;
+   ret = netlink_msg(sock, &nlpps);
+   if (ret < 0) {
+      close(sock);
+      return ret;
+   }
+
+   strncpy(path, nlpps.path, pathlen);
+   strncpy(idstring, nlpps.name, idlen);
+
+   close(sock);
+   return 0;
+}
+
+#endif   /* !__KERNEL__ */
+#endif   /* _SYS_TIMEPPS_H_ */
