Used by synce_dev to interact with its ports.
Co-developed-by: Anatolii Gerasymenko <[email protected]>
Signed-off-by: Anatolii Gerasymenko <[email protected]>
Co-developed-by: Michal Michalik <[email protected]>
Signed-off-by: Michal Michalik <[email protected]>
Signed-off-by: Arkadiusz Kubalewski <[email protected]>
---
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
[email protected]
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel