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 <richardcochran@gmail.com>master
parent
1bdc9143aa
commit
9c6e0f57b3
|
@ -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
|
3
makefile
3
makefile
|
@ -26,7 +26,8 @@ PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster ts2phc
|
||||||
FILTERS = filter.o mave.o mmedian.o
|
FILTERS = filter.o mave.o mmedian.o
|
||||||
SERVOS = linreg.o ntpshm.o nullf.o pi.o servo.o
|
SERVOS = linreg.o ntpshm.o nullf.o pi.o servo.o
|
||||||
TRANSP = raw.o transport.o udp.o udp6.o uds.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 \
|
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 \
|
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 \
|
port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o rtnl.o $(SERVOS) sk.o \
|
||||||
|
|
8
ts2phc.c
8
ts2phc.c
|
@ -45,6 +45,8 @@ static void usage(char *progname)
|
||||||
" -s [dev|name] source of the PPS signal\n"
|
" -s [dev|name] source of the PPS signal\n"
|
||||||
" may take any of the following forms:\n"
|
" may take any of the following forms:\n"
|
||||||
" generic - an external 1-PPS without ToD information\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"
|
" -v prints the software version and exits\n"
|
||||||
"\n",
|
"\n",
|
||||||
progname);
|
progname);
|
||||||
|
@ -179,7 +181,11 @@ int main(int argc, char *argv[])
|
||||||
return -1;
|
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);
|
master = ts2phc_master_create(cfg, pps_source, pps_type);
|
||||||
if (!master) {
|
if (!master) {
|
||||||
fprintf(stderr, "failed to create master\n");
|
fprintf(stderr, "failed to create master\n");
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
#include "ts2phc_generic_master.h"
|
#include "ts2phc_generic_master.h"
|
||||||
#include "ts2phc_master_private.h"
|
#include "ts2phc_master_private.h"
|
||||||
|
#include "ts2phc_phc_master.h"
|
||||||
|
|
||||||
struct ts2phc_master *ts2phc_master_create(struct config *cfg, const char *dev,
|
struct ts2phc_master *ts2phc_master_create(struct config *cfg, const char *dev,
|
||||||
enum ts2phc_master_type type)
|
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:
|
case TS2PHC_MASTER_NMEA:
|
||||||
break;
|
break;
|
||||||
case TS2PHC_MASTER_PHC:
|
case TS2PHC_MASTER_PHC:
|
||||||
|
master = ts2phc_phc_master_create(cfg, dev);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return master;
|
return master;
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
/**
|
||||||
|
* @file ts2phc_phc_master.c
|
||||||
|
* @note Copyright (C) 2019 Richard Cochran <richardcochran@gmail.com>
|
||||||
|
* @note SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
#include <linux/ptp_clock.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
/**
|
||||||
|
* @file ts2phc_phc_master.h
|
||||||
|
* @note Copyright (C) 2019 Richard Cochran <richardcochran@gmail.com>
|
||||||
|
* @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
|
Loading…
Reference in New Issue