The auxiliary snapshot feature allows you to store a snapshot
(timestamp) of the system time based on an external event.
This feature is supported on Intel and Synopsys GMAC NICs.
1PPS signal could be connected as external timestamping source
and used to estimate error.

Signed-off-by: Artem Panfilov <panfilov.art...@gmail.com>
---
 phc2sys.c | 165 +++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 140 insertions(+), 25 deletions(-)

diff --git a/phc2sys.c b/phc2sys.c
index a476e63..e11d4e5 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -653,6 +653,44 @@ static void enable_pps_output(clockid_t src)
                pr_warning("failed to enable PPS output");
 }
 
+static void normalize_pps_offset(uint64_t *ts, int64_t *offset)
+{
+       *offset = *ts % (NS_PER_SEC);
+       if (*offset > NS_PER_SEC / 2)
+               *offset -= (NS_PER_SEC);
+}
+
+static int get_pps_offset(struct node *node, struct clock *clock,
+                         const uint64_t *pps_ts, int64_t *pps_offset)
+{
+       int64_t phc_offset, phc_delay;
+       uint64_t phc_ts;
+       clockid_t src = node->master->clkid;
+
+       /* If a PHC is available, use it to get the whole number
+          of seconds in the offset and PPS for the rest. */
+       if (src != CLOCK_INVALID) {
+               if (!read_phc(src, clock->clkid, node->phc_readings,
+                             &phc_offset, &phc_ts, &phc_delay))
+                       return -1;
+
+               /* Convert the time stamp to the PHC time. */
+               phc_ts -= phc_offset;
+
+               /* Check if it is close to the start of the second. */
+               if (phc_ts % NS_PER_SEC > PHC_PPS_OFFSET_LIMIT) {
+                       pr_warning("PPS is not in sync with PHC"
+                                  " (0.%09lld)", phc_ts % NS_PER_SEC);
+                       return 0;
+               }
+
+               phc_ts = phc_ts / NS_PER_SEC * NS_PER_SEC;
+               *pps_offset = *pps_ts - phc_ts;
+       }
+
+       return 1;
+}
+
 static int read_pps(int fd, int64_t *offset, uint64_t *ts)
 {
        struct pps_fdata pfd;
@@ -668,17 +706,15 @@ static int read_pps(int fd, int64_t *offset, uint64_t *ts)
        *ts = pfd.info.assert_tu.sec * NS_PER_SEC;
        *ts += pfd.info.assert_tu.nsec;
 
-       *offset = *ts % NS_PER_SEC;
-       if (*offset > NS_PER_SEC / 2)
-               *offset -= NS_PER_SEC;
+       normalize_pps_offset(ts, offset);
 
        return 1;
 }
 
 static int do_pps_loop(struct node *node, struct clock *clock, int fd)
 {
-       int64_t pps_offset, phc_offset, phc_delay;
-       uint64_t pps_ts, phc_ts;
+       int64_t pps_offset;
+       uint64_t pps_ts;
        clockid_t src = node->master->clkid;
 
        node->master->source_label = "pps";
@@ -695,32 +731,96 @@ static int do_pps_loop(struct node *node, struct clock 
*clock, int fd)
                        continue;
                }
 
-               /* If a PHC is available, use it to get the whole number
-                  of seconds in the offset and PPS for the rest. */
-               if (src != CLOCK_INVALID) {
-                       if (!read_phc(src, clock->clkid, node->phc_readings,
-                                     &phc_offset, &phc_ts, &phc_delay))
-                               return -1;
+               if (!get_pps_offset(node, clock, &pps_ts, &pps_offset))
+                       continue;
 
-                       /* Convert the time stamp to the PHC time. */
-                       phc_ts -= phc_offset;
+               if (update_pmc(node, 0) < 0)
+                       continue;
+               update_clock(node, clock, pps_offset, pps_ts, -1);
+       }
+       close(fd);
+       return 0;
+}
 
