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>
---
config.c | 198 ++++++++++++++++++++++++++++++++++++----------
config.h | 8 ++
configs/synce.cfg | 194 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 358 insertions(+), 42 deletions(-)
create mode 100644 configs/synce.cfg
diff --git a/config.c b/config.c
index 4f3ceb8a20d7..f0ad329a7f18 100644
--- a/config.c
+++ b/config.c
@@ -41,6 +41,7 @@ enum config_section {
GLOBAL_SECTION,
UC_MTAB_SECTION,
PORT_SECTION,
+ DEVICE_SECTION,
UNKNOWN_SECTION,
};
@@ -68,6 +69,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];
@@ -79,12 +81,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, \
@@ -92,14 +100,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, \
@@ -107,33 +115,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 },
@@ -220,7 +234,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),
@@ -341,7 +355,37 @@ 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_INT("internal_input", 1, 0, 1),
+ DEV_ITEM_INT("external_input", 0, 0, 1),
+ 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("dpll_get_state_cmd", NULL),
+ DEV_ITEM_STR("dpll_holdover_value", NULL),
+ DEV_ITEM_STR("dpll_locked_ho_value", NULL),
+ DEV_ITEM_STR("dpll_locked_value", NULL),
+ DEV_ITEM_STR("dpll_freerun_value", NULL),
+ DEV_ITEM_STR("dpll_invalid_value", NULL),
+ PORT_ITEM_STR("allowed_qls", NULL),
+ PORT_ITEM_STR("allowed_ext_qls", NULL),
+ PORT_ITEM_INT("sync", 0, 0, 1),
+ 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,
@@ -392,6 +436,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)) {
@@ -406,8 +452,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);
@@ -512,10 +561,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++;
}
@@ -573,7 +625,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. */
@@ -601,8 +654,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) {
@@ -728,18 +782,26 @@ 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;
+ }
+ n_items = (type == PTP ? N_CONFIG_ITEMS_PTP : 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 */
@@ -757,6 +819,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");
@@ -787,6 +851,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);
@@ -795,6 +860,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;
}
@@ -814,14 +900,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:
@@ -830,7 +918,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",
@@ -856,7 +945,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;
@@ -870,7 +959,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);
@@ -879,12 +968,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) {
@@ -893,7 +987,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;
@@ -906,9 +1000,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)) {
@@ -918,12 +1021,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;
}
}
@@ -935,6 +1038,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;
@@ -1137,8 +1250,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 14d2f64415dc..a32e0079cf21 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 000000000000..eadb318bad6f
--- /dev/null
+++ b/configs/synce.cfg
@@ -0,0 +1,194 @@
+# 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>]
+
+#
+# If internal inputs are allowed
+# 0 if internal input sources shall not be used
+# 1 if internal input sources shall be used
+# default: 1
+#
+# Internal inputs 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 (from all the ports where "sync = 1") is selected and frequency
+# recovered on that port shall be used to feed its frequency to all the other
+# ports.
+#
+internal_input 1
+
+#
+# If external inputs are allowed
+# 0 if external input sources shall not be used
+# 1 if external input sources shall be used
+# default: 0
+#
+# External inputs are either 1PPS from buil-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")
+#
+external_input 0
+
+#
+# These values are sent to the peers on configured ports ONLY when 'external
+# input' 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 DPLL status of a
+# device.
+#
+dpll_get_state_cmd cat /sys/class/net/enp1s0f0/device/cgu_state
+
+#
+# DPLL state values, must equal to values produced by stdout of
+# "dpll_get_state_cmd" command
+#
+dpll_holdover_value 4
+dpll_locked_ho_value 3
+dpll_locked_value 2
+dpll_freerun_value 1
+dpll_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