ts2phc: instantiate a full clock structure for every PHC master
This propagates the use of "struct ts2phc_private" all the way into the master API, in preparation of a new use case that will be supported soon: some PPS masters (to be precise, the "PHC" kind) instantiate a struct clock which could be disciplined by ts2phc. When a PHC A emits a pulse and another PHC B timestamps it, the offset between their precise timestamps can be used to synchronize either one of them. So far in ts2phc, only the slave PHC (the one using extts) has been synchronized to the master (the one using perout). This is partly because there is no proper kernel API to report the precise timestamp of a perout pulse. We only have the periodic API, and that doesn't report precise timestamps either; we just use vague approximations of what the PPS master PHC's time was, based on reading that PHC immediately after a slave extts event was received by the application. While this is far from ideal, it does work, and does allow PHC A to be synchronized to B. This is particularly useful with the yet-to-be-introduced "automatic" mode of ts2phc (similar to '-a' of phc2sys), and the PPS distribution tree is fixed in hardware (as opposed to port states, which in "automatic" mode are dynamic, as the name suggests). Signed-off-by: Vladimir Oltean <olteanv@gmail.com>master
parent
1967a5fc26
commit
3f2e73e91b
2
ts2phc.c
2
ts2phc.c
|
@ -265,7 +265,7 @@ int main(int argc, char *argv[])
|
||||||
} else {
|
} else {
|
||||||
pps_type = TS2PHC_MASTER_PHC;
|
pps_type = TS2PHC_MASTER_PHC;
|
||||||
}
|
}
|
||||||
priv.master = ts2phc_master_create(cfg, pps_source, pps_type);
|
priv.master = ts2phc_master_create(&priv, pps_source, pps_type);
|
||||||
if (!priv.master) {
|
if (!priv.master) {
|
||||||
fprintf(stderr, "failed to create master\n");
|
fprintf(stderr, "failed to create master\n");
|
||||||
ts2phc_cleanup(&priv);
|
ts2phc_cleanup(&priv);
|
||||||
|
|
|
@ -47,7 +47,7 @@ static int ts2phc_generic_master_getppstime(struct ts2phc_master *m,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ts2phc_master *ts2phc_generic_master_create(struct config *cfg,
|
struct ts2phc_master *ts2phc_generic_master_create(struct ts2phc_private *priv,
|
||||||
const char *dev)
|
const char *dev)
|
||||||
{
|
{
|
||||||
struct ts2phc_generic_master *master;
|
struct ts2phc_generic_master *master;
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
#ifndef HAVE_TS2PHC_GENERIC_MASTER_H
|
#ifndef HAVE_TS2PHC_GENERIC_MASTER_H
|
||||||
#define HAVE_TS2PHC_GENERIC_MASTER_H
|
#define HAVE_TS2PHC_GENERIC_MASTER_H
|
||||||
|
|
||||||
|
#include "ts2phc.h"
|
||||||
#include "ts2phc_master.h"
|
#include "ts2phc_master.h"
|
||||||
|
|
||||||
struct ts2phc_master *ts2phc_generic_master_create(struct config *cfg,
|
struct ts2phc_master *ts2phc_generic_master_create(struct ts2phc_private *priv,
|
||||||
const char *dev);
|
const char *dev);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,25 +3,27 @@
|
||||||
* @note Copyright (C) 2019 Richard Cochran <richardcochran@gmail.com>
|
* @note Copyright (C) 2019 Richard Cochran <richardcochran@gmail.com>
|
||||||
* @note SPDX-License-Identifier: GPL-2.0+
|
* @note SPDX-License-Identifier: GPL-2.0+
|
||||||
*/
|
*/
|
||||||
|
#include "ts2phc.h"
|
||||||
#include "ts2phc_generic_master.h"
|
#include "ts2phc_generic_master.h"
|
||||||
#include "ts2phc_master_private.h"
|
#include "ts2phc_master_private.h"
|
||||||
#include "ts2phc_nmea_master.h"
|
#include "ts2phc_nmea_master.h"
|
||||||
#include "ts2phc_phc_master.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 ts2phc_private *priv,
|
||||||
|
const char *dev,
|
||||||
enum ts2phc_master_type type)
|
enum ts2phc_master_type type)
|
||||||
{
|
{
|
||||||
struct ts2phc_master *master = NULL;
|
struct ts2phc_master *master = NULL;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TS2PHC_MASTER_GENERIC:
|
case TS2PHC_MASTER_GENERIC:
|
||||||
master = ts2phc_generic_master_create(cfg, dev);
|
master = ts2phc_generic_master_create(priv, dev);
|
||||||
break;
|
break;
|
||||||
case TS2PHC_MASTER_NMEA:
|
case TS2PHC_MASTER_NMEA:
|
||||||
master = ts2phc_nmea_master_create(cfg, dev);
|
master = ts2phc_nmea_master_create(priv, dev);
|
||||||
break;
|
break;
|
||||||
case TS2PHC_MASTER_PHC:
|
case TS2PHC_MASTER_PHC:
|
||||||
master = ts2phc_phc_master_create(cfg, dev);
|
master = ts2phc_phc_master_create(priv, dev);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return master;
|
return master;
|
||||||
|
|
|
@ -13,6 +13,7 @@ struct config;
|
||||||
/**
|
/**
|
||||||
* Opaque type
|
* Opaque type
|
||||||
*/
|
*/
|
||||||
|
struct ts2phc_private;
|
||||||
struct ts2phc_master;
|
struct ts2phc_master;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,12 +27,13 @@ enum ts2phc_master_type {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance of a PPS master clock.
|
* Create a new instance of a PPS master clock.
|
||||||
* @param cfg Pointer to a valid configuration.
|
* @param priv Pointer to the program's data structure.
|
||||||
* @param dev Name of the master clock or NULL.
|
* @param dev Name of the master clock or NULL.
|
||||||
* @param type The type of the clock to create.
|
* @param type The type of the clock to create.
|
||||||
* @return A pointer to a new PPS master clock on success, NULL otherwise.
|
* @return A pointer to a new PPS master clock on success, NULL otherwise.
|
||||||
*/
|
*/
|
||||||
struct ts2phc_master *ts2phc_master_create(struct config *cfg, const char *dev,
|
struct ts2phc_master *ts2phc_master_create(struct ts2phc_private *priv,
|
||||||
|
const char *dev,
|
||||||
enum ts2phc_master_type type);
|
enum ts2phc_master_type type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -198,7 +198,8 @@ static int ts2phc_nmea_master_getppstime(struct ts2phc_master *master,
|
||||||
return fix_valid ? lstab_error : -1;
|
return fix_valid ? lstab_error : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ts2phc_master *ts2phc_nmea_master_create(struct config *cfg, const char *dev)
|
struct ts2phc_master *ts2phc_nmea_master_create(struct ts2phc_private *priv,
|
||||||
|
const char *dev)
|
||||||
{
|
{
|
||||||
struct ts2phc_nmea_master *master;
|
struct ts2phc_nmea_master *master;
|
||||||
const char *leapfile = NULL; // TODO - read from config.
|
const char *leapfile = NULL; // TODO - read from config.
|
||||||
|
@ -214,7 +215,7 @@ struct ts2phc_master *ts2phc_nmea_master_create(struct config *cfg, const char *
|
||||||
}
|
}
|
||||||
master->master.destroy = ts2phc_nmea_master_destroy;
|
master->master.destroy = ts2phc_nmea_master_destroy;
|
||||||
master->master.getppstime = ts2phc_nmea_master_getppstime;
|
master->master.getppstime = ts2phc_nmea_master_getppstime;
|
||||||
master->config = cfg;
|
master->config = priv->cfg;
|
||||||
pthread_mutex_init(&master->mutex, NULL);
|
pthread_mutex_init(&master->mutex, NULL);
|
||||||
err = pthread_create(&master->worker, NULL, monitor_nmea_status, master);
|
err = pthread_create(&master->worker, NULL, monitor_nmea_status, master);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
#ifndef HAVE_TS2PHC_NMEA_MASTER_H
|
#ifndef HAVE_TS2PHC_NMEA_MASTER_H
|
||||||
#define HAVE_TS2PHC_NMEA_MASTER_H
|
#define HAVE_TS2PHC_NMEA_MASTER_H
|
||||||
|
|
||||||
|
#include "ts2phc.h"
|
||||||
#include "ts2phc_master.h"
|
#include "ts2phc_master.h"
|
||||||
|
|
||||||
struct ts2phc_master *ts2phc_nmea_master_create(struct config *cfg,
|
struct ts2phc_master *ts2phc_nmea_master_create(struct ts2phc_private *priv,
|
||||||
const char *dev);
|
const char *dev);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -12,15 +12,14 @@
|
||||||
#include "phc.h"
|
#include "phc.h"
|
||||||
#include "print.h"
|
#include "print.h"
|
||||||
#include "missing.h"
|
#include "missing.h"
|
||||||
|
#include "ts2phc.h"
|
||||||
#include "ts2phc_master_private.h"
|
#include "ts2phc_master_private.h"
|
||||||
#include "ts2phc_phc_master.h"
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
struct ts2phc_phc_master {
|
struct ts2phc_phc_master {
|
||||||
struct ts2phc_master master;
|
struct ts2phc_master master;
|
||||||
clockid_t clkid;
|
struct clock *clock;
|
||||||
int channel;
|
int channel;
|
||||||
int fd;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ts2phc_phc_master_activate(struct config *cfg, const char *dev,
|
static int ts2phc_phc_master_activate(struct config *cfg, const char *dev,
|
||||||
|
@ -38,10 +37,10 @@ static int ts2phc_phc_master_activate(struct config *cfg, const char *dev,
|
||||||
desc.func = PTP_PF_PEROUT;
|
desc.func = PTP_PF_PEROUT;
|
||||||
desc.chan = master->channel;
|
desc.chan = master->channel;
|
||||||
|
|
||||||
if (phc_pin_setfunc(master->clkid, &desc)) {
|
if (phc_pin_setfunc(master->clock->clkid, &desc)) {
|
||||||
pr_warning("Failed to set the pin. Continuing bravely on...");
|
pr_warning("Failed to set the pin. Continuing bravely on...");
|
||||||
}
|
}
|
||||||
if (clock_gettime(master->clkid, &ts)) {
|
if (clock_gettime(master->clock->clkid, &ts)) {
|
||||||
perror("clock_gettime");
|
perror("clock_gettime");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -52,7 +51,8 @@ static int ts2phc_phc_master_activate(struct config *cfg, const char *dev,
|
||||||
perout_request.period.sec = 1;
|
perout_request.period.sec = 1;
|
||||||
perout_request.period.nsec = 0;
|
perout_request.period.nsec = 0;
|
||||||
|
|
||||||
if (ioctl(master->fd, PTP_PEROUT_REQUEST2, &perout_request)) {
|
if (ioctl(CLOCKID_TO_FD(master->clock->clkid), PTP_PEROUT_REQUEST2,
|
||||||
|
&perout_request)) {
|
||||||
pr_err(PTP_PEROUT_REQUEST_FAILED);
|
pr_err(PTP_PEROUT_REQUEST_FAILED);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -67,10 +67,11 @@ static void ts2phc_phc_master_destroy(struct ts2phc_master *master)
|
||||||
|
|
||||||
memset(&perout_request, 0, sizeof(perout_request));
|
memset(&perout_request, 0, sizeof(perout_request));
|
||||||
perout_request.index = m->channel;
|
perout_request.index = m->channel;
|
||||||
if (ioctl(m->fd, PTP_PEROUT_REQUEST2, &perout_request)) {
|
if (ioctl(CLOCKID_TO_FD(m->clock->clkid), PTP_PEROUT_REQUEST2,
|
||||||
|
&perout_request)) {
|
||||||
pr_err(PTP_PEROUT_REQUEST_FAILED);
|
pr_err(PTP_PEROUT_REQUEST_FAILED);
|
||||||
}
|
}
|
||||||
posix_clock_close(m->clkid);
|
clock_destroy(m->clock);
|
||||||
free(m);
|
free(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,14 +80,13 @@ static int ts2phc_phc_master_getppstime(struct ts2phc_master *m,
|
||||||
{
|
{
|
||||||
struct ts2phc_phc_master *master =
|
struct ts2phc_phc_master *master =
|
||||||
container_of(m, struct ts2phc_phc_master, master);
|
container_of(m, struct ts2phc_phc_master, master);
|
||||||
return clock_gettime(master->clkid, ts);
|
return clock_gettime(master->clock->clkid, ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ts2phc_master *ts2phc_phc_master_create(struct config *cfg,
|
struct ts2phc_master *ts2phc_phc_master_create(struct ts2phc_private *priv,
|
||||||
const char *dev)
|
const char *dev)
|
||||||
{
|
{
|
||||||
struct ts2phc_phc_master *master;
|
struct ts2phc_phc_master *master;
|
||||||
int junk;
|
|
||||||
|
|
||||||
master = calloc(1, sizeof(*master));
|
master = calloc(1, sizeof(*master));
|
||||||
if (!master) {
|
if (!master) {
|
||||||
|
@ -95,16 +95,17 @@ struct ts2phc_master *ts2phc_phc_master_create(struct config *cfg,
|
||||||
master->master.destroy = ts2phc_phc_master_destroy;
|
master->master.destroy = ts2phc_phc_master_destroy;
|
||||||
master->master.getppstime = ts2phc_phc_master_getppstime;
|
master->master.getppstime = ts2phc_phc_master_getppstime;
|
||||||
|
|
||||||
master->clkid = posix_clock_open(dev, &junk);
|
master->clock = clock_add(priv, dev);
|
||||||
if (master->clkid == CLOCK_INVALID) {
|
if (!master->clock) {
|
||||||
free(master);
|
free(master);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
master->fd = CLOCKID_TO_FD(master->clkid);
|
master->clock->is_destination = 0;
|
||||||
|
|
||||||
pr_debug("PHC master %s has ptp index %d", dev, junk);
|
pr_debug("PHC master %s has ptp index %d", dev,
|
||||||
|
master->clock->phc_index);
|
||||||
|
|
||||||
if (ts2phc_phc_master_activate(cfg, dev, master)) {
|
if (ts2phc_phc_master_activate(priv->cfg, dev, master)) {
|
||||||
ts2phc_phc_master_destroy(&master->master);
|
ts2phc_phc_master_destroy(&master->master);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
#ifndef HAVE_TS2PHC_PHC_MASTER_H
|
#ifndef HAVE_TS2PHC_PHC_MASTER_H
|
||||||
#define HAVE_TS2PHC_PHC_MASTER_H
|
#define HAVE_TS2PHC_PHC_MASTER_H
|
||||||
|
|
||||||
|
#include "ts2phc.h"
|
||||||
#include "ts2phc_master.h"
|
#include "ts2phc_master.h"
|
||||||
|
|
||||||
struct ts2phc_master *ts2phc_phc_master_create(struct config *cfg,
|
struct ts2phc_master *ts2phc_phc_master_create(struct ts2phc_private *priv,
|
||||||
const char *dev);
|
const char *dev);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "clockadj.h"
|
#include "clockadj.h"
|
||||||
|
#include "config.h"
|
||||||
#include "missing.h"
|
#include "missing.h"
|
||||||
#include "phc.h"
|
#include "phc.h"
|
||||||
#include "print.h"
|
#include "print.h"
|
||||||
|
|
Loading…
Reference in New Issue