LinuxPPS support (old APIs)

From EnneEnneWiki

Contents

If you like LinuxPPS make a donation with PayPal - it's fast, free and secure!

Note: this documentation has been updated to the LinuxPPS version 3.1.0 to 3.2.0.

Note: version series from 4.0.0-rc1 and 4.0.x are not documented anymore due they short life.

Old versions are currently NOT supported anymore due several incompatibilities, while newer versions starting from 5.0.0-rc1 are documented in LinuxPPS support page. This page is still available only for backward compatibility.

Introduction

If you wish using a PPS (Pulse Per Second) source on your GNU/Linux system (versions 2.6) you may try using my implementation. :)

This API uses the Linux's netlink layer to exchange data to the userland and a well defined kernel API (by exported symbols) to register new PPS sources (client).

The benefits of this solution are:

  • by using the netlink layer we can divide the PPS API from a particular driver or device: for example you don't need to modify the serial driver so heavy as in PPSkit-light-PPSAPI-alpha-1610m-2.6.5, just add some functions calls.
  • the PPS API can be the same for all PPS source: currently in PPSkit-light-PPSAPI-alpha-1610m-2.6.5 there are duplicated functions that do the same things: do_clock_gettime(), pps_update_event(), etc..
  • by using the exported kernel symbols we can manage the PPS source with a few and simple functions calls. See the example below.

Data and Performance Monitoring

Just to help you in choosing my solution or another one let me suggest to you this performance monitoring site by Steven Bjork where you can see several PPS implementation in action.

Also you may take a look at Steven Bjork's main site to learn more on the Network Time Protocol.

Getting help

Currently there is a mail list focused on LinuxPPS where you can find other people interested into the project.

The main documentation is reported here but on the mail list you can ask about you specific problems.

Downloading the code

The latest code is at my GITWeb but you can find some patches against some Linux versions here but you are warned that some of these patches may implement old LinuxPPS version! Refere to the README file in order to see which LinuxPPS version implements each patch.

You can also find also a very old and deprecated version of the software for kernel series 2.4 and 2.6 on my HTTP/FTP site but you are warned in using that code.

Cloning my repository

As already said the latest version of LinuxPPS code is on my GITWeb and I strongly suggest you in using such tool since it is simple to use. :)

First and simple way (but time consuming) is to clone my repository. This step is mandatory for further use of the GIT commands, the drawback is that you must download whole repository which is about 120MB...

Ok, once you have found enough space on your hard disk you should use the command:

giometti@zaigor:~$ git clone git://git.enneenne.com/linuxpps

after the clone is finished into directory linuxpps you can find the code.

At this point whenever new patches are added into my repository you may update your own repository just giving the command:

giometti@zaigor:~$ git pull git://git.enneenne.com/linuxpps

When finished your repository will be again a copy of mine.

Merging my repository with your current Linux Vanilla one

Ok, my internet connection is slow and you have no time to waste in waiting for code downloading... but if you have your own Linux Vanilla repository you are lucky!

In fact, another way to get LinuxPPS is just to create a new branch for it starting from your preferred linux version (for example 2.6.18-rc4):

git checkout -b linuxpps 

then download and merge only LinuxPPS changes from my server on master branch into your current local branch:

git pull git://git.enneenne.com/linuxpps master

This example came from Wikipedia (Great! We are on Wikipedia!!!)

If you wish cloning your local Linux Vanilla repository in order to have multiple copies of it, you can use the command:

git clone --reference /path/to/linux/clone 

