Monitor the port state change events from ptp4l, and use that
information to determine the "source" clock.

Then synchronize all other clocks in our list to that source, by feeding
into their respective servo loop an offset equal to the delta between
their timestamp and the timestamp of the source clock. All timestamps
are representative of the same event, which is the most recent perout
pulse of the ts2phc master.

Signed-off-by: Vladimir Oltean <olte...@gmail.com>
---
Changes in v4:
Use bool for boolean types.

Changes in v3:
None.

 ts2phc.c            | 130 ++++++++++++++++++++++++++++++++++++++++----
 ts2phc.h            |   2 +
 ts2phc_phc_master.c |   1 +
 ts2phc_slave.c      |   1 +
 4 files changed, 122 insertions(+), 12 deletions(-)

diff --git a/ts2phc.c b/ts2phc.c
index 5b648d9392c8..dbe7bf954943 100644
--- a/ts2phc.c
+++ b/ts2phc.c
@@ -125,6 +125,7 @@ static int ts2phc_recv_subscribed(void *context, struct 
ptp_message *msg,
                        state = clock_compute_state(priv, clock);
                        if (clock->state != state || clock->new_state) {
                                clock->new_state = state;
+                               priv->state_changed = true;
                        }
                }
                return 1;
@@ -326,33 +327,126 @@ static int auto_init_ports(struct ts2phc_private *priv)
        LIST_FOREACH(clock, &priv->clocks, list) {
                clock->new_state = clock_compute_state(priv, clock);
        }
+       priv->state_changed = true;
 
        return 0;
 }
 
