This patch adds a new "NSM" command to the pmc program.  The new code
handles only one outstanding NSM command at a time.  If and when all
four event time stamps have arrived, the code prints the instantaneous
estimated offset without any averaging or smoothing.

Signed-off-by: Richard Cochran <richardcoch...@gmail.com>
---
 pmc.c        |  28 +++++-
 pmc_common.c | 324 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 pmc_common.h |  20 ++++
 3 files changed, 363 insertions(+), 9 deletions(-)

diff --git a/pmc.c b/pmc.c
index a15e800..c75b590 100644
--- a/pmc.c
+++ b/pmc.c
@@ -36,6 +36,7 @@
 #include "version.h"
 
 #define BAD_ACTION   -1
+#define NSM_ACTION   -2
 #define BAD_ID       -1
 #define AMBIGUOUS_ID -2
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
@@ -117,8 +118,6 @@ static const char *action_string[] = {
        "ACKNOWLEDGE",
 };
 
-#define IFMT "\n\t\t"
-
 static char *text2str(struct PTPText *text)
 {
        static struct static_ptp_text s;
@@ -561,6 +560,8 @@ static int parse_action(char *s)
                return COMMAND;
        else if (0 == strncasecmp(s, "COMMAND", len))
                return COMMAND;
+       else if (0 == strncasecmp(s, "NSM", len))
+               return NSM_ACTION;
        return BAD_ACTION;
 }
 
@@ -613,6 +614,11 @@ static void print_help(FILE *fp)
        fprintf(fp, "\tTARGET [portIdentity]\n");
        fprintf(fp, "\tTARGET *\n");
        fprintf(fp, "\n");
+       fprintf(fp, "\tSend a NetSync Monitor request to a specific port 
address:\n");
+       fprintf(fp, "\n");
+       fprintf(fp, "\tNSM 111.222.333.444\n");
+       fprintf(fp, "\tNSM aa.bb.cc.dd.ee.ff\n");
+       fprintf(fp, "\n");
 }
 
 static int do_command(char *str)
@@ -632,6 +638,9 @@ static int do_command(char *str)
                return parse_target(id_str);
 
        action = parse_action(action_str);
+       if (action == NSM_ACTION) {
+               return pmc_nsm_request(pmc, id_str);
+       }
        id = parse_id(id_str);
 
        if (action == BAD_ACTION || id == BAD_ID)
@@ -688,7 +697,7 @@ int main(int argc, char *argv[])
        UInteger8 boundary_hops = 1, domain_number = 0, transport_specific = 0;
        struct ptp_message *msg;
        struct config *cfg;
-#define N_FD 2
+#define N_FD 3
        struct pollfd pollfd[N_FD];
 
        handle_term_signals();
@@ -794,6 +803,7 @@ int main(int argc, char *argv[])
 
        pollfd[0].fd = batch_mode ? -1 : STDIN_FILENO;
        pollfd[1].fd = pmc_get_transport_fd(pmc);
