Merge branch 'mlichvar_leap'
Fixed up trivial conflict in the makefile. Conflicts: makefile
This commit is contained in:
		
						commit
						bd28acffb3
					
				
							
								
								
									
										150
									
								
								clock.c
									
									
									
									
									
								
							
							
						
						
									
										150
									
								
								clock.c
									
									
									
									
									
								
							@ -24,6 +24,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "bmc.h"
 | 
					#include "bmc.h"
 | 
				
			||||||
#include "clock.h"
 | 
					#include "clock.h"
 | 
				
			||||||
 | 
					#include "clockadj.h"
 | 
				
			||||||
#include "foreign.h"
 | 
					#include "foreign.h"
 | 
				
			||||||
#include "mave.h"
 | 
					#include "mave.h"
 | 
				
			||||||
#include "missing.h"
 | 
					#include "missing.h"
 | 
				
			||||||
@ -76,6 +77,9 @@ struct clock {
 | 
				
			|||||||
	int free_running;
 | 
						int free_running;
 | 
				
			||||||
	int freq_est_interval;
 | 
						int freq_est_interval;
 | 
				
			||||||
	int utc_timescale;
 | 
						int utc_timescale;
 | 
				
			||||||
 | 
						int leap_set;
 | 
				
			||||||
 | 
						int kernel_leap;
 | 
				
			||||||
 | 
						enum servo_state servo_state;
 | 
				
			||||||
	tmv_t master_offset;
 | 
						tmv_t master_offset;
 | 
				
			||||||
	tmv_t path_delay;
 | 
						tmv_t path_delay;
 | 
				
			||||||
	struct mave *avg_delay;
 | 
						struct mave *avg_delay;
 | 
				
			||||||
@ -389,11 +393,13 @@ static enum servo_state clock_no_adjust(struct clock *c)
 | 
				
			|||||||
	freq = (1.0 - ratio) * 1e9;
 | 
						freq = (1.0 - ratio) * 1e9;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (c->stats.max_count > 1) {
 | 
						if (c->stats.max_count > 1) {
 | 
				
			||||||
		clock_stats_update(&c->stats, c->master_offset, freq);
 | 
							clock_stats_update(&c->stats,
 | 
				
			||||||
 | 
									   tmv_to_nanoseconds(c->master_offset), freq);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		pr_info("master offset %10" PRId64 " s%d freq %+7.0f "
 | 
							pr_info("master offset %10" PRId64 " s%d freq %+7.0f "
 | 
				
			||||||
			"path delay %9" PRId64,
 | 
								"path delay %9" PRId64,
 | 
				
			||||||
			c->master_offset, state, freq, c->path_delay);
 | 
								tmv_to_nanoseconds(c->master_offset), state, freq,
 | 
				
			||||||
 | 
								tmv_to_nanoseconds(c->path_delay));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fui = 1.0 + (c->status.cumulativeScaledRateOffset + 0.0) / POW2_41;
 | 
						fui = 1.0 + (c->status.cumulativeScaledRateOffset + 0.0) / POW2_41;
 | 
				
			||||||
@ -412,52 +418,6 @@ static enum servo_state clock_no_adjust(struct clock *c)
 | 
				
			|||||||
	return state;
 | 
						return state;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void clock_ppb(clockid_t clkid, double ppb)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct timex tx;
 | 
					 | 
				
			||||||
	memset(&tx, 0, sizeof(tx));
 | 
					 | 
				
			||||||
	tx.modes = ADJ_FREQUENCY;
 | 
					 | 
				
			||||||
	tx.freq = (long) (ppb * 65.536);
 | 
					 | 
				
			||||||
	if (clock_adjtime(clkid, &tx) < 0)
 | 
					 | 
				
			||||||
		pr_err("failed to adjust the clock: %m");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static double clock_ppb_read(clockid_t clkid)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	double f = 0.0;
 | 
					 | 
				
			||||||
	struct timex tx;
 | 
					 | 
				
			||||||
	memset(&tx, 0, sizeof(tx));
 | 
					 | 
				
			||||||
	if (clock_adjtime(clkid, &tx) < 0)
 | 
					 | 
				
			||||||
		pr_err("failed to read out the clock frequency adjustment: %m");
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		f = tx.freq / 65.536;
 | 
					 | 
				
			||||||
	return f;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void clock_step(clockid_t clkid, int64_t ns)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct timex tx;
 | 
					 | 
				
			||||||
	int sign = 1;
 | 
					 | 
				
			||||||
	if (ns < 0) {
 | 
					 | 
				
			||||||
		sign = -1;
 | 
					 | 
				
			||||||
		ns *= -1;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	memset(&tx, 0, sizeof(tx));
 | 
					 | 
				
			||||||
	tx.modes = ADJ_SETOFFSET | ADJ_NANO;
 | 
					 | 
				
			||||||
	tx.time.tv_sec  = sign * (ns / NS_PER_SEC);
 | 
					 | 
				
			||||||
	tx.time.tv_usec = sign * (ns % NS_PER_SEC);
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	 * The value of a timeval is the sum of its fields, but the
 | 
					 | 
				
			||||||
	 * field tv_usec must always be non-negative.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	if (tx.time.tv_usec < 0) {
 | 
					 | 
				
			||||||
		tx.time.tv_sec  -= 1;
 | 
					 | 
				
			||||||
		tx.time.tv_usec += 1000000000;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (clock_adjtime(clkid, &tx) < 0)
 | 
					 | 
				
			||||||
		pr_err("failed to step clock: %m");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void clock_update_grandmaster(struct clock *c)
 | 
					static void clock_update_grandmaster(struct clock *c)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct parentDS *pds = &c->dad.pds;
 | 
						struct parentDS *pds = &c->dad.pds;
 | 
				
			||||||
@ -500,23 +460,67 @@ static void clock_update_slave(struct clock *c)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void clock_utc_correct(struct clock *c)
 | 
					static int clock_utc_correct(struct clock *c, tmv_t ingress)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct timespec offset;
 | 
						struct timespec offset;
 | 
				
			||||||
 | 
						int utc_offset, leap, clock_leap;
 | 
				
			||||||
 | 
						uint64_t ts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!c->utc_timescale)
 | 
						if (!c->utc_timescale)
 | 
				
			||||||
		return;
 | 
							return 0;
 | 
				
			||||||
	if (!(c->tds.flags & PTP_TIMESCALE))
 | 
					
 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	if (c->tds.flags & UTC_OFF_VALID && c->tds.flags & TIME_TRACEABLE) {
 | 
						if (c->tds.flags & UTC_OFF_VALID && c->tds.flags & TIME_TRACEABLE) {
 | 
				
			||||||
		offset.tv_sec = c->tds.currentUtcOffset;
 | 
							utc_offset = c->tds.currentUtcOffset;
 | 
				
			||||||
	} else if (c->tds.currentUtcOffset > CURRENT_UTC_OFFSET) {
 | 
						} else if (c->tds.currentUtcOffset > CURRENT_UTC_OFFSET) {
 | 
				
			||||||
		offset.tv_sec = c->tds.currentUtcOffset;
 | 
							utc_offset = c->tds.currentUtcOffset;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		offset.tv_sec = CURRENT_UTC_OFFSET;
 | 
							utc_offset = CURRENT_UTC_OFFSET;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (c->tds.flags & LEAP_61) {
 | 
				
			||||||
 | 
							leap = 1;
 | 
				
			||||||
 | 
						} else if (c->tds.flags & LEAP_59) {
 | 
				
			||||||
 | 
							leap = -1;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							leap = 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Handle leap seconds. */
 | 
				
			||||||
 | 
						if ((leap || c->leap_set) && c->clkid == CLOCK_REALTIME) {
 | 
				
			||||||
 | 
							/* If the clock will be stepped, the time stamp has to be the
 | 
				
			||||||
 | 
							   target time. Ignore possible 1 second error in utc_offset. */
 | 
				
			||||||
 | 
							if (c->servo_state == SERVO_UNLOCKED) {
 | 
				
			||||||
 | 
								ts = tmv_to_nanoseconds(tmv_sub(ingress,
 | 
				
			||||||
 | 
												c->master_offset));
 | 
				
			||||||
 | 
								if (c->tds.flags & PTP_TIMESCALE)
 | 
				
			||||||
 | 
									ts -= utc_offset * NS_PER_SEC;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ts = tmv_to_nanoseconds(ingress);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Suspend clock updates in the last second before midnight. */
 | 
				
			||||||
 | 
							if (is_utc_ambiguous(ts)) {
 | 
				
			||||||
 | 
								pr_info("clock update suspended due to leap second");
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							clock_leap = leap_second_status(ts, c->leap_set,
 | 
				
			||||||
 | 
											&leap, &utc_offset);
 | 
				
			||||||
 | 
							if (c->leap_set != clock_leap) {
 | 
				
			||||||
 | 
								if (c->kernel_leap)
 | 
				
			||||||
 | 
									clockadj_set_leap(c->clkid, clock_leap);
 | 
				
			||||||
 | 
								c->leap_set = clock_leap;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!(c->tds.flags & PTP_TIMESCALE))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset.tv_sec = utc_offset;
 | 
				
			||||||
	offset.tv_nsec = 0;
 | 
						offset.tv_nsec = 0;
 | 
				
			||||||
	/* Local clock is UTC, but master is TAI. */
 | 
						/* Local clock is UTC, but master is TAI. */
 | 
				
			||||||
	c->master_offset = tmv_add(c->master_offset, timespec_to_tmv(offset));
 | 
						c->master_offset = tmv_add(c->master_offset, timespec_to_tmv(offset));
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int forwarding(struct clock *c, struct port *p)
 | 
					static int forwarding(struct clock *c, struct port *p)
 | 
				
			||||||
@ -565,6 +569,7 @@ struct clock *clock_create(int phc_index, struct interface *iface, int count,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	c->free_running = dds->free_running;
 | 
						c->free_running = dds->free_running;
 | 
				
			||||||
	c->freq_est_interval = dds->freq_est_interval;
 | 
						c->freq_est_interval = dds->freq_est_interval;
 | 
				
			||||||
 | 
						c->kernel_leap = dds->kernel_leap;
 | 
				
			||||||
	c->desc = dds->clock_desc;
 | 
						c->desc = dds->clock_desc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (c->free_running) {
 | 
						if (c->free_running) {
 | 
				
			||||||
@ -585,16 +590,20 @@ struct clock *clock_create(int phc_index, struct interface *iface, int count,
 | 
				
			|||||||
		c->clkid = CLOCK_REALTIME;
 | 
							c->clkid = CLOCK_REALTIME;
 | 
				
			||||||
		c->utc_timescale = 1;
 | 
							c->utc_timescale = 1;
 | 
				
			||||||
		max_adj = 512000;
 | 
							max_adj = 512000;
 | 
				
			||||||
 | 
							clockadj_set_leap(c->clkid, 0);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						c->leap_set = 0;
 | 
				
			||||||
 | 
						c->kernel_leap = dds->kernel_leap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (c->clkid != CLOCK_INVALID) {
 | 
						if (c->clkid != CLOCK_INVALID) {
 | 
				
			||||||
		fadj = (int) clock_ppb_read(c->clkid);
 | 
							fadj = (int) clockadj_get_freq(c->clkid);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	c->servo = servo_create(servo, -fadj, max_adj, sw_ts);
 | 
						c->servo = servo_create(servo, -fadj, max_adj, sw_ts);
 | 
				
			||||||
	if (!c->servo) {
 | 
						if (!c->servo) {
 | 
				
			||||||
		pr_err("Failed to create clock servo");
 | 
							pr_err("Failed to create clock servo");
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						c->servo_state = SERVO_UNLOCKED;
 | 
				
			||||||
	c->avg_delay = mave_create(MAVE_LENGTH);
 | 
						c->avg_delay = mave_create(MAVE_LENGTH);
 | 
				
			||||||
	if (!c->avg_delay) {
 | 
						if (!c->avg_delay) {
 | 
				
			||||||
		pr_err("Failed to create moving average");
 | 
							pr_err("Failed to create moving average");
 | 
				
			||||||
@ -957,7 +966,7 @@ void clock_path_delay(struct clock *c, struct timespec req, struct timestamp rx,
 | 
				
			|||||||
	pr_debug("path delay    %10lld %10lld", c->path_delay, pd);
 | 
						pr_debug("path delay    %10lld %10lld", c->path_delay, pd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (c->stats.delay)
 | 
						if (c->stats.delay)
 | 
				
			||||||
		stats_add_value(c->stats.delay, pd);
 | 
							stats_add_value(c->stats.delay, tmv_to_nanoseconds(pd));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void clock_peer_delay(struct clock *c, tmv_t ppd, double nrr)
 | 
					void clock_peer_delay(struct clock *c, tmv_t ppd, double nrr)
 | 
				
			||||||
@ -966,7 +975,7 @@ void clock_peer_delay(struct clock *c, tmv_t ppd, double nrr)
 | 
				
			|||||||
	c->nrr = nrr;
 | 
						c->nrr = nrr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (c->stats.delay)
 | 
						if (c->stats.delay)
 | 
				
			||||||
		stats_add_value(c->stats.delay, ppd);
 | 
							stats_add_value(c->stats.delay, tmv_to_nanoseconds(ppd));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void clock_remove_fda(struct clock *c, struct port *p, struct fdarray fda)
 | 
					void clock_remove_fda(struct clock *c, struct port *p, struct fdarray fda)
 | 
				
			||||||
@ -1018,37 +1027,42 @@ enum servo_state clock_synchronize(struct clock *c,
 | 
				
			|||||||
	c->master_offset = tmv_sub(ingress,
 | 
						c->master_offset = tmv_sub(ingress,
 | 
				
			||||||
		tmv_add(origin, tmv_add(c->path_delay, tmv_add(c->c1, c->c2))));
 | 
							tmv_add(origin, tmv_add(c->path_delay, tmv_add(c->c1, c->c2))));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	clock_utc_correct(c);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	c->cur.offsetFromMaster = tmv_to_TimeInterval(c->master_offset);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!c->path_delay)
 | 
						if (!c->path_delay)
 | 
				
			||||||
		return state;
 | 
							return state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (clock_utc_correct(c, ingress))
 | 
				
			||||||
 | 
							return c->servo_state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c->cur.offsetFromMaster = tmv_to_TimeInterval(c->master_offset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (c->free_running)
 | 
						if (c->free_running)
 | 
				
			||||||
		return clock_no_adjust(c);
 | 
							return clock_no_adjust(c);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	adj = servo_sample(c->servo, c->master_offset, ingress, &state);
 | 
						adj = servo_sample(c->servo, tmv_to_nanoseconds(c->master_offset),
 | 
				
			||||||
 | 
								   tmv_to_nanoseconds(ingress), &state);
 | 
				
			||||||
 | 
						c->servo_state = state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (c->stats.max_count > 1) {
 | 
						if (c->stats.max_count > 1) {
 | 
				
			||||||
		clock_stats_update(&c->stats, c->master_offset, adj);
 | 
							clock_stats_update(&c->stats,
 | 
				
			||||||
 | 
									   tmv_to_nanoseconds(c->master_offset), adj);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		pr_info("master offset %10" PRId64 " s%d freq %+7.0f "
 | 
							pr_info("master offset %10" PRId64 " s%d freq %+7.0f "
 | 
				
			||||||
			"path delay %9" PRId64,
 | 
								"path delay %9" PRId64,
 | 
				
			||||||
			c->master_offset, state, adj, c->path_delay);
 | 
								tmv_to_nanoseconds(c->master_offset), state, adj,
 | 
				
			||||||
 | 
								tmv_to_nanoseconds(c->path_delay));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (state) {
 | 
						switch (state) {
 | 
				
			||||||
	case SERVO_UNLOCKED:
 | 
						case SERVO_UNLOCKED:
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case SERVO_JUMP:
 | 
						case SERVO_JUMP:
 | 
				
			||||||
		clock_ppb(c->clkid, -adj);
 | 
							clockadj_set_freq(c->clkid, -adj);
 | 
				
			||||||
		clock_step(c->clkid, -c->master_offset);
 | 
							clockadj_step(c->clkid, -tmv_to_nanoseconds(c->master_offset));
 | 
				
			||||||
		c->t1 = tmv_zero();
 | 
							c->t1 = tmv_zero();
 | 
				
			||||||
		c->t2 = tmv_zero();
 | 
							c->t2 = tmv_zero();
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case SERVO_LOCKED:
 | 
						case SERVO_LOCKED:
 | 
				
			||||||
		clock_ppb(c->clkid, -adj);
 | 
							clockadj_set_freq(c->clkid, -adj);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return state;
 | 
						return state;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										96
									
								
								clockadj.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								clockadj.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,96 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @file clockadj.c
 | 
				
			||||||
 | 
					 * @note Copyright (C) 2013 Richard Cochran <richardcochran@gmail.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 2 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License along
 | 
				
			||||||
 | 
					 * with this program; if not, write to the Free Software Foundation, Inc.,
 | 
				
			||||||
 | 
					 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "clockadj.h"
 | 
				
			||||||
 | 
					#include "missing.h"
 | 
				
			||||||
 | 
					#include "print.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define NS_PER_SEC 1000000000LL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void clockadj_set_freq(clockid_t clkid, double freq)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct timex tx;
 | 
				
			||||||
 | 
						memset(&tx, 0, sizeof(tx));
 | 
				
			||||||
 | 
						tx.modes = ADJ_FREQUENCY;
 | 
				
			||||||
 | 
						tx.freq = (long) (freq * 65.536);
 | 
				
			||||||
 | 
						if (clock_adjtime(clkid, &tx) < 0)
 | 
				
			||||||
 | 
							pr_err("failed to adjust the clock: %m");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double clockadj_get_freq(clockid_t clkid)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						double f = 0.0;
 | 
				
			||||||
 | 
						struct timex tx;
 | 
				
			||||||
 | 
						memset(&tx, 0, sizeof(tx));
 | 
				
			||||||
 | 
						if (clock_adjtime(clkid, &tx) < 0)
 | 
				
			||||||
 | 
							pr_err("failed to read out the clock frequency adjustment: %m");
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							f = tx.freq / 65.536;
 | 
				
			||||||
 | 
						return f;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void clockadj_step(clockid_t clkid, int64_t step)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct timex tx;
 | 
				
			||||||
 | 
						int sign = 1;
 | 
				
			||||||
 | 
						if (step < 0) {
 | 
				
			||||||
 | 
							sign = -1;
 | 
				
			||||||
 | 
							step *= -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						memset(&tx, 0, sizeof(tx));
 | 
				
			||||||
 | 
						tx.modes = ADJ_SETOFFSET | ADJ_NANO;
 | 
				
			||||||
 | 
						tx.time.tv_sec  = sign * (step / NS_PER_SEC);
 | 
				
			||||||
 | 
						tx.time.tv_usec = sign * (step % NS_PER_SEC);
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * The value of a timeval is the sum of its fields, but the
 | 
				
			||||||
 | 
						 * field tv_usec must always be non-negative.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (tx.time.tv_usec < 0) {
 | 
				
			||||||
 | 
							tx.time.tv_sec  -= 1;
 | 
				
			||||||
 | 
							tx.time.tv_usec += 1000000000;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (clock_adjtime(clkid, &tx) < 0)
 | 
				
			||||||
 | 
							pr_err("failed to step clock: %m");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void clockadj_set_leap(clockid_t clkid, int leap)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct timex tx;
 | 
				
			||||||
 | 
						const char *m = NULL;
 | 
				
			||||||
 | 
						memset(&tx, 0, sizeof(tx));
 | 
				
			||||||
 | 
						tx.modes = ADJ_STATUS;
 | 
				
			||||||
 | 
						switch (leap) {
 | 
				
			||||||
 | 
						case -1:
 | 
				
			||||||
 | 
							tx.status = STA_DEL;
 | 
				
			||||||
 | 
							m = "clock set to delete leap second at midnight (UTC)";
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case 1:
 | 
				
			||||||
 | 
							tx.status = STA_INS;
 | 
				
			||||||
 | 
							m = "clock set to insert leap second at midnight (UTC)";
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							tx.status = 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (clock_adjtime(clkid, &tx) < 0)
 | 
				
			||||||
 | 
							pr_err("failed to set the clock status: %m");
 | 
				
			||||||
 | 
						else if (m)
 | 
				
			||||||
 | 
							pr_notice(m);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										54
									
								
								clockadj.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								clockadj.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @file clockadj.h
 | 
				
			||||||
 | 
					 * @brief Wraps clock_adjtime functionality.
 | 
				
			||||||
 | 
					 * @note Copyright (C) 2013 Miroslav Lichvar <mlichvar@redhat.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 2 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License along
 | 
				
			||||||
 | 
					 * with this program; if not, write to the Free Software Foundation, Inc.,
 | 
				
			||||||
 | 
					 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#ifndef HAVE_CLOCKADJ_H
 | 
				
			||||||
 | 
					#define HAVE_CLOCKADJ_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <inttypes.h>
 | 
				
			||||||
 | 
					#include <time.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Set clock's frequency offset.
 | 
				
			||||||
 | 
					 * @param clkid A clock ID obtained using phc_open() or CLOCK_REALTIME.
 | 
				
			||||||
 | 
					 * @param freq  The frequency offset in parts per billion (ppb).
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void clockadj_set_freq(clockid_t clkid, double freq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Read clock's frequency offset.
 | 
				
			||||||
 | 
					 * @param clkid A clock ID obtained using phc_open() or CLOCK_REALTIME.
 | 
				
			||||||
 | 
					 * @return      The frequency offset in parts per billion (ppb).
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					double clockadj_get_freq(clockid_t clkid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Step clock's time.
 | 
				
			||||||
 | 
					 * @param clkid A clock ID obtained using phc_open() or CLOCK_REALTIME.
 | 
				
			||||||
 | 
					 * @param step  The time step in nanoseconds.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void clockadj_step(clockid_t clkid, int64_t step);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Insert/delete leap second at midnight.
 | 
				
			||||||
 | 
					 * @param clkid CLOCK_REALTIME.
 | 
				
			||||||
 | 
					 * @param leap  +1 to insert leap second, -1 to delete leap second,
 | 
				
			||||||
 | 
					 *              0 to reset the leap state.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void clockadj_set_leap(clockid_t clkid, int leap);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										5
									
								
								config.c
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								config.c
									
									
									
									
									
								
							@ -399,6 +399,11 @@ static enum parser_result parse_global_setting(const char *option,
 | 
				
			|||||||
			return BAD_VALUE;
 | 
								return BAD_VALUE;
 | 
				
			||||||
		cfg->dds.stats_interval = val;
 | 
							cfg->dds.stats_interval = val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						} else if (!strcmp(option, "kernel_leap")) {
 | 
				
			||||||
 | 
							if (1 != sscanf(value, "%d", &val))
 | 
				
			||||||
 | 
								return BAD_VALUE;
 | 
				
			||||||
 | 
							cfg->dds.kernel_leap = val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	} else
 | 
						} else
 | 
				
			||||||
		return NOT_PARSED;
 | 
							return NOT_PARSED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -34,6 +34,7 @@ tx_timestamp_retries	100
 | 
				
			|||||||
use_syslog		1
 | 
					use_syslog		1
 | 
				
			||||||
verbose			0
 | 
					verbose			0
 | 
				
			||||||
summary_interval	0
 | 
					summary_interval	0
 | 
				
			||||||
 | 
					kernel_leap		1
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Servo Options
 | 
					# Servo Options
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								ds.h
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								ds.h
									
									
									
									
									
								
							@ -53,6 +53,7 @@ struct default_ds {
 | 
				
			|||||||
	int free_running;
 | 
						int free_running;
 | 
				
			||||||
	int freq_est_interval; /*log seconds*/
 | 
						int freq_est_interval; /*log seconds*/
 | 
				
			||||||
	int stats_interval; /*log seconds*/
 | 
						int stats_interval; /*log seconds*/
 | 
				
			||||||
 | 
						int kernel_leap;
 | 
				
			||||||
	struct clock_description clock_desc;
 | 
						struct clock_description clock_desc;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								gPTP.cfg
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								gPTP.cfg
									
									
									
									
									
								
							@ -33,6 +33,7 @@ tx_timestamp_retries	100
 | 
				
			|||||||
use_syslog		1
 | 
					use_syslog		1
 | 
				
			||||||
verbose			0
 | 
					verbose			0
 | 
				
			||||||
summary_interval	0
 | 
					summary_interval	0
 | 
				
			||||||
 | 
					kernel_leap		1
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Servo options
 | 
					# Servo options
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										10
									
								
								makefile
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								makefile
									
									
									
									
									
								
							@ -29,9 +29,9 @@ VER     = -DVER=$(version)
 | 
				
			|||||||
CFLAGS	= -Wall $(VER) $(INC) $(DEBUG) $(FEAT_CFLAGS) $(EXTRA_CFLAGS)
 | 
					CFLAGS	= -Wall $(VER) $(INC) $(DEBUG) $(FEAT_CFLAGS) $(EXTRA_CFLAGS)
 | 
				
			||||||
LDLIBS	= -lm -lrt $(EXTRA_LDFLAGS)
 | 
					LDLIBS	= -lm -lrt $(EXTRA_LDFLAGS)
 | 
				
			||||||
PRG	= ptp4l pmc phc2sys hwstamp_ctl
 | 
					PRG	= ptp4l pmc phc2sys hwstamp_ctl
 | 
				
			||||||
OBJ	= bmc.o clock.o config.o fault.o fsm.o ptp4l.o mave.o msg.o phc.o pi.o \
 | 
					OBJ     = bmc.o clock.o clockadj.o config.o fault.o fsm.o ptp4l.o mave.o \
 | 
				
			||||||
 port.o print.o raw.o servo.o sk.o stats.o tlv.o tmtab.o transport.o udp.o \
 | 
					 msg.o phc.o pi.o port.o print.o raw.o servo.o sk.o stats.o tlv.o tmtab.o \
 | 
				
			||||||
 udp6.o uds.o util.o version.o
 | 
					 transport.o udp.o udp6.o uds.o util.o version.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OBJECTS	= $(OBJ) hwstamp_ctl.o phc2sys.o pmc.o pmc_common.o sysoff.o
 | 
					OBJECTS	= $(OBJ) hwstamp_ctl.o phc2sys.o pmc.o pmc_common.o sysoff.o
 | 
				
			||||||
SRC	= $(OBJECTS:.o=.c)
 | 
					SRC	= $(OBJECTS:.o=.c)
 | 
				
			||||||
@ -52,8 +52,8 @@ ptp4l: $(OBJ)
 | 
				
			|||||||
pmc: msg.o pmc.o pmc_common.o print.o raw.o sk.o tlv.o transport.o udp.o \
 | 
					pmc: msg.o pmc.o pmc_common.o print.o raw.o sk.o tlv.o transport.o udp.o \
 | 
				
			||||||
 udp6.o uds.o util.o version.o
 | 
					 udp6.o uds.o util.o version.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
phc2sys: msg.o phc2sys.o pmc_common.o print.o pi.o servo.o raw.o sk.o stats.o \
 | 
					phc2sys: clockadj.o msg.o phc2sys.o pmc_common.o print.o pi.o servo.o raw.o \
 | 
				
			||||||
 sysoff.o tlv.o transport.o udp.o udp6.o uds.o util.o version.o
 | 
					 sk.o stats.o sysoff.o tlv.o transport.o udp.o udp6.o uds.o util.o version.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
hwstamp_ctl: hwstamp_ctl.o version.o
 | 
					hwstamp_ctl: hwstamp_ctl.o version.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										24
									
								
								phc2sys.8
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								phc2sys.8
									
									
									
									
									
								
							@ -31,6 +31,8 @@ phc2sys \- synchronize two clocks
 | 
				
			|||||||
] [
 | 
					] [
 | 
				
			||||||
.B \-w
 | 
					.B \-w
 | 
				
			||||||
] [
 | 
					] [
 | 
				
			||||||
 | 
					.B \-x
 | 
				
			||||||
 | 
					] [
 | 
				
			||||||
.BI \-l " print-level"
 | 
					.BI \-l " print-level"
 | 
				
			||||||
] [
 | 
					] [
 | 
				
			||||||
.B \-m
 | 
					.B \-m
 | 
				
			||||||
@ -106,13 +108,10 @@ minimize the error caused by random delays in scheduling and bus utilization.
 | 
				
			|||||||
The default is 5.
 | 
					The default is 5.
 | 
				
			||||||
.TP
 | 
					.TP
 | 
				
			||||||
.BI \-O " offset"
 | 
					.BI \-O " offset"
 | 
				
			||||||
Specify the offset between the slave and master times in seconds. With the
 | 
					Specify the offset between the slave and master times in seconds. The default
 | 
				
			||||||
 | 
					is set automatically with the
 | 
				
			||||||
.B \-w
 | 
					.B \-w
 | 
				
			||||||
option the default value is set automatically according to the currentUtcOffset
 | 
					option, 0 otherwise.
 | 
				
			||||||
value obtained from ptp4l and the direction of the clock synchronization.
 | 
					 | 
				
			||||||
Without
 | 
					 | 
				
			||||||
.B \-w
 | 
					 | 
				
			||||||
the default is 0.
 | 
					 | 
				
			||||||
.TP
 | 
					.TP
 | 
				
			||||||
.BI \-u " summary-updates"
 | 
					.BI \-u " summary-updates"
 | 
				
			||||||
Specify the number of clock updates included in summary statistics. The
 | 
					Specify the number of clock updates included in summary statistics. The
 | 
				
			||||||
@ -124,7 +123,18 @@ statistics. The messages are printed at the LOG_INFO level.
 | 
				
			|||||||
The default is 0 (disabled).
 | 
					The default is 0 (disabled).
 | 
				
			||||||
.TP
 | 
					.TP
 | 
				
			||||||
.B \-w
 | 
					.B \-w
 | 
				
			||||||
Wait until ptp4l is in a synchronized state.
 | 
					Wait until ptp4l is in a synchronized state. If the
 | 
				
			||||||
 | 
					.B \-O
 | 
				
			||||||
 | 
					option is not used, also keep the offset between the slave and master
 | 
				
			||||||
 | 
					times updated according to the currentUtcOffset value obtained from ptp4l and
 | 
				
			||||||
 | 
					the direction of the clock synchronization.
 | 
				
			||||||
 | 
					.TP
 | 
				
			||||||
 | 
					.B \-x
 | 
				
			||||||
 | 
					When a leap second is announced, don't apply it in the kernel by stepping the
 | 
				
			||||||
 | 
					clock, but let the servo correct the one-second offset slowly by changing the
 | 
				
			||||||
 | 
					clock frequency (unless the
 | 
				
			||||||
 | 
					.B \-S
 | 
				
			||||||
 | 
					option is used).
 | 
				
			||||||
.TP
 | 
					.TP
 | 
				
			||||||
.BI \-l " print-level"
 | 
					.BI \-l " print-level"
 | 
				
			||||||
Set the maximum syslog level of messages which should be printed or sent to
 | 
					Set the maximum syslog level of messages which should be printed or sent to
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										281
									
								
								phc2sys.c
									
									
									
									
									
								
							
							
						
						
									
										281
									
								
								phc2sys.c
									
									
									
									
									
								
							@ -27,13 +27,13 @@
 | 
				
			|||||||
#include <sys/ioctl.h>
 | 
					#include <sys/ioctl.h>
 | 
				
			||||||
#include <sys/stat.h>
 | 
					#include <sys/stat.h>
 | 
				
			||||||
#include <sys/types.h>
 | 
					#include <sys/types.h>
 | 
				
			||||||
#include <time.h>
 | 
					 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
#include <inttypes.h>
 | 
					#include <inttypes.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/pps.h>
 | 
					#include <linux/pps.h>
 | 
				
			||||||
#include <linux/ptp_clock.h>
 | 
					#include <linux/ptp_clock.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "clockadj.h"
 | 
				
			||||||
#include "ds.h"
 | 
					#include "ds.h"
 | 
				
			||||||
#include "fsm.h"
 | 
					#include "fsm.h"
 | 
				
			||||||
#include "missing.h"
 | 
					#include "missing.h"
 | 
				
			||||||
@ -45,6 +45,7 @@
 | 
				
			|||||||
#include "stats.h"
 | 
					#include "stats.h"
 | 
				
			||||||
#include "sysoff.h"
 | 
					#include "sysoff.h"
 | 
				
			||||||
#include "tlv.h"
 | 
					#include "tlv.h"
 | 
				
			||||||
 | 
					#include "util.h"
 | 
				
			||||||
#include "version.h"
 | 
					#include "version.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define KP 0.7
 | 
					#define KP 0.7
 | 
				
			||||||
@ -54,6 +55,10 @@
 | 
				
			|||||||
#define max_ppb  512000
 | 
					#define max_ppb  512000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define PHC_PPS_OFFSET_LIMIT 10000000
 | 
					#define PHC_PPS_OFFSET_LIMIT 10000000
 | 
				
			||||||
 | 
					#define PMC_UPDATE_INTERVAL (60 * NS_PER_SEC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct clock;
 | 
				
			||||||
 | 
					static int update_sync_offset(struct clock *clock, int64_t offset, uint64_t ts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static clockid_t clock_open(char *device)
 | 
					static clockid_t clock_open(char *device)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -75,52 +80,6 @@ static clockid_t clock_open(char *device)
 | 
				
			|||||||
	return FD_TO_CLOCKID(fd);
 | 
						return FD_TO_CLOCKID(fd);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void clock_ppb(clockid_t clkid, double ppb)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct timex tx;
 | 
					 | 
				
			||||||
	memset(&tx, 0, sizeof(tx));
 | 
					 | 
				
			||||||
	tx.modes = ADJ_FREQUENCY;
 | 
					 | 
				
			||||||
	tx.freq = (long) (ppb * 65.536);
 | 
					 | 
				
			||||||
	if (clock_adjtime(clkid, &tx) < 0)
 | 
					 | 
				
			||||||
		pr_err("failed to adjust the clock: %m");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static double clock_ppb_read(clockid_t clkid)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	double f = 0.0;
 | 
					 | 
				
			||||||
	struct timex tx;
 | 
					 | 
				
			||||||
	memset(&tx, 0, sizeof(tx));
 | 
					 | 
				
			||||||
	if (clock_adjtime(clkid, &tx) < 0)
 | 
					 | 
				
			||||||
		pr_err("failed to read out the clock frequency adjustment: %m");
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		f = tx.freq / 65.536;
 | 
					 | 
				
			||||||
	return f;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void clock_step(clockid_t clkid, int64_t ns)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct timex tx;
 | 
					 | 
				
			||||||
	int sign = 1;
 | 
					 | 
				
			||||||
	if (ns < 0) {
 | 
					 | 
				
			||||||
		sign = -1;
 | 
					 | 
				
			||||||
		ns *= -1;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	memset(&tx, 0, sizeof(tx));
 | 
					 | 
				
			||||||
	tx.modes = ADJ_SETOFFSET | ADJ_NANO;
 | 
					 | 
				
			||||||
	tx.time.tv_sec  = sign * (ns / NS_PER_SEC);
 | 
					 | 
				
			||||||
	tx.time.tv_usec = sign * (ns % NS_PER_SEC);
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	 * The value of a timeval is the sum of its fields, but the
 | 
					 | 
				
			||||||
	 * field tv_usec must always be non-negative.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	if (tx.time.tv_usec < 0) {
 | 
					 | 
				
			||||||
		tx.time.tv_sec  -= 1;
 | 
					 | 
				
			||||||
		tx.time.tv_usec += 1000000000;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (clock_adjtime(clkid, &tx) < 0)
 | 
					 | 
				
			||||||
		pr_err("failed to step clock: %m");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int read_phc(clockid_t clkid, clockid_t sysclk, int readings,
 | 
					static int read_phc(clockid_t clkid, clockid_t sysclk, int readings,
 | 
				
			||||||
		    int64_t *offset, uint64_t *ts, int64_t *delay)
 | 
							    int64_t *offset, uint64_t *ts, int64_t *delay)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -155,11 +114,21 @@ static int read_phc(clockid_t clkid, clockid_t sysclk, int readings,
 | 
				
			|||||||
struct clock {
 | 
					struct clock {
 | 
				
			||||||
	clockid_t clkid;
 | 
						clockid_t clkid;
 | 
				
			||||||
	struct servo *servo;
 | 
						struct servo *servo;
 | 
				
			||||||
 | 
						enum servo_state servo_state;
 | 
				
			||||||
	const char *source_label;
 | 
						const char *source_label;
 | 
				
			||||||
	struct stats *offset_stats;
 | 
						struct stats *offset_stats;
 | 
				
			||||||
	struct stats *freq_stats;
 | 
						struct stats *freq_stats;
 | 
				
			||||||
	struct stats *delay_stats;
 | 
						struct stats *delay_stats;
 | 
				
			||||||
	unsigned int stats_max_count;
 | 
						unsigned int stats_max_count;
 | 
				
			||||||
 | 
						int sync_offset;
 | 
				
			||||||
 | 
						int sync_offset_direction;
 | 
				
			||||||
 | 
						int leap;
 | 
				
			||||||
 | 
						int leap_set;
 | 
				
			||||||
 | 
						int kernel_leap;
 | 
				
			||||||
 | 
						struct pmc *pmc;
 | 
				
			||||||
 | 
						int pmc_ds_idx;
 | 
				
			||||||
 | 
						int pmc_ds_requested;
 | 
				
			||||||
 | 
						uint64_t pmc_last_update;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void update_clock_stats(struct clock *clock,
 | 
					static void update_clock_stats(struct clock *clock,
 | 
				
			||||||
@ -203,16 +172,24 @@ static void update_clock(struct clock *clock,
 | 
				
			|||||||
	enum servo_state state;
 | 
						enum servo_state state;
 | 
				
			||||||
	double ppb;
 | 
						double ppb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (update_sync_offset(clock, offset, ts))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (clock->sync_offset_direction)
 | 
				
			||||||
 | 
							offset += clock->sync_offset * NS_PER_SEC *
 | 
				
			||||||
 | 
								clock->sync_offset_direction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ppb = servo_sample(clock->servo, offset, ts, &state);
 | 
						ppb = servo_sample(clock->servo, offset, ts, &state);
 | 
				
			||||||
 | 
						clock->servo_state = state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (state) {
 | 
						switch (state) {
 | 
				
			||||||
	case SERVO_UNLOCKED:
 | 
						case SERVO_UNLOCKED:
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case SERVO_JUMP:
 | 
						case SERVO_JUMP:
 | 
				
			||||||
		clock_step(clock->clkid, -offset);
 | 
							clockadj_step(clock->clkid, -offset);
 | 
				
			||||||
		/* Fall through. */
 | 
							/* Fall through. */
 | 
				
			||||||
	case SERVO_LOCKED:
 | 
						case SERVO_LOCKED:
 | 
				
			||||||
		clock_ppb(clock->clkid, -ppb);
 | 
							clockadj_set_freq(clock->clkid, -ppb);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -253,13 +230,17 @@ static int read_pps(int fd, int64_t *offset, uint64_t *ts)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int do_pps_loop(struct clock *clock, int fd,
 | 
					static int do_pps_loop(struct clock *clock, int fd,
 | 
				
			||||||
		       clockid_t src, int n_readings, int sync_offset)
 | 
							       clockid_t src, int n_readings)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int64_t pps_offset, phc_offset, phc_delay;
 | 
						int64_t pps_offset, phc_offset, phc_delay;
 | 
				
			||||||
	uint64_t pps_ts, phc_ts;
 | 
						uint64_t pps_ts, phc_ts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	clock->source_label = "pps";
 | 
						clock->source_label = "pps";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* The sync offset can't be applied with PPS alone. */
 | 
				
			||||||
 | 
						if (src == CLOCK_INVALID)
 | 
				
			||||||
 | 
							clock->sync_offset_direction = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (1) {
 | 
						while (1) {
 | 
				
			||||||
		if (!read_pps(fd, &pps_offset, &pps_ts)) {
 | 
							if (!read_pps(fd, &pps_offset, &pps_ts)) {
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
@ -284,7 +265,6 @@ static int do_pps_loop(struct clock *clock, int fd,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			phc_ts = phc_ts / NS_PER_SEC * NS_PER_SEC;
 | 
								phc_ts = phc_ts / NS_PER_SEC * NS_PER_SEC;
 | 
				
			||||||
			pps_offset = pps_ts - phc_ts;
 | 
								pps_offset = pps_ts - phc_ts;
 | 
				
			||||||
			pps_offset -= sync_offset * NS_PER_SEC;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		update_clock(clock, pps_offset, pps_ts, -1);
 | 
							update_clock(clock, pps_offset, pps_ts, -1);
 | 
				
			||||||
@ -294,7 +274,7 @@ static int do_pps_loop(struct clock *clock, int fd,
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int do_sysoff_loop(struct clock *clock, clockid_t src,
 | 
					static int do_sysoff_loop(struct clock *clock, clockid_t src,
 | 
				
			||||||
			  int rate, int n_readings, int sync_offset)
 | 
								  int rate, int n_readings)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint64_t ts;
 | 
						uint64_t ts;
 | 
				
			||||||
	int64_t offset, delay;
 | 
						int64_t offset, delay;
 | 
				
			||||||
@ -308,14 +288,13 @@ static int do_sysoff_loop(struct clock *clock, clockid_t src,
 | 
				
			|||||||
			err = -1;
 | 
								err = -1;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		offset -= sync_offset * NS_PER_SEC;
 | 
					 | 
				
			||||||
		update_clock(clock, offset, ts, delay);
 | 
							update_clock(clock, offset, ts, delay);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int do_phc_loop(struct clock *clock, clockid_t src,
 | 
					static int do_phc_loop(struct clock *clock, clockid_t src,
 | 
				
			||||||
		       int rate, int n_readings, int sync_offset)
 | 
							       int rate, int n_readings)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint64_t ts;
 | 
						uint64_t ts;
 | 
				
			||||||
	int64_t offset, delay;
 | 
						int64_t offset, delay;
 | 
				
			||||||
@ -328,7 +307,6 @@ static int do_phc_loop(struct clock *clock, clockid_t src,
 | 
				
			|||||||
			      &offset, &ts, &delay)) {
 | 
								      &offset, &ts, &delay)) {
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		offset -= sync_offset * NS_PER_SEC;
 | 
					 | 
				
			||||||
		update_clock(clock, offset, ts, delay);
 | 
							update_clock(clock, offset, ts, delay);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
@ -360,58 +338,75 @@ static void *get_mgt_data(struct ptp_message *msg)
 | 
				
			|||||||
	return ((struct management_tlv *) msg->management.suffix)->data;
 | 
						return ((struct management_tlv *) msg->management.suffix)->data;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int run_pmc(int wait_sync, int *utc_offset)
 | 
					static int init_pmc(struct clock *clock)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						clock->pmc = pmc_create(TRANS_UDS, "/var/run/phc2sys", 0, 0, 0);
 | 
				
			||||||
 | 
						if (!clock->pmc) {
 | 
				
			||||||
 | 
							pr_err("failed to create pmc");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int run_pmc(struct clock *clock, int timeout,
 | 
				
			||||||
 | 
							   int wait_sync, int get_utc_offset)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ptp_message *msg;
 | 
						struct ptp_message *msg;
 | 
				
			||||||
	struct pmc *pmc;
 | 
					 | 
				
			||||||
	void *data;
 | 
						void *data;
 | 
				
			||||||
#define N_FD 1
 | 
					#define N_FD 1
 | 
				
			||||||
	struct pollfd pollfd[N_FD];
 | 
						struct pollfd pollfd[N_FD];
 | 
				
			||||||
 | 
						int cnt, ds_done;
 | 
				
			||||||
#define N_ID 2
 | 
					#define N_ID 2
 | 
				
			||||||
	int cnt, i = 0, ds_done, ds_requested = 0;
 | 
					 | 
				
			||||||
	int ds_ids[N_ID] = {
 | 
						int ds_ids[N_ID] = {
 | 
				
			||||||
		PORT_DATA_SET,
 | 
							PORT_DATA_SET,
 | 
				
			||||||
		TIME_PROPERTIES_DATA_SET
 | 
							TIME_PROPERTIES_DATA_SET
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pmc = pmc_create(TRANS_UDS, "/var/run/phc2sys", 0, 0, 0);
 | 
						while (clock->pmc_ds_idx < N_ID) {
 | 
				
			||||||
	if (!pmc) {
 | 
							/* Check if the data set is really needed. */
 | 
				
			||||||
		pr_err("failed to create pmc");
 | 
							if ((ds_ids[clock->pmc_ds_idx] == PORT_DATA_SET &&
 | 
				
			||||||
		return -1;
 | 
							     !wait_sync) ||
 | 
				
			||||||
	}
 | 
							    (ds_ids[clock->pmc_ds_idx] == TIME_PROPERTIES_DATA_SET &&
 | 
				
			||||||
 | 
							     !get_utc_offset)) {
 | 
				
			||||||
 | 
								clock->pmc_ds_idx++;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (i < N_ID) {
 | 
							pollfd[0].fd = pmc_get_transport_fd(clock->pmc);
 | 
				
			||||||
		pollfd[0].fd = pmc_get_transport_fd(pmc);
 | 
					 | 
				
			||||||
		pollfd[0].events = POLLIN|POLLPRI;
 | 
							pollfd[0].events = POLLIN|POLLPRI;
 | 
				
			||||||
		if (!ds_requested)
 | 
							if (!clock->pmc_ds_requested)
 | 
				
			||||||
			pollfd[0].events |= POLLOUT;
 | 
								pollfd[0].events |= POLLOUT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		cnt = poll(pollfd, N_FD, 1000);
 | 
							cnt = poll(pollfd, N_FD, timeout);
 | 
				
			||||||
		if (cnt < 0) {
 | 
							if (cnt < 0) {
 | 
				
			||||||
			pr_err("poll failed");
 | 
								pr_err("poll failed");
 | 
				
			||||||
			return -1;
 | 
								return -1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (!cnt) {
 | 
							if (!cnt) {
 | 
				
			||||||
			/* Request the data set again. */
 | 
								/* Request the data set again in the next run. */
 | 
				
			||||||
			ds_requested = 0;
 | 
								clock->pmc_ds_requested = 0;
 | 
				
			||||||
			pr_notice("Waiting for ptp4l...");
 | 
								return 0;
 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (pollfd[0].revents & POLLOUT) {
 | 
							/* Send a new request if there are no pending messages. */
 | 
				
			||||||
			pmc_send_get_action(pmc, ds_ids[i]);
 | 
							if ((pollfd[0].revents & POLLOUT) &&
 | 
				
			||||||
			ds_requested = 1;
 | 
							    !(pollfd[0].revents & (POLLIN|POLLPRI))) {
 | 
				
			||||||
 | 
								pmc_send_get_action(clock->pmc,
 | 
				
			||||||
 | 
										    ds_ids[clock->pmc_ds_idx]);
 | 
				
			||||||
 | 
								clock->pmc_ds_requested = 1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!(pollfd[0].revents & (POLLIN|POLLPRI)))
 | 
							if (!(pollfd[0].revents & (POLLIN|POLLPRI)))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		msg = pmc_recv(pmc);
 | 
							msg = pmc_recv(clock->pmc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!msg)
 | 
							if (!msg)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!is_msg_mgt(msg) || get_mgt_id(msg) != ds_ids[i]) {
 | 
							if (!is_msg_mgt(msg) ||
 | 
				
			||||||
 | 
							    get_mgt_id(msg) != ds_ids[clock->pmc_ds_idx]) {
 | 
				
			||||||
			msg_put(msg);
 | 
								msg_put(msg);
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -421,9 +416,6 @@ static int run_pmc(int wait_sync, int *utc_offset)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		switch (get_mgt_id(msg)) {
 | 
							switch (get_mgt_id(msg)) {
 | 
				
			||||||
		case PORT_DATA_SET:
 | 
							case PORT_DATA_SET:
 | 
				
			||||||
			if (!wait_sync)
 | 
					 | 
				
			||||||
				ds_done = 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			switch (((struct portDS *)data)->portState) {
 | 
								switch (((struct portDS *)data)->portState) {
 | 
				
			||||||
			case PS_MASTER:
 | 
								case PS_MASTER:
 | 
				
			||||||
			case PS_SLAVE:
 | 
								case PS_SLAVE:
 | 
				
			||||||
@ -433,21 +425,86 @@ static int run_pmc(int wait_sync, int *utc_offset)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		case TIME_PROPERTIES_DATA_SET:
 | 
							case TIME_PROPERTIES_DATA_SET:
 | 
				
			||||||
			*utc_offset = ((struct timePropertiesDS *)data)->
 | 
								clock->sync_offset = ((struct timePropertiesDS *)data)->
 | 
				
			||||||
					currentUtcOffset;
 | 
									currentUtcOffset;
 | 
				
			||||||
 | 
								if (((struct timePropertiesDS *)data)->flags & LEAP_61)
 | 
				
			||||||
 | 
									clock->leap = 1;
 | 
				
			||||||
 | 
								else if (((struct timePropertiesDS *)data)->flags & LEAP_59)
 | 
				
			||||||
 | 
									clock->leap = -1;
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									clock->leap = 0;
 | 
				
			||||||
			ds_done = 1;
 | 
								ds_done = 1;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (ds_done) {
 | 
							if (ds_done) {
 | 
				
			||||||
			/* Proceed with the next data set. */
 | 
								/* Proceed with the next data set. */
 | 
				
			||||||
			i++;
 | 
								clock->pmc_ds_idx++;
 | 
				
			||||||
			ds_requested = 0;
 | 
								clock->pmc_ds_requested = 0;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		msg_put(msg);
 | 
							msg_put(msg);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pmc_destroy(pmc);
 | 
						clock->pmc_ds_idx = 0;
 | 
				
			||||||
 | 
						return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void close_pmc(struct clock *clock)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pmc_destroy(clock->pmc);
 | 
				
			||||||
 | 
						clock->pmc = NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int update_sync_offset(struct clock *clock, int64_t offset, uint64_t ts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int clock_leap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (clock->pmc &&
 | 
				
			||||||
 | 
						    !(ts > clock->pmc_last_update &&
 | 
				
			||||||
 | 
						      ts - clock->pmc_last_update < PMC_UPDATE_INTERVAL)) {
 | 
				
			||||||
 | 
							if (run_pmc(clock, 0, 0, 1) > 0)
 | 
				
			||||||
 | 
								clock->pmc_last_update = ts;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Handle leap seconds. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!clock->leap && !clock->leap_set)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If the system clock is the master clock, get a time stamp from
 | 
				
			||||||
 | 
						   it, as it is the clock which will include the leap second. */
 | 
				
			||||||
 | 
						if (clock->clkid != CLOCK_REALTIME) {
 | 
				
			||||||
 | 
							struct timespec tp;
 | 
				
			||||||
 | 
							if (clock_gettime(CLOCK_REALTIME, &tp)) {
 | 
				
			||||||
 | 
								pr_err("failed to read clock: %m");
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If the clock will be stepped, the time stamp has to be the
 | 
				
			||||||
 | 
						   target time. Ignore possible 1 second error in UTC offset. */
 | 
				
			||||||
 | 
						if (clock->clkid == CLOCK_REALTIME &&
 | 
				
			||||||
 | 
						    clock->servo_state == SERVO_UNLOCKED) {
 | 
				
			||||||
 | 
							ts -= offset + clock->sync_offset * NS_PER_SEC *
 | 
				
			||||||
 | 
								clock->sync_offset_direction;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Suspend clock updates in the last second before midnight. */
 | 
				
			||||||
 | 
						if (is_utc_ambiguous(ts)) {
 | 
				
			||||||
 | 
							pr_info("clock update suspended due to leap second");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clock_leap = leap_second_status(ts, clock->leap_set,
 | 
				
			||||||
 | 
										&clock->leap, &clock->sync_offset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (clock->leap_set != clock_leap) {
 | 
				
			||||||
 | 
							/* Only the system clock can leap. */
 | 
				
			||||||
 | 
							if (clock->clkid == CLOCK_REALTIME && clock->kernel_leap)
 | 
				
			||||||
 | 
								clockadj_set_leap(clock->clkid, clock_leap);
 | 
				
			||||||
 | 
							clock->leap_set = clock_leap;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -469,6 +526,7 @@ static void usage(char *progname)
 | 
				
			|||||||
		" -O [offset]    slave-master time offset (0)\n"
 | 
							" -O [offset]    slave-master time offset (0)\n"
 | 
				
			||||||
		" -u [num]       number of clock updates in summary stats (0)\n"
 | 
							" -u [num]       number of clock updates in summary stats (0)\n"
 | 
				
			||||||
		" -w             wait for ptp4l\n"
 | 
							" -w             wait for ptp4l\n"
 | 
				
			||||||
 | 
							" -x             apply leap seconds by servo instead of kernel\n"
 | 
				
			||||||
		" -h             prints this message and exits\n"
 | 
							" -h             prints this message and exits\n"
 | 
				
			||||||
		" -v             prints the software version and exits\n"
 | 
							" -v             prints the software version and exits\n"
 | 
				
			||||||
		"\n",
 | 
							"\n",
 | 
				
			||||||
@ -479,11 +537,15 @@ int main(int argc, char *argv[])
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	char *progname, *ethdev = NULL;
 | 
						char *progname, *ethdev = NULL;
 | 
				
			||||||
	clockid_t src = CLOCK_INVALID;
 | 
						clockid_t src = CLOCK_INVALID;
 | 
				
			||||||
	int c, phc_readings = 5, phc_rate = 1, sync_offset = 0, pps_fd = -1;
 | 
						int c, phc_readings = 5, phc_rate = 1, pps_fd = -1;
 | 
				
			||||||
	int wait_sync = 0, forced_sync_offset = 0;
 | 
						int r, wait_sync = 0, forced_sync_offset = 0;
 | 
				
			||||||
	int print_level = LOG_INFO, use_syslog = 1, verbose = 0;
 | 
						int print_level = LOG_INFO, use_syslog = 1, verbose = 0;
 | 
				
			||||||
	double ppb;
 | 
						double ppb;
 | 
				
			||||||
	struct clock dst_clock = { .clkid = CLOCK_REALTIME };
 | 
						struct clock dst_clock = {
 | 
				
			||||||
 | 
							.clkid = CLOCK_REALTIME,
 | 
				
			||||||
 | 
							.servo_state = SERVO_UNLOCKED,
 | 
				
			||||||
 | 
							.kernel_leap = 1,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	configured_pi_kp = KP;
 | 
						configured_pi_kp = KP;
 | 
				
			||||||
	configured_pi_ki = KI;
 | 
						configured_pi_ki = KI;
 | 
				
			||||||
@ -492,7 +554,7 @@ int main(int argc, char *argv[])
 | 
				
			|||||||
	progname = strrchr(argv[0], '/');
 | 
						progname = strrchr(argv[0], '/');
 | 
				
			||||||
	progname = progname ? 1+progname : argv[0];
 | 
						progname = progname ? 1+progname : argv[0];
 | 
				
			||||||
	while (EOF != (c = getopt(argc, argv,
 | 
						while (EOF != (c = getopt(argc, argv,
 | 
				
			||||||
				  "c:d:hs:P:I:S:R:N:O:i:u:wl:mqv"))) {
 | 
									  "c:d:hs:P:I:S:R:N:O:i:u:wxl:mqv"))) {
 | 
				
			||||||
		switch (c) {
 | 
							switch (c) {
 | 
				
			||||||
		case 'c':
 | 
							case 'c':
 | 
				
			||||||
			dst_clock.clkid = clock_open(optarg);
 | 
								dst_clock.clkid = clock_open(optarg);
 | 
				
			||||||
@ -524,7 +586,8 @@ int main(int argc, char *argv[])
 | 
				
			|||||||
			phc_readings = atoi(optarg);
 | 
								phc_readings = atoi(optarg);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		case 'O':
 | 
							case 'O':
 | 
				
			||||||
			sync_offset = atoi(optarg);
 | 
								dst_clock.sync_offset = atoi(optarg);
 | 
				
			||||||
 | 
								dst_clock.sync_offset_direction = -1;
 | 
				
			||||||
			forced_sync_offset = 1;
 | 
								forced_sync_offset = 1;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		case 'i':
 | 
							case 'i':
 | 
				
			||||||
@ -536,6 +599,9 @@ int main(int argc, char *argv[])
 | 
				
			|||||||
		case 'w':
 | 
							case 'w':
 | 
				
			||||||
			wait_sync = 1;
 | 
								wait_sync = 1;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
							case 'x':
 | 
				
			||||||
 | 
								dst_clock.kernel_leap = 0;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
		case 'l':
 | 
							case 'l':
 | 
				
			||||||
			print_level = atoi(optarg);
 | 
								print_level = atoi(optarg);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
@ -596,36 +662,49 @@ int main(int argc, char *argv[])
 | 
				
			|||||||
	print_set_level(print_level);
 | 
						print_set_level(print_level);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (wait_sync) {
 | 
						if (wait_sync) {
 | 
				
			||||||
		int ptp_utc_offset;
 | 
							if (init_pmc(&dst_clock))
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		run_pmc(wait_sync, &ptp_utc_offset);
 | 
							while (1) {
 | 
				
			||||||
 | 
								r = run_pmc(&dst_clock, 1000,
 | 
				
			||||||
 | 
									    wait_sync, !forced_sync_offset);
 | 
				
			||||||
 | 
								if (r < 0)
 | 
				
			||||||
 | 
									return -1;
 | 
				
			||||||
 | 
								else if (r > 0)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									pr_notice("Waiting for ptp4l...");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!forced_sync_offset) {
 | 
							if (!forced_sync_offset) {
 | 
				
			||||||
			if (src != CLOCK_REALTIME &&
 | 
								if (src != CLOCK_REALTIME &&
 | 
				
			||||||
			    dst_clock.clkid == CLOCK_REALTIME)
 | 
								    dst_clock.clkid == CLOCK_REALTIME)
 | 
				
			||||||
				sync_offset = -ptp_utc_offset;
 | 
									dst_clock.sync_offset_direction = 1;
 | 
				
			||||||
			else if (src == CLOCK_REALTIME &&
 | 
								else if (src == CLOCK_REALTIME &&
 | 
				
			||||||
			    dst_clock.clkid != CLOCK_REALTIME)
 | 
								    dst_clock.clkid != CLOCK_REALTIME)
 | 
				
			||||||
				sync_offset = ptp_utc_offset;
 | 
									dst_clock.sync_offset_direction = -1;
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									dst_clock.sync_offset_direction = 0;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (forced_sync_offset || !dst_clock.sync_offset_direction)
 | 
				
			||||||
 | 
								close_pmc(&dst_clock);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ppb = clock_ppb_read(dst_clock.clkid);
 | 
						ppb = clockadj_get_freq(dst_clock.clkid);
 | 
				
			||||||
	/* The reading may silently fail and return 0, reset the frequency to
 | 
						/* The reading may silently fail and return 0, reset the frequency to
 | 
				
			||||||
	   make sure ppb is the actual frequency of the clock. */
 | 
						   make sure ppb is the actual frequency of the clock. */
 | 
				
			||||||
	clock_ppb(dst_clock.clkid, ppb);
 | 
						clockadj_set_freq(dst_clock.clkid, ppb);
 | 
				
			||||||
 | 
						clockadj_set_leap(dst_clock.clkid, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dst_clock.servo = servo_create(CLOCK_SERVO_PI, -ppb, max_ppb, 0);
 | 
						dst_clock.servo = servo_create(CLOCK_SERVO_PI, -ppb, max_ppb, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (pps_fd >= 0)
 | 
						if (pps_fd >= 0)
 | 
				
			||||||
		return do_pps_loop(&dst_clock, pps_fd, src,
 | 
							return do_pps_loop(&dst_clock, pps_fd, src, phc_readings);
 | 
				
			||||||
				   phc_readings, sync_offset);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dst_clock.clkid == CLOCK_REALTIME &&
 | 
						if (dst_clock.clkid == CLOCK_REALTIME &&
 | 
				
			||||||
	    SYSOFF_SUPPORTED == sysoff_probe(CLOCKID_TO_FD(src), phc_readings))
 | 
						    SYSOFF_SUPPORTED == sysoff_probe(CLOCKID_TO_FD(src), phc_readings))
 | 
				
			||||||
		return do_sysoff_loop(&dst_clock, src, phc_rate,
 | 
							return do_sysoff_loop(&dst_clock, src, phc_rate, phc_readings);
 | 
				
			||||||
				      phc_readings, sync_offset);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return do_phc_loop(&dst_clock, src, phc_rate,
 | 
						return do_phc_loop(&dst_clock, src, phc_rate, phc_readings);
 | 
				
			||||||
			   phc_readings, sync_offset);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										8
									
								
								ptp4l.8
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								ptp4l.8
									
									
									
									
									
								
							@ -351,6 +351,14 @@ empty string.
 | 
				
			|||||||
.B manufacturerIdentity
 | 
					.B manufacturerIdentity
 | 
				
			||||||
The manufacturer id which should be an OUI owned by the manufacturer.
 | 
					The manufacturer id which should be an OUI owned by the manufacturer.
 | 
				
			||||||
The default is 00:00:00.
 | 
					The default is 00:00:00.
 | 
				
			||||||
 | 
					.TP
 | 
				
			||||||
 | 
					.B kernel_leap
 | 
				
			||||||
 | 
					When a leap second is announced, let the kernel apply it by stepping the clock
 | 
				
			||||||
 | 
					instead of correcting the one-second offset with servo, which would correct the
 | 
				
			||||||
 | 
					one-second offset slowly by changing the clock frequency (unless the
 | 
				
			||||||
 | 
					.B pi_offset_const
 | 
				
			||||||
 | 
					option is set to correct such offset by stepping).
 | 
				
			||||||
 | 
					Relevant only with software time stamping. The default is 1 (enabled).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.SH SEE ALSO
 | 
					.SH SEE ALSO
 | 
				
			||||||
.BR pmc (8),
 | 
					.BR pmc (8),
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								ptp4l.c
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								ptp4l.c
									
									
									
									
									
								
							@ -53,6 +53,7 @@ static struct config cfg_settings = {
 | 
				
			|||||||
		.free_running = 0,
 | 
							.free_running = 0,
 | 
				
			||||||
		.freq_est_interval = 1,
 | 
							.freq_est_interval = 1,
 | 
				
			||||||
		.stats_interval = 0,
 | 
							.stats_interval = 0,
 | 
				
			||||||
 | 
							.kernel_leap = 1,
 | 
				
			||||||
		.clock_desc = {
 | 
							.clock_desc = {
 | 
				
			||||||
			.productDescription = {
 | 
								.productDescription = {
 | 
				
			||||||
				.max_symbols = 64,
 | 
									.max_symbols = 64,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										41
									
								
								util.c
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								util.c
									
									
									
									
									
								
							@ -22,6 +22,10 @@
 | 
				
			|||||||
#include "sk.h"
 | 
					#include "sk.h"
 | 
				
			||||||
#include "util.h"
 | 
					#include "util.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define NS_PER_SEC 1000000000LL
 | 
				
			||||||
 | 
					#define NS_PER_HOUR (3600 * NS_PER_SEC)
 | 
				
			||||||
 | 
					#define NS_PER_DAY (24 * NS_PER_HOUR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
char *ps_str[] = {
 | 
					char *ps_str[] = {
 | 
				
			||||||
	"NONE",
 | 
						"NONE",
 | 
				
			||||||
	"INITIALIZING",
 | 
						"INITIALIZING",
 | 
				
			||||||
@ -149,3 +153,40 @@ int static_ptp_text_set(struct static_ptp_text *dst, const char *src)
 | 
				
			|||||||
	dst->text[len] = '\0';
 | 
						dst->text[len] = '\0';
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int is_utc_ambiguous(uint64_t ts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* The Linux kernel inserts leap second by stepping the clock backwards
 | 
				
			||||||
 | 
						   at 0:00 UTC, the last second before midnight is played twice. */
 | 
				
			||||||
 | 
						if (NS_PER_DAY - ts % NS_PER_DAY <= NS_PER_SEC)
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int leap_second_status(uint64_t ts, int leap_set, int *leap, int *utc_offset)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int leap_status = leap_set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* The leap bits obtained by PTP should be set at most 12 hours before
 | 
				
			||||||
 | 
						   midnight and unset at most 2 announce intervals after midnight.
 | 
				
			||||||
 | 
						   Split updates which are too early and which are too late at 6 hours
 | 
				
			||||||
 | 
						   after midnight. */
 | 
				
			||||||
 | 
						if (ts % NS_PER_DAY > 6 * NS_PER_HOUR) {
 | 
				
			||||||
 | 
							if (!leap_status)
 | 
				
			||||||
 | 
								leap_status = *leap;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if (leap_status)
 | 
				
			||||||
 | 
								leap_status = 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Fix early or late update of leap and utc_offset. */
 | 
				
			||||||
 | 
						if (!*leap && leap_status) {
 | 
				
			||||||
 | 
							*utc_offset -= leap_status;
 | 
				
			||||||
 | 
							*leap = leap_status;
 | 
				
			||||||
 | 
						} else if (*leap && !leap_status) {
 | 
				
			||||||
 | 
							*utc_offset += *leap;
 | 
				
			||||||
 | 
							*leap = leap_status;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return leap_status;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										20
									
								
								util.h
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								util.h
									
									
									
									
									
								
							@ -94,4 +94,24 @@ int ptp_text_set(struct PTPText *dst, const char *src);
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
int static_ptp_text_set(struct static_ptp_text *dst, const char *src);
 | 
					int static_ptp_text_set(struct static_ptp_text *dst, const char *src);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Check if UTC time stamp can be both before and after a leap second.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param ts UTC time stamp in nanoseconds.
 | 
				
			||||||
 | 
					 * @return   0 if not, 1 if yes.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int is_utc_ambiguous(uint64_t ts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Get leap second status in given time.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param ts         UTC time stamp in nanoseconds.
 | 
				
			||||||
 | 
					 * @param leap_set   Previous leap second status (+1/0/-1).
 | 
				
			||||||
 | 
					 * @param leap       Announced leap second (+1/0/-1), will be corrected if
 | 
				
			||||||
 | 
					 *                   early/late.
 | 
				
			||||||
 | 
					 * @param utc_offset Announced UTC offset, will be corrected if early/late.
 | 
				
			||||||
 | 
					 * @return           0 if the leap second passed, +1 if leap second will be
 | 
				
			||||||
 | 
					 *                   inserted, -1 if leap second will be deleted.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int leap_second_status(uint64_t ts, int leap_set, int *leap, int *utc_offset);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user