From 9c6e0f57b3e55db9a16ba61ebd596dd1d39c6674 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 22 Dec 2019 12:51:43 -0800 Subject: [PATCH] ts2phc: Support using a PHC as the master clock. This patch introduces a new ts2phc source using a PHC device. There are multiple use cases for such a master. By connecting pins of two or more separate PHC devices together, one may act as the source, and the others may be synchronized to it in hardware. In this way, "just a bunch of devices" together forms a Transparent Clock. If the master clock is synchronized to a global time source (like a PPS from a GPS), then the system becomes a mutli-port Grand Master or a Boundary Clock with GM capability. Signed-off-by: Richard Cochran --- configs/ts2phc-TC.cfg | 27 ++++++++++ makefile | 3 +- ts2phc.c | 8 ++- ts2phc_master.c | 2 + ts2phc_phc_master.c | 113 ++++++++++++++++++++++++++++++++++++++++++ ts2phc_phc_master.h | 14 ++++++ 6 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 configs/ts2phc-TC.cfg create mode 100644 ts2phc_phc_master.c create mode 100644 ts2phc_phc_master.h diff --git a/configs/ts2phc-TC.cfg b/configs/ts2phc-TC.cfg new file mode 100644 index 0000000..3fa0b26 --- /dev/null +++ b/configs/ts2phc-TC.cfg @@ -0,0 +1,27 @@ +# +# This example shows ts2phc keeping a group of three Intel i210 cards +# synchronized to each other in order to form a Transparent Clock. +# The cards are configured to use their SDP0 pins connected in +# hardware. Here eth3 and eth4 will be slaved to eth6. +# +# Important! The polarity is set to "both" because the i210 always +# time stamps both the rising and the falling edges of the input +# signal. +# +[global] +use_syslog 0 +verbose 1 +logging_level 6 +ts2phc.pulsewidth 500000000 +[eth6] +ts2phc.channel 0 +ts2phc.master 1 +ts2phc.pin_index 0 +[eth3] +ts2phc.channel 0 +ts2phc.extts_polarity both +ts2phc.pin_index 0 +[eth4] +ts2phc.channel 0 +ts2phc.extts_polarity both +ts2phc.pin_index 0 diff --git a/makefile b/makefile index 9d8817a..17f2e55 100644 --- a/makefile +++ b/makefile @@ -26,7 +26,8 @@ PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster ts2phc FILTERS = filter.o mave.o mmedian.o SERVOS = linreg.o ntpshm.o nullf.o pi.o servo.o TRANSP = raw.o transport.o udp.o udp6.o uds.o -TS2PHC = ts2phc.o ts2phc_generic_master.o ts2phc_master.o ts2phc_slave.o +TS2PHC = ts2phc.o ts2phc_generic_master.o ts2phc_master.o ts2phc_phc_master.o \ + ts2phc_slave.o OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \ e2e_tc.o fault.o $(FILTERS) fsm.o hash.o interface.o msg.o phc.o port.o \ port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o rtnl.o $(SERVOS) sk.o \ diff --git a/ts2phc.c b/ts2phc.c index abc54b6..fe7fb3f 100644 --- a/ts2phc.c +++ b/ts2phc.c @@ -45,6 +45,8 @@ static void usage(char *progname) " -s [dev|name] source of the PPS signal\n" " may take any of the following forms:\n" " generic - an external 1-PPS without ToD information\n" + " /dev/ptp0 - a local PTP Hardware Clock (PHC)\n" + " eth0 - a local PTP Hardware Clock (PHC)\n" " -v prints the software version and exits\n" "\n", progname); @@ -179,7 +181,11 @@ int main(int argc, char *argv[]) return -1; } - pps_type = TS2PHC_MASTER_GENERIC; + if (!strcasecmp(pps_source, "generic")) { + pps_type = TS2PHC_MASTER_GENERIC; + } else { + pps_type = TS2PHC_MASTER_PHC; + } master = ts2phc_master_create(cfg, pps_source, pps_type); if (!master) { fprintf(stderr, "failed to create master\n"); diff --git a/ts2phc_master.c b/ts2phc_master.c index a1004f4..895a0f8 100644 --- a/ts2phc_master.c +++ b/ts2phc_master.c @@ -5,6 +5,7 @@ */ #include "ts2phc_generic_master.h" #include "ts2phc_master_private.h" +#include "ts2phc_phc_master.h" struct ts2phc_master *ts2phc_master_create(struct config *cfg, const char *dev, enum ts2phc_master_type type) @@ -18,6 +19,7 @@ struct ts2phc_master *ts2phc_master_create(struct config *cfg, const char *dev, case TS2PHC_MASTER_NMEA: break; case TS2PHC_MASTER_PHC: + master = ts2phc_phc_master_create(cfg, dev); break; } return master; diff --git a/ts2phc_phc_master.c b/ts2phc_phc_master.c new file mode 100644 index 0000000..9f1837b --- /dev/null +++ b/ts2phc_phc_master.c @@ -0,0 +1,113 @@ +/** + * @file ts2phc_phc_master.c + * @note Copyright (C) 2019 Richard Cochran + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include + +#include "config.h" +#include "phc.h" +#include "print.h" +#include "missing.h" +#include "ts2phc_master_private.h" +#include "ts2phc_phc_master.h" +#include "util.h" + +struct ts2phc_phc_master { + struct ts2phc_master master; + clockid_t clkid; + int channel; + int fd; +}; + +static int ts2phc_phc_master_activate(struct config *cfg, const char *dev, + struct ts2phc_phc_master *master) +{ + struct ptp_perout_request perout_request; + struct ptp_pin_desc desc; + struct timespec ts; + + memset(&desc, 0, sizeof(desc)); + + master->channel = config_get_int(cfg, dev, "ts2phc.channel"); + + desc.index = config_get_int(cfg, dev, "ts2phc.pin_index"); + desc.func = PTP_PF_PEROUT; + desc.chan = master->channel; + + if (phc_pin_setfunc(master->clkid, &desc)) { + pr_warning("Failed to set the pin. Continuing bravely on..."); + } + if (clock_gettime(master->clkid, &ts)) { + perror("clock_gettime"); + return -1; + } + memset(&perout_request, 0, sizeof(perout_request)); + perout_request.index = master->channel; + perout_request.start.sec = ts.tv_sec + 2; + perout_request.start.nsec = 0; + perout_request.period.sec = 1; + perout_request.period.nsec = 0; + + if (ioctl(master->fd, PTP_PEROUT_REQUEST2, &perout_request)) { + pr_err(PTP_PEROUT_REQUEST_FAILED); + return -1; + } + return 0; +} + +static void ts2phc_phc_master_destroy(struct ts2phc_master *master) +{ + struct ts2phc_phc_master *m = + container_of(master, struct ts2phc_phc_master, master); + struct ptp_perout_request perout_request; + + memset(&perout_request, 0, sizeof(perout_request)); + perout_request.index = m->channel; + if (ioctl(m->fd, PTP_PEROUT_REQUEST2, &perout_request)) { + pr_err(PTP_PEROUT_REQUEST_FAILED); + } + posix_clock_close(m->clkid); + free(m); +} + +static int ts2phc_phc_master_getppstime(struct ts2phc_master *m, + struct timespec *ts) +{ + struct ts2phc_phc_master *master = + container_of(m, struct ts2phc_phc_master, master); + return clock_gettime(master->clkid, ts); +} + +struct ts2phc_master *ts2phc_phc_master_create(struct config *cfg, + const char *dev) +{ + struct ts2phc_phc_master *master; + int junk; + + master = calloc(1, sizeof(*master)); + if (!master) { + return NULL; + } + master->master.destroy = ts2phc_phc_master_destroy; + master->master.getppstime = ts2phc_phc_master_getppstime; + + master->clkid = posix_clock_open(dev, &junk); + if (master->clkid == CLOCK_INVALID) { + free(master); + return NULL; + } + master->fd = CLOCKID_TO_FD(master->clkid); + + pr_debug("PHC master %s has ptp index %d", dev, junk); + + if (ts2phc_phc_master_activate(cfg, dev, master)) { + ts2phc_phc_master_destroy(&master->master); + return NULL; + } + + return &master->master; +} diff --git a/ts2phc_phc_master.h b/ts2phc_phc_master.h new file mode 100644 index 0000000..568df1a --- /dev/null +++ b/ts2phc_phc_master.h @@ -0,0 +1,14 @@ +/** + * @file ts2phc_phc_master.h + * @note Copyright (C) 2019 Richard Cochran + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef HAVE_TS2PHC_PHC_MASTER_H +#define HAVE_TS2PHC_PHC_MASTER_H + +#include "ts2phc_master.h" + +struct ts2phc_master *ts2phc_phc_master_create(struct config *cfg, + const char *dev); + +#endif