gpsd-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gpsd-dev] [Greg Troxel] [PATCH] Add NetBSD support for RFC2783 PPS.


From: Greg Troxel
Subject: [gpsd-dev] [Greg Troxel] [PATCH] Add NetBSD support for RFC2783 PPS.
Date: Sun, 24 Nov 2013 14:32:18 -0500
User-agent: Gnus/5.130006 (Ma Gnus v0.6) Emacs/23.4 (berkeley-unix)

I think I'm getting sender-filtered, so I'm forwarding this for comment.

This patch is not baked enough yet and should not be applied.
I can split it up into hunks; some of it is ready.

The situation is:

  * comments are written in terms of Linux rather than standards (not a
    big deal, but fixed)

  * Code to associate a serial port with a /dev/ppsN and find it is
    actually Linux-specific, rather than RFC2783 (which leaves this
    unspecified), but it was not ifdefed as such.  For other than linux,
    I just set the pps fd to the serial fd (POLA, and right on *BSD).

  * The fetch timeout timespec is inexplicable.  NetBSD appears not to
    support PPS_CANWAIT, which is surprising, but if one just calls it
    every second, one gets adequate data (as seen in my debug logs).

  * The code did not handle getting assert without clear, and decided to
    use assert (when for my unit clear is correct).

  * I casted pps_seq_t to unsigned long as a common printable variable.

And what remains is:

  * There seems to be an assumption that TIOCMIWAIT is always available,
    which is incorrect.

  * There is no support for (RFC2783-compliant) behavior when
    time_pps_fetch will not block, and always returns the previous
    timestamps immediately.  It seems that usleep() is in order, and
    skipping processing if we don't have a new edge.



--- Begin Message --- Subject: [PATCH] Add NetBSD support for RFC2783 PPS. Date: Sun, 24 Nov 2013 14:18:49 -0500
This commit rewrites the introductory comment to be OS-neutral, but
does not substantially change it for the "#ifdef linux" reader.

Add linux ifdefs around Linux-specific code, and add alternate code
for systems that use the serial port file descriptor for RFC2783
calls.
---
 ppsthread.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 84 insertions(+), 10 deletions(-)

diff --git a/ppsthread.c b/ppsthread.c
index fe1947b..668eb0a 100644
--- a/ppsthread.c
+++ b/ppsthread.c
@@ -4,12 +4,25 @@
  * If you are not good at threads do not touch this file!
  *
  * It helps to know that there are two PPS measurement methods in
- * play. One, kernel PPS (KPPS), is RFC2783 and available on many
- * OS and supplied through special /dev/pps devices. The other, plain
- * PPS, uses the TIOCMIWAIT ioctl to explicitly watch for PPS on
- * serial lines. KPPS requires root permissions for initialization;
- * plain PPS does not.  Plain PPS loses some functionality when not
- * initialized as root.
+ * play.  One is defined by RFC2783 and typically implemented in the
+ * kernel.  It is available on FreeBSD, Linux, and NetBSD.  On Linux
+ * it is referred to as KPPS, and is accessed via /dev/ppsN devices.
+ * On BSD it is accessed via the same device as the serial port.  This
+ * mechanism is preferred as it should provide the smallest latency
+ * and jitter from control line transition to timestamp.
+ * 
+ * The other mechanism is user-space PPS, which uses the (not
+ * standardized) TIOCMIWAIT ioctl to wait for PPS transitions on
+ * serial port control lines.  It is implemented on Linux and OpenBSD.
+ *
+ * On Linux, RFC2783 PPS requires root permissions for initialization;
+ * user-space PPS does not.  User-space PPS loses some functionality
+ * when not initialized as root.  In Linux, user-space PPS is referred
+ * to as "plain PPS".
+ *
+ * On {Free,Net}BSD, RFC2783 PPS should only require access to the
+ * serial port, but details have not yet been tested and documented
+ * here.
  *
  * To use the thread manager, you need to first fill in the two
  * thread_* methods in the session structure and/or the pps_hook in
@@ -63,21 +76,42 @@ static pthread_mutex_t ppslast_mutex;
 static int init_kernel_pps(struct gps_device_t *session)
 /* return handle for kernel pps, or -1; requires root privileges */
 {
-    int ldisc = 18;   /* the PPS line discipline */
 #ifndef S_SPLINT_S
     pps_params_t pp;
 #endif /* S_SPLINT_S */
+    int ret;
+#ifdef linux
+    /* These variables are only needed by Linux to find /dev/ppsN. */
+    int ldisc = 18;   /* the PPS line discipline */
     glob_t globbuf;
     size_t i;             /* to match type of globbuf.gl_pathc */
     char pps_num = '\0';  /* /dev/pps[pps_num] is our device */
     char path[GPS_PATH_MAX] = "";
-    int ret;
+#endif
 
     session->kernelpps_handle = -1;
     if ( isatty(session->gpsdata.gps_fd) == 0 ) {
        gpsd_report(session->context->debug, LOG_INF, "KPPS gps_fd not a 
tty\n");
        return -1;
     }
+
+    /*
+     * This next code block abuses "ret" by storing the filedescriptor
+     * to use for RFC2783 calls.
+     */
+    ret = -1;
+#ifndef linux
+    /*
+     * On BSDs that support RFC2783, one uses the API calls on serial
+     * port file descriptor.
+     */
+    ret  = session->gpsdata.gps_fd;
+#else /* linux */
+    /*
+     * On Linux, one must make calls to associate a serial port with a
+     * /dev/ppsN device and then grovel in system data to determine
+     * the association.
+     */
     /address@hidden@*/
     /* Attach the line PPS discipline, so no need to ldattach */
     /* This activates the magic /dev/pps0 device */
@@ -146,6 +180,12 @@ static int init_kernel_pps(struct gps_device_t *session)
                    "KPPS cannot open %s: %s\n", path, strerror(errno));
        return -1;
     }
