This is an immature implementation of non-GM 802.1AS-2011 time-aware bridge
which is based on P2P TC.
- Add BRIDGE clock type.
- Create bridge.c which is a copy of p2p_tc.c.
- Implement tc_fwd_folup_tlv() for bridge instead of tc_fwd_folup()
  used for p2p_tc.
- Create time-aware-bridge.cfg.

Signed-off-by: Yangbo Lu <yangbo...@nxp.com>
---
 bridge.c                      | 231 ++++++++++++++++++++++++++++++++++++++++++
 clock.c                       |   1 +
 clock.h                       |   1 +
 config.c                      |   1 +
 configs/time-aware-bridge.cfg |  38 +++++++
 makefile                      |   2 +-
 msg.c                         |   7 +-
 port.c                        |   6 +-
 port.h                        |   1 +
 port_private.h                |   3 +
 tc.c                          | 108 ++++++++++++++++++++
 tc.h                          |   2 +
 12 files changed, 398 insertions(+), 3 deletions(-)
 create mode 100644 bridge.c
 create mode 100644 configs/time-aware-bridge.cfg

diff --git a/bridge.c b/bridge.c
new file mode 100644
index 0000000..89be2b9
--- /dev/null
+++ b/bridge.c
@@ -0,0 +1,231 @@
+/**
+ * @file bridge.c
+ * @note Copyright (C) 2018 Richard Cochran <richardcoch...@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-1335 USA.
+ */
+#include <errno.h>
+
+#include "port.h"
+#include "port_private.h"
+#include "print.h"
+#include "rtnl.h"
+#include "tc.h"
+
+static int bridge_delay_request(struct port *p)
+{
+       switch (p->state) {
+       case PS_INITIALIZING:
+       case PS_FAULTY:
+       case PS_DISABLED:
+               return 0;
+       case PS_LISTENING:
+       case PS_PRE_MASTER:
+       case PS_MASTER:
+       case PS_PASSIVE:
+       case PS_UNCALIBRATED:
+       case PS_SLAVE:
+       case PS_GRAND_MASTER:
+               break;
+       }
+       return port_delay_request(p);
+}
+
+void bridge_dispatch(struct port *p, enum fsm_event event, int mdiff)
+{
+       if (!port_state_update(p, event, mdiff)) {
+               return;
+       }
+       if (!portnum(p)) {
+               /* UDS needs no timers. */
+               return;
+       }
+
+       port_clr_tmo(p->fda.fd[FD_ANNOUNCE_TIMER]);
+       port_clr_tmo(p->fda.fd[FD_SYNC_RX_TIMER]);
+       /* Leave FD_DELAY_TIMER running. */
+       port_clr_tmo(p->fda.fd[FD_QUALIFICATION_TIMER]);
+       port_clr_tmo(p->fda.fd[FD_MANNO_TIMER]);
+       port_clr_tmo(p->fda.fd[FD_SYNC_TX_TIMER]);
+
+       /*
+        * Handle the side effects of the state transition.
+        */
+       switch (p->state) {
+       case PS_INITIALIZING:
+               break;
+       case PS_FAULTY:
+       case PS_DISABLED:
+               port_disable(p);
+               break;
+       case PS_LISTENING:
+               port_set_announce_tmo(p);
+               port_set_delay_tmo(p);
+               break;
+       case PS_PRE_MASTER:
+               port_set_qualification_tmo(p);
+               break;
+       case PS_MASTER:
+       case PS_GRAND_MASTER:
+               break;
+       case PS_PASSIVE:
+               port_set_announce_tmo(p);
+               break;
+       case PS_UNCALIBRATED:
+       case PS_SLAVE:
+               port_set_announce_tmo(p);
+               break;
+       };
+}
+
+enum fsm_event bridge_event(struct port *p, int fd_index)
+{
+       int cnt, fd = p->fda.fd[fd_index];
+       enum fsm_event event = EV_NONE;
+       struct ptp_message *msg, *dup;
+
+       switch (fd_index) {
+       case FD_ANNOUNCE_TIMER:
+       case FD_SYNC_RX_TIMER:
+               pr_debug("port %hu: %s timeout", portnum(p),
+                        fd_index == FD_SYNC_RX_TIMER ? "rx sync" : "announce");
+               if (p->best) {
+                       fc_clear(p->best);
+               }
+               port_set_announce_tmo(p);
+               return EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES;
+
+       case FD_DELAY_TIMER:
+               pr_debug("port %hu: delay timeout", portnum(p));
+               port_set_delay_tmo(p);
+               tc_prune(p);
+               return bridge_delay_request(p) ? EV_FAULT_DETECTED : EV_NONE;
+
+       case FD_QUALIFICATION_TIMER:
+               pr_debug("port %hu: qualification timeout", portnum(p));
+               return EV_QUALIFICATION_TIMEOUT_EXPIRES;
+
+       case FD_MANNO_TIMER:
+       case FD_SYNC_TX_TIMER:
+       case FD_UNICAST_REQ_TIMER:
+       case FD_UNICAST_SRV_TIMER:
+               pr_err("unexpected timer expiration");
+               return EV_NONE;
+
+       case FD_RTNL:
+               pr_debug("port %hu: received link status notification", 
portnum(p));
+               rtnl_link_status(fd, p->name, port_link_status, p);
+               if (p->link_status == (LINK_UP|LINK_STATE_CHANGED)) {
+                       return EV_FAULT_CLEARED;
+               } else if ((p->link_status == (LINK_DOWN|LINK_STATE_CHANGED)) ||
+                          (p->link_status & TS_LABEL_CHANGED)) {
+                       return EV_FAULT_DETECTED;
+               } else {
+                       return EV_NONE;
+               }
+       }
+
+       msg = msg_allocate();
+       if (!msg) {
+               return EV_FAULT_DETECTED;
+       }
+       msg->hwts.type = p->timestamping;
+
+       cnt = transport_recv(p->trp, fd, msg);
+       if (cnt <= 0) {
+               pr_err("port %hu: recv message failed", portnum(p));
+               msg_put(msg);
+               return EV_FAULT_DETECTED;
+       }
+       if (msg_sots_valid(msg)) {
+               ts_add(&msg->hwts.ts, -p->rx_timestamp_offset);
+       }
+       if (msg_unicast(msg)) {
+               pl_warning(600, "cannot switch unicast messages!");
+               msg_put(msg);
+               return EV_NONE;
+       }
+
+       dup = msg_duplicate(msg, cnt);
+       if (!dup) {
+               msg_put(msg);
+               return EV_NONE;
+       }
+       if (tc_ignore(p, dup)) {
+               msg_put(dup);
+               dup = NULL;
+       }
+
+       switch (msg_type(msg)) {
+       case SYNC:
+               if (tc_fwd_sync(p, msg)) {
+                       event = EV_FAULT_DETECTED;
+                       break;
+               }
+               if (dup) {
+                       process_sync(p, dup);
+               }
+               break;
+       case DELAY_REQ:
+               break;
+       case PDELAY_REQ:
+               if (dup && process_pdelay_req(p, dup)) {
+                       event = EV_FAULT_DETECTED;
+               }
+               break;
+       case PDELAY_RESP:
+               if (dup && process_pdelay_resp(p, dup)) {
+                       event = EV_FAULT_DETECTED;
+               }
+               break;
+       case FOLLOW_UP:
+               if (tc_fwd_folup_tlv(p, msg, cnt)) {
+                       event = EV_FAULT_DETECTED;
+                       break;
+               }
+               if (dup) {
+                       process_follow_up(p, dup);
+               }
+               break;
+       case DELAY_RESP:
+               break;
+       case PDELAY_RESP_FOLLOW_UP:
+               if (dup) {
+                       process_pdelay_resp_fup(p, dup);
+               }
+               break;
+       case ANNOUNCE:
+               if (tc_forward(p, msg)) {
+                       event = EV_FAULT_DETECTED;
+                       break;
+               }
+               if (dup && process_announce(p, dup)) {
+                       event = EV_STATE_DECISION_EVENT;
+               }
+               break;
+       case SIGNALING:
+       case MANAGEMENT:
+               if (tc_forward(p, msg)) {
+                       event = EV_FAULT_DETECTED;
+               }
+               break;
+       }
+
+       msg_put(msg);
+       if (dup) {
+               msg_put(dup);
+       }
+       return event;
+}
diff --git a/clock.c b/clock.c
index 146576a..4e6fc79 100644
--- a/clock.c
+++ b/clock.c
@@ -878,6 +878,7 @@ struct clock *clock_create(enum clock_type type, struct 
config *config,
        case CLOCK_TYPE_BOUNDARY:
        case CLOCK_TYPE_P2P:
        case CLOCK_TYPE_E2E:
+       case CLOCK_TYPE_BRIDGE:
                c->type = type;
                break;
        case CLOCK_TYPE_MANAGEMENT:
diff --git a/clock.h b/clock.h
index 9d3133a..646aed7 100644
--- a/clock.h
+++ b/clock.h
@@ -40,6 +40,7 @@ enum clock_type {
        CLOCK_TYPE_P2P        = 0x2000,
        CLOCK_TYPE_E2E        = 0x1000,
        CLOCK_TYPE_MANAGEMENT = 0x0800,
+       CLOCK_TYPE_BRIDGE     = 0x0400,
 };
 
 /**
diff --git a/config.c b/config.c
index 12eb1f9..c335b02 100644
--- a/config.c
+++ b/config.c
@@ -143,6 +143,7 @@ static struct config_enum clock_type_enu[] = {
        { "BC",      CLOCK_TYPE_BOUNDARY },
        { "P2P_TC",  CLOCK_TYPE_P2P      },
        { "E2E_TC",  CLOCK_TYPE_E2E      },
+       { "BRIDGE",  CLOCK_TYPE_BRIDGE   },
        { NULL, 0 },
 };
 
diff --git a/configs/time-aware-bridge.cfg b/configs/time-aware-bridge.cfg
new file mode 100644
index 0000000..2517c6d
--- /dev/null
+++ b/configs/time-aware-bridge.cfg
@@ -0,0 +1,38 @@
+#
+# IEEE 802.1AS-2011 time-aware bridge example configuration containing
+# those attributes which differ from the defaults.  See the file,
+# default.cfg, for the complete list of available options.
+#
+[global]
+#
+# Default Data Set
+#
+priority1              255
+priority2              248
+free_running           0
+#
+# Port Data Set
+#
+neighborPropDelayThresh        800
+min_neighbor_prop_delay        -20000000
+syncReceiptTimeout     3
+logSyncInterval                -3
+#
+# Run time options
+#
+assume_two_step                1
+follow_up_info         1
+summary_interval       -3
+path_trace_enabled     1
+tc_spanning_tree       1
+#
+# Transport options
+#
+transportSpecific      0x1
+ptp_dst_mac            01:80:C2:00:00:0E
+#
+# Default interface options
+#
+clock_type             BRIDGE
+network_transport      L2
+delay_mechanism                P2P
diff --git a/makefile b/makefile
index 3397d3e..9bbac7a 100644
--- a/makefile
+++ b/makefile
@@ -23,7 +23,7 @@ VER     = -DVER=$(version)
 CFLAGS = -Wall $(VER) $(incdefs) $(DEBUG) $(EXTRA_CFLAGS)
 LDLIBS = -lm -lrt $(EXTRA_LDFLAGS)
 PRG    = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster
-OBJ     = bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \
+OBJ     = bmc.o bridge.o clock.o clockadj.o clockcheck.o config.o 
designated_fsm.o \
 e2e_tc.o fault.o filter.o fsm.o hash.o linreg.o mave.o mmedian.o msg.o 
ntpshm.o \
 nullf.o phc.o pi.o port.o port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o \
 raw.o rtnl.o servo.o sk.o stats.o tc.o telecom.o tlv.o transport.o tsproc.o \
diff --git a/msg.c b/msg.c
index d1619d4..ecabc28 100644
--- a/msg.c
+++ b/msg.c
@@ -240,9 +240,14 @@ static void timestamp_post_recv(struct ptp_message *m, 
struct Timestamp *ts)
 {
        uint32_t lsb = ntohl(ts->seconds_lsb);
        uint16_t msb = ntohs(ts->seconds_msb);
+       uint32_t nsec = ntohl(ts->nanoseconds);
 
        m->ts.pdu.sec  = ((uint64_t)lsb) | (((uint64_t)msb) << 32);
-       m->ts.pdu.nsec = ntohl(ts->nanoseconds);
+       m->ts.pdu.nsec = nsec;
+
+       ts->seconds_lsb = lsb;
+       ts->seconds_msb = msb;
+       ts->nanoseconds = nsec;
 }
 
 static void timestamp_pre_send(struct Timestamp *ts)
diff --git a/port.c b/port.c
index 07ad3f0..413611d 100644
--- a/port.c
+++ b/port.c
@@ -474,7 +474,7 @@ static int net_sync_resp_append(struct port *p, struct 
ptp_message *m)
        return 0;
 }
 
-static struct follow_up_info_tlv *follow_up_info_extract(struct ptp_message *m)
+struct follow_up_info_tlv *follow_up_info_extract(struct ptp_message *m)
 {
        struct follow_up_info_tlv *f;
        struct tlv_extra *extra;
@@ -2969,6 +2969,10 @@ struct port *port_open(int phc_index,
                p->dispatch = e2e_dispatch;
                p->event = e2e_event;
                break;
+       case CLOCK_TYPE_BRIDGE:
+               p->dispatch = bridge_dispatch;
+               p->event = bridge_event;
+               break;
        case CLOCK_TYPE_MANAGEMENT:
                goto err_port;
        }
diff --git a/port.h b/port.h
index aa3b1ec..61ace36 100644
--- a/port.h
+++ b/port.h
@@ -335,4 +335,5 @@ enum bmca_select port_bmca(struct port *p);
  */
 void tc_cleanup(void);
 
+struct follow_up_info_tlv *follow_up_info_extract(struct ptp_message *m);
 #endif
diff --git a/port_private.h b/port_private.h
index 5789fbb..a39bfdb 100644
--- a/port_private.h
+++ b/port_private.h
@@ -159,6 +159,9 @@ enum fsm_event e2e_event(struct port *p, int fd_index);
 void p2p_dispatch(struct port *p, enum fsm_event event, int mdiff);
 enum fsm_event p2p_event(struct port *p, int fd_index);
 
+void bridge_dispatch(struct port *p, enum fsm_event event, int mdiff);
+enum fsm_event bridge_event(struct port *p, int fd_index);
+
 int clear_fault_asap(struct fault_interval *faint);
 void delay_req_prune(struct port *p);
 void fc_clear(struct foreign_clock *fc);
diff --git a/tc.c b/tc.c
index fb46603..e1e8dd5 100644
--- a/tc.c
+++ b/tc.c
@@ -23,6 +23,8 @@
 #include "tc.h"
 #include "tmv.h"
 
+#define POW2_41 ((double)(1ULL << 41))
+
 enum tc_match {
        TC_MISMATCH,
        TC_SYNC_FUP,
@@ -235,6 +237,89 @@ static void tc_complete_syfup(struct port *q, struct port 
*p,
        tc_recycle(txd);
 }
 
+static void tc_send_folup_tlv(struct port *q, struct port *p,
+                             struct ptp_message *fup_dup,
+                             tmv_t residence)
+{
+       struct follow_up_info_tlv *fui;
+       Integer64 c1, c2;
+       double gm_rr, nrr;
+       int count;
+
+       fui = follow_up_info_extract(fup_dup);
+       if (!fui) {
+               pr_err("tc failed to extract folup_tlv!");
+               return;
+       }
+
+       nrr = q->nrate.ratio;
+       gm_rr = 1.0 + (fui->cumulativeScaledRateOffset + 0.0) / POW2_41;
+
+       /* Convert residence/peer_delay to grand master time */
+       c1 = fup_dup->header.correction;
+       c2 = c1 + tmv_to_TimeInterval(residence) * gm_rr * nrr;
+       c2 += tmv_to_TimeInterval(q->peer_delay) * gm_rr;
+       c2 += q->asymmetry;
+       fup_dup->header.correction = c2;
+
+       /* Accumulate neighbor rate ratio */
+       fui->cumulativeScaledRateOffset = (gm_rr * nrr - 1.0) * POW2_41;
+
+       if (msg_pre_send(fup_dup)) {
+               pr_err("tc failed to prepare folup_tlv on port %d", portnum(p));
+       }
+
+       count = transport_send(p->trp, &p->fda, TRANS_GENERAL, fup_dup);
+       if (count <= 0) {
+               pr_err("tc failed to forward follow up on port %d", portnum(p));
+               port_dispatch(p, EV_FAULT_DETECTED, 0);
+       }
+}
+
+static void tc_complete_folup_tlv(struct port *q, struct port *p,
+                                 struct ptp_message *msg,
+                                 struct ptp_message *dup)
+{
+       enum tc_match type = TC_MISMATCH;
+       struct tc_txd *txd;
+
+       TAILQ_FOREACH(txd, &p->tc_transmitted, list) {
+               type = tc_match_syfup(portnum(q), msg, txd);
+               switch (type) {
+               case TC_MISMATCH:
+                       break;
+               case TC_SYNC_FUP:
+                       break;
+               default:
+                       pr_err("tc: unexpected match in 
tc_complete_folup_tlv!");
+                       return;
+               }
+               if (type != TC_MISMATCH) {
+                       break;
+               }
+       }
+
+       if (type == TC_MISMATCH) {
+               txd = tc_allocate();
+               if (!txd) {
+                       port_dispatch(p, EV_FAULT_DETECTED, 0);
+                       return;
+               }
+               msg_get(msg);
+               txd->msg = msg;
+               txd->residence = tmv_zero();
+               txd->ingress_port = portnum(q);
+               TAILQ_INSERT_TAIL(&p->tc_transmitted, txd, list);
+               return;
+       }
+
+       tc_send_folup_tlv(q, p, dup, txd->residence);
+
+       TAILQ_REMOVE(&p->tc_transmitted, txd, list);
+       msg_put(txd->msg);
+       tc_recycle(txd);
+}
+
 static void tc_complete(struct port *q, struct port *p,
                        struct ptp_message *msg, tmv_t residence)
 {
@@ -420,6 +505,29 @@ int tc_fwd_folup(struct port *q, struct ptp_message *msg)
        return 0;
 }
 
+int tc_fwd_folup_tlv(struct port *q, struct ptp_message *msg, int cnt)
+{
+       struct port *p;
+
+       clock_gettime(CLOCK_MONOTONIC, &msg->ts.host);
+
+       for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) {
+               struct ptp_message *dup;
+
+               if (tc_blocked(q, p, msg)) {
+                       continue;
+               }
+
+               dup = msg_duplicate(msg, cnt);
+               if (!dup)
+                       return EV_NONE;
+
+               tc_complete_folup_tlv(q, p, msg, dup);
+               msg_put(dup);
+       }
+       return 0;
+}
+
 int tc_fwd_request(struct port *q, struct ptp_message *msg)
 {
        return tc_fwd_event(q, msg);
diff --git a/tc.h b/tc.h
index ef82351..83dcf70 100644
--- a/tc.h
+++ b/tc.h
@@ -49,6 +49,8 @@ int tc_forward(struct port *q, struct ptp_message *msg);
  */
 int tc_fwd_folup(struct port *q, struct ptp_message *msg);
 
+int tc_fwd_folup_tlv(struct port *q, struct ptp_message *msg, int cnt);
+
 /**
  * Forwards a given delay request message out all other ports.
  *
-- 
2.7.4



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

Reply via email to