If you have no Linux Vanilla repository to clone from you can use rsync to grab one (since the trees on kernel.org are available by all of http, git and rsync and go from there. You do need the --partial option to rsync (use -avrPSH). Once you get a rsync you can use it as a --bare clone. Clone from that (use an ssh url) and then use that clone for the --reference (thanks to James Cloos).

As above, whenever new patches are added into my repository you may update your own repository just giving the command:

giometti@zaigor:~$ git pull git://git.enneenne.com/linuxpps

Please note that is this case you must have checked out the linuxpps branch and not the master one as in the first example (repository cloning).

Using a patch

If you don't know GIT and you wish using the old-style way of obtaining the code you can just download a patch.

Note that I provide a patch only for few LinuxPPS versions and only against specific kernel versions!

You can get the patches here, but before using them refer to the README file who explains which versions of LinuxPPS are available.

Once you have chosen a patch you can do the job with the command:

giometti@zaigor:~/linux-2.6.21$ patch -p1 < ntp-pps-2.6.21.diff

Compiling the code

The kernel code

Once you have downloaded the code form my GITweb site you may decide to use my kernel (which derives from vanilla one) or try to get a patch to apply at your preferred tree (please refere to the GIT tools in learn how to do this or look here for already available patches).

In the first case simply run the kernel compilation tool to configure your new PPS support into the "Device Drivers" section. So, for example, use the command make menuconfig and then open the "PPS support" menu. Setup your PPS configuration and don't forget to choose a PPS client! If you wish enable the serial line support I suggest you to set something as follow:

        <M> PPS support                                                       
        [ ]   PPS debugging messages                                          
        ---   PPS clients support                                             
        <M>   Kernel timer client (Testing client, use for debug)             
        [*]   UART serial support                                             
        [*]   Parallel printer support                                        

Note that "PPS support" code is currently marked as experimental so you will need to enable prompt for experimental drivers in the "Code maturity level options" section:

        [*] Prompt for development and/or incomplete code/drivers             
        [*]   Select only drivers expected to compile cleanly (NEW)   

Also note that the "UART serial support" option may be forced off if you selected "PPS support" as a module and "8250/16550 and compatible serial support", into "Serial drivers" section, as built in, it needs to be a module too (similar behavior is for "Parallel printer support" and the same menu entry into "Character devices"). See below for an example:

        <M> PPS support                                                       
        [*]   PPS debugging messages                                          
        ---   PPS clients support                                             
        <M>   Kernel timer client (Testing client, use for debug)             
        ---   UART serial support (forced off)                                
        ---   Parallel printer support (forced off)                           

Well, now recompile the kernel and reboot.

If you decide to use a kernel patch just patch the kernel and then configure and compile the new kernel as above.

Note: each kernel patch adds a LinuxPPS snapshot at time of patch creation, so if you wish having latest code you should refer to the GITweb site.

The userland tools

After the kernel has been compiled and rebooted, before continuing, it's time to compile the userland tools by setting up the compilation environment.

In order to correctly compile the userland programs we need add our new kernel to the glibc, so go to the /usr/include directory and substitute the directories linux, asm and asm-generic as follows:

giometti@zaigor:/usr/include# mv linux linux.old
giometti@zaigor:/usr/include# mv asm asm.old
giometti@zaigor:/usr/include# mv asm-generic asm-generic.old
giometti@zaigor:/usr/include# ln -s /lib/modules/$(uname -r)/build/include/linux .
giometti@zaigor:/usr/include# ln -s /lib/modules/$(uname -r)/build/include/asm .
giometti@zaigor:/usr/include# ln -s /lib/modules/$(uname -r)/build/include/asm-generic .

then we need to add the file ppstime.h under the directory /usr/include.

ppstest and ppsctl

Now you can get the userland tools here and compiling them with the command:

giometti@zaigor:~/linuxpps/test$ make
cc -Wall -O2 -D_GNU_SOURCE -M ppstest.c ppsctl.c > .depend
cc -Wall -O2 -D_GNU_SOURCE    ppstest.c   -o ppstest
cc -Wall -O2 -D_GNU_SOURCE    ppsctl.c   -o ppsctl

If you get no errors your compilation environment should be ok.

setserial

If you wish using setserial to manage your serial ports you have to patch it with the following patch in order to support the hardpps parameter (at least version 2.17 has no such support):

--- setserial.c.old	2007-02-17 08:47:45.000000000 +0100
+++ setserial.c	2007-02-17 08:57:26.000000000 +0100
@@ -127,6 +127,7 @@
 	CMD_FLAG,	"hup_notify",	ASYNC_HUP_NOTIFY, ASYNC_HUP_NOTIFY, 0, FLAG_CAN_INVERT,
 	CMD_FLAG,	"skip_test",	ASYNC_SKIP_TEST,ASYNC_SKIP_TEST,2, FLAG_CAN_INVERT,
 	CMD_FLAG,	"auto_irq",	ASYNC_AUTO_IRQ,	ASYNC_AUTO_IRQ,	2, FLAG_CAN_INVERT,
+	CMD_FLAG,	"hardpps",	ASYNC_HARDPPS_CD, ASYNC_HARDPPS_CD, 0, FLAG_CAN_INVERT,
 	CMD_FLAG,	"split_termios", ASYNC_SPLIT_TERMIOS, ASYNC_SPLIT_TERMIOS, 2, FLAG_CAN_INVERT,
 	CMD_FLAG,	"session_lockout", ASYNC_SESSION_LOCKOUT, ASYNC_SESSION_LOCKOUT, 2, FLAG_CAN_INVERT,
 	CMD_FLAG,	"pgrp_lockout", ASYNC_PGRP_LOCKOUT, ASYNC_PGRP_LOCKOUT, 2, FLAG_CAN_INVERT,
@@ -725,6 +726,7 @@
 	fprintf(stderr, "\t^ fourport\tconfigure the port as an AST Fourport\n");
 	fprintf(stderr, "\t  autoconfig\tautomatically configure the serial port\n");
 	fprintf(stderr, "\t^ auto_irq\ttry to determine irq during autoconfiguration\n");
+	fprintf(stderr, "\t^ hardpps\tmanage PPS signal when CD changes status\n");
 	fprintf(stderr, "\t^ skip_test\tskip UART test during autoconfiguration\n");
 	fprintf(stderr, "\n");
 	fprintf(stderr, "\t^ sak\t\tset the break key as the Secure Attention Key\n");

So download the sources from the setserial home site or directly from your distribution. As example on my Debian I simply do:

giometti@zaigor:/tmp$ apt-get source setserial

then recompile the setserial tool as described into its documentation or, if you use Debian, use the command:

giometti@zaigor:/tmp/setserial-2.17$ fakeroot debian/rules binary

Checking the new system

After the system restart you should get the new directory /sys/class/pps and digging inside that directory you should see all your enabled clients:

giometti@zaigor:~/linuxpps$ tree /sys/class/pps
/sys/class/pps
|-- 00
|   |-- assert
|   |-- clear
|   |-- echo
|   |-- mode
|   |-- name
|   |-- path
|   |-- subsystem -> ../../../class/pps
|   `-- uevent
`-- 01
    |-- assert
    |-- echo
    |-- mode
    |-- name
    |-- path
    |-- subsystem -> ../../../class/pps
    `-- uevent

If you see nothing maybe you should load some client module and/or enable the PPS support for a specified client, for example for the 8250 serial line support, after insmoding the kernel module:

giometti@zaigor:~/linuxpps$ sudo modprobe 8250

use the commands:

giometti@zaigor:~/linuxpps$ setserial /dev/ttyS1 hardpps 
giometti@zaigor:~/linuxpps$ cat /sys/class/pps/02/path 
/dev/ttyS1 

to enable the PPS support for the serial line /dev/ttyS1, or, if you don't wish modify your current setserial, you can use my ppsctl as follow:

giometti@zaigor:~/linuxpps$ ppsctl /dev/ttyS1 enable

Ok, now the PPS support is active and you can try using the ppstest (as root) into in order to verify if you get the PPS signal. As example see my test below using the debugging PPS client ktimer:

giometti@zaigor:~/linuxpps$ sudo ./test/ppstest 
found PPS source #0 "ktimer" on ""
Assert timestamp: 1138812799.797267675, sequence: 477
Assert timestamp: 1138812800.797337675, sequence: 478
Assert timestamp: 1138812801.797392675, sequence: 479
Assert timestamp: 1138812802.797463675, sequence: 480
Assert timestamp: 1138812803.797522675, sequence: 481

If ktimer has a different id from 0 into your system, you can run ppstest as follow:

giometti@zaigor:~/linuxpps$ cat /sys/class/pps/01/name
ktimer
giometti@zaigor:~/linuxpps$ sudo ./test/ppstest 1
found PPS source #1 "ktimer" on ""
Assert timestamp: 1139092418.968589675, sequence: 514
Assert timestamp: 1139092419.968671675, sequence: 515
Assert timestamp: 1139092420.968718675, sequence: 516
Assert timestamp: 1139092421.968785675, sequence: 517

You can also test if the PPS events are cached by the system by using the sysfs support. In the /sys/class/pps directory you can find a dedicated directory for each PPS source registered into the system:

giometti@jeeg:~$ ls -l /sys/class/pps/
drwxr-xr-x 2 root root 0 Feb 18 20:08 00/
drwxr-xr-x 2 root root 0 Feb 18 20:08 01/
drwxr-xr-x 2 root root 0 Feb 18 20:11 02/

Each directory may contain file assert and/or clear depending on your PPS source capability, in the above example I get:

giometti@jeeg:~$ ls /sys/class/pps/??/{assert,clear}
/sys/class/pps/00/assert  /sys/class/pps/01/assert  /sys/class/pps/02/clear
/sys/class/pps/00/clear   /sys/class/pps/02/assert

since ktimer client supports only assert events and 8250 supports both. Reading this file you may get last assert or clear event time and sequence number:

giometti@jeeg:~$ cat /sys/class/pps/01/assert
1148727816.24772000 #326
giometti@jeeg:~$ cat /sys/class/pps/01/assert 
1148727816.24772000 #326
giometti@jeeg:~$ cat /sys/class/pps/01/assert 
1148727817.24872000 #327
giometti@jeeg:~$ cat /sys/class/pps/01/assert 
1148727817.24872000 #327
giometti@jeeg:~$ cat /sys/class/pps/01/assert 
1148727818.24945000 #328
giometti@jeeg:~$ cat /sys/class/pps/01/assert 
1148727818.24945000 #328
giometti@jeeg:~$ cat /sys/class/pps/01/assert 
1148727819.25003000 #329

if the PPS source is generating the events and you system sees them you should read different values each time you read the file.

NTPD support

Currently ntpd doesn't support LinuxPPS so you have to recompile it. To do it consider the specific instructions for your reference clock into the following list of (currently) tested reference clocks with LinuxPPS and after running the configure tool verify that the PPS support has been enabled by checking:

giometti@zaigor:~/ntp$ grep HAVE_PPSAPI config.h
#define HAVE_PPSAPI 1
Reference clock Driver Contact PPS source Docs Patches
Generic NMEA GPS Receiver (nmea) 20 Udo van den Heuvel pps_8250 NTPD driver 20 here for ntp-4.2.0.a.20050816-14
Atom PPS Clock Discipline (atom) 22 linux at horizon dot com pps_8250 NTPD driver 22 here for ntp-4.2.2+dfsg.2
Motorola Oncore GPS receiver (oncore) 30 Reg Clemens pps_8250 NTPD driver 30 none for ntp-dev-4.2.0b-rc1-20060306 or later
RIPE NCC interface for Trimble Palisade (ripencc) 43 Rodolfo Giometti pps_mpc8xx NTPD driver 43 here for ntp-4.2.0a+stable

Generic NMEA GPS Receiver (refclock_nmea - driver 20)

This driver implements reference clock support for NTPD package that can read time data from a Generic NMEA GPS receiver and optionally its PPS signal.

Installation

Patch refclock_nmea.c with the supplied patch file and then run the configure command disabling know LinuxPPS-buggy reference clocks (please refere to NTPD documentation in order to know which other compilation options will fit your needs):

giometti@zaigor:~/ntp$ ./configure --disable-all-clocks --disable-parse-clocks\
                                   --enable-NMEA --enable-LOCAL-CLOCK

Then compile the daemon:

giometti@zaigor:~/ntp$ make

If everything goes well you should now install the new NTDP daemon into your system following NTPD documentation and then start it.

Udo's configuration file ntp.conf appear as follow:

server 127.127.20.0 prefer minpoll 4
fudge 127.127.20.0 flag3 1 flag2 0 time1 0.0

Note

For further info on this reference clock support, please, refer to the file driver20.html in the NTP source tree.

The Generic NMEA GPS Receiver was tested successfully with a Garmin GPS 18 LVC and NTP 4.2.0.a.20050816-14 & 4.2.0.a.20060224 for Fedora Core 5.

Atom PPS Clock Discipline (refclock_atom - driver 22)

This driver implements reference clock support for NTPD package that can read time data from a Atom PPS Clock Discipline.

Installation

As mentioned in the NTPD documentation, this is a secondary PPS-input driver that can be used with any other time source, either a local reference clock or a network peer.

Because a PPS signal does not give a complete time, it must be attached to another time source which can disambiguate the seconds. If that time source is valid and within ±0.4 seconds of the PPS edge, the PPS clock will "take over" and provide the time measurements that NTPD uses.

Note that it is entirely normal for the PPS time source to use the same serial port as a serial time code. For serial ports, the PPS time source uses the edges of the DCD input signal.

The disambiguating time source is marked with the keyword prefer on its server or peer line. The PPS clock will not begin sampling until this clock is detected, so do not worry about not seeing events detected immediately.

An example PPS clock configuration is:

server 127.127.22.1 minpoll 4 maxpoll 4
fudge 127.127.22.1 flag flag2 1

The prefix 127.127.22 specifies the ATOM driver. The last number 1 specifies PPS source #1, which you should see in /sys/class/pps/ directory.

The fudge 127.127.22.1 flag2 1 specifies that the "clear" transtion of the PPS signal (/sys/class/pps/01/clear) should be used as a time source. The default, if flag2 is 0, is to use the assert edge.

PPS pulses are generally short, and the leading edge is the on-time mark, so by looking at the time of two adjacent edges with cat /sys/class/pps/01/{assert,clear}, you can see which one leads the other.

Note

For further info on this reference clock support, please, refere to the file driver22.html in the NTP source tree.

If you do not have a local reference clock, one rude hack which is possible is to mark the PPS source as prefer as well as a network clock. Then, the network clock is required for NTPD to start, but once it has started, as long as it doesn't lose track of the PPS signal, it can keep on by itself. However, this can result in an NTP server claiming to have good time when it is off by some seconds, and so is not recommended.

Motorola Oncore GPS receiver (refclock_oncore - driver 30)

This driver implements reference clock support for NTPD package that reads time data and PPS signals from a Motorola Oncore GPS receiver.

Installation

Run the configure command disabling know LinuxPPS-buggy reference clocks (please refere to NTPD documentation in order to know which other compilation options will fit your needs):

giometti@zaigor:~/ntp-dev-4.2.0b-rc1-20060306$ ./configure --disable-ATOM \
--disable-JUPITER --disable-NMEA

Then compile the daemon:

giometti@zaigor:~/ntp-4.2.0a+stable$ make

If everything goes well you should now install the new NTDP daemon into your system following the NTPD documentation and then start it.

Note

For further info on this reference clock support, please, refere to the file driver30.html into NTP source tree.

RIPE NCC interface for Trimble Palisad (refclock_ripencc - driver 43)

This driver implements reference clock support for NTPD package that can read time data from a Trimble Acutime 2000 GPS antenna (or by a generic GPS device connected to the serial line that supports the TSIP protocol).

Here you can find some patches for the existing file refclock_ripencc.c distribuited with NTP package version 4.2.0a+stable in order to make it functional.

I fixed the big/little endian support and the PPS support in such a way that you may use this reference clock with or without PPS support in your kernel.

I tested this reference clock with and without PPS support by using it on a MPC8xx board (ppc) runnig linux 2.4.24. The PPS signal was connected to special CPU's input pin (not with the serial line DCD).

Installation

Then unpack the code and patch it:

giometti@zaigor:~$ tar xfz ntp_4.2.0a+stable.tgz
giometti@zaigor:~$ cd ntp-4.2.0a+stable/
giometti@zaigor:~/ntp-4.2.0a+stable$ patch -p1 < ../patch.cross_compiling
patching file configure.in
Hunk #1 succeeded at 26 (offset 1 line).
patching file ntpdc/Makefile.am
giometti@zaigor:~/ntp-4.2.0a+stable$ patch -p1 < ../patch.ntp-4.2.0a+stable-configure.in
patching file configure.in
giometti@zaigor:~/ntp-4.2.0a+stable$ patch -p1 < ../patch.ntp-4.2.0a+stable-refclock_ripencc.c
patching file ntpd/refclock_ripencc.c

then rerun the auto configuration tools:

giometti@zaigor:~/ntp-4.2.0a+stable$ aclocal && autoconf && automake

Now run the configure command disabling know LinuxPPS-buggy reference clocks (please refere to NTPD documentation in order to know which other compilation options will fit your needs):

giometti@zaigor:~/ntp-4.2.0a+stable$ ./configure --enable-RIPENCC \
--disable-ATOM --disable-JUPITER --disable-NMEA

Then compile the daemon:

giometti@zaigor:~/ntp-4.2.0a+stable$ make

If everything goes well you should now install the new NTDP daemon into your system following the NTPD documentation and then start it.

Note

For further info on this reference clock support, please, refere to the file driver43.html in the NTP source tree.

How to contribute

If you wish contribute to this project, first of all, you should subscribe to the LinuxPPS's mail list.

Then if you wish to modify the source code I suggest you to get LinuxPPS's GIT repository (see above) and send back your patches.

Just use the command:

giometti@zaigor:~/linuxpps$ git diff linux-vanilla master > my_patch

this produces a patch against the linux vanilla code holded in the repository. Please, refer to the GIT documentation in order to know how to update current Linux vanilla into your LinuxPPS repository.

Then, if you are a good boy/girl who improved LinuxPPS or just who found a bug, you may send the patch to the mail list (adding the string "[PATCH]" in the subject) by using the command:

giometti@zaigor:~/linuxpps$ git diff > patch_file

Please, refere to the GIT manuals in order to have further information on how to use the git-diff command.

On the other hands, if you wish to suggest modifications to this documentation, please, use the following procedure:

1) by using the commands:

$ lynx --dump 'http://wiki.enneenne.com/index.php/LinuxPPS_support' > LinuxPPS.txt
$ cp LinuxPPS.txt LinuxPPS.txt.orig

you can get a text copy of this WEB page.

2) then modify the file LinuxPPS.txt and send back the output of the command:

$ diff -u LinuxPPS.txt.orig LinuxPPS.txt

in this manner I can read very easily what you changed (again, don't forget the string "[PATCH]" into the subject).

Hacking the code

Adding a new LinuxPPS source

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,
};

and then calling the function pps_register_source() in your intialization routine:

source = pps_register_source(&pps_ktimer_info,
                 PPS_CAPTUREASSERT|PPS_OFFSETASSERT,
                 -1 /* is up to the system */);

The pps_register_source() prototype is:

int pps_register_source(struct pps_source_info_s *info, int default_params, int try_id)

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) and try_id can be used to force a particular ID for your device into the system (just use -1 if you wish the system chooses one for you).

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.

Modifing a reference clock to work with LinuxPPS

The RFC 2783 says that

pps_handle_t type is an opaque scalar type used to represent a PPS source within the API

In my implementation I intentionally want to separate the concept of file descriptor to the concept of the PPS source since some devices do not have such association (some devices are directly connected to a dedicated interrupt line for example).

If your receiver is connected to a serial line then everything works well but, if this is not true, you have no filedes to pass to the function time_pps_create(). That's why I added a new function time_pps_findsource() in order to find a generic PPS source (note that this function is protected by the PPS_HAVE_FINDSOURCE define).

So, my opinion is that RFC 2783 should say that

pps_handle_t type is an opaque variable used to represent a PPS source within the API

and programs should not access it directly to it due to its opacity.

Also note that the RFC 2783 does not say that programs should check the pps_handle_t variable before calling the time_pps_*() functions, since each function should do this job internally.

According the above considerations you have two ways to make a generic reference clock working with LinuxPPS support.

The first one (and the oldest one) is by using the function time_pps_findsource() in order to get a PPS source ID to pass to the function time_pps_create():

+#ifdef PPS_HAVE_FINDSOURCE
+      /* Try to find the source (by using "index = -1" we ask just for a
+       * generic source) */
+      fd = time_pps_findsource(-1, path, PPS_MAX_NAME_LEN, id, PPS_MAX_NAME_LEN);
+      if (fd < 0) {
+              msyslog(LOG_ERR, "refclock: no available PPS source in the system");
+              return 1;
+      }
+      msyslog(LOG_INFO, "refclock: found PPS source #%d \"%s\" on \"%s\"", fd, path, id);
+#endif   /* PPS_HAVE_FINDSOURCE */
+
+
       if (time_pps_create(fd, &pps_handle) < 0) {
-              pps_handle = 0;
               msyslog(LOG_ERR, "refclock: time_pps_create failed: %m");
       }

In this manner fd is not a file descriptor but it is a PPS source ID. In an older program, by using the PPS_HAVE_FINDSOURCE define, everything should continue working the same as it did before.

The second way (and the most powerful one) is by using the function time_pps_findpath() with the function time_pps_readlink():

+#ifdef PPS_HAVE_FINDPATH
+      /* Get the PPS source's real name */
+      time_pps_readlink(link, LENPPS, path, PPS_MAX_NAME_LEN);
+
+      /* Try to find the source */
+      fd = time_pps_findpath(path, PPS_MAX_NAME_LEN, id, PPS_MAX_NAME_LEN);
+      if (fd < 0) {
+              msyslog(LOG_ERR, "refclock: cannot find PPS source \"%s\" in the system", path);
+              return 1;
+      }
+      msyslog(LOG_INFO, "refclock: found PPS source #%d \"%s\" on \"%s\"", fd, path, id);
+#endif   /* PPS_HAVE_FINDPATH */
+
+
       if (time_pps_create(fd, &pps_handle) < 0) {
-              pps_handle = 0;
               msyslog(LOG_ERR, "refclock: time_pps_create failed: %m");
       }

The variable link may be initialized with the string "/dev/gps0", in this manner we can surely address the right PPS device as show below:

giometti@jeeg:~/linuxpps$ cat /sys/class/pps/01/name
serial1 
giometti@jeeg:~/linuxpps$ cat /sys/class/pps/01/path
/dev/ttyS1 
giometti at jeeg:~/linuxpps/test$ sudo ln -sf /dev/ttyS1 /dev/gps0
giometti at jeeg:~/linuxpps/test$ sudo ./ppstest /dev/gps0
found PPS source #2 "serial1" on "/dev/ttyS1"
giometti at jeeg:~/linuxpps/test$ sudo ln -sf ktimer /dev/gps0
giometti at jeeg:~/linuxpps/test$ sudo ./ppstest /dev/gps0
found PPS source #0 "ktimer" on ""

Note that sources without a path can be addressed by their name.

Note: Just calling time_pps_create() function may NOT enable PPS signal generation! It depends on your particular client. For instance ktimer client enables PPS signal generation automatically but client 8250 does not, so, in this case, you also need to open the serial device, and, depending on your GPS antenna, you need to send some additional command(s) to it before you can get the PPS signal. NTPD reference clocks should do it all automagically...

Regarding the time_pps_destroy(), we have:

   #ifdef HAVE_PPSAPI
  -      if (pps_handle) {
  -              time_pps_destroy(pps_handle);
  -              pps_handle = 0;
  -      }
  +      time_pps_destroy(pps_handle);
   #endif /* HAVE_PPSAPI */

Here we do not need to check the pps_handle status before calling the time_pps_destroy() since the function checks it internally.

In the directory refclocks you can find patches for some refclocks that already support the LinuxPPS API.

FAQs

Q1

time_pps_create() first parameter should be "...an already-open UNIX file descriptor, for an appropriate special file...". This could be problematic for example by accessing the ktimer source, which has no device.
My idea: The PPS-API should create it's own special devices /dev/pps? and use those for identifying the expected source. Anything wrong with this idea?

A1

I avoid using special devices by using netlink API, that was my real intention! If I define a char device (for instance) I can use it to send/receive data from kernel without the netlink layer.
Using a char device (/dev/pps for instance) can rise the problem when you have a PPS source connected with a serial line or another char device. Which file descriptor should I send to the PPS functions? The one associated with /dev/pps or the one associated with /dev/whatever?
I decided to use netlink layer to avoid specific association between a PPS source and a char/block device. In this nammer I support well both PPS source which is connected with a char device and PPS source which has no devices connected to.
However I don't think filedes could be a real problem, since my API simply doesn't use it and the PPS API use it only in time_pps_create() and time_pps_destroy(). :)

Q2

pps_handle_t is defined as struct with two members, source and socket. But the RFC says that "...pps_handle_t type is an opaque scalar type...". The NTPD uses handles of this type with operators like == and !=, which wont work on structs. How I can resolve the problem?

A2

Here is the real problem with RFC 2783!
I need both a socket for the netlink layer and a source index to identify the PPS source.
I know what RFC specifies but I see that in pratical use there are no reasons to test if the handle is 0 or not, since all functions should return error (EBADF) if they get a wrong handle.
Usually the NTPD drivers test the handle just to know if they already opened it (here a code sample from refclock_ripencc.c):
      if (up->handle != 0)
              time_pps_destroy(up->handle);
But you can replace it with
      time_pps_destroy(up->handle);
since even if the handle is not valid you simply wish to destroy it and nobody tests the return value. :)
I think that RFC 2783 says that pps_handle_t should be a scalar type since it takes in account a specific implementation involving char devices, but in my opinion this solution is reductive. Implementations should use type pps_handle_t as they wish since it should be an opaque type.

Q3

Running my NTPD driver I see in the log messages:
[ntpd] refclock_nmea: time_pps_kcbind failed: Operation not supported
Is that an implementation bug?

A3

No. The function time_pps_kcbind() is defined as «optional» into RFC 2783 and, simply, LinuxPPS doesn't implement it! :)

Q4

How can I verify that my NTPD (or just the ppstest program) is reading the PPS data from LinuxPPS?

A4

If you enable the kernel debugging messages you should see something as follow:
pps_core: capture assert seq #292 for source 0
8250: serial8250: PPS assert event at 420817
This is the capture event from the GPS and saved by LinuxPPS.
pps_core: capture clear seq #292 for source 0
8250: serial8250: PPS clear event at 420942
This is the clear event from the GPS and saved by LinuxPPS. Also we can see that 420942-420817=125 which is ok if PPS duty cycle is 50% and the linux's clock (HZ variable) is set to 250 ticks per second.
pps_core: New message from PID 5205 (flags 0)
pps_core: PPS_FETCH: source 0
Here the userland application with PID 5205 (NTPD or ppstest) is reading PPS data from LinuxPPS.

Q5

OK, I see /dev/lp0 and/or /dev/ttyS0 down in /sys/class/pps, but I thought there also was supposed to be an entry in /proc. I don't see anything there! Why?

A5

No /proc support for Linux 2.6. It's deprecated!

Q6

There is an entry in /sys/class/pps/00/name of serial0 what is this? The actual name of the device is in path.

A6

Into name there is the driver's name while into path there is the device's path name.




If you like LinuxPPS make a donation with PayPal - it's fast, free and secure!
Personal tools