Currently in ts2phc, PPS sinks are also the only candidates for the
clocks that get synchronized. There are 2 aspects that make this too
restrictive:

- Not all PPS sinks may be target clocks for synchronization. Consider a
  dynamic environment of multiple DSA switches using boundary_clock_jbod,
  and only one port is in the PS_SLAVE state. In that case, the clock of
  that port should be the reference for synchronization, regardless of
  whether it supports the extts API or not.

- Not all target clocks for synchronization may be PPS sinks.
  Specifically, the "PHC" type of PPS master in ts2phc can also be,
  fundamentally, treated as a target clock, and disciplined.

The code base should be prepared to handle the distinction that exists
between these concepts, by recognizing that things that can be
disciplined by a servo should be represented by a "struct ts2phc_clock",
and things that can timestamp external events should be represented by a
"struct ts2phc_pps_sink".

Signed-off-by: Vladimir Oltean <olte...@gmail.com>
---
v4->v5:
- Rebased on top of data structure renames
v3->v4:
- rebased, needed to pass priv->cfg in ts2phc_nmea_master_create
- setting clock->is_destination can be delayed to a future patch
v2->v3:
None.

 ts2phc.c          | 81 ++++++++++++++++++++++++++++++++++++++++++
 ts2phc.h          | 21 +++++++++++
 ts2phc_pps_sink.c | 90 ++++++++++++++++++-----------------------------
 3 files changed, 137 insertions(+), 55 deletions(-)

diff --git a/ts2phc.c b/ts2phc.c
index f228351bae3a..f63e5503637d 100644
--- a/ts2phc.c
+++ b/ts2phc.c
@@ -6,10 +6,15 @@
  * @note Copyright (C) 2012 Richard Cochran <richardcoch...@gmail.com>
  * @note SPDX-License-Identifier: GPL-2.0+
  */
+#include <net/if.h>
 #include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
 
+#include "clockadj.h"
 #include "config.h"
 #include "interface.h"
+#include "phc.h"
 #include "print.h"
 #include "ts2phc.h"
 #include "version.h"
@@ -27,6 +32,82 @@ static void ts2phc_cleanup(struct ts2phc_private *priv)
                config_destroy(priv->cfg);
 }
 