+       pollfd[2].fd = pmc_get_transport_event_fd(pmc);
 
        while (is_running()) {
                if (batch_mode && !command) {
@@ -808,6 +818,7 @@ int main(int argc, char *argv[])
 
                pollfd[0].events = 0;
                pollfd[1].events = POLLIN | POLLPRI;
+               pollfd[2].events = POLLIN | POLLPRI;
 
                if (!batch_mode && !command)
                        pollfd[0].events |= POLLIN | POLLPRI;
@@ -856,7 +867,16 @@ int main(int argc, char *argv[])
                if (pollfd[1].revents & (POLLIN|POLLPRI)) {
                        msg = pmc_recv(pmc);
                        if (msg) {
-                               pmc_show(msg, stdout);
+                               if (!pmc_handle_nsm(pmc, msg, stdout)) {
+                                       pmc_show(msg, stdout);
+                               }
+                               msg_put(msg);
+                       }
+               }
+               if (pollfd[2].revents & (POLLIN|POLLPRI)) {
+                       msg = pmc_recv_event(pmc);
+                       if (msg) {
+                               pmc_handle_nsm(pmc, msg, stdout);
                                msg_put(msg);
                        }
                }
diff --git a/pmc_common.c b/pmc_common.c
index da25c21..6366f76 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -17,9 +17,13 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
+#include <arpa/inet.h>
 #include <errno.h>
-#include <string.h>
+#include <netinet/in.h>
+#include <stdint.h>
 #include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
 
 #include "print.h"
 #include "tlv.h"
@@ -60,8 +64,75 @@ struct pmc {
        struct fdarray fdarray;
        struct tsproc *tsproc;
        int zero_length_gets;
+
+       /* NetSync Monitor */
+       struct ptp_message *nsm_delay_req;
+       struct ptp_message *nsm_delay_resp;
+       struct ptp_message *nsm_sync;
+       struct ptp_message *nsm_fup;
+       enum timestamp_type timestamping;
+       Integer64 asymmetry;
 };
 
+static int nsm_complete(struct pmc *pmc)
+{
+       if (!pmc->nsm_sync) {
+               return 0;
+       }
+       if (one_step(pmc->nsm_sync)) {
+               return pmc->nsm_delay_resp ? 1 : 0;
+       }
+       return (pmc->nsm_delay_resp && pmc->nsm_fup) ? 1 : 0;
+}
+
+static int64_t nsm_compute_offset(struct tsproc *tsp,
+                                 struct ptp_message *syn,
+                                 struct ptp_message *fup,
+                                 struct ptp_message *req,
+                                 struct ptp_message *resp)
+{
+       tmv_t c1, c2, c3, t1, t1c, t2, t3, t4, t4c, offset;
+
+       c1 = correction_to_tmv(syn->header.correction);
+       c2 = correction_to_tmv(fup->header.correction);
+       c3 = correction_to_tmv(resp->header.correction);
+
+       t1 = timestamp_to_tmv(fup->ts.pdu);
+       t2 = timespec_to_tmv(syn->hwts.ts);
+       t3 = timespec_to_tmv(req->hwts.ts);
+       t4 = timestamp_to_tmv(resp->ts.pdu);
+
+       t1c = tmv_add(t1, tmv_add(c1, c2));
+       t4c = tmv_sub(t4, c3);
+
+       tsproc_reset(tsp, 1);
+       tsproc_down_ts(tsp, t1c, t2);
+       tsproc_up_ts(tsp, t3, t4c);
+       tsproc_update_offset(tsp, &offset, NULL);
+
+       return tmv_to_nanoseconds(offset);
+}
+
+static void nsm_reset(struct pmc *pmc)
+{
+       if (pmc->nsm_delay_req) {
+               msg_put(pmc->nsm_delay_req);
+       }
+       if (pmc->nsm_delay_resp) {
+               msg_put(pmc->nsm_delay_resp);
+       }
+       if (pmc->nsm_sync) {
+               msg_put(pmc->nsm_sync);
+       }
+       if (pmc->nsm_fup) {
+               msg_put(pmc->nsm_fup);
+       }
+       pmc->nsm_delay_req = NULL;
+       pmc->nsm_delay_resp = NULL;
+       pmc->nsm_sync = NULL;
+       pmc->nsm_fup = NULL;
+}
+
 struct pmc *pmc_create(struct config *cfg, enum transport_type transport_type,
                       const char *iface_name, UInteger8 boundary_hops,
                       UInteger8 domain_number, UInteger8 transport_specific,
@@ -107,6 +178,10 @@ struct pmc *pmc_create(struct config *cfg, enum 
transport_type transport_type,
        }
        pmc->zero_length_gets = zero_datalen ? 1 : 0;
 
+       pmc->timestamping = config_get_int(cfg, NULL, "time_stamping");
+       pmc->asymmetry = config_get_int(cfg, iface_name, "delayAsymmetry");
+       pmc->asymmetry <<= 16;
+
        return pmc;
 
 failed:
@@ -120,6 +195,7 @@ failed:
 
 void pmc_destroy(struct pmc *pmc)
 {
+       nsm_reset(pmc);
        tsproc_destroy(pmc->tsproc);
        transport_close(pmc->transport, &pmc->fdarray);
        transport_destroy(pmc->transport);
@@ -238,6 +314,228 @@ int pmc_get_transport_event_fd(struct pmc *pmc)
        return pmc->fdarray.fd[FD_EVENT];
 }
 
+int pmc_handle_nsm(struct pmc *pmc, struct ptp_message *msg, FILE *fp)
+{
+       struct nsm_resp_tlv_head *head;
+       struct nsm_resp_tlv_foot *foot;
+       struct timePropertiesDS *tp;
+       struct PortAddress *paddr;
+       struct currentDS cds;
+       struct parentDS *pds;
+       struct Timestamp ts;
+       unsigned char *ptr;
+       int64_t offset;
+
+       if (!pmc->nsm_delay_req) {
+               return 0;
+       }
+       if (msg->header.sequenceId !=
+           ntohs(pmc->nsm_delay_req->header.sequenceId)) {
+               return 0;
+       }
+       if (!(msg->header.flagField[0] & UNICAST)) {
+               return 0;
+       }
+
+       switch (msg_type(msg)) {
+       case SYNC:
+               if (!pmc->nsm_sync) {
+                       pmc->nsm_sync = msg;
+                       msg_get(msg);
+               }
+               break;
+       case FOLLOW_UP:
+               if (!pmc->nsm_fup) {
+                       pmc->nsm_fup = msg;
+                       msg_get(msg);
+               }
+               break;
+       case DELAY_RESP:
+               if (!pmc->nsm_delay_resp) {
+                       pmc->nsm_delay_resp = msg;
+                       msg_get(msg);
+               }
+               break;
+       case DELAY_REQ:
+       case PDELAY_REQ:
+       case PDELAY_RESP:
+       case PDELAY_RESP_FOLLOW_UP:
+       case ANNOUNCE:
+       case SIGNALING:
+       case MANAGEMENT:
+               return 0;
+       }
+
+       if (!nsm_complete(pmc)) {
+               return 1;
+       }
+
+       head = (struct nsm_resp_tlv_head *) 
pmc->nsm_delay_resp->delay_resp.suffix;
+       paddr = &head->parent_addr;
+
+       ptr = (unsigned char *) head;
+       ptr += sizeof(*head) + paddr->addressLength;
+       foot = (struct nsm_resp_tlv_foot *) ptr;
+
+       pds = &foot->parent;
+       memcpy(&cds, &foot->current, sizeof(cds));
+       tp = &foot->timeprop;
+       memcpy(&ts, &foot->lastsync, sizeof(ts));
+
+       offset = nsm_compute_offset(pmc->tsproc, pmc->nsm_sync, pmc->nsm_fup,
+                                   pmc->nsm_delay_req, pmc->nsm_delay_resp);
+
+       fprintf(fp, "NSM MEASUREMENT COMPLETE"
+               IFMT "offset                                %" PRId64
+               IFMT "portState                             %s"
+               IFMT "parentPortAddress                     %hu %s\n",
+               offset,
+               ps_str[head->port_state],
+               head->parent_addr.networkProtocol,
+               portaddr2str(&head->parent_addr));
+       fprintf(fp, "\tparentDataset"
+               IFMT "parentPortIdentity                    %s"
+               IFMT "parentStats                           %hhu"
+               IFMT "observedParentOffsetScaledLogVariance 0x%04hx"
+               IFMT "observedParentClockPhaseChangeRate    0x%08x"
+               IFMT "grandmasterPriority1                  %hhu"
+               IFMT "gm.ClockClass                         %hhu"
+               IFMT "gm.ClockAccuracy                      0x%02hhx"
+               IFMT "gm.OffsetScaledLogVariance            0x%04hx"
+               IFMT "grandmasterPriority2                  %hhu"
+               IFMT "grandmasterIdentity                   %s\n",
+               pid2str(&pds->parentPortIdentity),
+               pds->parentStats,
+               pds->observedParentOffsetScaledLogVariance,
+               pds->observedParentClockPhaseChangeRate,
+               pds->grandmasterPriority1,
+               pds->grandmasterClockQuality.clockClass,
+               pds->grandmasterClockQuality.clockAccuracy,
+               pds->grandmasterClockQuality.offsetScaledLogVariance,
+               pds->grandmasterPriority2,
+               cid2str(&pds->grandmasterIdentity));
+       fprintf(fp, "\tcurrentDataset"
+               IFMT "stepsRemoved                          %hd"
+               IFMT "offsetFromMaster                      %.1f"
+               IFMT "meanPathDelay                         %.1f\n",
+               cds.stepsRemoved, cds.offsetFromMaster / 65536.0,
+               cds.meanPathDelay / 65536.0);
+       fprintf(fp, "\ttimePropertiesDataset"
+               IFMT "currentUtcOffset                      %hd"
+               IFMT "leap61                                %d"
+               IFMT "leap59                                %d"
+               IFMT "currentUtcOffsetValid                 %d"
+               IFMT "ptpTimescale                          %d"
+               IFMT "timeTraceable                         %d"
+               IFMT "frequencyTraceable                    %d"
+               IFMT "timeSource                            0x%02hhx\n",
+               tp->currentUtcOffset,
+               tp->flags & LEAP_61 ? 1 : 0,
+               tp->flags & LEAP_59 ? 1 : 0,
+               tp->flags & UTC_OFF_VALID ? 1 : 0,
+               tp->flags & PTP_TIMESCALE ? 1 : 0,
+               tp->flags & TIME_TRACEABLE ? 1 : 0,
+               tp->flags & FREQ_TRACEABLE ? 1 : 0,
+               tp->timeSource);
+       fprintf(fp, "\tlastSyncTimestamp    %" PRId64 ".%09u\n",
+               ((uint64_t)ts.seconds_lsb) | (((uint64_t)ts.seconds_msb) << 32),
+               ts.nanoseconds);
+
+       fflush(fp);
+       nsm_reset(pmc);
+
+       return 1;
+}
+
+int pmc_nsm_request(struct pmc *pmc, char *target)
+{
+       enum transport_type type = transport_type(pmc->transport);
+       unsigned char mac[MAC_LEN];
+       struct in_addr ipv4_addr;
+       struct ptp_message *msg;
+       int cnt, err, pdulen;
+       struct address dst;
+       struct TLV *tlv;
+
+       memset(&dst, 0, sizeof(dst));
+
+       switch (type) {
+       case TRANS_UDS:
+       case TRANS_UDP_IPV6:
+       case TRANS_DEVICENET:
+       case TRANS_CONTROLNET:
+       case TRANS_PROFINET:
+               pr_err("sorry, NSM not support with this transport");
+               return -1;
+       case TRANS_UDP_IPV4:
+               if (!inet_aton(target, &ipv4_addr)) {
+                       pr_err("bad IPv4 address");
+                       return -1;
+               }
+               dst.sin.sin_family = AF_INET;
+               dst.sin.sin_addr = ipv4_addr;
+               dst.len = sizeof(dst.sin);
+               break;
+       case TRANS_IEEE_802_3:
+               if (str2mac(target, mac)) {
+                       pr_err("bad Layer-2 address");
+                       return -1;
+               }
+               dst.sll.sll_family = AF_PACKET;
+               dst.sll.sll_halen = MAC_LEN;
+               memcpy(&dst.sll.sll_addr, mac, MAC_LEN);
+               dst.len = sizeof(dst.sll);
+               break;
+       }
+
+       msg = msg_allocate();
+       if (!msg) {
+               return -1;
+       }
+       pdulen = sizeof(struct delay_req_msg) + sizeof(*tlv);
+       tlv = (struct TLV *) msg->delay_req.suffix;
+       tlv->type = TLV_PTPMON_REQ;
+       tlv->length = 0;
+       msg->tlv_count = 1;
+       msg->hwts.type = pmc->timestamping;
+
+       msg->header.tsmt               = DELAY_REQ | pmc->transport_specific;
+       msg->header.ver                = PTP_VERSION;
+       msg->header.messageLength      = pdulen;
+       msg->header.domainNumber       = pmc->domain_number;
+       msg->header.correction         = -pmc->asymmetry;
+       msg->header.sourcePortIdentity = pmc->port_identity;
+       msg->header.sequenceId         = pmc->sequence_id++;
+       msg->header.control            = CTL_DELAY_REQ;
+       msg->header.logMessageInterval = 0x7f;
+
+       msg->address = dst;
+       msg->header.flagField[0] |= UNICAST;
+
+       err = msg_pre_send(msg);
+       if (err) {
+               pr_err("msg_pre_send failed");
+               goto out;
+       }
+       cnt = transport_sendto(pmc->transport, &pmc->fdarray, 1, msg);
+       if (cnt <= 0) {
+               pr_err("transport_sendto failed");
+               err = -1;
+               goto out;
+       }
+       if (msg_sots_missing(msg)) {
+               pr_err("missing timestamp on transmitted delay request");
+               err = -1;
+               goto out;
+       }
+       nsm_reset(pmc);
+       pmc->nsm_delay_req = msg;
+       return 0;
+out:
+       msg_put(msg);
+       return err;
+}
+
 int pmc_send_get_action(struct pmc *pmc, int id)
 {
        int datalen, pdulen;
@@ -301,18 +599,24 @@ int pmc_send_set_action(struct pmc *pmc, int id, void 
*data, int datasize)
        return 0;
 }
 
-struct ptp_message *pmc_recv(struct pmc *pmc)
+static struct ptp_message *pmc_recv_internal(struct pmc *pmc, int event)
 {
        struct ptp_message *msg;
-       int cnt, err;
+       int cnt, err, fd;
 
        msg = msg_allocate();
        if (!msg) {
                pr_err("low memory");
                return NULL;
        }
-       msg->hwts.type = TS_SOFTWARE;
-       cnt = transport_recv(pmc->transport, pmc_get_transport_fd(pmc), msg);
+       if (event) {
+               msg->hwts.type = pmc->timestamping;
+               fd = pmc_get_transport_event_fd(pmc);
+       } else {
+               msg->hwts.type = TS_SOFTWARE;
+               fd = pmc_get_transport_fd(pmc);
+       }
+       cnt = transport_recv(pmc->transport, fd, msg);
        if (cnt <= 0) {
                pr_err("recv message failed");
                goto failed;
@@ -339,6 +643,16 @@ failed:
        return NULL;
 }
 
+struct ptp_message *pmc_recv(struct pmc *pmc)
+{
+       return pmc_recv_internal(pmc, 0);
+}
+
+struct ptp_message *pmc_recv_event(struct pmc *pmc)
+{
+       return pmc_recv_internal(pmc, 1);
+}
+
 int pmc_target(struct pmc *pmc, struct PortIdentity *pid)
 {
        pmc->target = *pid;
diff --git a/pmc_common.h b/pmc_common.h
index d5ccb29..f951d34 100644
--- a/pmc_common.h
+++ b/pmc_common.h
@@ -25,6 +25,8 @@
 #include "msg.h"
 #include "transport.h"
 
+#define IFMT "\n\t\t"
+
 struct pmc;
 
 struct pmc *pmc_create(struct config *cfg, enum transport_type transport_type,
@@ -37,11 +39,29 @@ void pmc_destroy(struct pmc *pmc);
 int pmc_get_transport_fd(struct pmc *pmc);
 int pmc_get_transport_event_fd(struct pmc *pmc);
 
+/**
+ * Handle a NetSync Monitor message.
+ * @param pmc  A pointer obtained with pmc_create().
+ * @param msg  A message which may or may not be a NetSync Monitor message.
+ * @param fp   An open file for logging.
+ * @return     One (1) if the message was handled, zero (0) otherwise.
+ */
+int pmc_handle_nsm(struct pmc *pmc, struct ptp_message *msg, FILE *fp);
+
+/**
+ * Sends a NetSync Monitor delay request to a given node.
+ * @param pmc     A pointer obtained with pmc_create().
+ * @param target  The address of the node to measure.
+ * @return        Zero on success, non-zero otherwise.
+ */
+int pmc_nsm_request(struct pmc *pmc, char *target);
+
 int pmc_send_get_action(struct pmc *pmc, int id);
 
 int pmc_send_set_action(struct pmc *pmc, int id, void *data, int datasize);
 
 struct ptp_message *pmc_recv(struct pmc *pmc);
+struct ptp_message *pmc_recv_event(struct pmc *pmc);
 
 int pmc_target(struct pmc *pmc, struct PortIdentity *pid);
 void pmc_target_port(struct pmc *pmc, UInteger16 portNumber);
-- 
2.11.0


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Linuxptp-devel mailing list
Linuxptp-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel

Reply via email to