This commit enables phc2sys to read the configuration file. A new
phc2sys section is added for specific options. Those options which carry
over from ptp4l are read from both global and (with higher precedence)
phc2sys section. I did not go to the extreme effort of refactoring
configuration to be more generic, so there is some level of duplicated
code. I couldn't find an easy way to make the whole config process more
generic without much more re-writing.
Signed-off-by: Jacob Keller <***@intel.com>
---
makefile | 2 +-
phc2sys.8 | 174 +++++++++++++++++++++++-
phc2sys.c | 454 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 626 insertions(+), 4 deletions(-)
diff --git a/makefile b/makefile
index 5469301af259..2a4ad9b6de2e 100644
--- a/makefile
+++ b/makefile
@@ -51,7 +51,7 @@ pmc: msg.o pmc.o pmc_common.o print.o raw.o sk.o tlv.o transport.o udp.o \
phc2sys: clockadj.o clockcheck.o linreg.o msg.o ntpshm.o phc.o phc2sys.o pi.o \
pmc_common.o print.o raw.o servo.o sk.o stats.o sysoff.o tlv.o \
- transport.o udp.o udp6.o uds.o util.o version.o
+ transport.o udp.o udp6.o uds.o util.o version.o config.o
hwstamp_ctl: hwstamp_ctl.o version.o
diff --git a/phc2sys.8 b/phc2sys.8
index 22d02c24db7a..ae4faa8d45b8 100644
--- a/phc2sys.8
+++ b/phc2sys.8
@@ -3,6 +3,9 @@
phc2sys \- synchronize two or more clocks
.SH SYNOPSIS
+.B phc2sys \-f
+[ options ]
+.br
.B phc2sys \-a
[
.B \-r
@@ -33,7 +36,7 @@ program.
With the
.B \-a
-option, the clocks to synchronize are fetched from the running
+option (automatic mode), the clocks to synchronize are fetched from the running
.B ptp4l
daemon and the direction of synchronization automatically follows changes of
the PTP port states.
@@ -47,6 +50,9 @@ and driver implementation.
.SH OPTIONS
.TP
+.BI \-f
+Read configuration from the specified file. No configuration is read by default.
+.TP
.BI \-a
Read the clocks to synchronize from running
.B ptp4l
@@ -218,6 +224,172 @@ Display a help message.
.B \-v
Prints the software version and exits.
+.SH CONFIGURATION FILE
+
+The configuration file is divided into sections, and may be shared with ptp4l.
+Each section starts with a line containing its name enclosed in brackets and it
+follows with settings. Each setting is placed on a separate line, it contains
+the name of the option and the value separated by whitespace characters. Empty
+lines and lines starting with # are ignored.
+
+The global section (indicated as
+.BR [global] )
+sets generic program options mostly used by ptp4l, as well as some options
+shared by phc2sys. Note that only the options relevant to phc2sys are outlined
+here. See ptp4l(8) for options relevant to the ptp4l program.
+
+.SH GENERAL PROGAM OPTIONS
+
+.TP
+.B domainNumber
+The domain attribute of the local clock.
+The default is 0.
+.TP
+.B clock_servo
+The servo which is used to synchronize the local clock. Valid values are pi for
+a PI controller, linreg for an adaptive controller using linear regression, and
+ntpshm for the NTP SHM reference clock to allow another process to synchronize
+the local clock (the SHM segment number is set to the domain number).
+The default is pi.
+.TP
+.B pi_proportional_const
+The proportional constant of the PI controller. When set to 0.0, the
+proportional constant will be set by the following formula from the current
+sync interval.
+The default is 0.0.
+
+kp = min(kp_scale * sync^kp_exponent, kp_norm_max / sync))
+.TP
+.B pi_integral_const
+The integral constant of the PI controller. When set to 0.0, the
+integral constant will be set by the following formula from the current
+sync interval.
+The default is 0.0.
+
+ki = min(ki_scale * sync^ki_exponent, ki_norm_max / sync)
+.TP
+.B step_threshold
+The maximum offset the servo will correct by changing the clock
+frequency instead of stepping the clock. When set to 0.0, the servo will
+never step the clock except on start. It's specified in seconds.
+The default is 0.0.
+This option used to be called
+.BR pi_offset_const .
+.TP
+.B first_step_threshold
+The maximum offset the servo will correct by changing the clock
+frequency instead of stepping the clock. This is only applied on the first
+update. It's specified in seconds. When set to 0.0, the servo won't step
+the clock on start.
+The default is 0.00002 (20 microseconds).
+This option used to be called
+.BR pi_f_offset_const .
+.TP
+.B sanity_freq_limit
+The maximum allowed frequency offset between uncorrected clock and the system
+monotonic clock in parts per billion (ppb). This is used as a sanity check of
+the synchronized clock. When a larger offset is measured, a warning message
+will be printed and the servo will be reset. When set to 0, the sanity check is
+disabled. The default is 200000000 (20%).
+.TP
+.B ntpshm_segment
+The number of the SHM segment used by ntpshm servo.
+The default is 0.
+.TP
+.B uds_address
+Specifies the address of the UNIX domain socket for connecting to the local
+ptp4l(8) instance. The default is /var/run/ptp4l.
+.TP
+.B logging_level
+The maximum logging level of messages which should be printed.
+The default is 6 (LOG_INFO).
+.TP
+.B verbose
+Print messages to the standard output if enabled.
+The default is 0 (disabled).
+.TP
+.B use_syslog
+Print messages to the system log if enabled.
+The default is 1 (enabled).
+.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 step_threshold
+option is set to correct such offset by stepping).
+Relevant only with software time stamping. The default is 1 (enabled).
+
+.SH PHC2SYS SPECIFIC OPTIONS
+
+Note that the general options above may be re-specified in the
+.B [phc2sys]
+section. In this case, the value will override any setting on the
+.B [global]
+section. This is to allow use of a single configuration file for both phc2sys
+and ptp4l if desired.
+
+.TP
+.B autoconfig
+Enable automatic configuration mode. Default is 0 (disabled). See above section for more details.
+
+.TP
+.B realtime_mode
+Set to
+.BI ignore
+to disable use of CLOCK_REALTIME. Set to
+.BI sync
+to sync CLOCK_REALTIME system clock. Set to
+.BI follow
+to enable CLOCK_REALTIME as a possible time source. Only valid when in automatic configuration mode. Default is "ignore". See also
+.BI \-r
+option above for more details.
+
+.TP
+.B slave_clock
+Set clock device to use as slave in manual mode. No effect when in automatic mode.
+
+.TP
+.B master_clock
+Set clock device to use as master when in manual mode. No effect when in automatic mode.
+
+.TP
+.B master_pps
+Set the PPS device to use as the PPS signal for the master clock. Disabled by default.
+
+.TP
+.B wait_for_ptp4l
+Specify whether to wait for ptp4l to synchronize before beginning adjustment.
+Default is disabled.
+
+.TP
+.B update_rate
+Specify the slave clock update rate when running in the direct synchronization
+mode. Default is 1 per second.
+
+.TP
+.B phc_readings
+Specify the number of master clock readings per one slave clock update. Only
+the fastest reading is used to update the slave clock. This may minimize error
+caused by random delays in scheduling and bus utilization. Default is 5.
+
+.TP
+.B sync_offset
+Specify the offset between the slave and master time in seconds. Note this option is not compatible with automatic configuration. See
+.SM
+.B TIME SCALE USAGE
+below.
+
+.TP
+.BI summary_updates
+Specify the number of clock updates included in summary statistics. The
+statistics include offset root mean square (RMS), maximum absolute offset,
+frequency offset mean and standard deviation, and mean of the delay in clock
+readings and standard deviation. The units are nanoseconds and parts per
+billion (ppb). If zero, the individual samples are printed instead of the
+statistics. The messages are printed at the LOG_INFO level.
+The default is 0 (disabled).
+
.SH TIME SCALE USAGE
.B Ptp4l
diff --git a/phc2sys.c b/phc2sys.c
index d59814d3159f..029bfc1e38e4 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -19,6 +19,7 @@
*/
#include <errno.h>
#include <fcntl.h>
+#include <ctype.h>
#include <float.h>
#include <inttypes.h>
#include <limits.h>
@@ -56,6 +57,7 @@
#include "uds.h"
#include "util.h"
#include "version.h"
+#include "config.h"
#define KP 0.7
#define KI 0.3
@@ -1170,6 +1172,398 @@ static int clock_handle_leap(struct node *node, struct clock *clock,
return 0;
}
+#define CFG_IGNORE_AUTOCFG (1 << 7)
+#define CFG_IGNORE_REALTIME (1 << 8)
+#define CFG_IGNORE_SLAVE_CLOCK (1 << 9)
+#define CFG_IGNORE_MASTER_PPS (1 << 10)
+#define CFG_IGNORE_MASTER_CLOCK (1 << 11)
+#define CFG_IGNORE_CLOCK_SERVO (1 << 12)
+#define CFG_IGNORE_PI_KP (1 << 13)
+#define CFG_IGNORE_PI_KI (1 << 14)
+#define CFG_IGNORE_STEP_THRESHOLD (1 << 15)
+#define CFG_IGNORE_FIRST_STEP_THRESHOLD (1 << 16)
+#define CFG_IGNORE_PHC_RATE (1 << 17)
+#define CFG_IGNORE_PHC_READINGS (1 << 18)
+#define CFG_IGNORE_SYNC_OFFSET (1 << 19)
+#define CFG_IGNORE_FREQ_LIMIT (1 << 20)
+#define CFG_IGNORE_NTPSHM_SEGMENT (1 << 21)
+#define CFG_IGNORE_STATS_MAX_COUNT (1 << 22)
+#define CFG_IGNORE_WAIT_SYNC (1 << 23)
+#define CFG_IGNORE_DOMAIN_NUMBER (1 << 24)
+#define CFG_IGNORE_KERNEL_LEAP (1 << 25)
+#define CFG_IGNORE_UDS_ADDRESS (1 << 26)
+
+struct phc_config {
+ /* configuration override */
+ int cfg_ignore;
+
+ struct node *node;
+
+ /* automatic mode */
+ int *autoconfig;
+ int *realtime;
+
+ /* manual mode */
+ char **slave_clock;
+ char **master_clock;
+ int *master_pps_fd;
+ int *wait_for_ptp4l;
+
+ double *step_threshold;
+ double *first_step_threshold;
+
+ double *pi_proportional_const;
+ double *pi_integral_const;
+ int *ntpshm_segment;
+
+ int *domain_number;
+
+ char *uds_address;
+
+ int *print_level;
+ int *use_syslog;
+ int *verbose;
+};
+
+static enum parser_result parse_phc2sys_global_setting(const char *option,
+ const char *value,
+ struct phc_config *cfg,
+ int ignore)
+{
+ double df;
+ int val;
+ unsigned int uval;
+
+ enum parser_result r;
+
+ if (!strcmp(option, "domainNumber")) {
+ r = get_ranged_uint(value, &uval, 0, 127);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_DOMAIN_NUMBER))
+ *cfg->domain_number = uval;
+ if (ignore)
+ cfg->cfg_ignore |= CFG_IGNORE_DOMAIN_NUMBER;
+
+ } else if (!strcmp(option, "pi_proportional_const")) {
+ r = get_ranged_double(value, &df, 0.0, DBL_MAX);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_PI_KP))
+ *cfg->pi_proportional_const = df;
+ if (ignore)
+ cfg->cfg_ignore |= CFG_IGNORE_PI_KP;
+
+ } else if (!strcmp(option, "pi_integral_const")) {
+ r = get_ranged_double(value, &df, 0.0, DBL_MAX);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_PI_KI))
+ *cfg->pi_integral_const = df;
+ if (ignore)
+ cfg->cfg_ignore |= CFG_IGNORE_PI_KI;
+
+ } else if (!strcmp(option, "step_threshold")) {
+ r = get_ranged_double(value, &df, 0.0, DBL_MAX);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_STEP_THRESHOLD))
+ *cfg->step_threshold = df;
+ if (ignore)
+ cfg->cfg_ignore |= CFG_IGNORE_STEP_THRESHOLD;
+
+ } else if (!strcmp(option, "first_step_threshold")) {
+ r = get_ranged_double(value, &df, 0.0, DBL_MAX);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_FIRST_STEP_THRESHOLD))
+ *cfg->first_step_threshold = df;
+ if (ignore)
+ cfg->cfg_ignore |= CFG_IGNORE_FIRST_STEP_THRESHOLD;
+
+ } else if (!strcmp(option, "sanity_freq_limit")) {
+ r = get_ranged_int(value, &val, 0, INT_MAX);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_FREQ_LIMIT))
+ cfg->node->sanity_freq_limit = val;
+ if (ignore)
+ cfg->cfg_ignore |= CFG_IGNORE_FREQ_LIMIT;
+
+ } else if (!strcmp(option, "ntpshm_segment")) {
+ r = get_ranged_int(value, &val, INT_MIN, INT_MAX);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_NTPSHM_SEGMENT))
+ *cfg->ntpshm_segment = val;
+ if (ignore)
+ cfg->cfg_ignore |= CFG_IGNORE_NTPSHM_SEGMENT;
+
+ } else if (!strcmp(option, "uds_address")) {
+ if (strlen(value) > MAX_IFNAME_SIZE)
+ return OUT_OF_RANGE;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_UDS_ADDRESS))
+ strncpy(cfg->uds_address, value, MAX_IFNAME_SIZE);
+ if (ignore)
+ cfg->cfg_ignore |= CFG_IGNORE_UDS_ADDRESS;
+
+ } else if (!strcmp(option, "logging_level")) {
+ r = get_ranged_int(value, &val,
+ PRINT_LEVEL_MIN, PRINT_LEVEL_MAX);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_PRINT_LEVEL))
+ *cfg->print_level = val;
+ if (ignore)
+ cfg->cfg_ignore |= CFG_IGNORE_PRINT_LEVEL;
+
+ } else if (!strcmp(option, "verbose")) {
+ r = get_ranged_int(value, &val, 0, 1);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_VERBOSE))
+ *cfg->verbose = val;
+ if (ignore)
+ cfg->cfg_ignore |= CFG_IGNORE_VERBOSE;
+
+ } else if (!strcmp(option, "use_syslog")) {
+ r = get_ranged_int(value, &val, 0, 1);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_USE_SYSLOG))
+ *cfg->use_syslog = val;
+ if (ignore)
+ cfg->cfg_ignore |= CFG_IGNORE_USE_SYSLOG;
+
+ } else if (!strcmp(option, "clock_servo")) {
+ if (!(cfg->cfg_ignore & CFG_IGNORE_CLOCK_SERVO)) {
+ if (!strcasecmp("pi", value))
+ cfg->node->servo_type = CLOCK_SERVO_PI;
+ else if (!strcasecmp("linreg", value))
+ cfg->node->servo_type = CLOCK_SERVO_LINREG;
+ else if (!strcasecmp("ntpshm", value))
+ cfg->node->servo_type = CLOCK_SERVO_NTPSHM;
+ else
+ return BAD_VALUE;
+ }
+ if (ignore)
+ cfg->cfg_ignore |= CFG_IGNORE_CLOCK_SERVO;
+
+ } else if (!strcmp(option, "kernel_leap")) {
+ r = get_ranged_int(value, &val, 0, 1);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_KERNEL_LEAP))
+ cfg->node->kernel_leap = val;
+ if (ignore)
+ cfg->cfg_ignore |= CFG_IGNORE_KERNEL_LEAP;
+
+ } else
+ return NOT_PARSED;
+
+ return PARSED_OK;
+}
+
+static enum parser_result parse_phc2sys_setting(const char *option,
+ const char *value,
+ struct phc_config *cfg)
+{
+ double df;
+ int i, val;
+ unsigned uval;
+
+ enum parser_result r;
+
+ r = parse_phc2sys_global_setting(option, value, cfg, 1);
+ if (r != NOT_PARSED)
+ return r;
+
+ if (!strcmp(option, "autoconfig")) {
+ r = get_ranged_uint(value, &uval, 0, 1);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_AUTOCFG))
+ *cfg->autoconfig = uval;
+
+ } else if (!strcmp(option, "realtime_mode")) {
+ if (!(cfg->cfg_ignore & CFG_IGNORE_REALTIME)) {
+ if (!strcasecmp("ignore", value))
+ *cfg->realtime = 0;
+ else if (!strcasecmp("sync", value))
+ *cfg->realtime = 1;
+ else if(!strcasecmp("follow", value))
+ *cfg->realtime = 2;
+ else
+ return BAD_VALUE;
+ }
+
+ } else if (!strcmp(option, "slave_clock")) {
+ if (strlen(value) > MAX_IFNAME_SIZE)
+ return OUT_OF_RANGE;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_SLAVE_CLOCK))
+ strncpy(*cfg->slave_clock, value, MAX_IFNAME_SIZE);
+
+ } else if (!strcmp(option, "master_clock")) {
+ if (strlen(value) > MAX_IFNAME_SIZE)
+ return OUT_OF_RANGE;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_MASTER_CLOCK))
+ strncpy(*cfg->master_clock, value, MAX_IFNAME_SIZE);
+
+ } else if (!strcmp(option, "master_pps")) {
+ if (strlen(value) > MAX_IFNAME_SIZE)
+ return OUT_OF_RANGE;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_MASTER_PPS)) {
+ i = open(value, O_RDONLY);
+ if (i < 0) {
+ fprintf(stderr, "cannot open '%s': %m\n", value);
+ return BAD_VALUE;
+ }
+ *cfg->master_pps_fd = i;
+ }
+
+ } else if (!strcmp(option, "wait_for_ptp4l")) {
+ r = get_ranged_uint(value, &uval, 0, 1);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_WAIT_SYNC))
+ *cfg->wait_for_ptp4l = uval;
+
+ } else if (!strcmp(option, "update_rate")) {
+ r = get_ranged_double(value, &df, 1e-9, DBL_MAX);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_PHC_RATE))
+ cfg->node->phc_interval = 1.0 / df;
+
+ } else if (!strcmp(option, "phc_readings")) {
+ r = get_ranged_uint(value, &uval, 1, INT_MAX);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_PHC_READINGS))
+ cfg->node->phc_readings = uval;
+
+ } else if (!strcmp(option, "sync_offset")) {
+ r = get_ranged_int(value, &val, INT_MIN, INT_MAX);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_SYNC_OFFSET)) {
+ cfg->node->sync_offset = val;
+ cfg->node->forced_sync_offset = -1;
+ }
+
+ } else if (!strcmp(option, "summary_updates")) {
+ r = get_ranged_uint(value, &uval, 0, UINT_MAX);
+ if (r != PARSED_OK)
+ return r;
+ if (!(cfg->cfg_ignore & CFG_IGNORE_STATS_MAX_COUNT))
+ cfg->node->stats_max_count = uval;
+
+ } else
+ return NOT_PARSED;
+
+ return PARSED_OK;
+}
+
+static int phc_config_read(char *name, struct phc_config *cfg)
+{
+ enum config_section current_section = UNKNOWN_SECTION;
+ enum parser_result parser_res;
+ FILE *fp;
+ char buf[1024], *line, *c;
+ char *section_name;
+ const char *option, *value;
+ int line_num;
+
+ fp = 0 == strncmp(name, "-", 2) ? stdin : fopen(name, "r");
+
+ if (!fp) {
+ fprintf(stderr, "failed to open configuration file %s: %m\n", name);
+ return -1;
+ }
+
+ for (line_num = 1; fgets(buf, sizeof(buf), fp); line_num++) {
+ c = buf;
+
+ /* skip whitespace characters */
+ while(isspace(*c))
+ c++;
+
+ /* ignore empty lines and comments */
+ if (*c == '#' || *c == '\n' || *c == '\0')
+ continue;
+
+ line = c;
+
+ /* remote trailing whitespace characters and newlines */
+ c += strlen(line) - 1;
+ while (c > line && (*c == '\n' || isspace(*c)))
+ *c-- = '\0';
+
+ /* parse section lines */
+ if (parse_section_line(line, ¤t_section) == PARSED_OK)
+ continue;
+
+ if (parse_setting_line(line, &option, &value)) {
+ fprintf(stderr, "could not parse line %d\n",
+ line_num);
+ goto parse_error;
+ }
+
+ check_deprecated_options(&option);
+
+ switch (current_section) {
+ case GLOBAL_SECTION:
+ section_name = "global";
+ parser_res = parse_phc2sys_global_setting(option, value, cfg, 0);
+ if (parser_res == NOT_PARSED)
+ parser_res = parse_known_global_options(option);
+
+ break;
+ case PHC2SYS_SECTION:
+ section_name = "phy2sys";
+ parser_res = parse_phc2sys_setting(option, value, cfg);
+ break;
+ case UNKNOWN_SECTION:
+ fprintf(stderr, "line %d is not in a section\n", line_num);
+ goto parse_error;
+ /* ignore per-port settings*/
+ case PORT_SECTION:
+ default:
+ continue;
+ }
+
+ switch (parser_res) {
+ case PARSED_OK:
+ break;
+ case NOT_PARSED:
+ fprintf(stderr, "unknown option %s at line %d in %s section\n",
+ option, line_num,
+ section_name);
+ goto parse_error;
+ case BAD_VALUE:
+ fprintf(stderr, "%s is a bad value for option %s at line %d\n",
+ value, option, line_num);
+ goto parse_error;
+ case MALFORMED:
+ fprintf(stderr, "%s is a malformed value for option %s at line %d\n",
+ value, option, line_num);
+ goto parse_error;
+ case OUT_OF_RANGE:
+ fprintf(stderr, "%s is an out of range value for option %s at line %d\n",
+ value, option, line_num);
+ goto parse_error;
+ }
+ }
+
+ fclose(fp);
+ return 0;
+
+parse_error:
+ fprintf(stderr, "failed to parse configuration file %s\n", name);
+ fclose(fp);
+ return -2;
+}
+
static void usage(char *progname)
{
fprintf(stderr,
@@ -1187,6 +1581,7 @@ static void usage(char *progname)
" -O [offset] slave-master time offset (0)\n"
" -w wait for ptp4l\n"
" common options:\n"
+ " -f [file] read configuration from 'file'\n"
" -E [pi|linreg] clock servo (pi)\n"
" -P [kp] proportional constant (0.7)\n"
" -I [ki] integration constant (0.3)\n"
@@ -1211,7 +1606,7 @@ static void usage(char *progname)
int main(int argc, char *argv[])
{
- char *progname;
+ char *progname, *config;
char *src_name = NULL, *dst_name = NULL;
struct clock *src, *dst;
int autocfg = 0, rt = 0;
@@ -1227,6 +1622,31 @@ int main(int argc, char *argv[])
.kernel_leap = 1,
};
+ struct phc_config cfg_settings = {
+ .node = &node,
+ .autoconfig = &autocfg,
+ .realtime = &rt,
+
+ .wait_for_ptp4l = &wait_sync,
+ .slave_clock = &dst_name,
+ .master_clock = &src_name,
+ .master_pps_fd = &pps_fd,
+
+ .pi_proportional_const = &configured_pi_kp,
+ .pi_integral_const = &configured_pi_ki,
+ .step_threshold = &servo_step_threshold,
+ .first_step_threshold = &servo_first_step_threshold,
+
+ .ntpshm_segment = &ntpshm_segment,
+ .domain_number = &domain_number,
+
+ .uds_address = uds_path,
+
+ .print_level = &print_level,
+ .verbose = &verbose,
+ .use_syslog = &use_syslog,
+ };
+
handle_term_signals();
configured_pi_kp = KP;
@@ -1236,16 +1656,19 @@ int main(int argc, char *argv[])
progname = strrchr(argv[0], '/');
progname = progname ? 1+progname : argv[0];
while (EOF != (c = getopt(argc, argv,
- "arc:d:s:E:P:I:S:F:R:N:O:L:M:i:u:wn:xz:l:mqvh"))) {
+ "arc:d:s:f:E:P:I:S:F:R:N:O:L:M:i:u:wn:xz:l:mqvh"))) {
switch (c) {
case 'a':
autocfg = 1;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_AUTOCFG;
break;
case 'r':
rt++;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_REALTIME;
break;
case 'c':
dst_name = strdup(optarg);
+ cfg_settings.cfg_ignore |= CFG_IGNORE_SLAVE_CLOCK;
break;
case 'd':
pps_fd = open(optarg, O_RDONLY);
@@ -1254,12 +1677,17 @@ int main(int argc, char *argv[])
"cannot open '%s': %m\n", optarg);
return -1;
}
+ cfg_settings.cfg_ignore |= CFG_IGNORE_MASTER_PPS;
break;
case 'i':
fprintf(stderr,
"'-i' has been deprecated. please use '-s' instead.\n");
case 's':
src_name = strdup(optarg);
+ cfg_settings.cfg_ignore |= CFG_IGNORE_MASTER_CLOCK;
+ break;
+ case 'f':
+ config = optarg;
break;
case 'E':
if (!strcasecmp(optarg, "pi")) {
@@ -1273,64 +1701,78 @@ int main(int argc, char *argv[])
"invalid servo name %s\n", optarg);
return -1;
}
+ cfg_settings.cfg_ignore |= CFG_IGNORE_CLOCK_SERVO;
break;
case 'P':
if (get_arg_val_d(c, optarg, &configured_pi_kp,
0.0, DBL_MAX))
return -1;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_PI_KP;
break;
case 'I':
if (get_arg_val_d(c, optarg, &configured_pi_ki,
0.0, DBL_MAX))
return -1;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_PI_KI;
break;
case 'S':
if (get_arg_val_d(c, optarg, &servo_step_threshold,
0.0, DBL_MAX))
return -1;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_STEP_THRESHOLD;
break;
case 'F':
if (get_arg_val_d(c, optarg, &servo_first_step_threshold,
0.0, DBL_MAX))
return -1;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_FIRST_STEP_THRESHOLD;
break;
case 'R':
if (get_arg_val_d(c, optarg, &phc_rate, 1e-9, DBL_MAX))
return -1;
node.phc_interval = 1.0 / phc_rate;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_PHC_RATE;
break;
case 'N':
if (get_arg_val_i(c, optarg, &node.phc_readings, 1, INT_MAX))
return -1;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_PHC_READINGS;
break;
case 'O':
if (get_arg_val_i(c, optarg, &node.sync_offset,
INT_MIN, INT_MAX))
return -1;
node.forced_sync_offset = -1;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_SYNC_OFFSET;
break;
case 'L':
if (get_arg_val_i(c, optarg, &node.sanity_freq_limit, 0, INT_MAX))
return -1;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_FREQ_LIMIT;
break;
case 'M':
if (get_arg_val_i(c, optarg, &ntpshm_segment, INT_MIN, INT_MAX))
return -1;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_NTPSHM_SEGMENT;
break;
case 'u':
if (get_arg_val_ui(c, optarg, &node.stats_max_count,
0, UINT_MAX))
return -1;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_STATS_MAX_COUNT;
break;
case 'w':
wait_sync = 1;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_WAIT_SYNC;
break;
case 'n':
if (get_arg_val_i(c, optarg, &domain_number, 0, 255))
return -1;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_DOMAIN_NUMBER;
break;
case 'x':
node.kernel_leap = 0;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_KERNEL_LEAP;
break;
case 'z':
if (strlen(optarg) > MAX_IFNAME_SIZE) {
@@ -1339,17 +1781,21 @@ int main(int argc, char *argv[])
return -1;
}
strncpy(uds_path, optarg, MAX_IFNAME_SIZE);
+ cfg_settings.cfg_ignore |= CFG_IGNORE_UDS_ADDRESS;
break;
case 'l':
if (get_arg_val_i(c, optarg, &print_level,
PRINT_LEVEL_MIN, PRINT_LEVEL_MAX))
return -1;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_PRINT_LEVEL;
break;
case 'm':
verbose = 1;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_VERBOSE;
break;
case 'q':
use_syslog = 0;
+ cfg_settings.cfg_ignore |= CFG_IGNORE_USE_SYSLOG;
break;
case 'v':
version_show(stdout);
@@ -1362,6 +1808,10 @@ int main(int argc, char *argv[])
}
}
+ if (config && (c = phc_config_read(config, &cfg_settings))) {
+ return c;
+ }
+
if (autocfg && (src_name || dst_name || pps_fd >= 0 || wait_sync || node.forced_sync_offset)) {
fprintf(stderr,
"autoconfiguration cannot be mixed with manual config options.\n");
--
2.1.2.555.gfbecd99