Move the code for sending various messages to ptp4l via pmc to a common
translation module, outside of phc2sys. This makes it available to other
programs that want to subscribe to port state change events too, such as
ts2phc.

This creates a smaller structure within phc2sys_private, which embeds
all properties related to the PMC. This structure is called "pmc_node",
which is somewhat reminiscent of the old name of phc2sys_private (struct
node). But the advantage is that struct pmc_node can be reused by other
modules.

Signed-off-by: Vladimir Oltean <olte...@gmail.com>
Reviewed-by: Jacob Keller <jacob.e.kel...@intel.com>
---
Changes in RFC v2:
- Added Jacob's review tag.

 phc2sys.c    | 404 +++++----------------------------------------------
 pmc_common.c | 337 ++++++++++++++++++++++++++++++++++++++++++
 pmc_common.h |  35 +++++
 3 files changed, 407 insertions(+), 369 deletions(-)

diff --git a/phc2sys.c b/phc2sys.c
index a36cbe071d7d..c4d72bd7d17a 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -56,18 +56,13 @@
 #include "uds.h"
 #include "util.h"
 #include "version.h"
+#include "contain.h"
 
 #define KP 0.7
 #define KI 0.3
 #define NS_PER_SEC 1000000000LL
 
 #define PHC_PPS_OFFSET_LIMIT 10000000