+#endif
+    /* assert(ret >= 0); */
+    gpsd_report(session->context->debug, LOG_INF,
+               "RFC2783 fd is %d\n",
+               ret);
+
     /* RFC 2783 implies the time_pps_setcap() needs priviledges *
      * keep root a tad longer just in case */
     if ( 0 > time_pps_create(ret, &session->kernelpps_handle )) {
@@ -166,9 +206,16 @@ static int init_kernel_pps(struct gps_device_t *session)
                        LOG_INF, "KPPS caps %0x\n", caps);
         }
 
+#ifndef linux
+       /*
+        * Attempt to follow RFC2783 as straightforwardly as possible.
+        */
+       pp.mode = PPS_TSFMT_TSPEC | PPS_CAPTUREBOTH;
+#else /* linux */
         /* linux 2.6.34 can not PPS_ECHOASSERT | PPS_ECHOCLEAR */
         memset( (void *)&pp, 0, sizeof(pps_params_t));
         pp.mode = PPS_CAPTUREBOTH;
+#endif
 #endif /* S_SPLINT_S */
 
         if ( 0 > time_pps_setparams(session->kernelpps_handle, &pp)) {
@@ -258,14 +305,36 @@ static /address@hidden@*/ void *gpsd_ppsmonitor(void *arg)
 #if defined(HAVE_SYS_TIMEPPS_H) && !defined(S_SPLINT_S)
         if ( 0 <= session->kernelpps_handle ) {
            struct timespec kernelpps_tv;
+#ifndef linux
+           /*
+            * RFC2783 specifies that a NULL timeval means to wait.
+            */
+           kernelpps_tv.tv_sec = 1;
+           kernelpps_tv.tv_nsec = 0;
+#else
+           /* 
+            * \todo Explain the use of a non-NULL zero timespec,
+            * which means to return immediately with -1 (section
+            * 3.4.3).  Further, explain the non-sensical comment,
+            * because the intent of RFC2783 is that the timestamp has
+            * already been captured in the kernel, and we are merely
+            * fetching it here.
+            */
            /* on a quad core 2.4GHz Xeon this removes about 20uS of
             * latency, and about +/-5uS of jitter over the other method */
             memset( (void *)&kernelpps_tv, 0, sizeof(kernelpps_tv));
+#endif
            if ( 0 > time_pps_fetch(session->kernelpps_handle, PPS_TSFMT_TSPEC
                , &pi, &kernelpps_tv)) {
                gpsd_report(session->context->debug, LOG_ERROR,
                            "KPPS kernel PPS failed\n");
            } else {
+               /* Wait until we have both edges. */
+               if (pi.assert_sequence == 0 || pi.clear_sequence == 0) {
+                   usleep(100000);
+                   continue;
+               }
+
                // find the last edge
                // FIXME a bit simplistic, should hook into the
                 // cycle/duration check below.
@@ -282,15 +351,20 @@ static /address@hidden@*/ void *gpsd_ppsmonitor(void *arg)
                    edge_kpps = 0;
                    ts_kpps = pi.clear_timestamp;
                }
+               /*
+                * pps_seq_t is uint32_t on NetBSD, so cast to
+                * unsigned long as a wider-or-equal type to
+                * accomodate Linux's type.
+                */
                gpsd_report(session->context->debug, LOG_PROG,
                            "KPPS assert %ld.%09ld, sequence: %ld - "
                            "clear  %ld.%09ld, sequence: %ld\n",
                            pi.assert_timestamp.tv_sec,
                            pi.assert_timestamp.tv_nsec,
-                           pi.assert_sequence,
+                           (unsigned long) pi.assert_sequence,
                            pi.clear_timestamp.tv_sec,
                            pi.clear_timestamp.tv_nsec,
-                           pi.clear_sequence);
+                           (unsigned long) pi.clear_sequence);
                gpsd_report(session->context->debug, LOG_PROG,
                            "KPPS data: using %s\n",
                            edge_kpps ? "assert" : "clear");
-- 
1.8.4


--- End Message ---

reply via email to

[Prev in Thread] Current Thread [Next in Thread]