On 8/1/2020 10:46 AM, Vladimir Oltean wrote:
> This API was introduced for 2 reasons:
> 
> 1. Some hardware can emit PPS signals but not starting from arbitrary
>    absolute times, but rather phase-aligned to the beginning of a
>    second. We _could_ patch ts2phc to always specify a start time of
>    0.000000000 to PTP_PEROUT_REQUEST, but in practice, we would never
>    know whether that would actually work with all in-tree PHC drivers.
>    So there was a need for a new flag that only specifies the phase of
>    the periodic signal, and not the absolute start time.
> 
> 2. Some hardware can, rather unfortunately, not distinguish between a
>    rising and a falling extts edge. And, since whatever rises also has
>    to fall before rising again, the strategy in ts2phc is to set a
>    'large' pulse width (half the period) and ignore the extts event
>    corresponding to the mid-way between one second and another. This is
>    all fine, but currently, ts2phc.pulsewidth is a read-only property in
>    the config file. The kernel is not instructed in any way to use this
>    value, it is simply that must be configured based on prior knowledge
>    of the PHC's implementation. This API changes that.
> 
> The introduction of a phase adjustment for the master PHC means we have
> to adjust our approximation of the precise perout timestamp. We put that
> code into a common function and convert all call sites to call that. We
> also need to do the same thing for the edge ignoring logic.
> 
> Signed-off-by: Vladimir Oltean <olte...@gmail.com>
> ---
>  config.c            |  1 +
>  missing.h           | 53 ++++++++++++++++++++++++++++
>  ts2phc.8            |  5 +++
>  ts2phc.c            | 86 ++++++++++++++++++++++++++++++---------------
>  ts2phc.h            |  1 +
>  ts2phc_phc_master.c | 20 +++++++++--
>  ts2phc_slave.c      | 16 +++++++--
>  7 files changed, 150 insertions(+), 32 deletions(-)
> 
> diff --git a/config.c b/config.c
> index d3446b4c8f27..a10f30998d10 100644
> --- a/config.c
> +++ b/config.c
> @@ -314,6 +314,7 @@ struct config_item config_tab[] = {
>       GLOB_ITEM_STR("ts2phc.nmea_remote_host", ""),
>       GLOB_ITEM_STR("ts2phc.nmea_remote_port", ""),
>       GLOB_ITEM_STR("ts2phc.nmea_serialport", "/dev/ttyS0"),
> +     PORT_ITEM_INT("ts2phc.perout_phase", -1, 0, 999999999),
>       PORT_ITEM_INT("ts2phc.pin_index", 0, 0, INT_MAX),
>       GLOB_ITEM_INT("ts2phc.pulsewidth", 500000000, 1000000, 999000000),
>       PORT_ITEM_ENU("tsproc_mode", TSPROC_FILTER, tsproc_enu),
> diff --git a/missing.h b/missing.h
> index 35eaf155fc51..e52915e8b621 100644
> --- a/missing.h
> +++ b/missing.h
> @@ -97,6 +97,59 @@ struct compat_ptp_clock_caps {
>  
>  #endif /*LINUX_VERSION_CODE < 5.8*/
>  
> +/*
> + * Bits of the ptp_perout_request.flags field:
> + */
> +
> +#ifndef PTP_PEROUT_ONE_SHOT
> +#define PTP_PEROUT_ONE_SHOT          (1<<0)
> +#endif
> +
> +#ifndef PTP_PEROUT_DUTY_CYCLE
> +#define PTP_PEROUT_DUTY_CYCLE                (1<<1)
> +#endif
> +
> +#ifndef PTP_PEROUT_PHASE
> +#define PTP_PEROUT_PHASE             (1<<2)
> +#endif
> +
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,9,0)
> +
> +/* from upcoming Linux kernel version 5.9 */
> +struct compat_ptp_perout_request {
> +     union {
> +             /*
> +              * Absolute start time.
> +              * Valid only if (flags & PTP_PEROUT_PHASE) is unset.
> +              */
> +             struct ptp_clock_time start;
> +             /*
> +              * Phase offset. The signal should start toggling at an
> +              * unspecified integer multiple of the period, plus this value.
> +              * The start time should be "as soon as possible".
> +              * Valid only if (flags & PTP_PEROUT_PHASE) is set.
> +              */
> +             struct ptp_clock_time phase;
> +     };
> +     struct ptp_clock_time period; /* Desired period, zero means disable. */
> +     unsigned int index;           /* Which channel to configure. */
> +     unsigned int flags;
> +     union {
> +             /*
> +              * The "on" time of the signal.
> +              * Must be lower than the period.
> +              * Valid only if (flags & PTP_PEROUT_DUTY_CYCLE) is set.
> +              */
> +             struct ptp_clock_time on;
> +             /* Reserved for future use. */
> +             unsigned int rsv[4];
> +     };
> +};
> +
> +#define ptp_perout_request compat_ptp_perout_request
> +
> +#endif /*LINUX_VERSION_CODE < 5.8*/
> +
>  #ifndef PTP_MAX_SAMPLES
>  #define PTP_MAX_SAMPLES 25 /* Maximum allowed offset measurement samples. */
>  #endif /* PTP_MAX_SAMPLES */
> diff --git a/ts2phc.8 b/ts2phc.8
> index 07a402601c9e..f690e243864d 100644
> --- a/ts2phc.8
> +++ b/ts2phc.8
> @@ -152,6 +152,11 @@ specified, then the given remote connection will be used 
> in preference
>  to the configured serial port.
>  The default is "/dev/ttyS0".
>  .TP
> +.B ts2phc.perout_phase
> +Configures the offset between the beginning of the second and the PPS
> +master's rising edge. Available only for a PHC master. The supported
> +range is 0 to 999999999 nanoseconds. The default is 0 nanoseconds.
> +.TP
>  .B ts2phc.pulsewidth
>  The expected pulse width of the external PPS signal in nanoseconds.
>  When 'ts2phc.extts_polarity' is "both", the given pulse width is used
> diff --git a/ts2phc.c b/ts2phc.c
> index 16cfe7cc101d..69cac305c791 100644
> --- a/ts2phc.c
> +++ b/ts2phc.c
> @@ -382,6 +382,40 @@ static void ts2phc_reconfigure(struct ts2phc_private 
> *priv)
>       pr_info("selecting %s as the source clock", src->name);
>  }
>  
> +static int ts2phc_approximate_master_tstamp(struct ts2phc_private *priv,
> +                                         tmv_t *master_tmv)
> +{
> +     struct timespec master_ts;
> +     tmv_t tmv;
> +     int err;
> +
> +     err = ts2phc_master_getppstime(priv->master, &master_ts);
> +     if (err < 0) {
> +             pr_err("master ts not valid");
> +             return err;
> +     }
> +
> +     tmv = timespec_to_tmv(master_ts);
> +     tmv = tmv_sub(tmv, priv->perout_phase);
> +     master_ts = tmv_to_timespec(tmv);
> +
> +     /*
> +      * As long as the kernel doesn't support a proper API for reporting
> +      * a precise perout timestamp, we'll have to use this crude
> +      * approximation.
> +      */
> +     if (master_ts.tv_nsec > NS_PER_SEC / 2)
> +             master_ts.tv_sec++;
> +     master_ts.tv_nsec = 0;
> +
> +     tmv = timespec_to_tmv(master_ts);
> +     tmv = tmv_add(tmv, priv->perout_phase);
> +

The assumption here is that the timestamp must be on a full second,
which makes sense since we specified this to be the case when setting up
the signal. Ok. I think this approximation works well, and the more
accurate your given PPS signal is for that hardware, the better this
would be.

> +     *master_tmv = tmv;
> +
> +     return 0;
> +}
> +
>  static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int 
> autocfg)
>  {
>       tmv_t source_tmv;
> @@ -400,18 +434,9 @@ static void ts2phc_synchronize_clocks(struct 
> ts2phc_private *priv, int autocfg)
>                       return;
>               }
>       } else {
> -             struct timespec source_ts;
> -
> -             err = ts2phc_master_getppstime(priv->master, &source_ts);
> -             if (err < 0) {
> -                     pr_err("source ts not valid");
> +             err = ts2phc_approximate_master_tstamp(priv, &source_tmv);
> +             if (err < 0)
>                       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);
>       }
>  
>       LIST_FOREACH(c, &priv->clocks, list) {
> @@ -460,7 +485,7 @@ static void ts2phc_synchronize_clocks(struct 
> ts2phc_private *priv, int autocfg)
>  static int ts2phc_collect_master_tstamp(struct ts2phc_private *priv)
>  {
>       struct clock *master_clock;
> -     struct timespec master_ts;
> +     tmv_t master_tmv;
>       int err;
>  
>       master_clock = ts2phc_master_get_clock(priv->master);
> @@ -473,22 +498,11 @@ static int ts2phc_collect_master_tstamp(struct 
> ts2phc_private *priv)
>       if (!master_clock)
>               return 0;
>  
> -     err = ts2phc_master_getppstime(priv->master, &master_ts);
> -     if (err < 0) {
> -             pr_err("source ts not valid");
> +     err = ts2phc_approximate_master_tstamp(priv, &master_tmv);
> +     if (err < 0)
>               return err;
> -     }
> -
> -     /*
> -      * As long as the kernel doesn't support a proper API for reporting
> -      * a precise perout timestamp, we'll have to use this crude
> -      * approximation.
> -      */
> -     if (master_ts.tv_nsec > NS_PER_SEC / 2)
> -             master_ts.tv_sec++;
> -     master_ts.tv_nsec = 0;
>  
> -     clock_add_tstamp(master_clock, timespec_to_tmv(master_ts));
> +     clock_add_tstamp(master_clock, master_tmv);
>  
>       return 0;
>  }
> @@ -636,13 +650,29 @@ int main(int argc, char *argv[])
>       }
>  
>       STAILQ_FOREACH(iface, &cfg->interfaces, list) {
> -             if (1 == config_get_int(cfg, interface_name(iface), 
> "ts2phc.master")) {
> +             const char *dev = interface_name(iface);
> +
> +             if (1 == config_get_int(cfg, dev, "ts2phc.master")) {
> +                     int perout_phase;
> +
>                       if (pps_source) {
>                               fprintf(stderr, "too many PPS sources\n");
>                               ts2phc_cleanup(&priv);
>                               return -1;
>                       }
> -                     pps_source = interface_name(iface);
> +                     pps_source = dev;
> +                     perout_phase = config_get_int(cfg, dev,
> +                                                   "ts2phc.perout_phase");
> +                     /*
> +                      * We use a default value of -1 to distinguish whether
> +                      * to use the PTP_PEROUT_PHASE API or not. But if we
> +                      * don't use that (and therefore we use absolute start
> +                      * time), the phase is still zero, by our application's
> +                      * convention.
> +                      */
> +                     if (perout_phase < 0)
> +                             perout_phase = 0;
> +                     priv.perout_phase = nanoseconds_to_tmv(perout_phase);
>               } else {
>                       if (ts2phc_slave_add(&priv, interface_name(iface))) {
>                               fprintf(stderr, "failed to add slave\n");
> diff --git a/ts2phc.h b/ts2phc.h
> index 4f656cc1c741..9b5df9e25e38 100644
> --- a/ts2phc.h
> +++ b/ts2phc.h
> @@ -58,6 +58,7 @@ struct ts2phc_private {
>       STAILQ_HEAD(slave_ifaces_head, ts2phc_slave) slaves;
>       unsigned int n_slaves;
>       struct ts2phc_slave_array *polling_array;
> +     tmv_t perout_phase;
>       struct config *cfg;
>       struct pmc_node node;
>       int state_changed;
> diff --git a/ts2phc_phc_master.c b/ts2phc_phc_master.c
> index 77b2452b5549..3feaacfbfb39 100644
> --- a/ts2phc_phc_master.c
> +++ b/ts2phc_phc_master.c
> @@ -27,6 +27,8 @@ static int ts2phc_phc_master_activate(struct config *cfg, 
> const char *dev,
>  {
>       struct ptp_perout_request perout_request;
>       struct ptp_pin_desc desc;
> +     int32_t perout_phase;
> +     int32_t pulsewidth;
>       struct timespec ts;
>  
>       memset(&desc, 0, sizeof(desc));
> @@ -44,12 +46,26 @@ static int ts2phc_phc_master_activate(struct config *cfg, 
> const char *dev,
>               perror("clock_gettime");
>               return -1;
>       }
> +     perout_phase = config_get_int(cfg, dev, "ts2phc.perout_phase");
>       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;
> +     perout_request.flags = 0;
> +     pulsewidth = config_get_int(cfg, dev, "ts2phc.pulsewidth");
> +     if (pulsewidth) {
> +             perout_request.flags |= PTP_PEROUT_DUTY_CYCLE;
> +             perout_request.on.sec = pulsewidth / NS_PER_SEC;
> +             perout_request.on.nsec = pulsewidth % NS_PER_SEC;
> +     }
> +     if (perout_phase != -1) {
> +             perout_request.flags |= PTP_PEROUT_PHASE;
> +             perout_request.phase.sec = perout_phase / NS_PER_SEC;
> +             perout_request.phase.nsec = perout_phase % NS_PER_SEC;
> +     } else {
> +             perout_request.start.sec = ts.tv_sec + 2;
> +             perout_request.start.nsec = 0;
> +     }
>  
>       if (ioctl(CLOCKID_TO_FD(master->clock->clkid), PTP_PEROUT_REQUEST2,
>                 &perout_request)) {
> diff --git a/ts2phc_slave.c b/ts2phc_slave.c
> index e3bce3b7f051..68fe355bdca7 100644
> --- a/ts2phc_slave.c
> +++ b/ts2phc_slave.c
> @@ -249,6 +249,19 @@ static void ts2phc_slave_destroy(struct ts2phc_slave 
> *slave)
>       free(slave);
>  }
>  
> +static bool ts2phc_slave_ignore(struct ts2phc_private *priv,
> +                             struct ts2phc_slave *slave,
> +                             struct timespec source_ts)
> +{
> +     tmv_t source_tmv = timespec_to_tmv(source_ts);
> +
> +     source_tmv = tmv_sub(source_tmv, priv->perout_phase);
> +     source_ts = tmv_to_timespec(source_tmv);
> +
> +     return source_ts.tv_nsec > slave->ignore_lower &&
> +            source_ts.tv_nsec < slave->ignore_upper;
> +}
> +
>  static enum extts_result ts2phc_slave_event(struct ts2phc_private *priv,
>                                           struct ts2phc_slave *slave)
>  {
> @@ -277,8 +290,7 @@ static enum extts_result ts2phc_slave_event(struct 
> ts2phc_private *priv,
>       }
>  
>       if (slave->polarity == (PTP_RISING_EDGE | PTP_FALLING_EDGE) &&
> -         source_ts.tv_nsec > slave->ignore_lower &&
> -         source_ts.tv_nsec < slave->ignore_upper) {
> +         ts2phc_slave_ignore(priv, slave, source_ts)) {
>  
>               pr_debug("%s SKIP extts index %u at %lld.%09u src %" PRIi64 
> ".%ld",
>                slave->name, event.index, event.t.sec, event.t.nsec,
> 


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

Reply via email to