-#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.
- */
 
 struct clock {
        LIST_ENTRY(clock) list;
@@ -105,17 +100,10 @@ struct phc2sys_private {
        enum servo_type servo_type;
        int phc_readings;
        double phc_interval;
-       int sync_offset;
        int forced_sync_offset;
-       int utc_offset_traceable;
-       int leap;
        int kernel_leap;
-       struct pmc *pmc;
-       int pmc_ds_requested;
-       uint64_t pmc_last_update;
        int state_changed;
-       int clock_identity_set;
-       struct ClockIdentity clock_identity;
+       struct pmc_node node;
        LIST_HEAD(port_head, port) ports;
        LIST_HEAD(clock_head, clock) clocks;
        LIST_HEAD(dst_clock_head, clock) dst_clocks;
@@ -124,18 +112,11 @@ struct phc2sys_private {
 
 static struct config *phc2sys_config;
 
-static int update_pmc(struct phc2sys_private *priv, int subscribe);
 static int clock_handle_leap(struct phc2sys_private *priv,
                             struct clock *clock,
                             int64_t offset, uint64_t ts);
-static int run_pmc_get_utc_offset(struct phc2sys_private *priv,
-                                 int timeout);
-static void run_pmc_events(struct phc2sys_private *priv);
 
 static int normalize_state(int state);
-static int run_pmc_port_properties(struct phc2sys_private *priv,
-                                  int timeout, unsigned int port,
-                                  int *state, int *tstamping, char *iface);
 
 static struct servo *servo_add(struct phc2sys_private *priv,
                               struct clock *clock)
@@ -324,7 +305,7 @@ static void clock_reinit(struct phc2sys_private *priv, 
struct clock *clock,
 
        LIST_FOREACH(p, &priv->ports, list) {
                if (p->clock == clock) {
-                       ret = run_pmc_port_properties(priv, 1000, p->number,
+                       ret = run_pmc_port_properties(&priv->node, 1000, 
p->number,
                                                      &state, &timestamping,
                                                      iface);
                        if (ret > 0)
@@ -659,7 +640,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct 
clock *clock,
 
        if (src == CLOCK_INVALID) {
                /* The sync offset can't be applied with PPS alone. */
-               priv->sync_offset = 0;
+               priv->node.sync_offset = 0;
        } else {
                enable_pps_output(priv->master->clkid);
        }
@@ -690,7 +671,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct 
clock *clock,
                        pps_offset = pps_ts - phc_ts;
                }
 
-               if (update_pmc(priv, 0) < 0)
+               if (update_pmc_node(&priv->node, 0) < 0)
                        continue;
                update_clock(priv, clock, pps_offset, pps_ts, -1);
        }
@@ -727,15 +708,15 @@ static int do_loop(struct phc2sys_private *priv, int 
subscriptions)
 
        while (is_running()) {
                clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL);
-               if (update_pmc(priv, subscriptions) < 0)
+               if (update_pmc_node(&priv->node, subscriptions) < 0)
                        continue;
 
                if (subscriptions) {
-                       run_pmc_events(priv);
+                       run_pmc_events(&priv->node);
                        if (priv->state_changed) {
                                /* force getting offset, as it may have
                                 * changed after the port state change */
-                               if (run_pmc_get_utc_offset(priv, 1000) <= 0) {
+                               if (run_pmc_get_utc_offset(&priv->node, 1000) 
<= 0) {
                                        pr_err("failed to get UTC offset");
                                        continue;
                                }
@@ -792,53 +773,6 @@ static int do_loop(struct phc2sys_private *priv, int 
subscriptions)
        return 0;
 }
 
-static int check_clock_identity(struct phc2sys_private *priv,
-                               struct ptp_message *msg)
-{
-       if (!priv->clock_identity_set)
-               return 1;
-       return cid_eq(&priv->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;
-}
-
-static int get_mgt_id(struct ptp_message *msg)
-{
-       struct management_tlv *mgt = (struct management_tlv *) 
msg->management.suffix;
-       return mgt->id;
-}
-
-static void *get_mgt_data(struct ptp_message *msg)
-{
-       struct management_tlv *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;
-}
-
 static int normalize_state(int state)
 {
        if (state != PS_MASTER && state != PS_SLAVE &&
@@ -868,9 +802,13 @@ static int clock_compute_state(struct phc2sys_private 
*priv,
        return state;
 }
 
-static int recv_subscribed(struct phc2sys_private *priv,
-                          struct ptp_message *msg, int excluded)
+#define node_to_phc2sys(node) \
+       container_of(node, struct phc2sys_private, node)
+
+static int phc2sys_recv_subscribed(struct pmc_node *node,
+                                  struct ptp_message *msg, int excluded)
 {
+       struct phc2sys_private *priv = node_to_phc2sys(node);
        int mgt_id, state;
        struct portDS *pds;
        struct port *port;
@@ -905,259 +843,6 @@ static int recv_subscribed(struct phc2sys_private *priv,
        return 0;
 }
 
-static void send_subscription(struct phc2sys_private *priv)
-{
-       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(priv->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, 
sizeof(sen));
-}
-
-static int init_pmc(struct config *cfg, struct phc2sys_private *priv)
-{
-       char uds_local[MAX_IFNAME_SIZE + 1];
-
-       snprintf(uds_local, sizeof(uds_local), "/var/run/phc2sys.%d",
-                getpid());
-       priv->pmc = pmc_create(cfg, TRANS_UDS, uds_local, 0,
-                              config_get_int(cfg, NULL, "domainNumber"),
-                              config_get_int(cfg, NULL, "transportSpecific") 
<< 4, 1);
-       if (!priv->pmc) {
-               pr_err("failed to create pmc");
-               return -1;
-       }
-
-       return 0;
-}
-
-/* Return values:
- * 1: success
- * 0: timeout
- * -1: error reported by the other side
- * -2: local error, fatal
- */
-static int run_pmc(struct phc2sys_private *priv, 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(priv->pmc);
-               pollfd[0].events = POLLIN|POLLPRI;
-               if (!priv->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. */
-                       priv->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(priv);
-                               break;
-                       default:
-                               pmc_send_get_action(priv->pmc, ds_id);
-                               break;
-                       }
-                       priv->pmc_ds_requested = 1;
-               }
-
-               if (!(pollfd[0].revents & (POLLIN|POLLPRI)))
-                       continue;
-
-               *msg = pmc_recv(priv->pmc);
-
-               if (!*msg)
-                       continue;
-
-               if (!check_clock_identity(priv, *msg)) {
-                       msg_put(*msg);
-                       *msg = NULL;
-                       continue;
-               }
-
-               res = is_msg_mgt(*msg);
-               if (res < 0 && get_mgt_err_id(*msg) == ds_id) {
-                       priv->pmc_ds_requested = 0;
-                       return -1;
-               }
-               if (res <= 0 || recv_subscribed(priv, *msg, ds_id) ||
-                   get_mgt_id(*msg) != ds_id) {
-                       msg_put(*msg);
-                       *msg = NULL;
-                       continue;
-               }
-               priv->pmc_ds_requested = 0;
-               return 1;
-       }
-}
-
-static int run_pmc_wait_sync(struct phc2sys_private *priv, int timeout)
-{
-       struct ptp_message *msg;
-       int res;
-       void *data;
-       Enumeration8 portState;
-
-       while (1) {
-               res = run_pmc(priv, 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) */
-               priv->pmc_ds_requested = 1;
-       }
-}
-
-static int run_pmc_get_utc_offset(struct phc2sys_private *priv, int timeout)
-{
-       struct ptp_message *msg;
-       int res;
-       struct timePropertiesDS *tds;
-
-       res = run_pmc(priv, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg);
-       if (res <= 0)
-               return res;
-
-       tds = (struct timePropertiesDS *)get_mgt_data(msg);
-       if (tds->flags & PTP_TIMESCALE) {
-               priv->sync_offset = tds->currentUtcOffset;
-               if (tds->flags & LEAP_61)
-                       priv->leap = 1;
-               else if (tds->flags & LEAP_59)
-                       priv->leap = -1;
-               else
-                       priv->leap = 0;
-               priv->utc_offset_traceable = tds->flags & UTC_OFF_VALID &&
-                                            tds->flags & TIME_TRACEABLE;
-       } else {
-               priv->sync_offset = 0;
-               priv->leap = 0;
-               priv->utc_offset_traceable = 0;
-       }
-       msg_put(msg);
-       return 1;
-}
-
-static int run_pmc_get_number_ports(struct phc2sys_private *priv, int timeout)
-{
-       struct ptp_message *msg;
-       int res;
-       struct defaultDS *dds;
-
-       res = run_pmc(priv, 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;
-}
-
-static int run_pmc_subscribe(struct phc2sys_private *priv, int timeout)
-{
-       struct ptp_message *msg;
-       int res;
-
-       res = run_pmc(priv, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg);
-       if (res <= 0)
-               return res;
-       msg_put(msg);
-       return 1;
-}
-
-static void run_pmc_events(struct phc2sys_private *priv)
-{
-       struct ptp_message *msg;
-
-       run_pmc(priv, 0, -1, &msg);
-}
-
-static int run_pmc_port_properties(struct phc2sys_private *priv, 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(priv->pmc, port);
-       while (1) {
-               res = run_pmc(priv, 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(priv->pmc);
-       return res;
-}
-
-static int run_pmc_clock_identity(struct phc2sys_private *priv, int timeout)
-{
-       struct ptp_message *msg;
-       struct defaultDS *dds;
-       int res;
-
-       res = run_pmc(priv, timeout, TLV_DEFAULT_DATA_SET, &msg);
-       if (res <= 0)
-               return res;
-
-       dds = (struct defaultDS *)get_mgt_data(msg);
-       memcpy(&priv->clock_identity, &dds->clockIdentity,
-              sizeof(struct ClockIdentity));
-       priv->clock_identity_set = 1;
-       msg_put(msg);
-       return 1;
-}
-
-static void close_pmc(struct phc2sys_private *priv)
-{
-       pmc_destroy(priv->pmc);
-       priv->pmc = NULL;
-}
-
 static int auto_init_ports(struct phc2sys_private *priv, int add_rt)
 {
        struct port *port;
@@ -1170,7 +855,7 @@ static int auto_init_ports(struct phc2sys_private *priv, 
int add_rt)
        while (1) {
                if (!is_running())
                        return -1;
-               res = run_pmc_clock_identity(priv, 1000);
+               res = run_pmc_clock_identity(&priv->node, 1000);
                if (res < 0)
                        return -1;
                if (res > 0)
@@ -1179,20 +864,20 @@ static int auto_init_ports(struct phc2sys_private *priv, 
int add_rt)
                pr_notice("Waiting for ptp4l...");
        }
 
-       number_ports = run_pmc_get_number_ports(priv, 1000);
+       number_ports = run_pmc_get_number_ports(&priv->node, 1000);
        if (number_ports <= 0) {
                pr_err("failed to get number of ports");
                return -1;
        }
 
-       res = run_pmc_subscribe(priv, 1000);
+       res = run_pmc_subscribe(&priv->node, 1000);
        if (res <= 0) {
                pr_err("failed to subscribe");
                return -1;
        }
 
        for (i = 1; i <= number_ports; i++) {
-               res = run_pmc_port_properties(priv, 1000, i, &state,
+               res = run_pmc_port_properties(&priv->node, 1000, i, &state,
                                              &timestamping, iface);
                if (res == -1) {
                        /* port does not exist, ignore the port */
@@ -1229,44 +914,20 @@ static int auto_init_ports(struct phc2sys_private *priv, 
int add_rt)
        }
 
        /* get initial offset */
-       if (run_pmc_get_utc_offset(priv, 1000) <= 0) {
+       if (run_pmc_get_utc_offset(&priv->node, 1000) <= 0) {
                pr_err("failed to get UTC offset");
                return -1;
        }
        return 0;
 }
 
-/* Returns: -1 in case of error, 0 otherwise */
-static int update_pmc(struct phc2sys_private *priv, 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 (priv->pmc &&
-           !(ts > priv->pmc_last_update &&
-             ts - priv->pmc_last_update < PMC_UPDATE_INTERVAL)) {
-               if (subscribe)
-                       run_pmc_subscribe(priv, 0);
-               if (run_pmc_get_utc_offset(priv, 0) > 0)
-                       priv->pmc_last_update = ts;
-       }
-
-       return 0;
-}
-
 /* Returns: non-zero to skip clock update */
 static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock,
                             int64_t offset, uint64_t ts)
 {
-       int clock_leap, node_leap = priv->leap;
+       int clock_leap, node_leap = priv->node.leap;
 
-       clock->sync_offset = priv->sync_offset;
+       clock->sync_offset = priv->node.sync_offset;
 
        if ((node_leap || clock->leap_set) &&
            clock->is_utc != priv->master->is_utc) {
@@ -1307,7 +968,7 @@ static int clock_handle_leap(struct phc2sys_private *priv, 
struct clock *clock,
                }
        }
 
-       if (priv->utc_offset_traceable &&
+       if (priv->node.utc_offset_traceable &&
            clock->utc_offset_set != clock->sync_offset) {
                if (clock->clkid == CLOCK_REALTIME)
                        sysclk_set_tai_offset(clock->sync_offset);
@@ -1361,6 +1022,7 @@ static void usage(char *progname)
 int main(int argc, char *argv[])
 {
        char *config = NULL, *dst_name = NULL, *progname, *src_name = NULL;
+       char uds_local[MAX_IFNAME_SIZE + 1];
        struct clock *src, *dst;
        struct config *cfg;
        struct option *opts;
@@ -1469,7 +1131,7 @@ int main(int argc, char *argv[])
                                goto end;
                        break;
                case 'O':
-                       if (get_arg_val_i(c, optarg, &priv.sync_offset,
+                       if (get_arg_val_i(c, optarg, &priv.node.sync_offset,
                                          INT_MIN, INT_MAX))
                                goto end;
                        priv.forced_sync_offset = -1;
@@ -1589,8 +1251,12 @@ int main(int argc, char *argv[])
        priv.kernel_leap = config_get_int(cfg, NULL, "kernel_leap");
        priv.sanity_freq_limit = config_get_int(cfg, NULL, "sanity_freq_limit");
 
+       snprintf(uds_local, sizeof(uds_local), "/var/run/phc2sys.%d",
+                getpid());
+
        if (autocfg) {
-               if (init_pmc(cfg, &priv))
+               if (init_pmc_node(cfg, &priv.node, uds_local,
+                                 phc2sys_recv_subscribed))
                        goto end;
                if (auto_init_ports(&priv, rt) < 0)
                        goto end;
@@ -1627,11 +1293,12 @@ int main(int argc, char *argv[])
        r = -1;
 
        if (wait_sync) {
-               if (init_pmc(cfg, &priv))
+               if (init_pmc_node(cfg, &priv.node, uds_local,
+                                 phc2sys_recv_subscribed))
                        goto end;
 
                while (is_running()) {
-                       r = run_pmc_wait_sync(&priv, 1000);
+                       r = run_pmc_wait_sync(&priv.node, 1000);
                        if (r < 0)
                                goto end;
                        if (r > 0)
@@ -1641,7 +1308,7 @@ int main(int argc, char *argv[])
                }
 
                if (!priv.forced_sync_offset) {
-                       r = run_pmc_get_utc_offset(&priv, 1000);
+                       r = run_pmc_get_utc_offset(&priv.node, 1000);
                        if (r <= 0) {
                                pr_err("failed to get UTC offset");
                                goto end;
@@ -1651,7 +1318,7 @@ int main(int argc, char *argv[])
                if (priv.forced_sync_offset ||
                    (src->clkid != CLOCK_REALTIME && dst->clkid != 
CLOCK_REALTIME) ||
                    src->clkid == CLOCK_INVALID)
-                       close_pmc(&priv);
+                       close_pmc_node(&priv.node);
        }
 
        if (pps_fd >= 0) {
@@ -1664,8 +1331,7 @@ int main(int argc, char *argv[])
        }
 
 end:
-       if (priv.pmc)
-               close_pmc(&priv);
+       close_pmc_node(&priv.node);
        clock_cleanup(&priv);
        port_cleanup(&priv);
        config_destroy(cfg);
diff --git a/pmc_common.c b/pmc_common.c
index f07f6f65568f..89d7f40b84fe 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -22,6 +22,8 @@
 #include <string.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <net/if.h>
+#include <poll.h>
 
 #include "notification.h"
 #include "print.h"
@@ -56,6 +58,13 @@
 /* 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);
@@ -711,3 +720,331 @@ 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 9fa72deb4c87..a28bab767e9c 100644
--- a/pmc_common.h
+++ b/pmc_common.h
@@ -24,6 +24,7 @@
 #include "config.h"
 #include "msg.h"
 #include "transport.h"
+#include "fsm.h"
 
 struct pmc;
 
@@ -49,4 +50,38 @@ 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
-- 
2.25.1



_______________________________________________
Linuxptp-devel mailing list
Linuxptp-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel

Reply via email to