+static struct servo *ts2phc_servo_create(struct ts2phc_private *priv,
+                                        struct ts2phc_clock *clock)
+{
+       enum servo_type type = config_get_int(priv->cfg, NULL, "clock_servo");
+       struct servo *servo;
+       int fadj, max_adj;
+
+       fadj = (int) clockadj_get_freq(clock->clkid);
+       /* Due to a bug in older kernels, the reading may silently fail
+        * and return 0. Set the frequency back to make sure fadj is
+        * the actual frequency of the clock.
+        */
+       if (!clock->no_adj) {
+               clockadj_set_freq(clock->clkid, fadj);
+       }
+
+       max_adj = phc_max_adj(clock->clkid);
+
+       servo = servo_create(priv->cfg, type, -fadj, max_adj, 0);
+       if (!servo)
+               return NULL;
+
+       servo_sync_interval(servo, SERVO_SYNC_INTERVAL);
+
+       return servo;
+}
+
+struct ts2phc_clock *ts2phc_clock_add(struct ts2phc_private *priv,
+                                     const char *device)
+{
+       clockid_t clkid = CLOCK_INVALID;
+       struct ts2phc_clock *c;
+       int phc_index = -1;
+       int err;
+
+       clkid = posix_clock_open(device, &phc_index);
+       if (clkid == CLOCK_INVALID)
+               return NULL;
+
+       LIST_FOREACH(c, &priv->clocks, list) {
+               if (c->phc_index == phc_index) {
+                       /* Already have the clock, don't add it again */
+                       posix_clock_close(clkid);
+                       return c;
+               }
+       }
+
+       c = calloc(1, sizeof(*c));
+       if (!c) {
+               pr_err("failed to allocate memory for a clock");
+               return NULL;
+       }
+       c->clkid = clkid;
+       c->phc_index = phc_index;
+       c->servo_state = SERVO_UNLOCKED;
+       c->servo = ts2phc_servo_create(priv, c);
+       c->no_adj = config_get_int(priv->cfg, NULL, "free_running");
+       err = asprintf(&c->name, "/dev/ptp%d", phc_index);
+       if (err < 0) {
+               free(c);
+               posix_clock_close(clkid);
+               return NULL;
+       }
+
+       LIST_INSERT_HEAD(&priv->clocks, c, list);
+       return c;
+}
+
+void ts2phc_clock_destroy(struct ts2phc_clock *c)
+{
+       servo_destroy(c->servo);
+       posix_clock_close(c->clkid);
+       free(c->name);
+       free(c);
+}
+
 static void usage(char *progname)
 {
        fprintf(stderr,
diff --git a/ts2phc.h b/ts2phc.h
index 14cb2b0c21a3..043215a51609 100644
--- a/ts2phc.h
+++ b/ts2phc.h
@@ -7,16 +7,37 @@
 #ifndef HAVE_TS2PHC_H
 #define HAVE_TS2PHC_H
 
+#include <sys/queue.h>
+#include <time.h>
+#include "servo.h"
+
 struct ts2phc_sink_array;
 
+#define SERVO_SYNC_INTERVAL    1.0
+
+struct ts2phc_clock {
+       LIST_ENTRY(ts2phc_clock) list;
+       clockid_t clkid;
+       int phc_index;
+       struct servo *servo;
+       enum servo_state servo_state;
+       char *name;
+       bool no_adj;
+};
+
 struct ts2phc_private {
        struct ts2phc_pps_source *src;
        STAILQ_HEAD(sink_ifaces_head, ts2phc_pps_sink) sinks;
        unsigned int n_sinks;
        struct ts2phc_sink_array *polling_array;
        struct config *cfg;
+       LIST_HEAD(clock_head, ts2phc_clock) clocks;
 };
 
+struct ts2phc_clock *ts2phc_clock_add(struct ts2phc_private *priv,
+                                     const char *device);
+void ts2phc_clock_destroy(struct ts2phc_clock *clock);
+
 #include "ts2phc_pps_source.h"
 #include "ts2phc_pps_sink.h"
 
diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c
index 6a537d08c21b..e272155eb73d 100644
--- a/ts2phc_pps_sink.c
+++ b/ts2phc_pps_sink.c
@@ -26,21 +26,17 @@
 
 #define NS_PER_SEC             1000000000LL
 #define SAMPLE_WEIGHT          1.0
-#define SERVO_SYNC_INTERVAL    1.0
 
 struct ts2phc_pps_sink {
        char *name;
        STAILQ_ENTRY(ts2phc_pps_sink) list;
        struct ptp_pin_desc pin_desc;
-       enum servo_state state;
        unsigned int polarity;
        int32_t correction;
        uint32_t ignore_lower;
        uint32_t ignore_upper;
-       struct servo *servo;
-       clockid_t clk;
+       struct ts2phc_clock *clock;
        int no_adj;
-       int fd;
 };
 
 struct ts2phc_sink_array {
@@ -96,8 +92,10 @@ static int ts2phc_pps_sink_array_create(struct 
ts2phc_private *priv)
                i++;
        }
        for (i = 0; i < priv->n_sinks; i++) {
+               struct ts2phc_pps_sink *sink = polling_array->sink[i];
+
                polling_array->pfd[i].events = POLLIN | POLLPRI;
-               polling_array->pfd[i].fd = polling_array->sink[i]->fd;
+               polling_array->pfd[i].fd = CLOCKID_TO_FD(sink->clock->clkid);
        }
 
        priv->polling_array = polling_array;
@@ -111,15 +109,15 @@ static void ts2phc_pps_sink_array_destroy(struct 
ts2phc_private *priv)
 
        free(polling_array->sink);
        free(polling_array->pfd);
-       polling_array->sink = NULL;
-       polling_array->pfd = NULL;
+       free(polling_array);
+       priv->polling_array = NULL;
 }
 
 static int ts2phc_pps_sink_clear_fifo(struct ts2phc_pps_sink *sink)
 {
        struct pollfd pfd = {
                .events = POLLIN | POLLPRI,
-               .fd = sink->fd,
+               .fd = CLOCKID_TO_FD(sink->clock->clkid),
        };
        struct ptp_extts_event event;
        int cnt, size;
@@ -152,10 +150,9 @@ static struct ts2phc_pps_sink 
*ts2phc_pps_sink_create(struct ts2phc_private *pri
                                                      const char *device)
 {
        struct config *cfg = priv->cfg;
-       enum servo_type servo = config_get_int(cfg, NULL, "clock_servo");
-       int err, fadj, junk, max_adj, pulsewidth;
        struct ptp_extts_request extts;
        struct ts2phc_pps_sink *sink;
+       int err, pulsewidth;
 
        sink = calloc(1, sizeof(*sink));
        if (!sink) {
@@ -179,34 +176,17 @@ static struct ts2phc_pps_sink 
*ts2phc_pps_sink_create(struct ts2phc_private *pri
        sink->ignore_upper = 1000000000 - pulsewidth;
        sink->ignore_lower = pulsewidth;
 
-       sink->clk = posix_clock_open(device, &junk);
-       if (sink->clk == CLOCK_INVALID) {
+       sink->clock = ts2phc_clock_add(priv, device);
+       if (!sink->clock) {
                pr_err("failed to open clock");
                goto no_posix_clock;
        }
-       sink->no_adj = config_get_int(cfg, NULL, "free_running");
-       sink->fd = CLOCKID_TO_FD(sink->clk);
-
-       pr_debug("PPS sink %s has ptp index %d", device, junk);
 
-       fadj = (int) clockadj_get_freq(sink->clk);
-       /* Due to a bug in older kernels, the reading may silently fail
-          and return 0. Set the frequency back to make sure fadj is
-          the actual frequency of the clock. */
-       if (!sink->no_adj) {
-               clockadj_set_freq(sink->clk, fadj);
-       }
-       max_adj = phc_max_adj(sink->clk);
+       pr_debug("PPS sink %s has ptp index %d", device,
+                sink->clock->phc_index);
 
-       sink->servo = servo_create(cfg, servo, -fadj, max_adj, 0);
-       if (!sink->servo) {
-               pr_err("failed to create servo");
-               goto no_servo;
-       }
-       servo_sync_interval(sink->servo, SERVO_SYNC_INTERVAL);
-
-       if (phc_number_pins(sink->clk) > 0) {
-               err = phc_pin_setfunc(sink->clk, &sink->pin_desc);
+       if (phc_number_pins(sink->clock->clkid) > 0) {
+               err = phc_pin_setfunc(sink->clock->clkid, &sink->pin_desc);
                if (err < 0) {
                        pr_err("PTP_PIN_SETFUNC request failed");
                        goto no_pin_func;
@@ -220,7 +200,8 @@ static struct ts2phc_pps_sink 
*ts2phc_pps_sink_create(struct ts2phc_private *pri
        memset(&extts, 0, sizeof(extts));
        extts.index = sink->pin_desc.chan;
        extts.flags = 0;
-       if (ioctl(sink->fd, PTP_EXTTS_REQUEST2, &extts)) {
+       if (ioctl(CLOCKID_TO_FD(sink->clock->clkid), PTP_EXTTS_REQUEST2,
+                 &extts)) {
                pr_err(PTP_EXTTS_REQUEST_FAILED);
        }
        if (ts2phc_pps_sink_clear_fifo(sink)) {
@@ -230,9 +211,7 @@ static struct ts2phc_pps_sink 
*ts2phc_pps_sink_create(struct ts2phc_private *pri
        return sink;
 no_ext_ts:
 no_pin_func:
-       servo_destroy(sink->servo);
-no_servo:
-       posix_clock_close(sink->clk);
+       ts2phc_clock_destroy(sink->clock);
 no_posix_clock:
        free(sink->name);
        free(sink);
@@ -246,11 +225,11 @@ static void ts2phc_pps_sink_destroy(struct 
ts2phc_pps_sink *sink)
        memset(&extts, 0, sizeof(extts));
        extts.index = sink->pin_desc.chan;
        extts.flags = 0;
-       if (ioctl(sink->fd, PTP_EXTTS_REQUEST2, &extts)) {
+       if (ioctl(CLOCKID_TO_FD(sink->clock->clkid), PTP_EXTTS_REQUEST2,
+                 &extts)) {
                pr_err(PTP_EXTTS_REQUEST_FAILED);
        }
-       servo_destroy(sink->servo);
-       posix_clock_close(sink->clk);
+       ts2phc_clock_destroy(sink->clock);
        free(sink->name);
        free(sink);
 }
@@ -278,27 +257,22 @@ static int ts2phc_pps_sink_event(struct ts2phc_pps_sink 
*sink,
                return 0;
        }
 
-       if (!source_ts.valid) {
-               pr_debug("%s ignoring invalid source time stamp", sink->name);
-               return 0;
-       }
-
-       adj = servo_sample(sink->servo, offset, extts_ts,
-                          SAMPLE_WEIGHT, &sink->state);
+       adj = servo_sample(sink->clock->servo, offset, extts_ts,
+                          SAMPLE_WEIGHT, &sink->clock->servo_state);
 
        pr_debug("%s source offset %10" PRId64 " s%d freq %+7.0f",
-                sink->name, offset, sink->state, adj);
+                sink->name, offset, sink->clock->servo_state, adj);
 
-       switch (sink->state) {
+       switch (sink->clock->servo_state) {
        case SERVO_UNLOCKED:
                break;
        case SERVO_JUMP:
-               clockadj_set_freq(sink->clk, -adj);
-               clockadj_step(sink->clk, -offset);
+               clockadj_set_freq(sink->clock->clkid, -adj);
+               clockadj_step(sink->clock->clkid, -offset);
                break;
        case SERVO_LOCKED:
        case SERVO_LOCKED_STABLE:
-               clockadj_set_freq(sink->clk, -adj);
+               clockadj_set_freq(sink->clock->clkid, -adj);
                break;
        }
        return 0;
@@ -314,7 +288,7 @@ ts2phc_pps_sink_offset(struct ts2phc_pps_sink *sink,
        uint64_t event_ns, source_ns;
        int cnt;
 
-       cnt = read(sink->fd, &event, sizeof(event));
+       cnt = read(CLOCKID_TO_FD(sink->clock->clkid), &event, sizeof(event));
        if (cnt != sizeof(event)) {
                pr_err("read extts event failed: %m");
                return EXTTS_ERROR;
@@ -386,7 +360,8 @@ int ts2phc_pps_sink_arm(struct ts2phc_private *priv)
        STAILQ_FOREACH(sink, &priv->sinks, list) {
                extts.index = sink->pin_desc.chan;
                extts.flags = sink->polarity | PTP_ENABLE_FEATURE;
-               err = ioctl(sink->fd, PTP_EXTTS_REQUEST2, &extts);
+               err = ioctl(CLOCKID_TO_FD(sink->clock->clkid),
+                           PTP_EXTTS_REQUEST2, &extts);
                if (err < 0) {
                        pr_err(PTP_EXTTS_REQUEST_FAILED);
                        return -1;
@@ -442,6 +417,11 @@ int ts2phc_pps_sink_poll(struct ts2phc_private *priv)
        err = ts2phc_pps_source_getppstime(priv->src, &source_ts.ts);
        source_ts.valid = err ? false : true;
 
+       if (!source_ts.valid) {
+               pr_debug("ignoring invalid source time stamp");
+               return 0;
+       }
+
        for (i = 0; i < priv->n_sinks; i++) {
                if (polling_array->pfd[i].revents & (POLLIN|POLLPRI)) {
                        ts2phc_pps_sink_event(polling_array->sink[i], 
source_ts);
-- 
2.25.1



_______________________________________________
Linuxptp-devel mailing list
Linuxptp-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel

Reply via email to