[LinuxPPS] Advise on kernel documentation

Rodolfo Giometti giometti at enneenne.com
Tue Feb 6 22:42:29 CET 2007


Hello,

please review this last version of LinuxPPS kernel documentation.

If you have modifications ___please___ produce a patch with:

   diff -ub pps.txt.orig pps.txt

Thanks,

Rodolfo

-- 

GNU/Linux Solutions                  e-mail:    giometti at enneenne.com
Linux Device Driver                             giometti at gnudd.com
Embedded Systems                     		giometti at linux.it
UNIX programming                     phone:     +39 349 2432127
-------------- next part --------------

			PPS - Pulse Per Second
			----------------------

(C) Copyright 2007 Rodolfo Giometti <giometti at enneenne.com>


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 who
provides an high and precise signal each second so that an application
can use it to adjust time clock.

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
usefull beside(!) 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 change the original RFC 2783:

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

into a modified:

   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.

This change seems to be neglibile because even the original RFC 2783
does not encourage programs to check (read: use) the pps_handle_t
variable before calling the time_pps_*() functions, since each
function should do this job internally.

If I intentionally separate the concept of "file descriptor" from the
concept of the "PPS source" I'm obliged to provide a solution to find
and register a PPS-source without using a file descriptor: it's done
by the functions time_pps_findsource() and time_pps_findpath() now.

According to this current NTPD drivers' code should be modified as
follows:

+#ifdef PPS_HAVE_FINDPATH
+      /* Get the PPS source's real name */
+      fd = readlink(link, path, STRING_LEN-1);
+      if (fd <= 0)
+              strncpy(path, link, STRING_LEN);
+      else
+              path[fd] = '\0';
+
+      /* Try to find the source */
+      fd = time_pps_findpath(path, STRING_LEN, id, STRING_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");
       }


Coding example
--------------

To register a PPS source into the kernel you should define a struct
linuxpps_source_info_s as follow:

    static struct linuxpps_source_info_s linuxpps_ktimer_info = {
            name         : "ktimer",
            path         : "",
            mode         : PPS_CAPTUREASSERT|PPS_OFFSETASSERT|PPS_ECHOASSERT| \
                           PPS_CANWAIT|PPS_TSFMT_TSPEC,
            echo         : linuxpps_ktimer_echo,
    };

and then calling the function linuxpps_register_source() in your
intialization routine as follow:

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

The linuxpps_register_source() prototipe is:

  int linuxpps_register_source(struct linuxpps_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
linuxpps_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:

    linuxpps_event(source, PPS_CAPTUREASSERT, ptr);

The same function may also run the defined echo function
(linuxpps_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.


PROCFS support
--------------

If the PROCFS support is enabled a new directory is created:

    $ ls /proc/pps/
    00       01       sources

The file "sources" holds a brief description of all PPS sources
defined into the system:

    $ cat /proc/pps/sources 
    id    mode		echo	name			path
    ----  ------	----	----------------	----------------
    00    1133		no	serial0			/dev/ttyS0
    01    1133		no	serial1			/dev/ttyS1

while other files are directories that hold one or two files according
to the ability of the associated source to provide "assert" and
"clear" timestamps:

    $ ls /proc/pps/00 
    assert		  clear

Inside each "assert" and "clear" file you can find the timestamp and a
sequence number as reported below:

    $ cat /proc/pps/00/assert 
    1170026870.983207967 #8


SYSFS support
-------------

The SYSFS support is enabled by default if the SYSFS filesystem is
enabled into the kernel and it provides a new class:

   $ ls /sys/class/pps/
   00/  01/  02/

Each directory is the ID of each PPS sources defined into the system
and inside each one you can find several files:

    $ ls /sys/class/pps/00/
    assert	clear  echo  mode  name  path  subsystem@  uevent

Files "assert" and "clear" have the same functionality as the PROCFS
alter ego (even if with different syntax due the one-value-per-file
sysfs pragma) while other files are:

* echo: reports if the PPS source has an echo function or not;

* mode: reports available PPS functioning modes (in the same form of
  PROCFS);

* 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).


Resources
---------

Wiki: http://wiki.enneenne.com/index.php/LinuxPPS_support and the LinuxPPS
ML:   http://ml.enneenne.com/cgi-bin/mailman/listinfo/linuxpps.


More information about the LinuxPPS mailing list