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 <jacob.e.kel...@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, &current_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


------------------------------------------------------------------------------
Dive into the World of Parallel Programming! The Go Parallel Website,
sponsored by Intel and developed in partnership with Slashdot Media, is your
hub for all things parallel software development, from weekly thought
leadership blogs to news, videos, case studies, tutorials and more. Take a
look and join the conversation now. http://goparallel.sourceforge.net
_______________________________________________
Linuxptp-devel mailing list
Linuxptp-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel

Reply via email to