-                       /* Check if it is close to the start of the second. */
-                       if (phc_ts % NS_PER_SEC > PHC_PPS_OFFSET_LIMIT) {
-                               pr_warning("PPS is not in sync with PHC"
-                                          " (0.%09lld)", phc_ts % NS_PER_SEC);
-                               continue;
-                       }
+static int enable_extts(clockid_t dst, int extts_index)
+{
+       struct ptp_extts_request extts_request;
+
+       memset(&extts_request, 0, sizeof(extts_request));
+       extts_request.index = extts_index;
+       extts_request.flags = PTP_RISING_EDGE | PTP_ENABLE_FEATURE;
+
+       if (ioctl(CLOCKID_TO_FD(dst), PTP_EXTTS_REQUEST, &extts_request) < 0) {
+               pr_warning("PTP_EXTTS_REQUEST failed");
+               return 0;
+       }
+
+       return 1;
+}
+
+static void disable_extts(clockid_t dst, int extts_index)
+{
+       struct ptp_extts_request extts_request;
+
+       memset(&extts_request, 0, sizeof(extts_request));
+       extts_request.flags = 0;
+       if (ioctl(CLOCKID_TO_FD(dst), PTP_EXTTS_REQUEST, &extts_request)) {
+               pr_warning("PTP_EXTTS_REQUEST failed");
+       }
+}
+
+static int read_extts(clockid_t dst, int64_t *offset, uint64_t *ts,
+                     int extts_index)
+{
+
+       struct ptp_extts_event event;
+
+       if (!read(CLOCKID_TO_FD(dst), &event, sizeof(event))) {
+               pr_err("failed to read extts event");
+               return 0;
+       }
+
+       if (event.index == extts_index) {
+               *ts = event.t.sec * NS_PER_SEC;
+               *ts += event.t.nsec;
+
+               normalize_pps_offset(ts, offset);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int do_extts_loop(struct node *node, struct clock *clock,
+                        int extts_index)
+{
+       int64_t extts_offset;
+       uint64_t extts_ts;
+       clockid_t src = node->master->clkid;
+       clockid_t dst = clock->clkid;
 
-                       phc_ts = phc_ts / NS_PER_SEC * NS_PER_SEC;
-                       pps_offset = pps_ts - phc_ts;
+       node->master->source_label = "extts";
+
+       if (src == CLOCK_INVALID) {
+               node->sync_offset = 0;
+       } else {
+               if (!enable_extts(dst, extts_index))
+                       return -1;
+       }
+
+       while (is_running()) {
+               if (!read_extts(dst, &extts_offset, &extts_ts, extts_index)) {
+                       continue;
                }
 
+               if (!get_pps_offset(node, clock, &extts_ts, &extts_offset))
+                       continue;
+
                if (update_pmc(node, 0) < 0)
                        continue;
-               update_clock(node, clock, pps_offset, pps_ts, -1);
+               update_clock(node, clock, extts_offset, extts_ts, -1);
        }
-       close(fd);
+       disable_extts(dst, extts_index);
        return 0;
 }
 
@@ -1354,6 +1454,7 @@ static void usage(char *progname)
                " manual configuration:\n"
                " -c [dev|name]  slave clock (CLOCK_REALTIME)\n"
                " -d [dev]       master PPS device\n"
+               " -e [channel]   index of event source\n"
                " -s [dev|name]  master clock\n"
                " -O [offset]    slave-master time offset (0)\n"
                " -w             wait for ptp4l\n"
@@ -1390,6 +1491,7 @@ int main(int argc, char *argv[])
        struct option *opts;
        int autocfg = 0, c, domain_number = 0, index, ntpshm_segment;
        int pps_fd = -1, print_level = LOG_INFO, r = -1, rt = 0, wait_sync = 0;
+       int extts_index = -1;
        double phc_rate, tmp;
        struct node node = {
                .phc_readings = 5,
@@ -1412,7 +1514,7 @@ int main(int argc, char *argv[])
        progname = strrchr(argv[0], '/');
        progname = progname ? 1+progname : argv[0];
        while (EOF != (c = getopt_long(argc, argv,
-                               
"arc:d:f:s:E:P:I:S:F:R:N:O:L:M:i:u:wn:xz:l:t:mqvh",
+                               
"arc:d:e:f:s:E:P:I:S:F:R:N:O:L:M:i:u:wn:xz:l:t:mqvh",
                                opts, &index))) {
                switch (c) {
                case 0:
@@ -1437,6 +1539,10 @@ int main(int argc, char *argv[])
                                goto end;
                        }
                        break;
+               case 'e':
+                       if (get_arg_val_i(c, optarg, &extts_index, 0, INT_MAX))
+                               goto end;
+                       break;
                case 'f':
                        config = optarg;
                        break;
@@ -1577,7 +1683,8 @@ int main(int argc, char *argv[])
                return c;
        }
 
-       if (autocfg && (src_name || dst_name || pps_fd >= 0 || wait_sync || 
node.forced_sync_offset)) {
+       if (autocfg && (src_name || dst_name || pps_fd >= 0 ||
+           extts_index >= 0 || wait_sync || node.forced_sync_offset)) {
                fprintf(stderr,
                        "autoconfiguration cannot be mixed with manual config 
options.\n");
                goto bad_usage;
@@ -1648,6 +1755,11 @@ int main(int argc, char *argv[])
                goto bad_usage;
        }
 
+       if (extts_index >= 0 && src->clkid != CLOCK_REALTIME) {
+               fprintf(stderr,
+                       "cannot use a external timestamping unless source is 
CLOCK_REALTIME\n");
+               goto bad_usage;
+       }
        r = -1;
 
        if (wait_sync) {
@@ -1683,6 +1795,9 @@ int main(int argc, char *argv[])
                 * implement a mean to specify PTP port to PPS mapping */
                servo_sync_interval(dst->servo, 1.0);
                r = do_pps_loop(&node, dst, pps_fd);
+       } else if (extts_index >= 0) {
+               servo_sync_interval(dst->servo, 1.0);
+               r = do_extts_loop(&node, dst, extts_index);
        } else {
                r = do_loop(&node, 0);
        }
-- 
2.17.1



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

Reply via email to