-static void ts2phc_synchronize_clocks(struct ts2phc_private *priv)
+static void ts2phc_reconfigure(struct ts2phc_private *priv)
+{
+       struct clock *c, *src = NULL, *last = NULL;
+       int src_cnt = 0, dst_cnt = 0;
+
+       pr_info("reconfiguring after port state change");
+       priv->state_changed = false;
+
+       LIST_FOREACH(c, &priv->clocks, list) {
+               if (c->new_state) {
+                       c->state = c->new_state;
+                       c->new_state = 0;
+               }
+
+               switch (c->state) {
+               case PS_FAULTY:
+               case PS_DISABLED:
+               case PS_LISTENING:
+               case PS_PRE_MASTER:
+               case PS_MASTER:
+               case PS_PASSIVE:
+                       if (!c->is_destination) {
+                               pr_info("selecting %s for synchronization",
+                                       c->name);
+                               c->is_destination = true;
+                       }
+                       dst_cnt++;
+                       break;
+               case PS_UNCALIBRATED:
+                       src_cnt++;
+                       break;
+               case PS_SLAVE:
+                       src = c;
+                       src_cnt++;
+                       break;
+               default:
+                       break;
+               }
+               last = c;
+       }
+       if (dst_cnt >= 1 && !src) {
+               priv->source = last;
+               priv->source->is_destination = false;
+               /* Reset to original state in next reconfiguration. */
+               priv->source->new_state = priv->source->state;
+               priv->source->state = PS_SLAVE;
+               pr_info("no source, selecting %s as the default clock",
+                       last->name);
+               return;
+       }
+       if (src_cnt > 1) {
+               pr_info("multiple source clocks available, postponing sync...");
+               priv->source = NULL;
+               return;
+       }
+       if (src_cnt > 0 && !src) {
+               pr_info("source clock not ready, waiting...");
+               priv->source = NULL;
+               return;
+       }
+       if (!src_cnt && !dst_cnt) {
+               pr_info("no PHC ready, waiting...");
+               priv->source = NULL;
+               return;
+       }
+       if (!src_cnt) {
+               pr_info("nothing to synchronize");
+               priv->source = NULL;
+               return;
+       }
+       src->is_destination = false;
+       priv->source = src;
+       pr_info("selecting %s as the source clock", src->name);
+}
+
+static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int autocfg)
 {
-       struct timespec source_ts;
        tmv_t source_tmv;
        struct clock *c;
        int valid, err;
 
-       err = ts2phc_master_getppstime(priv->master, &source_ts);
-       if (err < 0) {
-               pr_err("source ts not valid");
-               return;
-       }
-       if (source_ts.tv_nsec > NS_PER_SEC / 2)
-               source_ts.tv_sec++;
-       source_ts.tv_nsec = 0;
+       if (autocfg) {
+               if (!priv->source) {
+                       pr_debug("no source, skipping");
+                       return;
+               }
+               valid = clock_get_tstamp(priv->source, &source_tmv);
+               if (!valid) {
+                       pr_err("source clock (%s) timestamp not valid, 
skipping",
+                               priv->source->name);
+                       return;
+               }
+       } else {
+               struct timespec source_ts;
+
+               err = ts2phc_master_getppstime(priv->master, &source_ts);
+               if (err < 0) {
+                       pr_err("source ts not valid");
+                       return;
+               }
+               if (source_ts.tv_nsec > NS_PER_SEC / 2)
+                       source_ts.tv_sec++;
+               source_ts.tv_nsec = 0;
 
-       source_tmv = timespec_to_tmv(source_ts);
+               source_tmv = timespec_to_tmv(source_ts);
+       }
 
        LIST_FOREACH(c, &priv->clocks, list) {
                int64_t offset;
                double adj;
                tmv_t ts;
 
+               if (!c->is_destination)
+                       continue;
+
                valid = clock_get_tstamp(c, &ts);
                if (!valid) {
                        pr_debug("%s timestamp not valid, skipping", c->name);
@@ -588,6 +682,18 @@ int main(int argc, char *argv[])
        while (is_running()) {
                struct clock *c;
 
+               if (autocfg) {
+                       /* Collect updates from ptp4l */
+                       err = pmc_agent_update(priv.agent);
+                       if (err < 0) {
+                               pr_err("pmc_agent_update returned %d", err);
+                               break;
+                       }
+
+                       if (priv.state_changed)
+                               ts2phc_reconfigure(&priv);
+               }
+
                LIST_FOREACH(c, &priv.clocks, list)
                        clock_flush_tstamp(c);
 
@@ -597,7 +703,7 @@ int main(int argc, char *argv[])
                        break;
                }
                if (err > 0)
-                       ts2phc_synchronize_clocks(&priv);
+                       ts2phc_synchronize_clocks(&priv, autocfg);
        }
 
        ts2phc_cleanup(&priv);
diff --git a/ts2phc.h b/ts2phc.h
index 156e41998100..73bdb8e6a2bd 100644
--- a/ts2phc.h
+++ b/ts2phc.h
@@ -28,6 +28,7 @@ struct clock {
        enum servo_state servo_state;
        char *name;
        bool no_adj;
+       bool is_destination;
        bool is_ts_available;
        tmv_t last_ts;
 };
@@ -47,6 +48,7 @@ struct ts2phc_private {
        struct config *cfg;
        struct pmc_agent *agent;
        struct clock *source;
+       bool state_changed;
        LIST_HEAD(port_head, port) ports;
        LIST_HEAD(clock_head, clock) clocks;
 };
diff --git a/ts2phc_phc_master.c b/ts2phc_phc_master.c
index 009cb3101d9a..1a944a960f18 100644
--- a/ts2phc_phc_master.c
+++ b/ts2phc_phc_master.c
@@ -100,6 +100,7 @@ struct ts2phc_master *ts2phc_phc_master_create(struct 
ts2phc_private *priv,
                free(master);
                return NULL;
        }
+       master->clock->is_destination = false;
 
        pr_debug("PHC master %s has ptp index %d", dev,
                 master->clock->phc_index);
diff --git a/ts2phc_slave.c b/ts2phc_slave.c
index 06fe36483965..14cb8079cb3c 100644
--- a/ts2phc_slave.c
+++ b/ts2phc_slave.c
@@ -187,6 +187,7 @@ static struct ts2phc_slave *ts2phc_slave_create(struct 
ts2phc_private *priv,
                pr_err("failed to open clock");
                goto no_posix_clock;
        }
+       slave->clock->is_destination = true;
 
        pr_debug("PHC slave %s has ptp index %d", device,
                 slave->clock->phc_index);
-- 
2.25.1



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

Reply via email to