06c396fbe3
The issue reported is a particular case of a BC configured with redundant PTP clocks with same priority. When a clock recovers from a failure, as both clock were configured with same priority it's expected the active clock source to remain active. But if the recovered clock presented a better local clock class than active, it was being selected active. This specific case was fixed. Closes-bug: 2084723 Test plan: BC with same priority PASS: Start the PTP service with all clocks out of requirements, one is selected, no matter which one. PASS: Then, when the backup clock recovers from failure it is selected active. PASS: Then, when the other clock recovers from failure it remains as backup, no matter the local clock class. PASS: Then, when the active goes out of requirement, the backup is set active. Test plan: GM with same priority PASS: Start the PTP service with all clocks out of requirements, one is selected, no matter which one. PASS: Then, when the backup clock recovers from failure it is selected active. PASS: Then, when the other clock recovers from failure it remains as backup, no matter the local clock class. PASS: Then, when the active goes out of requirement, the backup is set active. Change-Id: Id2568bc8bbaad4cbf15070314f7904d3c3bbd53d Signed-off-by: Andre Mauricio Zelak <andre.zelak@windriver.com>
910 lines
23 KiB
Diff
910 lines
23 KiB
Diff
From: Andre Mauricio Zelak <andre.zelak@windriver.com>
|
|
Date: Mon, 12 Jun 2023 14:40:59 -0300
|
|
Subject: [PATCH 14/61] Introduce the PMC agent module.
|
|
|
|
The logic for placing PTP management queries migrated out of phc2sys into
|
|
pmc_common in order to be shared with other programs in the future. This
|
|
logic uses pmc_common rather than extending it, and so it should live in
|
|
its own module stacked on top of pmc_common.
|
|
|
|
This patch moves the code into its own file verbatim without making any
|
|
other changes.
|
|
|
|
Signed-off-by: Richard Cochran <richardcochran@gmail.com>
|
|
|
|
[commit f266740e1a8aacc03f97205ae14fc43c59081433 upstream]
|
|
Signed-off-by: Andre Mauricio Zelak <andre.zelak@windriver.com>
|
|
---
|
|
makefile | 6 +-
|
|
phc2sys.c | 2 +-
|
|
pmc_agent.c | 361 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
pmc_agent.h | 62 ++++++++++
|
|
pmc_common.c | 338 -------------------------------------------------------
|
|
pmc_common.h | 34 ------
|
|
6 files changed, 427 insertions(+), 376 deletions(-)
|
|
create mode 100644 pmc_agent.c
|
|
create mode 100644 pmc_agent.h
|
|
|
|
diff --git a/makefile b/makefile
|
|
index 27c4d78..33e7ca0 100644
|
|
--- a/makefile
|
|
+++ b/makefile
|
|
@@ -34,8 +34,8 @@ OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \
|
|
sk.o stats.o tc.o $(TRANSP) telecom.o tlv.o tsproc.o unicast_client.o \
|
|
unicast_fsm.o unicast_service.o util.o version.o
|
|
|
|
-OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_common.o \
|
|
- sysoff.o timemaster.o $(TS2PHC)
|
|
+OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_agent.o \
|
|
+ pmc_common.o sysoff.o timemaster.o $(TS2PHC)
|
|
SRC = $(OBJECTS:.o=.c)
|
|
DEPEND = $(OBJECTS:.o=.d)
|
|
srcdir := $(dir $(lastword $(MAKEFILE_LIST)))
|
|
@@ -59,7 +59,7 @@ pmc: config.o hash.o interface.o msg.o phc.o pmc.o pmc_common.o print.o sk.o \
|
|
tlv.o $(TRANSP) util.o version.o
|
|
|
|
phc2sys: clockadj.o clockcheck.o config.o hash.o interface.o msg.o \
|
|
- phc.o phc2sys.o pmc_common.o print.o $(SERVOS) sk.o stats.o \
|
|
+ phc.o phc2sys.o pmc_agent.o pmc_common.o print.o $(SERVOS) sk.o stats.o \
|
|
sysoff.o tlv.o $(TRANSP) util.o version.o
|
|
|
|
hwstamp_ctl: hwstamp_ctl.o version.o
|
|
diff --git a/phc2sys.c b/phc2sys.c
|
|
index 9184db6..648ba61 100644
|
|
--- a/phc2sys.c
|
|
+++ b/phc2sys.c
|
|
@@ -47,7 +47,7 @@
|
|
#include "ntpshm.h"
|
|
#include "phc.h"
|
|
#include "pi.h"
|
|
-#include "pmc_common.h"
|
|
+#include "pmc_agent.h"
|
|
#include "print.h"
|
|
#include "servo.h"
|
|
#include "sk.h"
|
|
diff --git a/pmc_agent.c b/pmc_agent.c
|
|
new file mode 100644
|
|
index 0000000..774e94d
|
|
--- /dev/null
|
|
+++ b/pmc_agent.c
|
|
@@ -0,0 +1,361 @@
|
|
+/**
|
|
+ * @file pmc_agent.c
|
|
+ * @note Copyright (C) 2012 Richard Cochran <richardcochran@gmail.com>
|
|
+ * @note Copyright (C) 2013 Miroslav Lichvar <mlichvar@redhat.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along
|
|
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
+ */
|
|
+#include <net/if.h>
|
|
+#include <poll.h>
|
|
+
|
|
+#include "notification.h"
|
|
+#include "pmc_agent.h"
|
|
+#include "print.h"
|
|
+#include "util.h"
|
|
+
|
|
+#define PMC_UPDATE_INTERVAL (60 * NS_PER_SEC)
|
|
+#define PMC_SUBSCRIBE_DURATION 180 /* 3 minutes */
|
|
+/* Note that PMC_SUBSCRIBE_DURATION has to be longer than
|
|
+ * PMC_UPDATE_INTERVAL otherwise subscription will time out before it is
|
|
+ * renewed.
|
|
+ */
|
|
+
|
|
+static void send_subscription(struct pmc_node *node)
|
|
+{
|
|
+ struct subscribe_events_np sen;
|
|
+
|
|
+ memset(&sen, 0, sizeof(sen));
|
|
+ sen.duration = PMC_SUBSCRIBE_DURATION;
|
|
+ sen.bitmask[0] = 1 << NOTIFY_PORT_STATE;
|
|
+ pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen));
|
|
+}
|
|
+
|
|
+static int check_clock_identity(struct pmc_node *node, struct ptp_message *msg)
|
|
+{
|
|
+ if (!node->clock_identity_set)
|
|
+ return 1;
|
|
+ return cid_eq(&node->clock_identity,
|
|
+ &msg->header.sourcePortIdentity.clockIdentity);
|
|
+}
|
|
+
|
|
+static int is_msg_mgt(struct ptp_message *msg)
|
|
+{
|
|
+ struct TLV *tlv;
|
|
+
|
|
+ if (msg_type(msg) != MANAGEMENT)
|
|
+ return 0;
|
|
+ if (management_action(msg) != RESPONSE)
|
|
+ return 0;
|
|
+ if (msg_tlv_count(msg) != 1)
|
|
+ return 0;
|
|
+ tlv = (struct TLV *) msg->management.suffix;
|
|
+ if (tlv->type == TLV_MANAGEMENT)
|
|
+ return 1;
|
|
+ if (tlv->type == TLV_MANAGEMENT_ERROR_STATUS)
|
|
+ return -1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int get_mgt_id(struct ptp_message *msg)
|
|
+{
|
|
+ struct management_tlv *mgt;
|
|
+
|
|
+ mgt = (struct management_tlv *) msg->management.suffix;
|
|
+ return mgt->id;
|
|
+}
|
|
+
|
|
+void *get_mgt_data(struct ptp_message *msg)
|
|
+{
|
|
+ struct management_tlv *mgt;
|
|
+
|
|
+ mgt = (struct management_tlv *) msg->management.suffix;
|
|
+ return mgt->data;
|
|
+}
|
|
+
|
|
+static int get_mgt_err_id(struct ptp_message *msg)
|
|
+{
|
|
+ struct management_error_status *mgt;
|
|
+
|
|
+ mgt = (struct management_error_status *)msg->management.suffix;
|
|
+ return mgt->id;
|
|
+}
|
|
+
|
|
+/* Return values:
|
|
+ * 1: success
|
|
+ * 0: timeout
|
|
+ * -1: error reported by the other side
|
|
+ * -2: local error, fatal
|
|
+ */
|
|
+static int run_pmc(struct pmc_node *node, int timeout, int ds_id,
|
|
+ struct ptp_message **msg)
|
|
+{
|
|
+#define N_FD 1
|
|
+ struct pollfd pollfd[N_FD];
|
|
+ int cnt, res;
|
|
+
|
|
+ while (1) {
|
|
+ pollfd[0].fd = pmc_get_transport_fd(node->pmc);
|
|
+ pollfd[0].events = POLLIN|POLLPRI;
|
|
+ if (!node->pmc_ds_requested && ds_id >= 0)
|
|
+ pollfd[0].events |= POLLOUT;
|
|
+
|
|
+ cnt = poll(pollfd, N_FD, timeout);
|
|
+ if (cnt < 0) {
|
|
+ pr_err("poll failed");
|
|
+ return -2;
|
|
+ }
|
|
+ if (!cnt) {
|
|
+ /* Request the data set again in the next run. */
|
|
+ node->pmc_ds_requested = 0;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Send a new request if there are no pending messages. */
|
|
+ if ((pollfd[0].revents & POLLOUT) &&
|
|
+ !(pollfd[0].revents & (POLLIN|POLLPRI))) {
|
|
+ switch (ds_id) {
|
|
+ case TLV_SUBSCRIBE_EVENTS_NP:
|
|
+ send_subscription(node);
|
|
+ break;
|
|
+ default:
|
|
+ pmc_send_get_action(node->pmc, ds_id);
|
|
+ break;
|
|
+ }
|
|
+ node->pmc_ds_requested = 1;
|
|
+ }
|
|
+
|
|
+ if (!(pollfd[0].revents & (POLLIN|POLLPRI)))
|
|
+ continue;
|
|
+
|
|
+ *msg = pmc_recv(node->pmc);
|
|
+
|
|
+ if (!*msg)
|
|
+ continue;
|
|
+
|
|
+ if (!check_clock_identity(node, *msg)) {
|
|
+ msg_put(*msg);
|
|
+ *msg = NULL;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ res = is_msg_mgt(*msg);
|
|
+ if (res < 0 && get_mgt_err_id(*msg) == ds_id) {
|
|
+ node->pmc_ds_requested = 0;
|
|
+ return -1;
|
|
+ }
|
|
+ if (res <= 0 || node->recv_subscribed(node, *msg, ds_id) ||
|
|
+ get_mgt_id(*msg) != ds_id) {
|
|
+ msg_put(*msg);
|
|
+ *msg = NULL;
|
|
+ continue;
|
|
+ }
|
|
+ node->pmc_ds_requested = 0;
|
|
+ return 1;
|
|
+ }
|
|
+}
|
|
+
|
|
+int run_pmc_wait_sync(struct pmc_node *node, int timeout)
|
|
+{
|
|
+ struct ptp_message *msg;
|
|
+ Enumeration8 portState;
|
|
+ void *data;
|
|
+ int res;
|
|
+
|
|
+ while (1) {
|
|
+ res = run_pmc(node, timeout, TLV_PORT_DATA_SET, &msg);
|
|
+ if (res <= 0)
|
|
+ return res;
|
|
+
|
|
+ data = get_mgt_data(msg);
|
|
+ portState = ((struct portDS *)data)->portState;
|
|
+ msg_put(msg);
|
|
+
|
|
+ switch (portState) {
|
|
+ case PS_MASTER:
|
|
+ case PS_SLAVE:
|
|
+ return 1;
|
|
+ }
|
|
+ /* try to get more data sets (for other ports) */
|
|
+ node->pmc_ds_requested = 1;
|
|
+ }
|
|
+}
|
|
+
|
|
+int run_pmc_get_utc_offset(struct pmc_node *node, int timeout)
|
|
+{
|
|
+ struct ptp_message *msg;
|
|
+ int res;
|
|
+ struct timePropertiesDS *tds;
|
|
+
|
|
+ res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg);
|
|
+ if (res <= 0)
|
|
+ return res;
|
|
+
|
|
+ tds = (struct timePropertiesDS *)get_mgt_data(msg);
|
|
+ if (tds->flags & PTP_TIMESCALE) {
|
|
+ node->sync_offset = tds->currentUtcOffset;
|
|
+ if (tds->flags & LEAP_61)
|
|
+ node->leap = 1;
|
|
+ else if (tds->flags & LEAP_59)
|
|
+ node->leap = -1;
|
|
+ else
|
|
+ node->leap = 0;
|
|
+ node->utc_offset_traceable = tds->flags & UTC_OFF_VALID &&
|
|
+ tds->flags & TIME_TRACEABLE;
|
|
+ } else {
|
|
+ node->sync_offset = 0;
|
|
+ node->leap = 0;
|
|
+ node->utc_offset_traceable = 0;
|
|
+ }
|
|
+ msg_put(msg);
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+int run_pmc_get_number_ports(struct pmc_node *node, int timeout)
|
|
+{
|
|
+ struct ptp_message *msg;
|
|
+ int res;
|
|
+ struct defaultDS *dds;
|
|
+
|
|
+ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg);
|
|
+ if (res <= 0)
|
|
+ return res;
|
|
+
|
|
+ dds = (struct defaultDS *)get_mgt_data(msg);
|
|
+ res = dds->numberPorts;
|
|
+ msg_put(msg);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+int run_pmc_subscribe(struct pmc_node *node, int timeout)
|
|
+{
|
|
+ struct ptp_message *msg;
|
|
+ int res;
|
|
+
|
|
+ res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg);
|
|
+ if (res <= 0)
|
|
+ return res;
|
|
+ msg_put(msg);
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+void run_pmc_events(struct pmc_node *node)
|
|
+{
|
|
+ struct ptp_message *msg;
|
|
+
|
|
+ run_pmc(node, 0, -1, &msg);
|
|
+}
|
|
+
|
|
+int run_pmc_port_properties(struct pmc_node *node, int timeout,
|
|
+ unsigned int port, int *state,
|
|
+ int *tstamping, char *iface)
|
|
+{
|
|
+ struct ptp_message *msg;
|
|
+ int res, len;
|
|
+ struct port_properties_np *ppn;
|
|
+
|
|
+ pmc_target_port(node->pmc, port);
|
|
+ while (1) {
|
|
+ res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg);
|
|
+ if (res <= 0)
|
|
+ goto out;
|
|
+
|
|
+ ppn = get_mgt_data(msg);
|
|
+ if (ppn->portIdentity.portNumber != port) {
|
|
+ msg_put(msg);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ *state = ppn->port_state;
|
|
+ *tstamping = ppn->timestamping;
|
|
+ len = ppn->interface.length;
|
|
+ if (len > IFNAMSIZ - 1)
|
|
+ len = IFNAMSIZ - 1;
|
|
+ memcpy(iface, ppn->interface.text, len);
|
|
+ iface[len] = '\0';
|
|
+
|
|
+ msg_put(msg);
|
|
+ res = 1;
|
|
+ break;
|
|
+ }
|
|
+out:
|
|
+ pmc_target_all(node->pmc);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+int run_pmc_clock_identity(struct pmc_node *node, int timeout)
|
|
+{
|
|
+ struct ptp_message *msg;
|
|
+ struct defaultDS *dds;
|
|
+ int res;
|
|
+
|
|
+ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg);
|
|
+ if (res <= 0)
|
|
+ return res;
|
|
+
|
|
+ dds = (struct defaultDS *)get_mgt_data(msg);
|
|
+ memcpy(&node->clock_identity, &dds->clockIdentity,
|
|
+ sizeof(struct ClockIdentity));
|
|
+ node->clock_identity_set = 1;
|
|
+ msg_put(msg);
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/* Returns: -1 in case of error, 0 otherwise */
|
|
+int update_pmc_node(struct pmc_node *node, int subscribe)
|
|
+{
|
|
+ struct timespec tp;
|
|
+ uint64_t ts;
|
|
+
|
|
+ if (clock_gettime(CLOCK_MONOTONIC, &tp)) {
|
|
+ pr_err("failed to read clock: %m");
|
|
+ return -1;
|
|
+ }
|
|
+ ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec;
|
|
+
|
|
+ if (node->pmc &&
|
|
+ !(ts > node->pmc_last_update &&
|
|
+ ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) {
|
|
+ if (subscribe)
|
|
+ run_pmc_subscribe(node, 0);
|
|
+ if (run_pmc_get_utc_offset(node, 0) > 0)
|
|
+ node->pmc_last_update = ts;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds,
|
|
+ pmc_node_recv_subscribed_t *recv_subscribed)
|
|
+{
|
|
+ node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0,
|
|
+ config_get_int(cfg, NULL, "domainNumber"),
|
|
+ config_get_int(cfg, NULL, "transportSpecific") << 4, 1);
|
|
+ if (!node->pmc) {
|
|
+ pr_err("failed to create pmc");
|
|
+ return -1;
|
|
+ }
|
|
+ node->recv_subscribed = recv_subscribed;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void close_pmc_node(struct pmc_node *node)
|
|
+{
|
|
+ if (!node->pmc)
|
|
+ return;
|
|
+
|
|
+ pmc_destroy(node->pmc);
|
|
+ node->pmc = NULL;
|
|
+}
|
|
diff --git a/pmc_agent.h b/pmc_agent.h
|
|
new file mode 100644
|
|
index 0000000..90245b1
|
|
--- /dev/null
|
|
+++ b/pmc_agent.h
|
|
@@ -0,0 +1,62 @@
|
|
+/**
|
|
+ * @file pmc_agent.h
|
|
+ * @brief Client code for making PTP management requests.
|
|
+ * @note Copyright (C) 2013 Miroslav Lichvar <mlichvar@redhat.com>
|
|
+ * @note Copyright (C) 2020 Richard Cochran <richardcochran@gmail.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along
|
|
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
+ */
|
|
+
|
|
+#ifndef HAVE_PMC_AGENT_H
|
|
+#define HAVE_PMC_AGENT_H
|
|
+
|
|
+#include "pmc_common.h"
|
|
+
|
|
+struct pmc_node;
|
|
+
|
|
+typedef int pmc_node_recv_subscribed_t(struct pmc_node *node,
|
|
+ struct ptp_message *msg,
|
|
+ int excluded);
|
|
+
|
|
+struct pmc_node {
|
|
+ struct pmc *pmc;
|
|
+ int pmc_ds_requested;
|
|
+ uint64_t pmc_last_update;
|
|
+ int sync_offset;
|
|
+ int leap;
|
|
+ int utc_offset_traceable;
|
|
+ int clock_identity_set;
|
|
+ struct ClockIdentity clock_identity;
|
|
+ pmc_node_recv_subscribed_t *recv_subscribed;
|
|
+};
|
|
+
|
|
+int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds,
|
|
+ pmc_node_recv_subscribed_t *recv_subscribed);
|
|
+void close_pmc_node(struct pmc_node *node);
|
|
+int update_pmc_node(struct pmc_node *node, int subscribe);
|
|
+int run_pmc_subscribe(struct pmc_node *node, int timeout);
|
|
+int run_pmc_clock_identity(struct pmc_node *node, int timeout);
|
|
+int run_pmc_wait_sync(struct pmc_node *node, int timeout);
|
|
+int run_pmc_get_number_ports(struct pmc_node *node, int timeout);
|
|
+void run_pmc_events(struct pmc_node *node);
|
|
+int run_pmc_port_properties(struct pmc_node *node, int timeout,
|
|
+ unsigned int port, int *state,
|
|
+ int *tstamping, char *iface);
|
|
+int run_pmc_get_utc_offset(struct pmc_node *node, int timeout);
|
|
+int get_mgt_id(struct ptp_message *msg);
|
|
+void *get_mgt_data(struct ptp_message *msg);
|
|
+
|
|
+#endif
|
|
+
|
|
diff --git a/pmc_common.c b/pmc_common.c
|
|
index c9cdf18..a117904 100644
|
|
--- a/pmc_common.c
|
|
+++ b/pmc_common.c
|
|
@@ -18,8 +18,6 @@
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
#include <errno.h>
|
|
-#include <net/if.h>
|
|
-#include <poll.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
@@ -29,7 +27,6 @@
|
|
#include "print.h"
|
|
#include "tlv.h"
|
|
#include "transport.h"
|
|
-#include "util.h"
|
|
#include "pmc_common.h"
|
|
|
|
#define BAD_ACTION -1
|
|
@@ -58,13 +55,6 @@
|
|
/* Includes one extra byte to make length even. */
|
|
#define EMPTY_PTP_TEXT 2
|
|
|
|
-#define PMC_UPDATE_INTERVAL (60 * NS_PER_SEC)
|
|
-#define PMC_SUBSCRIBE_DURATION 180 /* 3 minutes */
|
|
-/* Note that PMC_SUBSCRIBE_DURATION has to be longer than
|
|
- * PMC_UPDATE_INTERVAL otherwise subscription will time out before it is
|
|
- * renewed.
|
|
- */
|
|
-
|
|
static void do_get_action(struct pmc *pmc, int action, int index, char *str);
|
|
static void do_set_action(struct pmc *pmc, int action, int index, char *str);
|
|
static void not_supported(struct pmc *pmc, int action, int index, char *str);
|
|
@@ -720,331 +710,3 @@ int pmc_do_command(struct pmc *pmc, char *str)
|
|
|
|
return 0;
|
|
}
|
|
-
|
|
-static void send_subscription(struct pmc_node *node)
|
|
-{
|
|
- struct subscribe_events_np sen;
|
|
-
|
|
- memset(&sen, 0, sizeof(sen));
|
|
- sen.duration = PMC_SUBSCRIBE_DURATION;
|
|
- sen.bitmask[0] = 1 << NOTIFY_PORT_STATE;
|
|
- pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen));
|
|
-}
|
|
-
|
|
-static int check_clock_identity(struct pmc_node *node, struct ptp_message *msg)
|
|
-{
|
|
- if (!node->clock_identity_set)
|
|
- return 1;
|
|
- return cid_eq(&node->clock_identity,
|
|
- &msg->header.sourcePortIdentity.clockIdentity);
|
|
-}
|
|
-
|
|
-static int is_msg_mgt(struct ptp_message *msg)
|
|
-{
|
|
- struct TLV *tlv;
|
|
-
|
|
- if (msg_type(msg) != MANAGEMENT)
|
|
- return 0;
|
|
- if (management_action(msg) != RESPONSE)
|
|
- return 0;
|
|
- if (msg_tlv_count(msg) != 1)
|
|
- return 0;
|
|
- tlv = (struct TLV *) msg->management.suffix;
|
|
- if (tlv->type == TLV_MANAGEMENT)
|
|
- return 1;
|
|
- if (tlv->type == TLV_MANAGEMENT_ERROR_STATUS)
|
|
- return -1;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-int get_mgt_id(struct ptp_message *msg)
|
|
-{
|
|
- struct management_tlv *mgt;
|
|
-
|
|
- mgt = (struct management_tlv *) msg->management.suffix;
|
|
- return mgt->id;
|
|
-}
|
|
-
|
|
-void *get_mgt_data(struct ptp_message *msg)
|
|
-{
|
|
- struct management_tlv *mgt;
|
|
-
|
|
- mgt = (struct management_tlv *) msg->management.suffix;
|
|
- return mgt->data;
|
|
-}
|
|
-
|
|
-static int get_mgt_err_id(struct ptp_message *msg)
|
|
-{
|
|
- struct management_error_status *mgt;
|
|
-
|
|
- mgt = (struct management_error_status *)msg->management.suffix;
|
|
- return mgt->id;
|
|
-}
|
|
-
|
|
-/* Return values:
|
|
- * 1: success
|
|
- * 0: timeout
|
|
- * -1: error reported by the other side
|
|
- * -2: local error, fatal
|
|
- */
|
|
-static int run_pmc(struct pmc_node *node, int timeout, int ds_id,
|
|
- struct ptp_message **msg)
|
|
-{
|
|
-#define N_FD 1
|
|
- struct pollfd pollfd[N_FD];
|
|
- int cnt, res;
|
|
-
|
|
- while (1) {
|
|
- pollfd[0].fd = pmc_get_transport_fd(node->pmc);
|
|
- pollfd[0].events = POLLIN|POLLPRI;
|
|
- if (!node->pmc_ds_requested && ds_id >= 0)
|
|
- pollfd[0].events |= POLLOUT;
|
|
-
|
|
- cnt = poll(pollfd, N_FD, timeout);
|
|
- if (cnt < 0) {
|
|
- pr_err("poll failed");
|
|
- return -2;
|
|
- }
|
|
- if (!cnt) {
|
|
- /* Request the data set again in the next run. */
|
|
- node->pmc_ds_requested = 0;
|
|
- return 0;
|
|
- }
|
|
-
|
|
- /* Send a new request if there are no pending messages. */
|
|
- if ((pollfd[0].revents & POLLOUT) &&
|
|
- !(pollfd[0].revents & (POLLIN|POLLPRI))) {
|
|
- switch (ds_id) {
|
|
- case TLV_SUBSCRIBE_EVENTS_NP:
|
|
- send_subscription(node);
|
|
- break;
|
|
- default:
|
|
- pmc_send_get_action(node->pmc, ds_id);
|
|
- break;
|
|
- }
|
|
- node->pmc_ds_requested = 1;
|
|
- }
|
|
-
|
|
- if (!(pollfd[0].revents & (POLLIN|POLLPRI)))
|
|
- continue;
|
|
-
|
|
- *msg = pmc_recv(node->pmc);
|
|
-
|
|
- if (!*msg)
|
|
- continue;
|
|
-
|
|
- if (!check_clock_identity(node, *msg)) {
|
|
- msg_put(*msg);
|
|
- *msg = NULL;
|
|
- continue;
|
|
- }
|
|
-
|
|
- res = is_msg_mgt(*msg);
|
|
- if (res < 0 && get_mgt_err_id(*msg) == ds_id) {
|
|
- node->pmc_ds_requested = 0;
|
|
- return -1;
|
|
- }
|
|
- if (res <= 0 || node->recv_subscribed(node, *msg, ds_id) ||
|
|
- get_mgt_id(*msg) != ds_id) {
|
|
- msg_put(*msg);
|
|
- *msg = NULL;
|
|
- continue;
|
|
- }
|
|
- node->pmc_ds_requested = 0;
|
|
- return 1;
|
|
- }
|
|
-}
|
|
-
|
|
-int run_pmc_wait_sync(struct pmc_node *node, int timeout)
|
|
-{
|
|
- struct ptp_message *msg;
|
|
- Enumeration8 portState;
|
|
- void *data;
|
|
- int res;
|
|
-
|
|
- while (1) {
|
|
- res = run_pmc(node, timeout, TLV_PORT_DATA_SET, &msg);
|
|
- if (res <= 0)
|
|
- return res;
|
|
-
|
|
- data = get_mgt_data(msg);
|
|
- portState = ((struct portDS *)data)->portState;
|
|
- msg_put(msg);
|
|
-
|
|
- switch (portState) {
|
|
- case PS_MASTER:
|
|
- case PS_SLAVE:
|
|
- return 1;
|
|
- }
|
|
- /* try to get more data sets (for other ports) */
|
|
- node->pmc_ds_requested = 1;
|
|
- }
|
|
-}
|
|
-
|
|
-int run_pmc_get_utc_offset(struct pmc_node *node, int timeout)
|
|
-{
|
|
- struct ptp_message *msg;
|
|
- int res;
|
|
- struct timePropertiesDS *tds;
|
|
-
|
|
- res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg);
|
|
- if (res <= 0)
|
|
- return res;
|
|
-
|
|
- tds = (struct timePropertiesDS *)get_mgt_data(msg);
|
|
- if (tds->flags & PTP_TIMESCALE) {
|
|
- node->sync_offset = tds->currentUtcOffset;
|
|
- if (tds->flags & LEAP_61)
|
|
- node->leap = 1;
|
|
- else if (tds->flags & LEAP_59)
|
|
- node->leap = -1;
|
|
- else
|
|
- node->leap = 0;
|
|
- node->utc_offset_traceable = tds->flags & UTC_OFF_VALID &&
|
|
- tds->flags & TIME_TRACEABLE;
|
|
- } else {
|
|
- node->sync_offset = 0;
|
|
- node->leap = 0;
|
|
- node->utc_offset_traceable = 0;
|
|
- }
|
|
- msg_put(msg);
|
|
- return 1;
|
|
-}
|
|
-
|
|
-int run_pmc_get_number_ports(struct pmc_node *node, int timeout)
|
|
-{
|
|
- struct ptp_message *msg;
|
|
- int res;
|
|
- struct defaultDS *dds;
|
|
-
|
|
- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg);
|
|
- if (res <= 0)
|
|
- return res;
|
|
-
|
|
- dds = (struct defaultDS *)get_mgt_data(msg);
|
|
- res = dds->numberPorts;
|
|
- msg_put(msg);
|
|
- return res;
|
|
-}
|
|
-
|
|
-int run_pmc_subscribe(struct pmc_node *node, int timeout)
|
|
-{
|
|
- struct ptp_message *msg;
|
|
- int res;
|
|
-
|
|
- res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg);
|
|
- if (res <= 0)
|
|
- return res;
|
|
- msg_put(msg);
|
|
- return 1;
|
|
-}
|
|
-
|
|
-void run_pmc_events(struct pmc_node *node)
|
|
-{
|
|
- struct ptp_message *msg;
|
|
-
|
|
- run_pmc(node, 0, -1, &msg);
|
|
-}
|
|
-
|
|
-int run_pmc_port_properties(struct pmc_node *node, int timeout,
|
|
- unsigned int port, int *state,
|
|
- int *tstamping, char *iface)
|
|
-{
|
|
- struct ptp_message *msg;
|
|
- int res, len;
|
|
- struct port_properties_np *ppn;
|
|
-
|
|
- pmc_target_port(node->pmc, port);
|
|
- while (1) {
|
|
- res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg);
|
|
- if (res <= 0)
|
|
- goto out;
|
|
-
|
|
- ppn = get_mgt_data(msg);
|
|
- if (ppn->portIdentity.portNumber != port) {
|
|
- msg_put(msg);
|
|
- continue;
|
|
- }
|
|
-
|
|
- *state = ppn->port_state;
|
|
- *tstamping = ppn->timestamping;
|
|
- len = ppn->interface.length;
|
|
- if (len > IFNAMSIZ - 1)
|
|
- len = IFNAMSIZ - 1;
|
|
- memcpy(iface, ppn->interface.text, len);
|
|
- iface[len] = '\0';
|
|
-
|
|
- msg_put(msg);
|
|
- res = 1;
|
|
- break;
|
|
- }
|
|
-out:
|
|
- pmc_target_all(node->pmc);
|
|
- return res;
|
|
-}
|
|
-
|
|
-int run_pmc_clock_identity(struct pmc_node *node, int timeout)
|
|
-{
|
|
- struct ptp_message *msg;
|
|
- struct defaultDS *dds;
|
|
- int res;
|
|
-
|
|
- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg);
|
|
- if (res <= 0)
|
|
- return res;
|
|
-
|
|
- dds = (struct defaultDS *)get_mgt_data(msg);
|
|
- memcpy(&node->clock_identity, &dds->clockIdentity,
|
|
- sizeof(struct ClockIdentity));
|
|
- node->clock_identity_set = 1;
|
|
- msg_put(msg);
|
|
- return 1;
|
|
-}
|
|
-
|
|
-/* Returns: -1 in case of error, 0 otherwise */
|
|
-int update_pmc_node(struct pmc_node *node, int subscribe)
|
|
-{
|
|
- struct timespec tp;
|
|
- uint64_t ts;
|
|
-
|
|
- if (clock_gettime(CLOCK_MONOTONIC, &tp)) {
|
|
- pr_err("failed to read clock: %m");
|
|
- return -1;
|
|
- }
|
|
- ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec;
|
|
-
|
|
- if (node->pmc &&
|
|
- !(ts > node->pmc_last_update &&
|
|
- ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) {
|
|
- if (subscribe)
|
|
- run_pmc_subscribe(node, 0);
|
|
- if (run_pmc_get_utc_offset(node, 0) > 0)
|
|
- node->pmc_last_update = ts;
|
|
- }
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds,
|
|
- pmc_node_recv_subscribed_t *recv_subscribed)
|
|
-{
|
|
- node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0,
|
|
- config_get_int(cfg, NULL, "domainNumber"),
|
|
- config_get_int(cfg, NULL, "transportSpecific") << 4, 1);
|
|
- if (!node->pmc) {
|
|
- pr_err("failed to create pmc");
|
|
- return -1;
|
|
- }
|
|
- node->recv_subscribed = recv_subscribed;
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-void close_pmc_node(struct pmc_node *node)
|
|
-{
|
|
- if (!node->pmc)
|
|
- return;
|
|
-
|
|
- pmc_destroy(node->pmc);
|
|
- node->pmc = NULL;
|
|
-}
|
|
diff --git a/pmc_common.h b/pmc_common.h
|
|
index 476ccea..8bea2e0 100644
|
|
--- a/pmc_common.h
|
|
+++ b/pmc_common.h
|
|
@@ -50,38 +50,4 @@ void pmc_target_all(struct pmc *pmc);
|
|
const char *pmc_action_string(int action);
|
|
int pmc_do_command(struct pmc *pmc, char *str);
|
|
|
|
-struct pmc_node;
|
|
-
|
|
-typedef int pmc_node_recv_subscribed_t(struct pmc_node *node,
|
|
- struct ptp_message *msg,
|
|
- int excluded);
|
|
-
|
|
-struct pmc_node {
|
|
- struct pmc *pmc;
|
|
- int pmc_ds_requested;
|
|
- uint64_t pmc_last_update;
|
|
- int sync_offset;
|
|
- int leap;
|
|
- int utc_offset_traceable;
|
|
- int clock_identity_set;
|
|
- struct ClockIdentity clock_identity;
|
|
- pmc_node_recv_subscribed_t *recv_subscribed;
|
|
-};
|
|
-
|
|
-int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds,
|
|
- pmc_node_recv_subscribed_t *recv_subscribed);
|
|
-void close_pmc_node(struct pmc_node *node);
|
|
-int update_pmc_node(struct pmc_node *node, int subscribe);
|
|
-int run_pmc_subscribe(struct pmc_node *node, int timeout);
|
|
-int run_pmc_clock_identity(struct pmc_node *node, int timeout);
|
|
-int run_pmc_wait_sync(struct pmc_node *node, int timeout);
|
|
-int run_pmc_get_number_ports(struct pmc_node *node, int timeout);
|
|
-void run_pmc_events(struct pmc_node *node);
|
|
-int run_pmc_port_properties(struct pmc_node *node, int timeout,
|
|
- unsigned int port, int *state,
|
|
- int *tstamping, char *iface);
|
|
-int run_pmc_get_utc_offset(struct pmc_node *node, int timeout);
|
|
-int get_mgt_id(struct ptp_message *msg);
|
|
-void *get_mgt_data(struct ptp_message *msg);
|
|
-
|
|
#endif
|