From: Arkadiusz Kubalewski <arkadiusz.kubalew...@intel.com> Allow config interface to parse SyncE related config files.
Co-developed-by: Piotr Kwapulinski <piotr.kwapulin...@intel.com> Signed-off-by: Piotr Kwapulinski <piotr.kwapulin...@intel.com> Co-developed-by: Michal Michalik <michal.micha...@intel.com> Signed-off-by: Michal Michalik <michal.micha...@intel.com> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalew...@intel.com> --- v4: - changed order of patch in patch-series - remove 'internal_mode'/'external_input' mode parameters and change them to string parameter 'input_mode' - change 'DPLL' -> 'EEC' to be more HW agnostic - remove 'sync' parameter v3: rebase patch series v2: updated license headers config.c | 195 ++++++++++++++++++++++++++++++++++++++++++------------ config.h | 8 +++ configs/synce.cfg | 174 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 335 insertions(+), 42 deletions(-) create mode 100644 configs/synce.cfg diff --git a/config.c b/config.c index 6ba9996..fa99118 100644 --- a/config.c +++ b/config.c @@ -45,6 +45,7 @@ enum config_section { GLOBAL_SECTION, UC_MTAB_SECTION, PORT_SECTION, + DEVICE_SECTION, UNKNOWN_SECTION, }; @@ -72,6 +73,7 @@ typedef union { #define CFG_ITEM_LOCKED (1 << 1) /* command line value, may not be changed */ #define CFG_ITEM_PORT (1 << 2) /* item may appear in port sections */ #define CFG_ITEM_DYNSTR (1 << 4) /* string value dynamically allocated */ +#define CFG_ITEM_DEVICE (1 << 8) /* item may appear in device sections */ struct config_item { char label[CONFIG_LABEL_SIZE]; @@ -83,12 +85,18 @@ struct config_item { any_t max; }; -#define N_CONFIG_ITEMS (sizeof(config_tab) / sizeof(config_tab[0])) +#define N_CONFIG_ITEMS_PTP (sizeof(config_tab_ptp) / sizeof(config_tab_ptp[0])) +#define N_CONFIG_ITEMS_SYNCE ((sizeof(config_tab_synce) / \ + sizeof(config_tab_synce[0]))) + +#define PORT_TO_FLAG(_port) (_port == PORT_SECTION ? CFG_ITEM_PORT : \ + _port == DEVICE_SECTION ? CFG_ITEM_DEVICE : \ + CFG_ITEM_STATIC) #define CONFIG_ITEM_DBL(_label, _port, _default, _min, _max) { \ .label = _label, \ .type = CFG_TYPE_DOUBLE, \ - .flags = _port ? CFG_ITEM_PORT : 0, \ + .flags = PORT_TO_FLAG(_port), \ .val.d = _default, \ .min.d = _min, \ .max.d = _max, \ @@ -96,14 +104,14 @@ struct config_item { #define CONFIG_ITEM_ENUM(_label, _port, _default, _table) { \ .label = _label, \ .type = CFG_TYPE_ENUM, \ - .flags = _port ? CFG_ITEM_PORT : 0, \ + .flags = PORT_TO_FLAG(_port), \ .tab = _table, \ .val.i = _default, \ } #define CONFIG_ITEM_INT(_label, _port, _default, _min, _max) { \ .label = _label, \ .type = CFG_TYPE_INT, \ - .flags = _port ? CFG_ITEM_PORT : 0, \ + .flags = PORT_TO_FLAG(_port), \ .val.i = _default, \ .min.i = _min, \ .max.i = _max, \ @@ -111,33 +119,39 @@ struct config_item { #define CONFIG_ITEM_STRING(_label, _port, _default) { \ .label = _label, \ .type = CFG_TYPE_STRING, \ - .flags = _port ? CFG_ITEM_PORT : 0, \ + .flags = PORT_TO_FLAG(_port), \ .val.s = _default, \ } #define GLOB_ITEM_DBL(label, _default, min, max) \ - CONFIG_ITEM_DBL(label, 0, _default, min, max) + CONFIG_ITEM_DBL(label, GLOBAL_SECTION, _default, min, max) #define GLOB_ITEM_ENU(label, _default, table) \ - CONFIG_ITEM_ENUM(label, 0, _default, table) + CONFIG_ITEM_ENUM(label, GLOBAL_SECTION, _default, table) #define GLOB_ITEM_INT(label, _default, min, max) \ - CONFIG_ITEM_INT(label, 0, _default, min, max) + CONFIG_ITEM_INT(label, GLOBAL_SECTION, _default, min, max) #define GLOB_ITEM_STR(label, _default) \ - CONFIG_ITEM_STRING(label, 0, _default) + CONFIG_ITEM_STRING(label, GLOBAL_SECTION, _default) #define PORT_ITEM_DBL(label, _default, min, max) \ - CONFIG_ITEM_DBL(label, 1, _default, min, max) + CONFIG_ITEM_DBL(label, PORT_SECTION, _default, min, max) #define PORT_ITEM_ENU(label, _default, table) \ - CONFIG_ITEM_ENUM(label, 1, _default, table) + CONFIG_ITEM_ENUM(label, PORT_SECTION, _default, table) #define PORT_ITEM_INT(label, _default, min, max) \ - CONFIG_ITEM_INT(label, 1, _default, min, max) + CONFIG_ITEM_INT(label, PORT_SECTION, _default, min, max) #define PORT_ITEM_STR(label, _default) \ - CONFIG_ITEM_STRING(label, 1, _default) + CONFIG_ITEM_STRING(label, PORT_SECTION, _default) + +#define DEV_ITEM_INT(label, _default, min, max) \ + CONFIG_ITEM_INT(label, DEVICE_SECTION, _default, min, max) + +#define DEV_ITEM_STR(label, _default) \ + CONFIG_ITEM_STRING(label, DEVICE_SECTION, _default) static struct config_enum clock_servo_enu[] = { { "pi", CLOCK_SERVO_PI }, @@ -224,7 +238,7 @@ static struct config_enum bmca_enu[] = { { NULL, 0 }, }; -struct config_item config_tab[] = { +struct config_item config_tab_ptp[] = { PORT_ITEM_INT("announceReceiptTimeout", 3, 2, UINT8_MAX), PORT_ITEM_ENU("asCapable", AS_CAPABLE_AUTO, as_capable_enu), GLOB_ITEM_INT("assume_two_step", 0, 0, 1), @@ -347,7 +361,35 @@ struct config_item config_tab[] = { GLOB_ITEM_INT("write_phase_mode", 0, 0, 1), }; +struct config_item config_tab_synce[] = { + GLOB_ITEM_INT("logging_level", LOG_INFO, PRINT_LEVEL_MIN, PRINT_LEVEL_MAX), + GLOB_ITEM_STR("message_tag", NULL), + GLOB_ITEM_INT("use_syslog", 1, 0, 1), + GLOB_ITEM_STR("userDescription", ""), + GLOB_ITEM_INT("verbose", 0, 0, 1), + DEV_ITEM_STR("input_mode", "line"), + DEV_ITEM_INT("external_input_QL", 0, 0, 15), + DEV_ITEM_INT("external_input_ext_QL", 0, 0, 255), + DEV_ITEM_INT("extended_tlv", 0, 0, 1), + DEV_ITEM_INT("network_option", 1, 1, 2), + DEV_ITEM_INT("recover_time", 300, 10, 720), + DEV_ITEM_STR("eec_get_state_cmd", NULL), + DEV_ITEM_STR("eec_holdover_value", NULL), + DEV_ITEM_STR("eec_locked_ho_value", NULL), + DEV_ITEM_STR("eec_locked_value", NULL), + DEV_ITEM_STR("eec_freerun_value", NULL), + DEV_ITEM_STR("eec_invalid_value", NULL), + PORT_ITEM_STR("allowed_qls", NULL), + PORT_ITEM_STR("allowed_ext_qls", NULL), + PORT_ITEM_STR("recover_clock_enable_cmd", NULL), + PORT_ITEM_STR("recover_clock_disable_cmd", NULL), + PORT_ITEM_INT("tx_heartbeat_msec", 1000, 100, 3000), + PORT_ITEM_INT("rx_heartbeat_msec", 50, 10, 500), +}; + static struct unicast_master_table *current_uc_mtab; +static struct interface *__config_create_interface(const char *name, struct config *cfg, + const char *type); static enum parser_result parse_fault_interval(struct config *cfg, const char *section, @@ -398,6 +440,8 @@ static struct config_item *config_item_alloc(struct config *cfg, } strncpy(ci->label, name, CONFIG_LABEL_SIZE - 1); ci->type = type; + ci->val.s = NULL; + ci->flags = 0; snprintf(buf, sizeof(buf), "%s.%s", section, ci->label); if (hash_insert(cfg->htab, buf, ci)) { @@ -412,8 +456,11 @@ static struct config_item *config_item_alloc(struct config *cfg, static void config_item_free(void *ptr) { struct config_item *ci = ptr; - if (ci->type == CFG_TYPE_STRING && ci->flags & CFG_ITEM_DYNSTR) + if (ci->type == CFG_TYPE_STRING && ci->flags & CFG_ITEM_DYNSTR + && ci->val.s != NULL) { free(ci->val.s); + ci->val.s = NULL; + } if (ci->flags & CFG_ITEM_STATIC) return; free(ci); @@ -518,10 +565,13 @@ static enum parser_result parse_section_line(char *s, enum config_section *secti current_uc_mtab = NULL; } else if (s[0] == '[') { char c; - *section = PORT_SECTION; - /* Replace square brackets with white space. */ + if (s[1] == '<') + *section = DEVICE_SECTION; + else + *section = PORT_SECTION; + /* Replace brackets with white space. */ while (0 != (c = *s)) { - if (c == '[' || c == ']') + if (c == '[' || c == ']' || c == '<' || c == '>') *s = ' '; s++; } @@ -579,7 +629,8 @@ static enum parser_result parse_item(struct config *cfg, } if (section) { - if (!(cgi->flags & CFG_ITEM_PORT)) { + if (!(cgi->flags & CFG_ITEM_PORT) && + !(cgi->flags & CFG_ITEM_DEVICE)) { return NOT_PARSED; } /* Create or update this port specific item. */ @@ -607,8 +658,9 @@ static enum parser_result parse_item(struct config *cfg, dst->val.d = df; break; case CFG_TYPE_STRING: - if (dst->flags & CFG_ITEM_DYNSTR) { + if (dst->flags & CFG_ITEM_DYNSTR && dst->val.s != NULL) { free(dst->val.s); + dst->val.s = NULL; } dst->val.s = strdup(value); if (!dst->val.s) { @@ -734,18 +786,25 @@ static void check_deprecated_options(const char **option) } } -static struct option *config_alloc_longopts(void) +static struct option *config_alloc_longopts(enum feature_type type) { - struct config_item *ci; + struct config_item *ci, *ci_tab; struct option *opts; - int i; + int i, n_items; - opts = calloc(1, (1 + N_CONFIG_ITEMS) * sizeof(*opts)); + if (type == PTP) { + ci_tab = &config_tab_ptp[0]; + n_items = N_CONFIG_ITEMS_PTP; + } else { + ci_tab = &config_tab_synce[0]; + n_items = N_CONFIG_ITEMS_SYNCE; + } + opts = calloc(1, (1 + n_items) * sizeof(*opts)); if (!opts) { return NULL; } - for (i = 0; i < N_CONFIG_ITEMS; i++) { - ci = &config_tab[i]; + for (i = 0; i < n_items; i++) { + ci = &ci_tab[i]; opts[i].name = ci->label; opts[i].has_arg = required_argument; /* Avoid bug in detection of ambiguous options in glibc */ @@ -763,6 +822,8 @@ int config_read(const char *name, struct config *cfg) char buf[1024], *line, *c; const char *option, *value; struct interface *current_port = NULL; + struct interface *current_device = NULL; + bool is_synce = (cfg->type == SYNCE); int line_num; fp = 0 == strncmp(name, "-", 2) ? stdin : fopen(name, "r"); @@ -793,6 +854,7 @@ int config_read(const char *name, struct config *cfg) if (parse_section_line(line, ¤t_section) == PARSED_OK) { if (current_section == PORT_SECTION) { char port[17]; + if (1 != sscanf(line, " %16s", port)) { fprintf(stderr, "could not parse port name on line %d\n", line_num); @@ -801,6 +863,27 @@ int config_read(const char *name, struct config *cfg) current_port = config_create_interface(port, cfg); if (!current_port) goto parse_error; + if (is_synce) { + if (current_device) { + interface_se_set_parent_dev(current_port, + interface_name(current_device)); + } else { + goto parse_error; + } + } + } else if (current_section == DEVICE_SECTION) { + /* clear port on new device found in config */ + current_port = NULL; + char device[17]; + + if (sscanf(line, " %16s", device) != 1) { + fprintf(stderr, "could not parse device name on line %d\n", + line_num); + goto parse_error; + } + current_device = __config_create_interface(device, cfg, "device"); + if (!current_device) + goto parse_error; } continue; } @@ -820,14 +903,16 @@ int config_read(const char *name, struct config *cfg) if (parse_setting_line(line, &option, &value)) { fprintf(stderr, "could not parse line %d in %s section\n", line_num, current_section == GLOBAL_SECTION ? - "global" : interface_name(current_port)); + "global" : interface_name(current_port ? + current_port : current_device)); goto parse_error; } check_deprecated_options(&option); parser_res = parse_item(cfg, 0, current_section == GLOBAL_SECTION ? - NULL : interface_name(current_port), + NULL : interface_name(current_port ? + current_port : current_device), option, value); switch (parser_res) { case PARSED_OK: @@ -836,7 +921,8 @@ int config_read(const char *name, struct config *cfg) fprintf(stderr, "unknown option %s at line %d in %s section\n", option, line_num, current_section == GLOBAL_SECTION ? "global" : - interface_name(current_port)); + interface_name(current_port ? + current_port : current_device)); goto parse_error; case BAD_VALUE: fprintf(stderr, "%s is a bad value for option %s at line %d\n", @@ -862,7 +948,7 @@ parse_error: return -2; } -struct interface *config_create_interface(const char *name, struct config *cfg) +struct interface *__config_create_interface(const char *name, struct config *cfg, const char *type) { struct interface *iface; const char *ifname; @@ -876,7 +962,7 @@ struct interface *config_create_interface(const char *name, struct config *cfg) iface = interface_create(name); if (!iface) { - fprintf(stderr, "cannot allocate memory for a port\n"); + fprintf(stderr, "cannot allocate memory for a %s\n", type); return NULL; } STAILQ_INSERT_TAIL(&cfg->interfaces, iface, list); @@ -885,12 +971,17 @@ struct interface *config_create_interface(const char *name, struct config *cfg) return iface; } -struct config *config_create(void) +struct interface *config_create_interface(const char *name, struct config *cfg) +{ + return __config_create_interface(name, cfg, "port"); +} + +struct config *__config_create(enum feature_type type) { char buf[CONFIG_LABEL_SIZE + 8]; - struct config_item *ci; + struct config_item *ci, *ci_tab; struct config *cfg; - int i; + int i, end; cfg = calloc(1, sizeof(*cfg)); if (!cfg) { @@ -899,7 +990,7 @@ struct config *config_create(void) STAILQ_INIT(&cfg->interfaces); STAILQ_INIT(&cfg->unicast_master_tables); - cfg->opts = config_alloc_longopts(); + cfg->opts = config_alloc_longopts(type); if (!cfg->opts) { free(cfg); return NULL; @@ -912,9 +1003,18 @@ struct config *config_create(void) return NULL; } + cfg->type = type; + if (type == PTP) { + ci_tab = &config_tab_ptp[0]; + end = N_CONFIG_ITEMS_PTP; + } else { + ci_tab = &config_tab_synce[0]; + end = N_CONFIG_ITEMS_SYNCE; + } + /* Populate the hash table with global defaults. */ - for (i = 0; i < N_CONFIG_ITEMS; i++) { - ci = &config_tab[i]; + for (i = 0; i < end; i++) { + ci = &ci_tab[i]; ci->flags |= CFG_ITEM_STATIC; snprintf(buf, sizeof(buf), "global.%s", ci->label); if (hash_insert(cfg->htab, buf, ci)) { @@ -924,12 +1024,12 @@ struct config *config_create(void) } /* Perform a Built In Self Test.*/ - for (i = 0; i < N_CONFIG_ITEMS; i++) { - ci = &config_tab[i]; + for (i = 0; i < end; i++) { + ci = &ci_tab[i]; ci = config_global_item(cfg, ci->label); - if (ci != &config_tab[i]) { + if (ci != &ci_tab[i]) { fprintf(stderr, "config BIST failed at %s\n", - config_tab[i].label); + ci_tab[i].label); goto fail; } } @@ -941,6 +1041,16 @@ fail: return NULL; } +struct config *config_create(void) +{ + return __config_create(PTP); +} + +struct config *config_create_synce(void) +{ + return __config_create(SYNCE); +} + void config_destroy(struct config *cfg) { struct unicast_master_address *address; @@ -1143,8 +1253,9 @@ int config_set_string(struct config *cfg, const char *option, return -1; } ci->flags |= CFG_ITEM_LOCKED; - if (ci->flags & CFG_ITEM_DYNSTR) { + if (ci->flags & CFG_ITEM_DYNSTR && ci->val.s != NULL) { free(ci->val.s); + ci->val.s = NULL; } ci->val.s = strdup(val); if (!ci->val.s) { diff --git a/config.h b/config.h index 14d2f64..a32e007 100644 --- a/config.h +++ b/config.h @@ -32,10 +32,16 @@ #include "servo.h" #include "sk.h" +enum feature_type { + PTP = 0, + SYNCE +}; + struct config { /* configured interfaces */ STAILQ_HEAD(interfaces_head, interface) interfaces; int n_interfaces; + enum feature_type type; /* for parsing command line options */ struct option *opts; @@ -55,6 +61,8 @@ void config_destroy(struct config *cfg); struct config *config_create(void); +struct config *config_create_synce(void); + double config_get_double(struct config *cfg, const char *section, const char *option); diff --git a/configs/synce.cfg b/configs/synce.cfg new file mode 100644 index 0000000..b368876 --- /dev/null +++ b/configs/synce.cfg @@ -0,0 +1,174 @@ +# Global section is for debuging mostly +[global] +# +# Runtime options +# +logging_level 7 +use_syslog 0 +verbose 1 +message_tag [synce4l] + + +# +# Device section +# Per-device configuration +# +# User defined name of a one logical device configured for SyncE in the system. +# All the ports configured after this section will be a part of this device +# (until next device section). +[<synce1>] + +# +# Mode for synce4l operation. +# There are two currently supported modes: +# - line +# - external +# +# Input mode "line" mean the inputs recovered from the PHY's. +# The ports configured (in the port-sections [<dev name>], under the device +# section) will be monitored for the QL (Quality Level). +# QL is sent by the peer connected to the port and represents the Holdover +# performance of the peer. +# The best QL is selected and frequency recovered on that port shall be used +# to feed its frequency to all the other ports. +# +# The "external" input mode are either 1PPS from built-in GPS module or 1PPS +# from the on-board SMA connectors. Device must be pre-confiured to use this +# setting. # Before running the application, one of the external inputs shall be +# selected. This is done through the interface supplied by the NIC vendor. +# +# In this mode synce4l application is only responsible for sending +# the QL to the peers on all configured ports (where "sync = 1") +# The QL value that is sent equals configured "external_input_QL" +# (and "external_input_ext_QL" in case of "extended_tlv=1") +# +input_mode line +# input_mode external + +# +# These values are sent to the peers on configured ports ONLY when 'external' +# mode is enabled. +# Valid values are defined in Table 11-7 and Table 11-8 of recommendation +# ITU-T G.8264. +# They shall be configured appropriately so they are understood by the peer. +# +# external_input_QL corresponds to the SSM code column. +# +# external_input_ext_QL corresponds to the Enhanced SSM code column +# (is used only if "extended_tlv = 1") +# +external_input_QL 2 +external_input_ext_QL 255 + +# +# If extended TLV shall be supported on the device. +# 0 if no extended tlv shall be supported +# 1 if extended tlv shall be supported +# default: 0 +# +# In case of 0: +# - the port will always TX the non-extended TLV, for RX only +# non-extended TLV will be processed for reference signal selection +# In case of 1: +# - If port is configured with external_input=1, the TX will always use +# extended TLV (no RX is required in this case) +# - If port is configured with external_input=0 and internal_input=1, the +# TX version of TLV will be propagated from the port that was chosen as +# candidate for frequency synchronization +# +extended_tlv 1 + +# +# Which network option shall be supported +# +# 1 or 2 as defined in T-REC-G.8264 +# default: 1 +# +# This is rather per-network option, all device in SyncE network +# shall have this configured for the same value +# +network_option 1 + +# +# Seconds indicating minimum time to recover from the QL-failed state on the +# port. +# Range: 10-720 +# Default: 300 +# +# If valid QL was not received from one of the source ports within 5 seconds +# the port is no longer a valid source (marked as QL-failed) +# +# Valid QL must be received for more then "recover_time" seconds on that port +# to use its PHY recovered signal again as a valid source +# +recover_time 10 + +# +# Shell command to be executed in order to obtain current EEC status of a +# device. +# +eec_get_state_cmd cat /sys/class/net/enp1s0f0/device/cgu_state + +# +# EEC state values, must equal to values produced by stdout of +# "eec_get_state_cmd" command +# +eec_holdover_value 4 +eec_locked_ho_value 3 +eec_locked_value 2 +eec_freerun_value 1 +eec_invalid_value 0 + +# +# Port section(s) +# +# It starts per-port configuration. +# Each port (of the device) that is used for SyncE, shall have its own section +# with at least sync = 1 (which defines port as synchronous mode) +# +[enp1s0f0] + +# +# msec resolution of TX the QL from this port to the peer +# [100-3000], default:1000 (1000 = 1 second is expected by the standard) +# +# As the standard expects 1 sec, it is not recommended to use different +# than a 1000. +# +tx_heartbeat_msec 2000 + +# +# recovered PHY signal can be lost at anytime, this is msec resolution of +# reading the socket, acting on signal lost shall be done just after +# [10-500], default:50 +# +rx_heartbeat_msec 500 + +# +# Shell commands for enabling/disabling this port as main recovered clock on a +# device. +# +recover_clock_enable_cmd echo 1 0 > /sys/class/net/enp1s0f0/device/phy/synce +recover_clock_disable_cmd echo 0 0 > /sys/class/net/enp1s0f0/device/phy/synce + +# +# next configured interface for the device +# +[enp1s0f1] +sync 1 +recover_clock_enable_cmd echo 1 0 > /sys/class/net/enp1s0f1/device/phy/synce +recover_clock_disable_cmd echo 0 0 > /sys/class/net/enp1s0f1/device/phy/synce + + +############################################################ +# +# next SyncE device section +# +#[<synce2>] +#internal_input 0 +#external_input 1 + +# +# new port belonging to the "new" device +# +#[enp7s0f0] -- 2.9.5 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel