Used by synce_dev to interact with its ports. Co-developed-by: Anatolii Gerasymenko <anatolii.gerasyme...@intel.com> Signed-off-by: Anatolii Gerasymenko <anatolii.gerasyme...@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>
--- v2: updated license headers synce_port.c | 491 +++++++++++++++++++++++++++++++++++++++++++++++++++ synce_port.h | 184 +++++++++++++++++++ 2 files changed, 675 insertions(+) create mode 100644 synce_port.c create mode 100644 synce_port.h diff --git a/synce_port.c b/synce_port.c new file mode 100644 index 000000000000..029b901f0b43 --- /dev/null +++ b/synce_port.c @@ -0,0 +1,491 @@ +/** + * @file synce_port.c + * @brief Interface between synce device and port controller module. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#include <stdlib.h> +#include <errno.h> +#include <sys/queue.h> +#include <net/if.h> +#include <linux/limits.h> + +#include "synce_port.h" +#include "print.h" +#include "config.h" +#include "synce_port_ctrl.h" +#include "synce_msg.h" + +enum port_mode { + NON_SYNC_MODE = 0, + SYNC_MODE, +}; + +enum port_state { + PORT_UNKNOWN = 0, + PORT_CREATED, + PORT_INITED, + PORT_RUNNING, + PORT_FAILED, + PORT_NOT_USED, +}; + +static int init_ext_tlv(struct synce_port *port, struct synce_msg_ext_ql *msg) +{ + memset(msg, 0, sizeof(*msg)); + + if (generate_clock_identity(&msg->clockIdentity, port->name)) { + pr_err("failed to generate a clock identity"); + return -ENXIO; + } + + return 0; +} + +static int ext_ql_msg_start_chain(struct synce_msg_ext_ql *ext_ql_msg) +{ + if (!ext_ql_msg) { + pr_err("ext_ql_msg is NULL"); + return -EFAULT; + } + + /* This is first node in chain */ + ext_ql_msg->cascaded_EEcs = 0; + ext_ql_msg->cascaded_eEEcs = 1; + + return 0; +} + +static int ext_ql_msg_update_chain(struct synce_port *port) +{ + int rx_ext_tlv = synce_port_ctrl_rx_ext_tlv(port->pc); + + if (rx_ext_tlv == 1) { + /* if extended tlv came on best port just increase eEEC by one */ + port->ext_ql_msg.cascaded_eEEcs++; + } else if (rx_ext_tlv == 0) { + /* If extended tlv was not on the wire, the chain has just started. + * Previous known number of EEC is 1. + * This node is first eEEC in the new chain. + * The flags set accordingly. + * This behavior is described in the SyncE specification. + */ + port->ext_ql_msg.cascaded_EEcs = 1; + port->ext_ql_msg.cascaded_eEEcs = 1; + port->ext_ql_msg.flag |= (MIXED_EEC_CHAIN_FLAG | + PARTIAL_EEC_CHAIN_FLAG); + } else { + pr_err("failed rx_ext_tlv on %s", port->name); + return -ENODEV; + } + + return 0; +} +static int init_port_ql_val(struct synce_port *port, uint8_t forced_ql, + int network_option) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + port->ql_forced = forced_ql; + port->ql_dnu = synce_get_dnu_value(network_option, false); + port->ql = port->ql_dnu; + + return 0; +} + +static int init_port_ext_ql(struct synce_port *port, uint8_t forced_ext_ql, + int network_option) +{ + int ret; + + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + ret = init_ext_tlv(port, &port->ext_ql_msg_dnu); + if (ret) { + pr_err("init ext_ql_msg_dnu failed on %s", port->name); + return ret; + } + + port->ext_ql_msg_dnu.enhancedSsmCode = + synce_get_dnu_value(network_option, true); + ret = ext_ql_msg_start_chain(&port->ext_ql_msg_dnu); + if (ret) { + pr_err("start chain failed on %s", port->name); + return ret; + } + + memcpy(&port->ext_ql_msg, &port->ext_ql_msg_dnu, + sizeof(port->ext_ql_msg)); + memcpy(&port->ext_ql_msg_forced, &port->ext_ql_msg, + sizeof(port->ext_ql_msg_forced)); + + port->ext_ql_msg_forced.enhancedSsmCode = forced_ext_ql; + + return ret; +} + +static int new_tx_ql_and_rebuild(struct synce_port *port, uint8_t ql, + struct synce_msg_ext_ql *ext_ql_msg) +{ + int ret = synce_port_ctrl_set_tx_ql(port->pc, ql); + + if (ret) { + pr_err("set QL on %s failed", port->name); + return ret; + } + + if (ext_ql_msg) { + ret = synce_port_ctrl_set_tx_ext_ql(port->pc, + ext_ql_msg); + if (ret) { + pr_err("set ext QL on %s failed", port->name); + return ret; + } + } + + ret = synce_port_ctrl_rebuild_tx(port->pc); + if (ret) { + pr_err("set rebuild tx on %s failed", port->name); + } + + return ret; +} + +struct synce_port *synce_port_create(const char *port_name) +{ + struct synce_port *p = NULL; + + if (!port_name) { + pr_err("%s failed - port_name not provided", __func__); + return NULL; + } + + p = malloc(sizeof(struct synce_port)); + if (!p) { + pr_err("%s failed", __func__); + return NULL; + } + memcpy(p->name, port_name, sizeof(p->name)); + p->state = PORT_CREATED; + + return p; +} + +int synce_port_init(struct synce_port *port, struct config *cfg, + int network_option, int is_extended, + int rx_enabled, int recovery_time, + uint8_t forced_ql, uint8_t forced_ext_ql) +{ + int ret; + + if (!port) { + pr_err("%s port is NULL", __func__); + return -ENODEV; + } + + if (port->state != PORT_CREATED) { + goto err_port; + } + + port->sync_mode = config_get_int(cfg, port->name, "sync"); + + if (port->sync_mode != SYNC_MODE) { + pr_err("port %s not in a sync mode", port->name); + goto err_port; + } + + if (rx_enabled) { + port->recover_clock_enable_cmd = + config_get_string(cfg, port->name, + "recover_clock_enable_cmd"); + if (!port->recover_clock_enable_cmd) { + pr_err("recover_clock_enable_cmd config not provided for %s", + port->name); + goto err_port; + } + port->recover_clock_disable_cmd = + config_get_string(cfg, port->name, + "recover_clock_disable_cmd"); + if (!port->recover_clock_disable_cmd) { + pr_err("recover_clock_disable_cmd config not provided for %s", + port->name); + goto err_port; + } + } else { + port->recover_clock_enable_cmd = NULL; + port->recover_clock_disable_cmd = NULL; + } + + port->pc = synce_port_ctrl_create(port->name); + if (!port->pc) { + pr_err("port_ctrl create failed on %s", port->name); + goto err_port; + } + + ret = init_port_ql_val(port, forced_ql, network_option); + if (ret) { + pr_err("init port QL values failed on %s", port->name); + return ret; + } + + if (is_extended) { + ret = init_port_ext_ql(port, forced_ext_ql, network_option); + if (ret) { + pr_err("init port ext QL values failed on %s", + port->name); + return ret; + } + } + + ret = synce_port_ctrl_init(port->pc, cfg, rx_enabled, is_extended, + recovery_time, network_option); + if (ret) { + pr_err("port_ctrl init failed on port %s", port->name); + return ret; + } + + ret = synce_port_set_tx_ql_dnu(port, is_extended); + if (ret) { + pr_err("tlv init failed on port %s", port->name); + return ret; + } + + ret = synce_port_ctrl_enable_tx(port->pc); + if (ret) { + pr_err("enabled tx failed on port %s", port->name); + return ret; + } + + port->state = PORT_INITED; + + return ret; +err_port: + port->state = PORT_FAILED; + return -ENODEV; +} + +int synce_port_in_sync_mode(struct config *cfg, const char *port_name) +{ + if (!cfg) { + pr_err("%s cfg is NULL", __func__); + return -EFAULT; + } + + if (!port_name) { + pr_err("%s port_name is NULL", __func__); + return -EFAULT; + } + + return (config_get_int(cfg, port_name, "sync") == SYNC_MODE); +} + +void synce_port_destroy(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return; + } + + if (port->pc) { + synce_port_ctrl_destroy(port->pc); + free(port->pc); + } else { + pr_warning("%s pc is NULL", __func__); + } +} + +int synce_port_thread_running(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + return synce_port_ctrl_running(port->pc); +} + +int synce_port_rx_ql_failed(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + return synce_port_ctrl_rx_ql_failed(port->pc); +} + +int synce_port_rx_ql_changed(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + return synce_port_ctrl_rx_ql_changed(port->pc); +} + +int synce_port_set_tx_ql_dnu(struct synce_port *port, int extended) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + return new_tx_ql_and_rebuild(port, port->ql_dnu, + extended ? + &port->ext_ql_msg_dnu : NULL); +} + +int synce_port_set_tx_ql_forced(struct synce_port *port, int extended) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + return new_tx_ql_and_rebuild(port, port->ql_forced, + extended ? + &port->ext_ql_msg_forced : NULL); +} + +int synce_port_set_tx_ql_from_best_input(struct synce_port *port, + struct synce_port *best_p, + int extended) +{ + struct synce_msg_ext_ql rx_ext_ql_msg; + int ret = -EFAULT; + uint8_t rx_ql; + + if (!port) { + pr_err("%s port is NULL", __func__); + return ret; + } + + if (!best_p) { + pr_err("%s best_p is NULL", __func__); + return ret; + } + + ret = synce_port_ctrl_get_rx_ql(best_p->pc, &rx_ql); + if (ret) { + pr_err("get rx QL failed on %s", best_p->name); + return ret; + } + port->ql = rx_ql; + + if (extended) { + ret = synce_port_ctrl_get_rx_ext_ql(best_p->pc, + &rx_ext_ql_msg); + if (ret) { + pr_err("get ext rx QL failed on %s", best_p->name); + return ret; + } + memcpy(&port->ext_ql_msg, &rx_ext_ql_msg, + sizeof(port->ext_ql_msg)); + ret = generate_clock_identity(&port->ext_ql_msg.clockIdentity, + port->name); + if (ret) { + pr_err("failed to generate clock identity on %s", + port->name); + return ret; + } + + ret = ext_ql_msg_update_chain(port); + if (ret) { + pr_err("failed to update chain on %s", + port->name); + return ret; + } + } + ret = new_tx_ql_and_rebuild(port, port->ql, + extended ? &port->ext_ql_msg : NULL); + + return ret; +} + +int synce_port_is_rx_dnu(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + return synce_port_ctrl_rx_dnu(port->pc, port->ql_dnu); +} + +struct synce_port *synce_port_compare_ql(struct synce_port *left, + struct synce_port *right) +{ + struct synce_port_ctrl *best; + + best = synce_port_ctrl_compare_ql(left ? left->pc : NULL, + right ? right->pc : NULL); + if (!best) { + return NULL; + } + + if (left && best == left->pc) { + return left; + } else if (right && best == right->pc) { + return right; + } + + return NULL; +} + +const char *synce_port_get_name(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return NULL; + } + + return port->name; +} + +int synce_port_enable_recover_clock(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EINVAL; + } + + if (!port->recover_clock_disable_cmd) { + pr_err("recover_clock_enable_cmd is null on %s", port->name); + return -EINVAL; + } + + pr_debug("using recover_clock_enable_cmd: %s on %s", + port->recover_clock_enable_cmd, port->name); + + return system(port->recover_clock_enable_cmd); +} + +int synce_port_disable_recover_clock(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EINVAL; + } + + if (!port->recover_clock_disable_cmd) { + pr_err("recover_clock_disable_cmd is null on %s", port->name); + return -EINVAL; + } + + pr_debug("using recover_clock_disable_cmd: %s on %s", + port->recover_clock_disable_cmd, port->name); + + return system(port->recover_clock_disable_cmd); +} + +void synce_port_invalidate_rx_ql(struct synce_port *port) +{ + synce_port_ctrl_invalidate_rx_ql(port->pc); +} diff --git a/synce_port.h b/synce_port.h new file mode 100644 index 000000000000..45e155a5fe3c --- /dev/null +++ b/synce_port.h @@ -0,0 +1,184 @@ +/** + * @file synce_port.h + * @brief Interface between synce device and port controller module. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef HAVE_SYNCE_PORT_H +#define HAVE_SYNCE_PORT_H +#include <stdint.h> +#include "synce_msg.h" + +struct synce_dev; +struct config; + +struct synce_port { + LIST_ENTRY(synce_port) list; + int sync_mode; + int state; + struct synce_port_ctrl *pc; + uint8_t ql; + uint8_t ql_dnu; + uint8_t ql_forced; + struct synce_msg_ext_ql ext_ql_msg; + struct synce_msg_ext_ql ext_ql_msg_dnu; + struct synce_msg_ext_ql ext_ql_msg_forced; + char name[IF_NAMESIZE]; + char *recover_clock_enable_cmd; + char *recover_clock_disable_cmd; +}; + +/** + * Alloc memory for a single synce_port instance. + * + * @param port_name Human readable name of a port + * @return Pointer to allocated instance + */ +struct synce_port *synce_port_create(const char *port_name); + +/** + * Initialize synce device capable port after port was created. + * + * @param port synce_port instance to be initialized + * @param cfg Configuration struct based on SYNCE type, + * must hold properities of the configured port. + * @param network_option Network option that shall be used on the device + * @param is_extended If extended tlv support is on + * @param rx_enabled If rx of ESMC shall start + * @param recovery_time Seconds for period of recovering from QL-failed + * state. + * @param forced_ql Value of QL when QL is forced for the device, + * used in external input mode + * @param forced_ext_ql Value of ext QL when QL is forced for the + * device, used in external input mode + * @return 0 on success, failure otherwise + */ +int synce_port_init(struct synce_port *port, struct config *cfg, + int network_option, int is_extended, + int rx_enabled, int recovery_time, + uint8_t forced_ql, uint8_t forced_ext_ql); + +/** + * Check if port shall be configured for synchronous mode. + * + * @param cfg Configuration struct based on SYNCE type, must hold + * properities of the configured port. + * @param port_name Human readable name of a port being questioned + * @return 0 if false, otherwise true + */ +int synce_port_in_sync_mode(struct config *cfg, const char *port_name); + +/** + * Free resource under the synce_port instance. Caller shall free the passed + * pointer afterwards. + * + * @param port Pointer to the port being released + */ +void synce_port_destroy(struct synce_port *port); + +/** + * Check if port ctrl threads are running. + * + * @param port Questioned port + * @return 0 if false, otherwise true + */ +int synce_port_thread_running(struct synce_port *port); + +/** + * Check if QL-failed condition is present. + * + * @param port Questioned instance + * @return 1 if true, 0 if false, negative on failure + */ +int synce_port_rx_ql_failed(struct synce_port *port); + +/** + * Check if QL has changed on RX. + * + * @param port Questioned instance + * @return 1 if true, 0 if false, negative on failure + */ +int synce_port_rx_ql_changed(struct synce_port *port); + +/** + * Set QL-DNU on TX TLV of associated port. + * + * @param port Managed port + * @param extended If new extended TLV shall be created + * @return 0 on success, negative otherwise + */ +int synce_port_set_tx_ql_dnu(struct synce_port *port, int extended); + +/** + * Set QL from config file on TX TLV of associated port. Useful in + * external_input scenario. + * + * + * @param port Managed port + * @param extended If new extended TLV shall be created + * @return 0 on success, negative otherwise + */ +int synce_port_set_tx_ql_forced(struct synce_port *port, int extended); + +/** + * Set QL for TX thread - but copy QL from best port. + * + * @param port Managed instance + * @param best_p Best port instance + * @param extended If new extended TLV shall be created + * @return 0 on success, negative on failure + */ +int synce_port_set_tx_ql_from_best_input(struct synce_port *port, + struct synce_port *best_p, + int extended); + +/** + * Check if given port has Do Not Use QL. + * + * @param port Questioned instance + * @return 1 if DNU is present, 0 if not, negative on failure + */ +int synce_port_is_rx_dnu(struct synce_port *port); + +/** + * Compare left with right port, which has higher incoming Quality Level. + * + * @param left Port instance for comparison + * @param righ Port instance for comparison + * @return Pointer to best QL instance, NULL on failure or equal + */ +struct synce_port *synce_port_compare_ql(struct synce_port *left, + struct synce_port *right); + +/** + * Get name of a port. + * + * @param port Questioned instance + * @return Name of a port + */ +const char *synce_port_get_name(struct synce_port *port); + +/** + * Enable recover clock on a port. + * + * @param port Questioned instance + * @return 0 on success, negative on failure + */ +int synce_port_enable_recover_clock(struct synce_port *port); + +/** + * Enable recover clock on a port. + * + * @param port Questioned instance + * @return 0 on success, negative on failure + */ +int synce_port_disable_recover_clock(struct synce_port *port); + +/** + * Invalidate QL received on the port in the past. + * + * @param port Questioned instance + */ +void synce_port_invalidate_rx_ql(struct synce_port *port); + +#endif /* HAVE_SYNCE_PORT_H */ -- 2.34.1 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel