From: SyncMonk <servi...@syncmonk.net>

1. Inclusion of virtual PTP port on a PTP clock. (G.8275 Annex B)
2. Alternate master support (G.8275.2 Section 6.9 and G.8275.2 Appendix III)
3. TLV support for interface rate (G.8275.2 Annex D)
   When master and slave instance interacting with each other operating
   at different interface speed, delay assymetry needs to be compensated
   as described in G.8271 appendix V.
4. Delay mechanism
   As per IEEE1588-2019, Table 21 adding NO_MECHANISM support. In this
   case port does not implement the delay mechanism.
5. Packet Timing Signal Fail (PTSF)(G.8275.2 Section 6.7.11)
     - PTSF-lossSync
        signal raised due to  lack of reception of PTP Sycn, Followup or
        delay response messages
     - PTSF-unusable
        unusable PTP packet timing signal received by the request-port,
        exceeding the input tolerance of the request-port
6. Dynamic data set updates for external servo.
    - defaultDS.clockQuality.clockClass
        To support holdover in the T-BC-P/A and T-GM.
    - timePropertiesDS.frequencyTraceable
        To support availability of a PRC-traceable physical layer
        frequency input signal
    - PTSF Unusable.

Signed-off-by: Greg Armstrong <greg.armstrong...@renesas.com>;
Signed-off-by: Vipin Sharma <vipin.sha...@syncmonk.net>;
Signed-off-by: Devasish Dey <devasish....@syncmonk.net>;
Signed-off-by: Leon Goldin <leon.goldin...@renesas.com>
---
 clock.c                 |  77 ++++++++++-
 clock.h                 |  23 ++++
 config.c                |  30 ++++-
 configs/default.cfg     |   3 +
 dm.h                    |  18 +++
 ds.h                    |  17 +++
 interface.c             |  58 ++++++++
 interface.h             |  29 ++++
 makefile                |   8 +-
 monitor.c               |  71 ++++++++++
 monitor.h               |   3 +
 msg.h                   |  17 +++
 pdt.h                   |   9 ++
 pmc.c                   |  69 +++++++++-
 pmc_common.c            |  29 ++++
 port.c                  | 194 +++++++++++++++++++++++----
 port_private.h          |  16 ++-
 port_signaling.c        |  41 ++++++
 ptp4l.8                 |  11 +-
 ptp4l.c                 |  19 ++-
 sk.c                    |  74 +++++++++++
 sk.h                    |  26 ++++
 tlv.c                   |  48 +++++++
 tlv.h                   |  54 ++++++++
 transport.c             |  12 ++
 transport.h             |   9 ++
 ts2phc_phc_pps_source.c |  24 ++++
 ts2phc_phc_pps_source.h |   4 +
 ts2phc_pps_sink.c       |  14 +-
 ts2phc_vport.c          | 288 ++++++++++++++++++++++++++++++++++++++++
 ts2phc_vport.h          |  31 +++++
 tsproc.c                |  13 ++
 tsproc.h                |   9 ++
 unicast_client.c        |  18 ++-
 unicast_service.c       |  41 +++++-
 util.c                  |   9 ++
 vport.c                 | 142 ++++++++++++++++++++
 vport.h                 |  26 ++++
 38 files changed, 1530 insertions(+), 54 deletions(-)
 create mode 100644 ts2phc_vport.c
 create mode 100644 ts2phc_vport.h
 create mode 100644 vport.c
 create mode 100644 vport.h

diff --git a/clock.c b/clock.c
index f808b35..2e854d8 100644
--- a/clock.c
+++ b/clock.c
@@ -15,6 +15,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #include <errno.h>
 #include <time.h>
@@ -43,6 +51,7 @@
 #include "tlv.h"
 #include "tsproc.h"
 #include "uds.h"
+#include "vport.h"
 #include "util.h"
 
 #define N_CLOCK_PFD (N_POLLFD + 1) /* one extra per port, for the fault timer 
*/
@@ -97,9 +106,10 @@ struct clock {
        LIST_HEAD(ports_head, port) ports;
        struct port *uds_rw_port;
        struct port *uds_ro_port;
+       struct port *vport_port;
        struct pollfd *pollfd;
        int pollfd_valid;
-       int nports; /* does not include the two UDS ports */
+       int nports; /* does not include the two UDS ports + 1 Virtual Port */
        int last_port_number;
        int sde;
        int free_running;
@@ -133,10 +143,12 @@ struct clock {
        struct clockcheck *sanity_check;
        struct interface *uds_rw_if;
        struct interface *uds_ro_if;
+       struct interface *vport_if;
        LIST_HEAD(clock_subscribers_head, clock_subscriber) subscribers;
        struct monitor *slave_event_monitor;
        int step_window_counter;
        int step_window;
+       bool interface_rate_tlv_support;
 };
 
 struct clock the_clock;
@@ -272,6 +284,7 @@ void clock_destroy(struct clock *c)
 
        interface_destroy(c->uds_rw_if);
        interface_destroy(c->uds_ro_if);
+       interface_destroy(c->vport_if);
        clock_flush_subscriptions(c);
        LIST_FOREACH_SAFE(p, &c->ports, list, tmp) {
                clock_remove_port(c, p);
@@ -901,6 +914,7 @@ struct clock *clock_create(enum clock_type type, struct 
config *config,
        int phc_index, conf_phc_index, required_modes = 0;
        struct clock *c = &the_clock;
        const char *uds_ifname;
+       const char *vport_ifname;
        struct port *p;
        unsigned char oui[OUI_LEN];
        struct interface *iface;
@@ -1005,6 +1019,8 @@ struct clock *clock_create(enum clock_type type, struct 
config *config,
                rtnl_get_ts_device(interface_name(iface), ts_label);
                interface_set_label(iface, ts_label);
                interface_ensure_tslabel(iface);
+               /* Interface speed information */
+               interface_get_ifinfo(iface);
                interface_get_tsinfo(iface);
                if (interface_tsinfo_valid(iface) &&
                    !interface_tsmodes_supported(iface, required_modes)) {
@@ -1094,6 +1110,28 @@ struct clock *clock_create(enum clock_type type, struct 
config *config,
                                   "delay_filter_length", 1)) {
                return NULL;
        }
+       /* Configure the Virtual Port. */
+
+       vport_ifname = config_get_string(config, NULL, "vport_address");
+       if (vport_ifname && vport_ifname[0]) {
+               c->vport_if = interface_create(vport_ifname);
+               if (config_set_section_int(config, interface_name(c->vport_if),
+                                          "announceReceiptTimeout", 2)) {
+                       return NULL;
+               }
+               if (config_set_section_int(config, interface_name(c->vport_if),
+                                           "delay_mechanism", 
DM_NO_MECHANISM)) {
+                       return NULL;
+               }
+               if (config_set_section_int(config, interface_name(c->vport_if),
+                                           "network_transport", TRANS_VPORT)) {
+                       return NULL;
+               }
+               if (config_set_section_int(config, interface_name(c->vport_if),
+                                          "delay_filter_length", 1)) {
+                       return NULL;
+               }
+       }
 
        c->config = config;
        c->free_running = config_get_int(config, NULL, "free_running");
@@ -1105,6 +1143,7 @@ struct clock *clock_create(enum clock_type type, struct 
config *config,
        c->utc_offset = config_get_int(config, NULL, "utc_offset");
        c->time_source = config_get_int(config, NULL, "timeSource");
        c->step_window = config_get_int(config, NULL, "step_window");
+       c->interface_rate_tlv_support = config_get_int(config, NULL, 
"interface_rate_tlv_support");
 
        if (c->free_running) {
                c->clkid = CLOCK_INVALID;
@@ -1232,6 +1271,17 @@ struct clock *clock_create(enum clock_type type, struct 
config *config,
                pr_err("failed to open the UDS-RO port");
                return NULL;
        }
+
+       if (c->vport_if) {
+               c->vport_port = port_open(phc_device, phc_index, timestamping, 
0,
+                                          c->vport_if, c);
+               if (!c->vport_port) {
+                       pr_err("failed to open the Virtual port");
+                       return NULL;
+               }
+               LIST_INSERT_HEAD(&c->ports, c->vport_port, list);
+               c->nports++;
+       }
        clock_fda_changed(c);
 
        c->slave_event_monitor = monitor_create(config, c->uds_rw_port);
@@ -1255,6 +1305,9 @@ struct clock *clock_create(enum clock_type type, struct 
config *config,
        }
        port_dispatch(c->uds_rw_port, EV_INITIALIZE, 0);
        port_dispatch(c->uds_ro_port, EV_INITIALIZE, 0);
+       if (c->vport_port) {
+               port_dispatch(c->vport_port, EV_INITIALIZE, 0);
+       }
 
        return c;
 }
@@ -1671,6 +1724,18 @@ int clock_poll(struct clock *c)
                        /* sde is not expected on the UDS-RO port */
                }
        }
+       /* Check the Virtual port. */
+       if (c->vport_port) {
+               cur += N_CLOCK_PFD;
+               for (i = 0; i < N_POLLFD; i++) {
+                       if (cur[i].revents & (POLLIN|POLLPRI)) {
+                               event = port_event(c->vport_port, i);
+                               if (event == EV_STATE_DECISION_EVENT) {
+                                       c->sde = 1;
+                               }
+                       }
+               }
+       }
 
        if (c->sde) {
                handle_state_decision_event(c);
@@ -1716,6 +1781,11 @@ int clock_slave_only(struct clock *c)
        return c->dds.flags & DDS_SLAVE_ONLY;
 }
 
+bool clock_telecom_profile(struct clock *c)
+{
+       return (c->dscmp == telecom_dscmp);
+}
+
 UInteger8 clock_max_steps_removed(struct clock *c)
 {
        return c->max_steps_removed;
@@ -2081,3 +2151,8 @@ enum servo_state clock_servo_state(struct clock *c)
 {
        return c->servo_state;
 }
+
+bool clock_interface_rate_support(struct clock *c)
+{
+       return c->interface_rate_tlv_support;
+}
diff --git a/clock.h b/clock.h
index 0534f21..0d6c3d1 100644
--- a/clock.h
+++ b/clock.h
@@ -16,6 +16,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #ifndef HAVE_CLOCK_H
 #define HAVE_CLOCK_H
@@ -289,6 +297,13 @@ int clock_slave_only(struct clock *c);
  */
 UInteger8 clock_max_steps_removed(struct clock *c);
 
+/**
+ * Obtain the clock is set for telecom profile .
+ * @param c  The clock instance.
+ * @return   True if the profile is telecom, false otherwise.
+ */
+bool clock_telecom_profile(struct clock *c);
+
 /**
  * Obtain the clock class threshold field from a clock's default data set.
  * @param c  The clock instance.
@@ -388,4 +403,12 @@ void clock_check_ts(struct clock *c, uint64_t ts);
  */
 double clock_rate_ratio(struct clock *c);
 
+/**
+ * Obtain interface rate tlv support configuration.
+ * @param c  The clock instance.
+ * @return   The interface_rate_supoort config true if supported, else false.
+ */
+bool clock_interface_rate_support(struct clock *c);
+
+
 #endif
diff --git a/config.c b/config.c
index 6ba9996..5e3ce91 100644
--- a/config.c
+++ b/config.c
@@ -15,6 +15,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #include <ctype.h>
 #include <float.h>
@@ -168,9 +176,12 @@ static struct config_enum delay_filter_enu[] = {
 };
 
 static struct config_enum delay_mech_enu[] = {
-       { "Auto", DM_AUTO },
-       { "E2E",  DM_E2E },
-       { "P2P",  DM_P2P },
+       { "Auto",         DM_AUTO },
+       { "E2E",          DM_E2E },
+       { "P2P",          DM_P2P },
+       { "COMMON_P2P",   DM_COMMON_P2P },
+       { "SPECIAL",      DM_SPECIAL },
+       { "NO_MECHANISM", DM_NO_MECHANISM },
        { NULL, 0 },
 };
 
@@ -192,6 +203,7 @@ static struct config_enum nw_trans_enu[] = {
        { "L2",    TRANS_IEEE_802_3 },
        { "UDPv4", TRANS_UDP_IPV4   },
        { "UDPv6", TRANS_UDP_IPV6   },
+       { "vPort", TRANS_VPORT   },
        { NULL, 0 },
 };
 
@@ -205,10 +217,11 @@ static struct config_enum timestamping_enu[] = {
 };
 
 static struct config_enum tsproc_enu[] = {
-       { "filter",        TSPROC_FILTER        },
-       { "raw",           TSPROC_RAW           },
-       { "filter_weight", TSPROC_FILTER_WEIGHT },
-       { "raw_weight",    TSPROC_RAW_WEIGHT    },
+       { "filter",        TSPROC_FILTER                },
+       { "raw",           TSPROC_RAW           },
+       { "filter_weight", TSPROC_FILTER_WEIGHT },
+       { "raw_weight",    TSPROC_RAW_WEIGHT    },
+       { "no_delay",      TSPROC_NO_DELAY_MECHANISM    },
        { NULL, 0 },
 };
 
@@ -225,6 +238,7 @@ static struct config_enum bmca_enu[] = {
 };
 
 struct config_item config_tab[] = {
+       PORT_ITEM_INT("altMaster", 0, 1, 1),
        PORT_ITEM_INT("announceReceiptTimeout", 3, 2, UINT8_MAX),
        PORT_ITEM_ENU("asCapable", AS_CAPABLE_AUTO, as_capable_enu),
        GLOB_ITEM_INT("assume_two_step", 0, 0, 1),
@@ -240,6 +254,7 @@ struct config_item config_tab[] = {
        GLOB_ITEM_ENU("clock_type", CLOCK_TYPE_ORDINARY, clock_type_enu),
        GLOB_ITEM_ENU("dataset_comparison", DS_CMP_IEEE1588, dataset_comp_enu),
        PORT_ITEM_INT("delayAsymmetry", 0, INT_MIN, INT_MAX),
+       GLOB_ITEM_INT("interface_rate_tlv_support", 0, 0, 1),
        PORT_ITEM_ENU("delay_filter", FILTER_MOVING_MEDIAN, delay_filter_enu),
        PORT_ITEM_INT("delay_filter_length", 10, 1, INT_MAX),
        PORT_ITEM_ENU("delay_mechanism", DM_E2E, delay_mech_enu),
@@ -344,6 +359,7 @@ struct config_item config_tab[] = {
        GLOB_ITEM_STR("userDescription", ""),
        GLOB_ITEM_INT("utc_offset", CURRENT_UTC_OFFSET, 0, INT_MAX),
        GLOB_ITEM_INT("verbose", 0, 0, 1),
+       GLOB_ITEM_STR("vport_address", ""),
        GLOB_ITEM_INT("write_phase_mode", 0, 0, 1),
 };
 
diff --git a/configs/default.cfg b/configs/default.cfg
index 1b5b806..55ded13 100644
--- a/configs/default.cfg
+++ b/configs/default.cfg
@@ -19,6 +19,7 @@ dscp_general          0
 dataset_comparison     ieee1588
 G.8275.defaultDS.localPriority 128
 maxStepsRemoved                255
+interface_rate_tlv_support     0
 #
 # Port Data Set
 #
@@ -41,6 +42,7 @@ BMCA                    ptp
 inhibit_announce        0
 inhibit_delay_req       0
 ignore_source_id        0
+altMaster              0
 #
 # Run time options
 #
@@ -95,6 +97,7 @@ uds_address           /var/run/ptp4l
 uds_file_mode          0660
 uds_ro_address         /var/run/ptp4lro
 uds_ro_file_mode               0666
+vport_address          /var/run/vport0
 #
 # Default interface options
 #
diff --git a/dm.h b/dm.h
index 2491c63..68758bf 100644
--- a/dm.h
+++ b/dm.h
@@ -16,6 +16,15 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
+ *
  */
 #ifndef HAVE_DM_H
 #define HAVE_DM_H
@@ -33,6 +42,15 @@ enum delay_mechanism {
 
        /** Peer delay mechanism. */
        DM_P2P,
+
+       /** Common P2P Delay mechanism. */
+       DM_COMMON_P2P,
+
+       /** Special Port Delay  mechanism. */
+       DM_SPECIAL,
+
+       /** No Delay Mechanism. */
+       DM_NO_MECHANISM = 0xFE,
 };
 
 #endif
diff --git a/ds.h b/ds.h
index dff6d5e..9bed8cb 100644
--- a/ds.h
+++ b/ds.h
@@ -16,6 +16,13 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors' 
reputations.
  */
 #ifndef HAVE_DS_H
 #define HAVE_DS_H
@@ -40,6 +47,8 @@ struct defaultDS {
        struct ClockIdentity clockIdentity;
        UInteger8            domainNumber;
        UInteger8            reserved2;
+       UInteger8            localPriority; /* G.8275.X */
+       UInteger8            maxStepsRemoved;
 } PACKED;
 
 #define OUI_LEN 3
@@ -95,6 +104,10 @@ struct timePropertiesDS {
        Enumeration8 timeSource;
 } PACKED;
 
+#define PDS_PTSF_LOSS_SYNC        (1<<0)
+#define PDS_PTSF_UNUSABLE         (1<<1)
+#define PDS_PTSF_LOSS_DELAY_RESP  (1<<2)
+
 struct portDS {
        struct PortIdentity portIdentity;
        Enumeration8        portState;
@@ -106,6 +119,10 @@ struct portDS {
        Enumeration8        delayMechanism;
        Integer8            logMinPdelayReqInterval;
        UInteger8           versionNumber;
+       UInteger8           localPriority;
+       UInteger8           signalFail;
+       UInteger8           syncReceiptTimeout;
+       UInteger8           delayRespReceiptTimeout;
 } PACKED;
 
 #define FRI_ASAP (-128)
diff --git a/interface.c b/interface.c
index 445a270..e3c9c2e 100644
--- a/interface.c
+++ b/interface.c
@@ -3,6 +3,14 @@
  * @brief Implements network interface data structures.
  * @note Copyright (C) 2020 Richard Cochran <richardcoch...@gmail.com>
  * @note SPDX-License-Identifier: GPL-2.0+
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #include <stdlib.h>
 #include "interface.h"
@@ -12,6 +20,7 @@ struct interface {
        char name[MAX_IFNAME_SIZE + 1];
        char ts_label[MAX_IFNAME_SIZE + 1];
        struct sk_ts_info ts_info;
+       struct sk_if_info if_info;
        int vclock;
 };
 
@@ -46,6 +55,11 @@ int interface_get_tsinfo(struct interface *iface)
        return sk_get_ts_info(iface->ts_label, &iface->ts_info);
 }
 
+int interface_get_ifinfo(struct interface *iface)
+{
+       return sk_get_if_info(iface->ts_label, &iface->if_info);
+}
+
 const char *interface_label(struct interface *iface)
 {
        return iface->ts_label;
@@ -71,6 +85,11 @@ bool interface_tsinfo_valid(struct interface *iface)
        return iface->ts_info.valid ? true : false;
 }
 
+bool interface_ifinfo_valid(struct interface *iface)
+{
+       return iface->if_info.valid ? true : false;
+}
+
 bool interface_tsmodes_supported(struct interface *iface, int modes)
 {
        if ((iface->ts_info.so_timestamping & modes) == modes) {
@@ -88,3 +107,42 @@ int interface_get_vclock(struct interface *iface)
 {
        return iface->vclock;
 }
+
+uint64_t interface_bitperiod(struct interface *iface)
+{
+       uint64_t if_atto_sec_bit_period = 0;
+
+       if (!iface->if_info.valid)
+       return if_atto_sec_bit_period;
+
+       switch (iface->if_info.speed) {
+               case 1:
+                       if_atto_sec_bit_period = 0x0DE0B6B3A7640000;
+                       break;
+               case 10:
+                       if_atto_sec_bit_period = 0x000000174876E800;
+                       break;
+               case 100:
+                       if_atto_sec_bit_period = 0x00000002540BE400;
+                       break;
+               case 1000:
+                       if_atto_sec_bit_period = 0x000000003B9ACA00;
+                       break;
+               case 10000:
+                       if_atto_sec_bit_period = 0x0000000005F5E100;
+                       break;
+               case 25000:
+                       if_atto_sec_bit_period = 0x0000000002625A00;
+                       break;
+               case 40000:
+                       if_atto_sec_bit_period = 0x00000000017D7840;
+                       break;
+               case 100000:
+                       if_atto_sec_bit_period = 0x0000000000989680;
+                       break;
+               case 1000000:
+                       if_atto_sec_bit_period = 0x00000000000F4240;
+                       break;
+       }
+       return if_atto_sec_bit_period;
+}
diff --git a/interface.h b/interface.h
index 752f4f1..82f655c 100644
--- a/interface.h
+++ b/interface.h
@@ -3,6 +3,14 @@
  * @brief Implements network interface data structures.
  * @note Copyright (C) 2020 Richard Cochran <richardcoch...@gmail.com>
  * @note SPDX-License-Identifier: GPL-2.0+
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #ifndef HAVE_INTERFACE_H
 #define HAVE_INTERFACE_H
@@ -46,6 +54,13 @@ void interface_ensure_tslabel(struct interface *iface);
  */
 int interface_get_tsinfo(struct interface *iface);
 
+/**
+ * Populate the time stamping information of a given interface.
+ * @param iface  The interface of interest.
+ * @return       zero on success, negative on failure.
+ */
+int interface_get_ifinfo(struct interface *iface);
+
 /**
  * Obtain the time stamping label of a network interface.  This can be
  * different from the name of the interface when bonding is in effect.
@@ -83,6 +98,13 @@ void interface_set_label(struct interface *iface, const char 
*label);
  */
 bool interface_tsinfo_valid(struct interface *iface);
 
+/**
+ * Tests whether an interface's interface information is valid or not.
+ * @param iface  The interface of interest.
+ * @return       True if the interface information is valid, false otherwise.
+ */
+bool interface_ifinfo_valid(struct interface *iface);
+
 /**
  * Tests whether an interface supports a set of given time stamping modes.
  * @param iface  The interface of interest.
@@ -91,6 +113,13 @@ bool interface_tsinfo_valid(struct interface *iface);
  */
 bool interface_tsmodes_supported(struct interface *iface, int modes);
 
+/**
+ * Obtains the interface bit period based on the speed.
+ * @param iface  The interface of interest.
+ * @return       if valid speed return interface bitperiod in atto seconds.
+ */
+uint64_t interface_bitperiod(struct interface *iface);
+
 /**
  * Set the vclock (virtual PHC) to be used for timestamping on an interface.
  * @param iface  The interface of interest.
diff --git a/makefile b/makefile
index 5295b60..3631f1d 100644
--- a/makefile
+++ b/makefile
@@ -25,9 +25,9 @@ LDLIBS        = -lm -lrt -pthread $(EXTRA_LDFLAGS)
 PRG    = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster ts2phc
 FILTERS        = filter.o mave.o mmedian.o
 SERVOS = linreg.o ntpshm.o nullf.o pi.o servo.o
-TRANSP = raw.o transport.o udp.o udp6.o uds.o
+TRANSP = raw.o transport.o udp.o udp6.o uds.o vport.o
 TS2PHC = ts2phc.o lstab.o nmea.o serial.o sock.o ts2phc_generic_pps_source.o \
- ts2phc_nmea_pps_source.o ts2phc_phc_pps_source.o ts2phc_pps_sink.o 
ts2phc_pps_source.o
+ ts2phc_nmea_pps_source.o ts2phc_phc_pps_source.o ts2phc_pps_sink.o 
ts2phc_pps_source.o ts2phc_vport.o
 OBJ    = bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \
  e2e_tc.o fault.o $(FILTERS) fsm.o hash.o interface.o monitor.o msg.o phc.o \
  port.o port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o rtnl.o $(SERVOS) \
@@ -68,8 +68,8 @@ phc_ctl: phc_ctl.o phc.o sk.o util.o clockadj.o sysoff.o 
print.o version.o
 
 timemaster: phc.o print.o rtnl.o sk.o timemaster.o util.o version.o
 
-ts2phc: config.o clockadj.o hash.o interface.o phc.o print.o $(SERVOS) sk.o \
- $(TS2PHC) util.o version.o
+ts2phc: config.o clockadj.o hash.o msg.o tlv.o interface.o phc.o print.o 
$(SERVOS) sk.o \
+ $(TS2PHC) $(TRANSP) util.o version.o
 
 version.o: .version version.sh $(filter-out version.d,$(DEPEND))
 
diff --git a/monitor.c b/monitor.c
index ed451ac..041cd5b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -2,6 +2,14 @@
  * @file monitor.c
  * @note Copyright (C) 2020 Richard Cochran <richardcoch...@gmail.com>
  * @note SPDX-License-Identifier: GPL-2.0+
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #include <stdbool.h>
 #include <stdlib.h>
@@ -21,8 +29,10 @@ struct monitor_message {
 struct monitor {
        struct port *dst_port;
        struct slave_rx_sync_timing_data_tlv *sync_tlv;
+       struct slave_rx_sync_computed_data_tlv *computed_tlv;
        struct slave_delay_timing_data_tlv *delay_tlv;
        struct monitor_message delay;
+       struct monitor_message computed;
        struct monitor_message sync;
 };
 
@@ -115,6 +125,23 @@ static int monitor_init_sync(struct monitor *monitor, 
struct address address)
        return 0;
 }
 
+static int monitor_init_computed(struct monitor *monitor, struct address 
address)
+{
+       const size_t tlv_size = sizeof(struct slave_rx_sync_computed_data_tlv) +
+               sizeof(struct slave_rx_sync_computed_record) * 
RECORDS_PER_MESSAGE;
+       struct tlv_extra *extra;
+
+       extra = monitor_init_message(&monitor->computed, monitor->dst_port,
+                                    TLV_SLAVE_RX_SYNC_COMPUTED_DATA, tlv_size,
+                                    address);
+       if (!extra) {
+               return -1;
+       }
+       monitor->computed_tlv = (struct slave_rx_sync_computed_data_tlv *) 
extra->tlv;
+
+       return 0;
+}
+
 struct monitor *monitor_create(struct config *config, struct port *dst)
 {
        struct monitor *monitor;
@@ -148,6 +175,12 @@ struct monitor *monitor_create(struct config *config, 
struct port *dst)
                free(monitor);
                return NULL;
        }
+       if (monitor_init_computed(monitor, address)) {
+               msg_put(monitor->sync.msg);
+               msg_put(monitor->delay.msg);
+               free(monitor);
+               return NULL;
+       }
 
        return monitor;
 }
@@ -193,6 +226,9 @@ void monitor_destroy(struct monitor *monitor)
        if (monitor->sync.msg) {
                msg_put(monitor->sync.msg);
        }
+       if (monitor->computed.msg) {
+               msg_put(monitor->computed.msg);
+       }
        free(monitor);
 }
 
@@ -229,3 +265,38 @@ int monitor_sync(struct monitor *monitor, struct 
PortIdentity source_pid,
        }
        return 0;
 }
+
+int monitor_computed(struct monitor *monitor, struct PortIdentity source_pid,
+                uint16_t seqid, tmv_t offset, tmv_t pathdelay, int32_t ratio,
+                uint8_t best_master)
+{
+       struct slave_rx_sync_computed_record *record;
+       struct ptp_message *msg;
+
+       if (!monitor_active(monitor)) {
+               return 0;
+       }
+
+       msg = monitor->computed.msg;
+
+       if (!pid_eq(&monitor->computed_tlv->sourcePortIdentity, &source_pid)) {
+               /* There was a change in remote master. Drop stale records. */
+               memcpy(&monitor->computed_tlv->sourcePortIdentity, &source_pid,
+                      sizeof(monitor->computed_tlv->sourcePortIdentity));
+               monitor->computed.count = 0;
+       }
+
+       record = monitor->computed_tlv->record + monitor->computed.count;
+       monitor->computed_tlv->reserved    = 0;
+       record->sequenceId                 = seqid;
+       record->offsetFromMaster           = tmv_to_TimeInterval(offset);
+       record->meanPathDelay              = tmv_to_TimeInterval(pathdelay);
+       record->scaledNeighborRateRatio    = ratio;
+
+       monitor->computed.count++;
+       if (monitor->computed.count == monitor->computed.records_per_msg) {
+               monitor->computed.count = 0;
+               return monitor_forward(monitor->dst_port, msg);
+       }
+       return 0;
+}
diff --git a/monitor.h b/monitor.h
index c489aa9..9beb57e 100644
--- a/monitor.h
+++ b/monitor.h
@@ -22,4 +22,7 @@ void monitor_destroy(struct monitor *monitor);
 int monitor_sync(struct monitor *monitor, struct PortIdentity source_pid,
                 uint16_t seqid, tmv_t t1, tmv_t corr, tmv_t t2);
 
+int monitor_computed(struct monitor *monitor, struct PortIdentity source_pid,
+                uint16_t seqid, tmv_t offset, tmv_t pathdelay, int32_t ratio,
+                uint8_t best_master);
 #endif
diff --git a/msg.h b/msg.h
index b7423ee..6e32d41 100644
--- a/msg.h
+++ b/msg.h
@@ -16,6 +16,13 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors' 
reputations.
  */
 #ifndef HAVE_MSG_H
 #define HAVE_MSG_H
@@ -461,6 +468,16 @@ static inline Boolean one_step(struct ptp_message *m)
        return !field_is_set(m, 0, TWO_STEP);
 }
 
+/**
+ * Test whether a message from ALTERNATE Master.
+ * @param m  Message to test.
+ * @return   One if the alt_master flag in message is set, zero otherwise.
+ */
+static inline Boolean alt_master(struct ptp_message *m)
+{
+       return field_is_set(m, 0, ALT_MASTER);
+}
+
 /**
  * Convert a 64 bit word into network byte order.
  */
diff --git a/pdt.h b/pdt.h
index e46b218..73d825a 100644
--- a/pdt.h
+++ b/pdt.h
@@ -16,6 +16,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #ifndef HAVE_PDT_H
 #define HAVE_PDT_H
@@ -39,6 +47,7 @@ typedef uint16_t  UInteger16;
 typedef int32_t   Integer32;
 typedef uint32_t  UInteger32;
 typedef int64_t   Integer64;
+typedef uint64_t  UInteger64;
 typedef uint8_t   Octet;
 
 #endif
diff --git a/pmc.c b/pmc.c
index e218ca4..f2df230 100644
--- a/pmc.c
+++ b/pmc.c
@@ -16,6 +16,13 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors' 
reputations.
  */
 #include <errno.h>
 #include <poll.h>
@@ -88,6 +95,19 @@ static void pmc_show_rx_sync_timing(struct 
slave_rx_sync_timing_record *record,
                SHOW_TIMESTAMP(record->syncEventIngressTimestamp));
 }
 
+static void pmc_show_rx_sync_computed_data(struct 
slave_rx_sync_computed_record *record,
+                                 FILE *fp)
+{
+       fprintf(fp,
+               IFMT "sequenceId                 %hu"
+               IFMT "offsetFromMaster           %" PRId64
+               IFMT "meanPathDelay              %" PRId64
+               IFMT "scaledNeighborRateRatio    %d",
+               record->sequenceId,
+               record->offsetFromMaster,
+               record->meanPathDelay,
+               record->scaledNeighborRateRatio);
+}
 
 static void pmc_show_unicast_master_entry(struct unicast_master_entry *entry,
                                    FILE *fp)
@@ -110,8 +130,10 @@ static void pmc_show_signaling(struct ptp_message *msg, 
FILE *fp)
 {
        struct slave_rx_sync_timing_record *sync_record;
        struct slave_delay_timing_record *delay_record;
+       struct slave_rx_sync_computed_record *sync_computed_record;
        struct slave_rx_sync_timing_data_tlv *srstd;
        struct slave_delay_timing_data_tlv *sdtdt;
+       struct slave_rx_sync_computed_data_tlv *srscdt;
        struct tlv_extra *extra;
        int i, cnt;
 
@@ -147,6 +169,22 @@ static void pmc_show_signaling(struct ptp_message *msg, 
FILE *fp)
                                delay_record++;
                        }
                        break;
+               case TLV_SLAVE_RX_SYNC_COMPUTED_DATA:
+                       srscdt = (struct slave_rx_sync_computed_data_tlv *) 
extra->tlv;
+                       cnt = (srscdt->length - 
sizeof(srscdt->sourcePortIdentity)) /
+                               sizeof(*sync_computed_record);
+                       fprintf(fp, "SLAVE_RX_SYNC_COMPUTED_DATA N %d "
+                               IFMT "sourcePortIdentity         %s"
+                               IFMT "computedFlags              %02x"
+                               IFMT "reserved                   %02x",
+                               cnt, pid2str(&srscdt->sourcePortIdentity),
+                               srscdt->computedFlags, srscdt->reserved);
+                       sync_computed_record = srscdt->record;
+                       for (i = 0; i < cnt; i++) {
+                               
pmc_show_rx_sync_computed_data(sync_computed_record, fp);
+                               sync_computed_record++;
+                       }
+                       break;
                default:
                        break;
                }
@@ -176,6 +214,8 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
        struct currentDS *cds;
        struct parentDS *pds;
        struct portDS *p;
+       struct port_ptsf_unusable_np *ptsf;
+       struct port_ptsf_data_np *ptsf_data;
        struct TLV *tlv;
        uint8_t *buf;
        int action;
@@ -426,6 +466,26 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
                fprintf(fp, "SYNCHRONIZATION_UNCERTAIN_NP "
                        IFMT "uncertain %hhu", mtd->val);
                break;
+       case MID_PORT_PTSF_UNUSABLE_NP:
+               ptsf = (struct port_ptsf_unusable_np *) mgt->data;
+               fprintf(fp, "PORT_PTSF_UNUSABLE_NP "
+                       IFMT "unusable %hu", ptsf->ptsf_unusable);
+               break;
+       case MID_PORT_PTSF_DATA_NP:
+               ptsf_data = (struct port_ptsf_data_np *) mgt->data;
+               fprintf(fp, "PORT_PTSF_DATA_NP "
+                       IFMT "signalFail                %hu"
+                       IFMT "PTSF.lossSync             %d"
+                       IFMT "PTSF.syncMsgLoss          %d"
+                       IFMT "PTSF.delayRespMsgLoss     %d"
+                       IFMT "PTSF.unusable             %d",
+                       ptsf_data->signalFail  ? 1 : 0,
+                       ((ptsf_data->signalFail & PDS_PTSF_LOSS_SYNC) |
+                        (ptsf_data->signalFail & PDS_PTSF_LOSS_DELAY_RESP)) ? 
1 : 0,
+                       ptsf_data->signalFail & PDS_PTSF_LOSS_SYNC ? 1 : 0,
+                       ptsf_data->signalFail & PDS_PTSF_LOSS_DELAY_RESP ? 1 : 
0,
+                       ptsf_data->signalFail & PDS_PTSF_UNUSABLE ? 1 : 0);
+               break;
        case MID_PORT_DATA_SET:
                p = (struct portDS *) mgt->data;
                if (p->portState > PS_SLAVE) {
@@ -438,16 +498,21 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
                        IFMT "peerMeanPathDelay       %" PRId64
                        IFMT "logAnnounceInterval     %hhd"
                        IFMT "announceReceiptTimeout  %hhu"
+                       IFMT "syncReceiptTimeout      %hhu"
+                       IFMT "delayRespReceiptTimeout %hhu"
                        IFMT "logSyncInterval         %hhd"
                        IFMT "delayMechanism          %hhu"
                        IFMT "logMinPdelayReqInterval %hhd"
-                       IFMT "versionNumber           %u",
+                       IFMT "versionNumber           %u"
+                       IFMT "signalFail              %d",
                        pid2str(&p->portIdentity), ps_str[p->portState],
                        p->logMinDelayReqInterval, p->peerMeanPathDelay >> 16,
                        p->logAnnounceInterval, p->announceReceiptTimeout,
+                       p->syncReceiptTimeout, p->delayRespReceiptTimeout,
                        p->logSyncInterval, p->delayMechanism,
                        p->logMinPdelayReqInterval,
-                       p->versionNumber & MAJOR_VERSION_MASK);
+                       p->versionNumber & MAJOR_VERSION_MASK,
+                       p->signalFail ? 1 : 0);
                break;
        case MID_PORT_DATA_SET_NP:
                pnp = (struct port_ds_np *) mgt->data;
diff --git a/pmc_common.c b/pmc_common.c
index 43ad22c..5fc2074 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -16,6 +16,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #include <errno.h>
 #include <stdlib.h>
@@ -130,6 +138,7 @@ struct management_id idtab[] = {
        { "GRANDMASTER_SETTINGS_NP", MID_GRANDMASTER_SETTINGS_NP, do_set_action 
},
        { "SUBSCRIBE_EVENTS_NP", MID_SUBSCRIBE_EVENTS_NP, do_set_action },
        { "SYNCHRONIZATION_UNCERTAIN_NP", MID_SYNCHRONIZATION_UNCERTAIN_NP, 
do_set_action },
+       { "PORT_PTSF_UNUSABLE_NP", MID_PORT_PTSF_UNUSABLE_NP, do_set_action },
 /* Port management ID values */
        { "NULL_MANAGEMENT", MID_NULL_MANAGEMENT, null_management },
        { "CLOCK_DESCRIPTION", MID_CLOCK_DESCRIPTION, do_get_action },
@@ -154,6 +163,7 @@ struct management_id idtab[] = {
        { "PORT_SERVICE_STATS_NP", MID_PORT_SERVICE_STATS_NP, do_get_action },
        { "UNICAST_MASTER_TABLE_NP", MID_UNICAST_MASTER_TABLE_NP, do_get_action 
},
        { "PORT_HWCLOCK_NP", MID_PORT_HWCLOCK_NP, do_get_action },
+       { "PORT_PTSF_DATA_NP", MID_PORT_PTSF_DATA_NP, do_get_action },
 };
 
 static void do_get_action(struct pmc *pmc, int action, int index, char *str)
@@ -172,6 +182,7 @@ static void do_set_action(struct pmc *pmc, int action, int 
index, char *str)
        struct management_tlv_datum mtd;
        struct subscribe_events_np sen;
        struct port_ds_np pnp;
+       struct port_ptsf_unusable_np ptsf;
        char onoff_port_state[4] = "off";
        char onoff_time_status[4] = "off";
 
@@ -302,6 +313,24 @@ static void do_set_action(struct pmc *pmc, int action, int 
index, char *str)
                }
                pmc_send_set_action(pmc, code, &pnp, sizeof(pnp));
                break;
+       case MID_PORT_PTSF_UNUSABLE_NP:
+               cnt = sscanf(str, " %*s %*s %hu %hu", &ptsf.ptsf_unusable, 
&ptsf.portIdentity.portNumber);
+               if (cnt != 2) {
+                       fprintf(stderr, "%s SET needs 2 value\n",
+                               idtab[index].name);
+                       break;
+               }
+               switch (ptsf.ptsf_unusable) {
+               case TRUE:
+               case FALSE:
+                       pmc_send_set_action(pmc, code, &ptsf, sizeof(ptsf));
+                       break;
+               default:
+                       fprintf(stderr, "\nusage: set PORT_PTSF_UNUSABLE_NP%hhu 
(false) or %hhu (true)\n\n",
+                               FALSE,
+                               TRUE);
+               }
+               break;
        }
 }
 
diff --git a/port.c b/port.c
index d9dac38..df59ef5 100644
--- a/port.c
+++ b/port.c
@@ -15,6 +15,13 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors' 
reputations.
  */
 #include <arpa/inet.h>
 #include <errno.h>
@@ -57,6 +64,7 @@ enum syfu_event {
 
 static int port_is_ieee8021as(struct port *p);
 static int port_is_uds(struct port *p);
+static int port_is_vport(struct port *p);
 static void port_nrate_initialize(struct port *p);
 
 static int announce_compare(struct ptp_message *m1, struct ptp_message *m2)
@@ -822,6 +830,11 @@ static int port_is_uds(struct port *p)
        return transport_type(p->trp) == TRANS_UDS;
 }
 
+static int port_is_vport(struct port *p)
+{
+       return transport_type(p->trp) == TRANS_VPORT;
+}
+
 static void port_management_send_error(struct port *p, struct port *ingress,
                                       struct ptp_message *msg, int error_id)
 {
@@ -852,6 +865,8 @@ static int port_management_fill_response(struct port 
*target,
        struct PortIdentity pid;
        const char *ts_label;
        struct portDS *pds;
+       struct port_ptsf_unusable_np *ptsf;
+       struct port_ptsf_data_np *ptsf_data;
        uint16_t u16;
        uint8_t *buf;
        int datalen;
@@ -951,6 +966,9 @@ static int port_management_fill_response(struct port 
*target,
                }
                pds->logMinPdelayReqInterval = target->logMinPdelayReqInterval;
                pds->versionNumber           = target->versionNumber;
+               pds->syncReceiptTimeout      = target->syncReceiptTimeout;
+               pds->delayRespReceiptTimeout = target->delay_response_timeout;
+               pds->signalFail              = target->signalFail;
                datalen = sizeof(*pds);
                break;
        case MID_LOG_ANNOUNCE_INTERVAL:
@@ -1015,6 +1033,18 @@ static int port_management_fill_response(struct port 
*target,
                psn->stats = target->stats;
                datalen = sizeof(*psn);
                break;
+       case MID_PORT_PTSF_UNUSABLE_NP:
+               ptsf = (struct port_ptsf_unusable_np *)tlv->data;
+               ptsf->portIdentity = target->portIdentity;
+               ptsf->ptsf_unusable = (target->signalFail & PDS_PTSF_UNUSABLE) 
? 1 : 0;
+               datalen = sizeof(*ptsf);
+               break;
+       case MID_PORT_PTSF_DATA_NP:
+               ptsf_data = (struct port_ptsf_data_np *)tlv->data;
+               ptsf_data->portIdentity = target->portIdentity;
+               ptsf_data->signalFail = target->signalFail;
+               datalen = sizeof(*ptsf_data);
+               break;
        case MID_PORT_SERVICE_STATS_NP:
                pssn = (struct port_service_stats_np *)tlv->data;
                pssn->portIdentity = target->portIdentity;
@@ -1115,7 +1145,8 @@ static int port_management_set(struct port *target,
        int respond = 0;
        struct management_tlv *tlv;
        struct port_ds_np *pdsnp;
-
+       struct port_ptsf_unusable_np *ptsf;
+       UInteger16 signalFail;
        tlv = (struct management_tlv *) req->management.suffix;
 
        switch (id) {
@@ -1124,6 +1155,24 @@ static int port_management_set(struct port *target,
                target->neighborPropDelayThresh = 
pdsnp->neighborPropDelayThresh;
                respond = 1;
                break;
+       case MID_PORT_PTSF_UNUSABLE_NP:
+               if (clock_telecom_profile(target->clock)) {
+                       ptsf = (struct port_ptsf_unusable_np *) tlv->data;
+                       signalFail = target->signalFail;
+                       if (ptsf->portIdentity.portNumber == 
target->portIdentity.portNumber)
+                       {
+                               if (ptsf->ptsf_unusable)
+                               {
+                                       target->signalFail |= PDS_PTSF_UNUSABLE;
+                               } else {
+                                       target->signalFail &= 
~PDS_PTSF_UNUSABLE;
+                               }
+                               if (signalFail != target->signalFail)
+                                       clock_set_sde(target->clock, 1);
+                       }
+                       respond = 1;
+               }
+               break;
        }
        if (respond && !port_management_get_response(target, ingress, id, req))
                pr_err("%s: failed to send management set response", 
target->log_name);
@@ -1199,9 +1248,11 @@ int port_set_delay_tmo(struct port *p)
                return 0;
        }
 
-       if (p->delayMechanism == DM_P2P) {
+       if (p->delayMechanism == DM_NO_MECHANISM) {
+               return 0;
+       } else if (p->delayMechanism == DM_P2P) {
                return set_tmo_log(p->fda.fd[FD_DELAY_TIMER], 1,
-                              p->logPdelayReqInterval);
+                               p->logPdelayReqInterval);
        } else {
                return set_tmo_random(p->fda.fd[FD_DELAY_TIMER], 0, 2,
                                p->logMinDelayReqInterval);
@@ -1284,6 +1335,7 @@ static void port_synchronize(struct port *p,
        enum servo_state state, last_state;
        tmv_t t1, t1c, t2, c1, c2;
 
+       p->signalFail &= ~PDS_PTSF_LOSS_SYNC;
        port_set_sync_rx_tmo(p);
 
        t1 = timestamp_to_tmv(origin_ts);
@@ -1293,11 +1345,16 @@ static void port_synchronize(struct port *p,
        t1c = tmv_add(t1, tmv_add(c1, c2));
 
        switch (p->state) {
+       case PS_MASTER:
+               return;
        case PS_UNCALIBRATED:
        case PS_SLAVE:
                monitor_sync(p->slave_event_monitor,
                             clock_parent_identity(p->clock), seqid,
                             t1, tmv_add(c1, c2), t2);
+               monitor_computed(p->slave_event_monitor,
+                            clock_parent_identity(p->clock), seqid,
+                            tmv_sub(t2, t1c), tmv_sub(t2, t1c), 0, 1);
                break;
        default:
                break;
@@ -1460,6 +1517,10 @@ static int port_pdelay_request(struct port *p)
        }
        p->multiple_pdr_detected = 0;
 
+       if (p->delayMechanism == DM_SPECIAL || p->delayMechanism == 
DM_NO_MECHANISM) {
+               return 0;
+       }
+
        msg = msg_allocate();
        if (!msg) {
                return -1;
@@ -1509,7 +1570,11 @@ out:
 int port_delay_request(struct port *p)
 {
        struct ptp_message *msg;
+       struct ptp_message *dst;
 
+       if (p->delayMechanism == DM_NO_MECHANISM) {
+               return 0;
+       }
        /* Time to send a new request, forget current pdelay resp and fup */
        if (p->peer_delay_resp) {
                msg_put(p->peer_delay_resp);
@@ -1541,10 +1606,20 @@ int port_delay_request(struct port *p)
        msg->header.control            = CTL_DELAY_REQ;
        msg->header.logMessageInterval = 0x7f;
 
-       if (p->hybrid_e2e) {
-               struct ptp_message *dst = TAILQ_FIRST(&p->best->messages);
+       if (p->hybrid_e2e && p->state != PS_MASTER) {
+               dst = TAILQ_FIRST(&p->best->messages);
                msg->address = dst->address;
                msg->header.flagField[0] |= UNICAST;
+       } else if (p->altMaster) {
+               struct foreign_clock *fc;
+               fc = LIST_FIRST(&p->foreign_masters);
+               if (fc) {
+                       dst = TAILQ_FIRST(&fc->messages);
+                       if (dst) {
+                               msg->address = dst->address;
+                               msg->header.flagField[0] |= UNICAST;
+                       }
+               }
        }
 
        if (port_prepare_and_send(p, msg, TRANS_EVENT)) {
@@ -1595,6 +1670,10 @@ int port_tx_announce(struct port *p, struct address 
*dst, uint16_t sequence_id)
 
        msg->header.flagField[1] = tp.flags;
 
+       if (p->state != PS_MASTER && clock_telecom_profile(p->clock)) {
+               msg->header.flagField[0] |= ALT_MASTER;
+       }
+
        if (dst) {
                msg->address = *dst;
                msg->header.flagField[0] |= UNICAST;
@@ -1674,6 +1753,10 @@ int port_tx_sync(struct port *p, struct address *dst, 
uint16_t sequence_id)
                msg->header.flagField[0] |= TWO_STEP;
        }
 
+       if (p->state != PS_MASTER && clock_telecom_profile(p->clock)) {
+               msg->header.flagField[0] |= ALT_MASTER;
+       }
+
        if (dst) {
                msg->address = *dst;
                msg->header.flagField[0] |= UNICAST;
@@ -1707,6 +1790,9 @@ int port_tx_sync(struct port *p, struct address *dst, 
uint16_t sequence_id)
        fup->header.logMessageInterval = p->logSyncInterval;
 
        fup->follow_up.preciseOriginTimestamp = tmv_to_Timestamp(msg->hwts.ts);
+       if (p->state != PS_MASTER && clock_telecom_profile(p->clock)) {
+               fup->header.flagField[0] |= ALT_MASTER;
+       }
 
        if (dst) {
                fup->address = *dst;
@@ -1842,6 +1928,7 @@ int port_initialize(struct port *p)
        p->neighborPropDelayThresh = config_get_int(cfg, p->name, 
"neighborPropDelayThresh");
        p->min_neighbor_prop_delay = config_get_int(cfg, p->name, 
"min_neighbor_prop_delay");
        p->delay_response_timeout  = config_get_int(cfg, p->name, 
"delay_response_timeout");
+       p->altMaster               = config_get_int(cfg, p->name, "altMaster");
 
        if (config_get_int(cfg, p->name, "asCapable") == AS_CAPABLE_TRUE) {
                p->asCapable = ALWAYS_CAPABLE;
@@ -1992,6 +2079,16 @@ int process_announce(struct port *p, struct ptp_message 
*m)
                return result;
        }
 
+       if (clock_telecom_profile(p->clock)) {
+               if ((alt_master(m) || p->signalFail)) {
+                       pr_debug("(%s) Announce message ignored for %s", 
p->name,
+                                       alt_master(m) ? "alternate master flag" 
:
+                                       "signal fail condition");
+                       return result;
+               }
+
+       }
+
        if (m->announce.grandmasterClockQuality.clockClass >
                clock_get_clock_class_threshold(p->clock)) {
                pl_err(60, "%s: Master clock quality received is "
@@ -2027,7 +2124,12 @@ static int process_delay_req(struct port *p, struct 
ptp_message *m)
 
        nsm = port_nsm_reply(p, m);
 
-       if (!nsm && p->state != PS_MASTER && p->state != PS_GRAND_MASTER) {
+       if (!nsm && p->state != PS_MASTER && p->state != PS_GRAND_MASTER && 
p->state != PS_PASSIVE) {
+               return 0;
+       }
+
+       if (p->delayMechanism == DM_SPECIAL || p->delayMechanism == 
DM_NO_MECHANISM) {
+               pr_warning("port %hu: delay request on special port", 
portnum(p));
                return 0;
        }
 
@@ -2062,6 +2164,11 @@ static int process_delay_req(struct port *p, struct 
ptp_message *m)
                msg->header.flagField[0] |= UNICAST;
                msg->header.logMessageInterval = 0x7f;
        }
+
+       if (p->state != PS_MASTER && clock_telecom_profile(p->clock)) {
+               msg->header.flagField[0] |= ALT_MASTER;
+       }
+
        if (nsm && net_sync_resp_append(p, msg)) {
                pr_err("%s: append NSM failed", p->log_name);
                err = -1;
@@ -2086,15 +2193,35 @@ void process_delay_resp(struct port *p, struct 
ptp_message *m)
        struct ptp_message *req;
        tmv_t c3, t3, t4, t4c;
 
-       if (p->state != PS_UNCALIBRATED && p->state != PS_SLAVE) {
+       /* Set delay response time out. */
+       p->signalFail &= ~PDS_PTSF_LOSS_DELAY_RESP;
+
+    switch (p->state) {
+       case PS_INITIALIZING:
+       case PS_FAULTY:
+       case PS_DISABLED:
+       case PS_LISTENING:
+       case PS_PRE_MASTER:
+       case PS_GRAND_MASTER:
+       case PS_PASSIVE:
                return;
+       case PS_MASTER:
+               if (!p->altMaster) {
+                       return;
+               }
+               break;
+       case PS_UNCALIBRATED:
+       case PS_SLAVE:
+               if (check_source_identity(p, m)) {
+                       return;
+               }
+               break;
        }
+
        if (!pid_eq(&rsp->requestingPortIdentity, &p->portIdentity)) {
                return;
        }
-       if (check_source_identity(p, m)) {
-               return;
-       }
+
        TAILQ_FOREACH(req, &p->delay_req, list) {
                if (rsp->hdr.sequenceId == 
ntohs(req->delay_req.hdr.sequenceId)) {
                        break;
@@ -2148,19 +2275,22 @@ void process_follow_up(struct port *p, struct 
ptp_message *m)
        case PS_DISABLED:
        case PS_LISTENING:
        case PS_PRE_MASTER:
-       case PS_MASTER:
        case PS_GRAND_MASTER:
        case PS_PASSIVE:
                return;
+       case PS_MASTER:
+               if (!p->altMaster) {
+                       return;
+               }
+               break;
        case PS_UNCALIBRATED:
        case PS_SLAVE:
+               if (check_source_identity(p, m)) {
+                       return;
+               }
                break;
        }
 
-       if (check_source_identity(p, m)) {
-               return;
-       }
-
        if (p->follow_up_info) {
                struct follow_up_info_tlv *fui = follow_up_info_extract(m);
                if (!fui)
@@ -2452,19 +2582,22 @@ void process_sync(struct port *p, struct ptp_message *m)
        case PS_DISABLED:
        case PS_LISTENING:
        case PS_PRE_MASTER:
-       case PS_MASTER:
        case PS_GRAND_MASTER:
        case PS_PASSIVE:
                return;
+       case PS_MASTER:
+               if (!p->altMaster) {
+                       return;
+               }
+               break;
        case PS_UNCALIBRATED:
        case PS_SLAVE:
+               if (check_source_identity(p, m)) {
+                       return;
+               }
                break;
        }
 
-       if (check_source_identity(p, m)) {
-               return;
-       }
-
        if (!msg_unicast(m) &&
            m->header.logMessageInterval != p->log_sync_interval) {
                if (m->header.logMessageInterval < -10 ||
@@ -2580,16 +2713,19 @@ static void port_e2e_transition(struct port *p, enum 
port_state next)
        case PS_PRE_MASTER:
                port_set_qualification_tmo(p);
                break;
+       case PS_PASSIVE:
+               port_set_announce_tmo(p);
        case PS_MASTER:
+               flush_last_sync(p);
+               if (p->altMaster) {
+                       port_set_delay_tmo(p);
+               }
        case PS_GRAND_MASTER:
                if (!p->inhibit_announce) {
                        set_tmo_log(p->fda.fd[FD_MANNO_TIMER], 1, -10); /*~1ms*/
                }
                port_set_sync_tx_tmo(p);
                break;
-       case PS_PASSIVE:
-               port_set_announce_tmo(p);
-               break;
        case PS_UNCALIBRATED:
                flush_last_sync(p);
                flush_delay_req(p);
@@ -2626,6 +2762,7 @@ static void port_p2p_transition(struct port *p, enum 
port_state next)
                port_set_qualification_tmo(p);
                break;
        case PS_MASTER:
+               flush_last_sync(p);
        case PS_GRAND_MASTER:
                if (!p->inhibit_announce) {
                        set_tmo_log(p->fda.fd[FD_MANNO_TIMER], 1, -10); /*~1ms*/
@@ -2690,6 +2827,11 @@ void port_link_status(void *ctx, int linkup, int 
ts_index)
                p->link_status = link_state;
        } else {
                p->link_status = link_state | LINK_STATE_CHANGED;
+               /* Update Interface speed information on Link up*/
+               if (linkup) {
+                       interface_get_ifinfo(p->iface);
+               }
+
                pr_notice("%s: link %s", p->log_name, linkup ? "up" : "down");
        }
 
@@ -2763,6 +2905,7 @@ static enum fsm_event bc_event(struct port *p, int 
fd_index)
 
                if (fd_index == FD_SYNC_RX_TIMER) {
                        p->service_stats.sync_timeout++;
+                       p->signalFail |= PDS_PTSF_LOSS_SYNC;
                } else {
                        p->service_stats.announce_timeout++;
                }
@@ -2804,6 +2947,7 @@ static enum fsm_event bc_event(struct port *p, int 
fd_index)
                if (p->delay_response_timeout && p->state == PS_SLAVE) {
                        p->delay_response_counter++;
                        if (p->delay_response_counter >= 
p->delay_response_timeout) {
+                               p->signalFail |= PDS_PTSF_LOSS_DELAY_RESP;
                                p->delay_response_counter = 0;
                                tsproc_reset(clock_get_tsproc(p->clock), 1);
                                pr_err("%s: delay response timeout", 
p->log_name);
@@ -3249,8 +3393,8 @@ struct port *port_open(const char *phc_device,
                p->state_machine = clock_slave_only(clock) ? ptp_slave_fsm : 
ptp_fsm;
        }
 
-       if (port_is_uds(p)) {
-               ; /* UDS cannot have a PHC. */
+       if (port_is_uds(p) || port_is_vport(p)) {
+               ; /* UDS & VPORT cannot have a PHC. */
        } else if (!interface_tsinfo_valid(interface)) {
                pr_warning("%s: get_ts_info not supported", p->log_name);
        } else if (p->phc_index >= 0 &&
diff --git a/port_private.h b/port_private.h
index d27dceb..56ae4e0 100644
--- a/port_private.h
+++ b/port_private.h
@@ -15,6 +15,13 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors' 
reputations.
  */
 #ifndef HAVE_PORT_PRIVATE_H
 #define HAVE_PORT_PRIVATE_H
@@ -126,6 +133,8 @@ struct port {
        Integer8            operLogPdelayReqInterval;
        Integer8            logPdelayReqInterval;
        UInteger32          neighborPropDelayThresh;
+       UInteger8           signalFail;
+       UInteger8           altMaster;
        int                 follow_up_info;
        int                 freq_est_interval;
        int                 hybrid_e2e;
@@ -145,6 +154,7 @@ struct port {
        UInteger8           versionNumber; /* UInteger4 */
        UInteger8           delay_response_counter;
        UInteger8           delay_response_timeout;
+       Integer64           portAsymmetry;
        struct PortStats    stats;
        struct PortServiceStats    service_stats;
        /* foreignMasterDS */
@@ -152,12 +162,12 @@ struct port {
        /* TC book keeping */
        TAILQ_HEAD(tct, tc_txd) tc_transmitted;
        /* unicast client mode */
-       struct unicast_master_table *unicast_master_table;
+       struct unicast_master_table  *unicast_master_table;
        /* unicast service mode */
-       struct unicast_service *unicast_service;
+       struct unicast_service       *unicast_service;
        int inhibit_multicast_service;
        /* slave event monitoring */
-       struct monitor *slave_event_monitor;
+       struct monitor                *slave_event_monitor;
 };
 
 #define portnum(p) (p->portIdentity.portNumber)
diff --git a/port_signaling.c b/port_signaling.c
index ed217c0..8e6451a 100644
--- a/port_signaling.c
+++ b/port_signaling.c
@@ -16,12 +16,20 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors' 
reputations.
  */
 #include "port.h"
 #include "port_private.h"
 #include "print.h"
 #include "unicast_client.h"
 #include "unicast_service.h"
+#include "clock.h"
 
 const struct PortIdentity wildcard_pid = {
        .clockIdentity = {
@@ -103,10 +111,39 @@ static int process_interval_request(struct port *p,
        return 0;
 }
 
+static int process_interface_rate(struct port *p,
+                                   struct msg_interface_rate_tlv *r)
+{
+       Integer64 delayAsymmetry;
+       double    nsDelay;
+
+       if (clock_interface_rate_support(p->clock) &&
+                       interface_ifinfo_valid(p->iface)) {
+               /* Delay Asymmetry Calculation */
+               delayAsymmetry  = r->interfaceBitPeriod -
+                       interface_bitperiod(p->iface);
+               delayAsymmetry  = delayAsymmetry/2;
+               nsDelay = (double)delayAsymmetry / 1000000000;
+               if (nsDelay) {
+                       delayAsymmetry =
+                               (r->numberOfBitsAfterTimestamp -
+                                r->numberOfBitsBeforeTimestamp)  * nsDelay;
+                       if (delayAsymmetry != p->portAsymmetry) {
+                               /* Updating the nanosecond part */
+                               p->asymmetry += ((delayAsymmetry -
+                                                       p->portAsymmetry) << 
16);
+                               p->portAsymmetry = delayAsymmetry;
+                       }
+               }
+       }
+       return 0;
+}
+
 int process_signaling(struct port *p, struct ptp_message *m)
 {
        struct tlv_extra *extra;
        struct msg_interval_req_tlv *r;
+       struct msg_interface_rate_tlv *rate;
        int err = 0, result;
 
        switch (p->state) {
@@ -161,10 +198,14 @@ int process_signaling(struct port *p, struct ptp_message 
*m)
 
                case TLV_ORGANIZATION_EXTENSION:
                        r = (struct msg_interval_req_tlv *) extra->tlv;
+                       rate = (struct msg_interface_rate_tlv *) extra->tlv;
 
                        if (0 == memcmp(r->id, ieee8021_id, 
sizeof(ieee8021_id)) &&
                            r->subtype[0] == 0 && r->subtype[1] == 0 && 
r->subtype[2] == 2)
                                err = process_interval_request(p, r);
+                       else if (0 == memcmp(r->id, itu_t_id, sizeof(itu_t_id)) 
&&
+                           r->subtype[0] == 0 && r->subtype[1] == 0 && 
r->subtype[2] == 2)
+                               err = process_interface_rate(p, rate);
                        break;
                }
        }
diff --git a/ptp4l.8 b/ptp4l.8
index e33454a..154b93f 100644
--- a/ptp4l.8
+++ b/ptp4l.8
@@ -245,7 +245,8 @@ the fault be reset immediately.
 The default is 16 seconds.
 .TP
 .B delay_mechanism
-Select the delay mechanism. Possible values are E2E, P2P and Auto.
+Select the delay mechanism. Possible values are E2E, P2P, no_mechanism,
+common_p2p, special and Auto.
 The default is E2E.
 .TP
 .B hybrid_e2e
@@ -338,10 +339,10 @@ smaller than this value the port is marked as not 802.1AS 
capable.
 .TP
 .B tsproc_mode
 Select the time stamp processing mode used to calculate offset and delay.
-Possible values are filter, raw, filter_weight, raw_weight. Raw modes perform
-well when the rate of sync messages (logSyncInterval) is similar to the rate of
-delay messages (logMinDelayReqInterval or logMinPdelayReqInterval). Weighting
-is useful with larger network jitters (e.g. software time stamping).
+Possible values are filter, raw, filter_weight, raw_weight, no_delay. Raw modes
+perform well when the rate of sync messages (logSyncInterval) is similar to the
+rate of delay messages (logMinDelayReqInterval or logMinPdelayReqInterval).
+Weighting is useful with larger network jitters (e.g. software time stamping).
 The default is filter.
 .TP
 .B delay_filter
diff --git a/ptp4l.c b/ptp4l.c
index c61175b..9f24b03 100644
--- a/ptp4l.c
+++ b/ptp4l.c
@@ -43,7 +43,10 @@ static void usage(char *progname)
                " Delay Mechanism\n\n"
                " -A        Auto, starting with E2E\n"
                " -E        E2E, delay request-response (default)\n"
-               " -P        P2P, peer delay mechanism\n\n"
+               " -P        P2P, peer delay mechanism\n"
+               " -N        NO_MECHANISM, no mechanism\n"
+               " -C        Common P2P\n"
+               " -X        Special\n\n"
                " Network Transport\n\n"
                " -2        IEEE 802.3\n"
                " -4        UDP IPV4 (default)\n"
@@ -89,7 +92,7 @@ int main(int argc, char *argv[])
        /* Process the command line arguments. */
        progname = strrchr(argv[0], '/');
        progname = progname ? 1+progname : argv[0];
-       while (EOF != (c = getopt_long(argc, argv, "AEP246HSLf:i:p:sl:mqvh",
+       while (EOF != (c = getopt_long(argc, argv, "AEP246HSLf:i:p:sl:mqvhNCX",
                                       opts, &index))) {
                switch (c) {
                case 0:
@@ -108,6 +111,18 @@ int main(int argc, char *argv[])
                        if (config_set_int(cfg, "delay_mechanism", DM_P2P))
                                goto out;
                        break;
+               case 'N':
+                       if (config_set_int(cfg, "delay_mechanism", 
DM_NO_MECHANISM))
+                               goto out;
+                       break;
+               case 'C':
+                       if (config_set_int(cfg, "delay_mechanism", 
DM_COMMON_P2P))
+                               goto out;
+                       break;
+               case 'X':
+                       if (config_set_int(cfg, "delay_mechanism", DM_SPECIAL))
+                               goto out;
+                       break;
                case '2':
                        if (config_set_int(cfg, "network_transport",
                                            TRANS_IEEE_802_3))
diff --git a/sk.c b/sk.c
index b55d6b5..acb7b18 100644
--- a/sk.c
+++ b/sk.c
@@ -16,6 +16,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #include <errno.h>
 #include <time.h>
@@ -200,6 +208,71 @@ failed:
        return -1;
 }
 
+int sk_get_if_info(const char *name, struct sk_if_info *if_info)
+{
+#ifdef ETHTOOL_GLINKSETTINGS
+       struct ifreq ifr;
+       int fd, err;
+
+       struct {
+               struct ethtool_link_settings req;
+               __u32 link_mode_data[3 * 127];
+       } ecmd;
+
+       memset(&ifr, 0, sizeof(ifr));
+       memset(&ecmd, 0, sizeof(ecmd));
+
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (fd < 0) {
+               goto failed;
+       }
+
+       ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
+
+       strncpy(ifr.ifr_name, name, IFNAMSIZ - 1);
+       ifr.ifr_data = (char *) &ecmd;
+
+       /* Handshake with kernel to determine number of words for link
+        * mode bitmaps. When requested number of bitmap words is not
+        * the one expected by kernel, the latter returns the integer
+        * opposite of what it is expecting. We request length 0 below
+        * (aka. invalid bitmap length) to get this info.
+        */
+       err = ioctl(fd, SIOCETHTOOL, &ifr);
+       if (err < 0) {
+               pr_err("ioctl SIOCETHTOOL failed: %m");
+               close(fd);
+               goto failed;
+       }
+
+       if (ecmd.req.link_mode_masks_nwords >= 0 ||
+                       ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) {
+               return 1;
+       }
+       ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
+
+       err = ioctl(fd, SIOCETHTOOL, &ifr);
+       if (err < 0) {
+               pr_err("ioctl SIOCETHTOOL failed: %m");
+               close(fd);
+               goto failed;
+       }
+
+       close(fd);
+
+       /* copy the necessary data to sk_info */
+       memset(if_info, 0, sizeof(struct sk_if_info));
+       if_info->valid = 1;
+       if_info->speed = ecmd.req.speed;
+
+       return 0;
+failed:
+#endif
+       /* clear data and ensure it is not marked valid */
+       memset(if_info, 0, sizeof(struct sk_if_info));
+       return -1;
+}
+
 static int sk_interface_guidaddr(const char *name, unsigned char *guid)
 {
        char file_name[64], buf[64], addr[8];
@@ -503,6 +576,7 @@ int sk_timestamping_init(int fd, const char *device, enum 
timestamp_type type,
                case TRANS_CONTROLNET:
                case TRANS_PROFINET:
                case TRANS_UDS:
+               case TRANS_VPORT:
                        return -1;
                }
                err = hwts_init(fd, device, filter1, filter2, tx_type);
diff --git a/sk.h b/sk.h
index 486dbc4..6473e0d 100644
--- a/sk.h
+++ b/sk.h
@@ -16,6 +16,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #ifndef HAVE_SK_H
 #define HAVE_SK_H
@@ -49,6 +57,16 @@ struct sk_ts_info {
        unsigned int rx_filters;
 };
 
+/**
+ * Contains interface information returned by theGLINKSETTINGS ioctl.
+ * @valid:            set to non-zero when the info struct contains valid data.
+ * @speed:            interface speed.
+ */
+struct sk_if_info {
+       int valid;
+       int speed;
+};
+
 /**
  * Obtains a socket suitable for use with sk_interface_index().
  * @return  An open socket on success, -1 otherwise.
@@ -78,6 +96,14 @@ int sk_general_init(int fd);
  */
 int sk_get_ts_info(const char *name, struct sk_ts_info *sk_info);
 
+/**
+ * Obtain supporte interface information
+ * @param name     The name of the interface
+ * @param info      Struct containing obtained interface information.
+ * @return          zero on success, negative on failure.
+ */
+int sk_get_if_info(const char *name, struct sk_if_info *sk_info);
+
 /**
  * Obtain the MAC address of a network interface.
  * @param name  The name of the interface
diff --git a/tlv.c b/tlv.c
index 1c13460..8bb9102 100644
--- a/tlv.c
+++ b/tlv.c
@@ -15,6 +15,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #include <arpa/inet.h>
 #include <errno.h>
@@ -35,6 +43,7 @@
        (tlv->length < sizeof(struct type) - sizeof(struct TLV))
 
 uint8_t ieee8021_id[3] = { IEEE_802_1_COMMITTEE };
+uint8_t itu_t_id[3] = { ITU_T_COMMITTEE };
 
 static TAILQ_HEAD(tlv_pool, tlv_extra) tlv_pool =
        TAILQ_HEAD_INITIALIZER(tlv_pool);
@@ -130,6 +139,8 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t 
data_len,
        struct defaultDS *dds;
        struct parentDS *pds;
        struct portDS *p;
+       struct port_ptsf_unusable_np *ptsf;
+       struct port_ptsf_data_np *ptsf_data;
        uint8_t *buf;
        uint16_t u16;
 
@@ -398,6 +409,16 @@ static int mgt_post_recv(struct management_tlv *m, 
uint16_t data_len,
                phn->phc_index = ntohl(phn->phc_index);
                extra_len = sizeof(struct port_hwclock_np);
                break;
+       case MID_PORT_PTSF_UNUSABLE_NP:
+               if (data_len != sizeof(struct port_ptsf_unusable_np))
+                       goto bad_length;
+               ptsf = (struct port_ptsf_unusable_np *) m->data;
+               ptsf->ptsf_unusable = ntohs(ptsf->ptsf_unusable);
+               break;
+       case MID_PORT_PTSF_DATA_NP:
+               ptsf_data = (struct port_ptsf_data_np *) m->data;
+               ptsf_data->portIdentity.portNumber = 
ntohs(ptsf_data->portIdentity.portNumber);
+               break;
        case MID_SAVE_IN_NON_VOLATILE_STORAGE:
        case MID_RESET_NON_VOLATILE_STORAGE:
        case MID_INITIALIZE:
@@ -436,6 +457,8 @@ static void mgt_pre_send(struct management_tlv *m, struct 
tlv_extra *extra)
        struct defaultDS *dds;
        struct currentDS *cds;
        struct parentDS *pds;
+       struct port_ptsf_unusable_np *ptsf;
+       struct port_ptsf_data_np *ptsf_data;
        struct portDS *p;
        uint8_t *buf;
        int i;
@@ -569,6 +592,14 @@ static void mgt_pre_send(struct management_tlv *m, struct 
tlv_extra *extra)
                phn->portIdentity.portNumber = 
htons(phn->portIdentity.portNumber);
                phn->phc_index = htonl(phn->phc_index);
                break;
+       case MID_PORT_PTSF_UNUSABLE_NP:
+               ptsf = (struct port_ptsf_unusable_np *) m->data;
+               ptsf->ptsf_unusable = htons(ptsf->ptsf_unusable);
+               break;
+       case MID_PORT_PTSF_DATA_NP:
+               ptsf_data = (struct port_ptsf_data_np *) m->data;
+               ptsf_data->portIdentity.portNumber = 
htons(ptsf_data->portIdentity.portNumber);
+               break;
        }
 }
 
@@ -700,6 +731,15 @@ static int org_post_recv(struct organization_tlv *org)
                        if (org->length + sizeof(struct TLV) != sizeof(struct 
msg_interval_req_tlv))
                                goto bad_length;
                }
+       } else if (memcmp(org->id, itu_t_id, sizeof(itu_t_id)) == 0) {
+               if (org->subtype[0] || org->subtype[1]) {
+                       return 0;
+               }
+               switch (org->subtype[2]) {
+               case 2:
+                       if (org->length + sizeof(struct TLV) != sizeof(struct 
msg_interface_rate_tlv))
+                               goto bad_length;
+               }
        }
        return 0;
 bad_length:
@@ -723,6 +763,14 @@ static void org_pre_send(struct organization_tlv *org)
                        f->scaledLastGmPhaseChange = 
htonl(f->scaledLastGmPhaseChange);
                        break;
                }
+       } else if (memcmp(org->id, itu_t_id, sizeof(itu_t_id)) == 0) {
+               if (org->subtype[0] || org->subtype[1]) {
+                       return;
+               }
+               switch (org->subtype[2]) {
+               case 2:
+                       break;
+               }
        }
 }
 
diff --git a/tlv.h b/tlv.h
index 8966696..eeae153 100644
--- a/tlv.h
+++ b/tlv.h
@@ -16,6 +16,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #ifndef HAVE_TLV_H
 #define HAVE_TLV_H
@@ -128,6 +136,8 @@ enum management_action {
 #define MID_PORT_SERVICE_STATS_NP                      0xC007
 #define MID_UNICAST_MASTER_TABLE_NP                    0xC008
 #define MID_PORT_HWCLOCK_NP                            0xC009
+#define MID_PORT_PTSF_UNUSABLE_NP                      0xC00A
+#define MID_PORT_PTSF_DATA_NP                          0xC00B
 
 /* Management error ID values */
 #define MID_RESPONSE_TOO_BIG                           0x0001
@@ -283,6 +293,27 @@ struct slave_rx_sync_timing_data_tlv {
          sizeof(struct slave_rx_sync_timing_data_tlv)) /               \
         sizeof(struct slave_rx_sync_timing_record))
 
+struct slave_rx_sync_computed_record {
+       UInteger16          sequenceId;
+       TimeInterval        offsetFromMaster;
+       TimeInterval        meanPathDelay;
+       Integer32           scaledNeighborRateRatio;
+} PACKED;
+
+struct slave_rx_sync_computed_data_tlv {
+       Enumeration16        type;
+       UInteger16           length;
+       struct PortIdentity  sourcePortIdentity;
+       uint8_t              computedFlags;
+       uint8_t              reserved;
+       struct slave_rx_sync_computed_record record[0];
+} PACKED;
+
+#define SLAVE_RX_SYNC_COMPUTED_MAX \
+       ((sizeof(struct message_data) - sizeof(struct signaling_msg) -  \
+         sizeof(struct slave_rx_sync_computed_data_tlv)) /             \
+        sizeof(struct slave_rx_sync_computed_record))
+
 typedef struct Integer96 {
        uint16_t nanoseconds_msb;
        uint64_t nanoseconds_lsb;
@@ -312,6 +343,20 @@ struct msg_interval_req_tlv {
        Octet         reserved[2];
 } PACKED;
 
+/* Organizationally Unique Identifiers */
+#define ITU_T_COMMITTEE 0x00, 0x19, 0xA7
+extern uint8_t itu_t_id[3];
+
+struct msg_interface_rate_tlv {
+       Enumeration16 type;
+       UInteger16    length;
+       Octet         id[3];
+       Octet         subtype[3];
+       UInteger64    interfaceBitPeriod;
+       UInteger16    numberOfBitsBeforeTimestamp;
+       UInteger16    numberOfBitsAfterTimestamp;
+} PACKED;
+
 struct time_status_np {
        int64_t       master_offset; /*nanoseconds*/
        int64_t       ingress_time;  /*nanoseconds*/
@@ -335,6 +380,11 @@ struct port_ds_np {
        Integer32     asCapable;
 } PACKED;
 
+struct port_ptsf_unusable_np {
+       struct PortIdentity portIdentity;
+       UInteger16    ptsf_unusable;
+} PACKED;
+
 
 #define EVENT_BITMASK_CNT 64
 
@@ -349,6 +399,10 @@ struct port_properties_np {
        uint8_t timestamping;
        struct PTPText interface;
 } PACKED;
+struct port_ptsf_data_np {
+       struct PortIdentity portIdentity;
+       UInteger8  signalFail;
+} PACKED;
 
 struct port_hwclock_np {
        struct PortIdentity portIdentity;
diff --git a/transport.c b/transport.c
index 9366fbf..a0905cd 100644
--- a/transport.c
+++ b/transport.c
@@ -15,6 +15,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 
 #include <arpa/inet.h>
@@ -25,6 +33,7 @@
 #include "udp.h"
 #include "udp6.h"
 #include "uds.h"
+#include "vport.h"
 
 int transport_close(struct transport *t, struct fdarray *fda)
 {
@@ -115,6 +124,9 @@ struct transport *transport_create(struct config *cfg,
        case TRANS_IEEE_802_3:
                t = raw_transport_create();
                break;
+       case TRANS_VPORT:
+               t = vport_transport_create();
+               break;
        case TRANS_DEVICENET:
        case TRANS_CONTROLNET:
        case TRANS_PROFINET:
diff --git a/transport.h b/transport.h
index 7a7f87b..2834887 100644
--- a/transport.h
+++ b/transport.h
@@ -16,6 +16,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #ifndef HAVE_TRANSPORT_H
 #define HAVE_TRANSPORT_H
@@ -39,6 +47,7 @@ enum transport_type {
        TRANS_DEVICENET,
        TRANS_CONTROLNET,
        TRANS_PROFINET,
+       TRANS_VPORT,
 };
 
 /**
diff --git a/ts2phc_phc_pps_source.c b/ts2phc_phc_pps_source.c
index 5f15e8e..9e959ef 100644
--- a/ts2phc_phc_pps_source.c
+++ b/ts2phc_phc_pps_source.c
@@ -2,6 +2,14 @@
  * @file ts2phc_phc_pps_source.c
  * @note Copyright (C) 2019 Richard Cochran <richardcoch...@gmail.com>
  * @note SPDX-License-Identifier: GPL-2.0+
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #include <linux/ptp_clock.h>
 #include <stdlib.h>
@@ -14,10 +22,12 @@
 #include "print.h"
 #include "ts2phc_phc_pps_source.h"
 #include "ts2phc_pps_source_private.h"
+#include "ts2phc_vport.h"
 #include "util.h"
 
 struct ts2phc_phc_pps_source {
        struct ts2phc_pps_source pps_source;
+       struct virtual_port *vport;
        clockid_t clkid;
        int channel;
        int fd;
@@ -82,6 +92,15 @@ static int ts2phc_phc_pps_source_getppstime(struct 
ts2phc_pps_source *src,
        return clock_gettime(s->clkid, ts);
 }
 
+int ts2phc_phc_pps_source_vport_transmit(struct ts2phc_pps_source *src,
+                                       struct timespec *ts)
+{
+       struct ts2phc_phc_pps_source *s =
+               container_of(src, struct ts2phc_phc_pps_source, pps_source);
+       return virtual_port_tx_announce(s->vport);
+}
+
+
 struct ts2phc_pps_source *ts2phc_phc_pps_source_create(struct config *cfg,
                                                       const char *dev)
 {
@@ -94,6 +113,11 @@ struct ts2phc_pps_source 
*ts2phc_phc_pps_source_create(struct config *cfg,
        }
        s->pps_source.destroy = ts2phc_phc_pps_source_destroy;
        s->pps_source.getppstime = ts2phc_phc_pps_source_getppstime;
+       s->vport = virtual_port_create(cfg);
+       if (!s->vport) {
+               free(s);
+               return NULL;
+       }
 
        s->clkid = posix_clock_open(dev, &junk);
        if (s->clkid == CLOCK_INVALID) {
diff --git a/ts2phc_phc_pps_source.h b/ts2phc_phc_pps_source.h
index c9ab54e..bacc689 100644
--- a/ts2phc_phc_pps_source.h
+++ b/ts2phc_phc_pps_source.h
@@ -8,7 +8,11 @@
 
 #include "ts2phc_pps_source.h"
 
+struct ts2phc_phc_pps_source;
+
 struct ts2phc_pps_source *ts2phc_phc_pps_source_create(struct config *cfg,
                                                       const char *dev);
 
+int ts2phc_phc_pps_source_vport_transmit(struct ts2phc_pps_source *m,
+                                       struct timespec *ts);
 #endif
diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c
index 91bd7c9..c599404 100644
--- a/ts2phc_pps_sink.c
+++ b/ts2phc_pps_sink.c
@@ -3,6 +3,14 @@
  * @brief Utility program to synchronize the PHC clock to external events
  * @note Copyright (C) 2019 Balint Ferencz <fer...@sch.bme.hu>
  * @note SPDX-License-Identifier: GPL-2.0+
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #include <errno.h>
 #include <linux/ptp_clock.h>
@@ -23,6 +31,7 @@
 #include "servo.h"
 #include "ts2phc_pps_sink.h"
 #include "ts2phc_pps_source.h"
+#include "ts2phc_phc_pps_source.h"
 #include "util.h"
 
 #define NS_PER_SEC             1000000000LL
@@ -426,7 +435,10 @@ int ts2phc_pps_sink_poll(struct ts2phc_pps_source *src)
 
        err = ts2phc_pps_source_getppstime(src, &source_ts.ts);
        source_ts.valid = err ? false : true;
-
+       if (source_ts.valid) {
+               /* Send Announce and Sync Message on Virtual Port  */
+               ts2phc_phc_pps_source_vport_transmit(src, &source_ts.ts);
+       }
        for (i = 0; i < ts2phc_n_sinks; i++) {
                if (polling_array.pfd[i].revents & (POLLIN|POLLPRI)) {
                        ts2phc_pps_sink_event(polling_array.sink[i], source_ts);
diff --git a/ts2phc_vport.c b/ts2phc_vport.c
new file mode 100644
index 0000000..6031b6d
--- /dev/null
+++ b/ts2phc_vport.c
@@ -0,0 +1,288 @@
+/**
+ * @file ts2phc_vport.c
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * @note SPDX-License-Identifier: GPL-2.0+
+ *
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
+ */
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "address.h"
+#include "ts2phc_vport.h"
+#include "print.h"
+#include "port.h"
+#include "transport.h"
+
+#define RECORDS_PER_MESSAGE 1
+
+struct virtual_port_message {
+       struct ptp_message *msg;
+       int records_per_msg;
+       int count;
+};
+
+struct virtual_port {
+       struct fdarray fda;
+       struct transport *trp;
+       struct interface *iface;
+       struct virtual_port_message announce;
+       struct virtual_port_message sync;
+       struct {
+               UInteger16 announce;
+               UInteger16 sync;
+       } seqnum;
+       struct PortIdentity portIdentity;
+       Integer16 utcOffset;
+       UInteger8 priority1;
+       struct ClockQuality clockQuality;
+       UInteger8 priority2;
+       struct ClockIdentity clockIdentity;
+       UInteger8 transportSpecific;
+       UInteger8 domainNumber;
+       UInteger8 flags;
+       Integer8 logAnnounceInterval;
+       Integer8 logSyncInterval;
+       Enumeration8 timeSource;
+};
+
+static bool virtual_port_active(struct virtual_port *virtual_port)
+{
+       return virtual_port->trp ? true : false;
+}
+
+static int virtual_port_forward(struct virtual_port *port, struct ptp_message 
*msg)
+{
+       int cnt;
+       if (msg_pre_send(msg)) {
+               return -1;
+       }
+       cnt = transport_sendto(port->trp, &port->fda, TRANS_GENERAL, msg);
+       if (cnt <= 0) {
+               pr_debug("failed to send message to virtual_port: ");
+       }
+
+       msg->header.sequenceId++;
+       return 0;
+}
+
+static int virtual_port_init_announce(struct virtual_port *p,
+                                     struct address address)
+{
+
+       struct ptp_message *msg;
+
+       msg = msg_allocate();
+       if (!msg) {
+               return -1;
+       }
+
+       msg->hwts.type = TS_ONESTEP;
+       msg->header.tsmt               = ANNOUNCE | p->transportSpecific;
+       msg->header.ver                = PTP_VERSION;
+       msg->header.messageLength      = sizeof(struct announce_msg);
+       msg->header.domainNumber       = p->domainNumber;
+       msg->header.sourcePortIdentity = p->portIdentity;
+       msg->header.sequenceId         = p->seqnum.announce++;
+       msg->header.control            = CTL_OTHER;
+       msg->header.logMessageInterval = p->logAnnounceInterval;
+       msg->header.flagField[1]       = p->flags | PTP_TIMESCALE;
+
+       msg->announce.currentUtcOffset        = p->utcOffset;
+       msg->announce.grandmasterPriority1    = p->priority1;
+       msg->announce.grandmasterClockQuality = p->clockQuality;
+       msg->announce.grandmasterPriority2    = p->priority2;
+       msg->announce.grandmasterIdentity     = p->clockIdentity;
+       msg->announce.stepsRemoved            = 0;
+       msg->announce.timeSource              = p->timeSource;
+       msg->address = address;
+
+       p->announce.msg = msg;
+
+       return 0;
+}
+
+static int virtual_port_init_sync(struct virtual_port *p,
+                                 struct address address)
+{
+
+       struct ptp_message *msg;
+
+       msg = msg_allocate();
+       if (!msg) {
+               return -1;
+       }
+       msg->hwts.type = TS_ONESTEP;
+       msg->header.tsmt               = SYNC | p->transportSpecific;
+       msg->header.ver                = PTP_VERSION;
+       msg->header.messageLength      = sizeof(struct sync_msg);
+       msg->header.domainNumber       = p->domainNumber;
+       msg->header.sourcePortIdentity = p->portIdentity;
+       msg->header.sequenceId         = p->seqnum.sync++;
+       msg->header.control            = CTL_SYNC;
+       msg->header.logMessageInterval = p->logSyncInterval;
+       msg->address = address;
+
+       p->sync.msg = msg;
+
+       return 0;
+}
+
+struct virtual_port *virtual_port_create(struct config *config)
+{
+       struct virtual_port *virtual_port;
+       struct address address;
+       struct sockaddr_un sa;
+
+       virtual_port = calloc(1, sizeof(*virtual_port));
+       if (!virtual_port) {
+               return NULL;
+       }
+       const char *iface_name;
+
+       iface_name = config_get_string(config, NULL, "vport_address");
+       if (!iface_name || !iface_name[0]) {
+               /* Return an inactive virtual_port. */
+               return virtual_port;
+       }
+       memset(&sa, 0, sizeof(sa));
+       sa.sun_family = AF_LOCAL;
+       snprintf(sa.sun_path, sizeof(sa.sun_path) - 1, "%s", iface_name);
+       address.sun = sa;
+       address.len = sizeof(sa);
+
+       virtual_port->trp = transport_create(config, TRANS_VPORT);
+       if (!virtual_port->trp) {
+               pr_err("failed to create transport");
+               free(virtual_port);
+               return NULL;
+       }
+
+       virtual_port->iface = interface_create(iface_name);
+       if (!virtual_port->iface) {
+               pr_err("failed to create interface");
+               goto failed;
+       }
+       interface_ensure_tslabel(virtual_port->iface);
+
+       if (transport_open(virtual_port->trp, virtual_port->iface,
+                          &virtual_port->fda, TS_SOFTWARE)) {
+               pr_err("failed to open transport");
+               goto no_trans_open;
+       }
+
+       virtual_port->utcOffset =
+               config_get_int(config, NULL, "utc_offset");
+       virtual_port->transportSpecific =
+               config_get_int(config, NULL, "transportSpecific") << 4;
+       virtual_port->domainNumber =
+               config_get_int(config, NULL, "domainNumber");
+
+       virtual_port->logSyncInterval =
+               config_get_int(config, NULL, "logSyncInterval");
+       virtual_port->logAnnounceInterval =
+               config_get_int(config, NULL, "logAnnounceInterval");
+
+       virtual_port->priority1  =
+               config_get_int(config, NULL, "priority1");
+       virtual_port->priority2  =
+               config_get_int(config, NULL, "priority2");
+       virtual_port->timeSource =
+               config_get_int(config, NULL, "timeSource");
+
+       virtual_port->clockQuality.clockClass =
+                       config_get_int(config, NULL, "clockClass");
+       virtual_port->clockQuality.clockAccuracy =
+                       config_get_int(config, NULL, "clockAccuracy");
+       virtual_port->clockQuality.offsetScaledLogVariance =
+                       config_get_int(config, NULL, "offsetScaledLogVariance");
+
+       if (strcmp(config_get_string(config, NULL, "clockIdentity"),
+                  "000000.0000.000000") == 0) {
+               pr_warning("vPort clockIdentity 000000.0000.000000");
+       } else {
+               if (str2cid(config_get_string(config, NULL, "clockIdentity"),
+                                             &virtual_port->clockIdentity)) {
+                       pr_err("failed to set clock identity");
+                       return NULL;
+               }
+       }
+
+       virtual_port->portIdentity.clockIdentity = virtual_port->clockIdentity;
+
+       if (virtual_port_init_announce(virtual_port, address)) {
+               goto no_trans_open;
+       }
+       if (virtual_port_init_sync(virtual_port, address)) {
+               msg_put(virtual_port->announce.msg);
+               goto no_trans_open;
+       }
+
+       return virtual_port;
+
+no_trans_open:
+       interface_destroy(virtual_port->iface);
+failed:
+       if (virtual_port->trp)
+               transport_destroy(virtual_port->trp);
+       free(virtual_port);
+       return NULL;
+}
+
+void virtual_port_destroy(struct virtual_port *virtual_port)
+{
+       if (virtual_port->announce.msg) {
+               msg_put(virtual_port->announce.msg);
+       }
+       if (virtual_port->sync.msg) {
+               msg_put(virtual_port->sync.msg);
+       }
+       free(virtual_port);
+}
+
+int virtual_port_tx_sync(struct virtual_port *virtual_port,
+                         tmv_t t1, tmv_t corr, tmv_t t2)
+{
+       struct ptp_message *msg;
+       int err;
+       struct address address;
+
+       if (!virtual_port_active(virtual_port)) {
+               return 0;
+       }
+
+       msg = virtual_port->sync.msg;
+       address = msg->address;
+       virtual_port->sync.count++;
+
+       err = virtual_port_forward(virtual_port, msg);
+       msg_put(msg);
+       virtual_port_init_announce(virtual_port, address);
+       return err;
+}
+
+
+int virtual_port_tx_announce(struct virtual_port *virtual_port)
+{
+       struct ptp_message *msg;
+       int err;
+       struct address address;
+
+       if (!virtual_port_active(virtual_port)) {
+               return 0;
+       }
+
+       msg = virtual_port->announce.msg;
+       address = msg->address;
+       virtual_port->announce.count++;
+
+       err = virtual_port_forward(virtual_port, msg);
+       msg_put(msg);
+       virtual_port_init_announce(virtual_port, address);
+       return err;
+}
diff --git a/ts2phc_vport.h b/ts2phc_vport.h
new file mode 100644
index 0000000..db17cd5
--- /dev/null
+++ b/ts2phc_vport.h
@@ -0,0 +1,31 @@
+/**
+ * @file ts2phc_vport.h
+ * @note Copyright (C) 2021 SyncMonk Technologies <servi...@syncmonk.net>
+ * @note SPDX-License-Identifier: GPL-2.0+
+ *
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
+ */
+#ifndef HAVE_TS2PHC_VPORT_H
+#define HAVE_TS2PHC_VPORT_H
+
+#include "config.h"
+#include "port.h"
+#include "tmv.h"
+
+struct virtual_port;
+
+struct virtual_port *virtual_port_create(struct config *config);
+
+void virtual_port_destroy(struct virtual_port *virtual_port);
+
+int virtual_port_tx_announce(struct virtual_port *virtual_port);
+
+int virtual_port_tx_sync(struct virtual_port *virtual_port, tmv_t t1, tmv_t 
corr,
+                        tmv_t t2);
+
+#endif
diff --git a/tsproc.c b/tsproc.c
index a871049..1decd5a 100644
--- a/tsproc.c
+++ b/tsproc.c
@@ -15,6 +15,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 
 #include <stdlib.h>
@@ -52,6 +60,7 @@ static int weighting(struct tsproc *tsp)
        switch (tsp->mode) {
        case TSPROC_FILTER:
        case TSPROC_RAW:
+       case TSPROC_NO_DELAY_MECHANISM:
                return 0;
        case TSPROC_FILTER_WEIGHT:
        case TSPROC_RAW_WEIGHT:
@@ -74,6 +83,7 @@ struct tsproc *tsproc_create(enum tsproc_mode mode,
        case TSPROC_RAW:
        case TSPROC_FILTER_WEIGHT:
        case TSPROC_RAW_WEIGHT:
+       case TSPROC_NO_DELAY_MECHANISM:
                tsp->mode = mode;
                break;
        default:
@@ -171,6 +181,7 @@ int tsproc_update_delay(struct tsproc *tsp, tmv_t *delay)
                break;
        case TSPROC_RAW:
        case TSPROC_RAW_WEIGHT:
+       case TSPROC_NO_DELAY_MECHANISM:
                *delay = raw_delay;
                break;
        }
@@ -207,6 +218,8 @@ int tsproc_update_offset(struct tsproc *tsp, tmv_t *offset, 
double *weight)
                raw_delay = get_raw_delay(tsp);
                delay = tsp->filtered_delay;
                break;
+       case TSPROC_NO_DELAY_MECHANISM:
+               break;
        }
 
        /* offset = t2 - t1 - delay */
diff --git a/tsproc.h b/tsproc.h
index fdb35a8..193b97d 100644
--- a/tsproc.h
+++ b/tsproc.h
@@ -16,6 +16,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #ifndef HAVE_TSPROC_H
 #define HAVE_TSPROC_H
@@ -33,6 +41,7 @@ enum tsproc_mode {
        TSPROC_RAW,
        TSPROC_FILTER_WEIGHT,
        TSPROC_RAW_WEIGHT,
+       TSPROC_NO_DELAY_MECHANISM,
 };
 
 /**
diff --git a/unicast_client.c b/unicast_client.c
index 8ebe06f..df31844 100644
--- a/unicast_client.c
+++ b/unicast_client.c
@@ -16,6 +16,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #include <stdlib.h>
 
@@ -26,6 +34,7 @@
 
 #define E2E_SYDY_MASK  (1 << ANNOUNCE | 1 << SYNC | 1 << DELAY_RESP)
 #define P2P_SYDY_MASK  (1 << ANNOUNCE | 1 << SYNC)
+#define E2E_SYDY_NO_MECHANISM (1 << ANNOUNCE | 1 << SYNC)
 
 static int attach_ack(struct ptp_message *msg, uint8_t message_type_flags)
 {
@@ -200,7 +209,8 @@ static int unicast_client_renew(struct port *p,
                if (err) {
                        goto out;
                }
-               if (p->delayMechanism != DM_P2P) {
+               if (p->delayMechanism != DM_P2P &&
+                               p->delayMechanism != DM_NO_MECHANISM) {
                        err = attach_request(msg, p->logMinDelayReqInterval,
                                             DELAY_RESP,
                                             p->unicast_req_duration);
@@ -253,7 +263,7 @@ static int unicast_client_sydy(struct port *p,
        if (err) {
                goto out;
        }
-       if (p->delayMechanism != DM_P2P) {
+       if (p->delayMechanism != DM_P2P && p->delayMechanism != 
DM_NO_MECHANISM) {
                err = attach_request(msg, p->logMinDelayReqInterval, DELAY_RESP,
                                     p->unicast_req_duration);
                if (err) {
@@ -400,6 +410,8 @@ int unicast_client_initialize(struct port *p)
                }
                if (p->delayMechanism == DM_P2P) {
                        master->sydymsk = P2P_SYDY_MASK;
+               } else if (p->delayMechanism == DM_NO_MECHANISM) {
+                       master->sydymsk = E2E_SYDY_NO_MECHANISM;
                } else {
                        master->sydymsk = E2E_SYDY_MASK;
                }
@@ -525,7 +537,7 @@ void unicast_client_state_changed(struct port *p)
        pid = clock_parent_identity(p->clock);
 
        STAILQ_FOREACH(ucma, &p->unicast_master_table->addrs, list) {
-               if (pid_eq(&ucma->portIdentity, &pid)) {
+               if (pid_eq(&ucma->portIdentity, &pid) || p->altMaster) {
                        ucma->state = unicast_fsm(ucma->state, UC_EV_SELECTED);
                } else {
                        ucma->state = unicast_fsm(ucma->state, 
UC_EV_UNSELECTED);
diff --git a/unicast_service.c b/unicast_service.c
index 3154894..f4ee593 100644
--- a/unicast_service.c
+++ b/unicast_service.c
@@ -16,6 +16,14 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
  */
 #include <stdlib.h>
 #include <sys/queue.h>
@@ -84,6 +92,30 @@ static int attach_grant(struct ptp_message *msg,
        return 0;
 }
 
+static int attach_interface_rate(struct ptp_message *msg,
+                       uint64_t iface_bit_period,
+                       uint16_t  no_of_bits_before_ts,
+                       uint16_t  no_of_bits_after_ts)
+{
+       struct msg_interface_rate_tlv *mir;
+       struct tlv_extra *extra;
+
+       extra = msg_tlv_append(msg, sizeof(*mir));
+       if (!extra) {
+               return -1;
+       }
+       mir = (struct msg_interface_rate_tlv *) extra->tlv;
+       mir->type = TLV_ORGANIZATION_EXTENSION;
+       mir->length = sizeof(*mir) - sizeof(mir->type) - sizeof(mir->length);
+       memcpy(mir->id, itu_t_id, sizeof(itu_t_id));
+       mir->subtype[2] = 2;
+       mir->interfaceBitPeriod = iface_bit_period;
+       mir->numberOfBitsBeforeTimestamp = no_of_bits_before_ts;
+       mir->numberOfBitsAfterTimestamp = no_of_bits_after_ts;
+
+       return 0;
+}
+
 static int compare_timeout(void *ain, void *bin)
 {
        struct unicast_service_interval *a, *b;
@@ -256,6 +288,13 @@ static int unicast_service_reply(struct port *p, struct 
ptp_message *dst,
        if (err) {
                goto out;
        }
+       if (clock_interface_rate_support(p->clock) && duration > 0 &&
+                       clock_telecom_profile(p->clock) && 
interface_ifinfo_valid(p->iface)) {
+               err = attach_interface_rate(msg, interface_bitperiod(p->iface), 
64, 720);
+               if (err) {
+                       goto out;
+               }
+       }
        err = port_prepare_and_send(p, msg, TRANS_GENERAL);
        if (err) {
                pr_err("%s: signaling message failed", p->log_name);
@@ -493,10 +532,10 @@ int unicast_service_timer(struct port *p)
        case PS_DISABLED:
        case PS_LISTENING:
        case PS_PRE_MASTER:
-       case PS_PASSIVE:
        case PS_UNCALIBRATED:
        case PS_SLAVE:
                break;
+       case PS_PASSIVE:
        case PS_MASTER:
        case PS_GRAND_MASTER:
                master = 1;
diff --git a/util.c b/util.c
index a59b559..e6aa70f 100644
--- a/util.c
+++ b/util.c
@@ -15,6 +15,13 @@
  * 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.
+ *
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors' 
reputations.
  */
 #include <arpa/inet.h>
 #include <errno.h>
@@ -114,6 +121,7 @@ int addreq(enum transport_type type, struct address *a, 
struct address *b)
        case TRANS_DEVICENET:
        case TRANS_CONTROLNET:
        case TRANS_PROFINET:
+       case TRANS_VPORT:
        default:
                pr_err("sorry, cannot compare addresses for this transport");
                return 0;
@@ -280,6 +288,7 @@ int str2addr(enum transport_type type, const char *s, 
struct address *addr)
        case TRANS_DEVICENET:
        case TRANS_CONTROLNET:
        case TRANS_PROFINET:
+       case TRANS_VPORT:
                pr_err("sorry, cannot convert addresses for this transport");
                return -1;
        case TRANS_UDP_IPV4:
diff --git a/vport.c b/vport.c
new file mode 100644
index 0000000..cdfc0c0
--- /dev/null
+++ b/vport.c
@@ -0,0 +1,142 @@
+/**
+ * @file vport.c
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * @note SPDX-License-Identifier: GPL-2.0+
+ *
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "address.h"
+#include "contain.h"
+#include "print.h"
+#include "transport_private.h"
+#include "vport.h"
+
+#define VPORT_FILEMODE (0660) /*0660*/
+
+struct vport {
+       struct transport t;
+       struct address address;
+};
+
+static int vport_close(struct transport *t, struct fdarray *fda)
+{
+       struct sockaddr_un sa;
+       socklen_t len = sizeof(sa);
+
+       if (!getsockname(fda->fd[FD_GENERAL], (struct sockaddr *) &sa, &len) &&
+           sa.sun_family == AF_LOCAL) {
+               unlink(sa.sun_path);
+       }
+
+       close(fda->fd[FD_GENERAL]);
+       return 0;
+}
+
+static int vport_open(struct transport *t, struct interface *iface, struct 
fdarray *fda,
+                   enum timestamp_type tt)
+{
+       char *vport_path = config_get_string(t->cfg, NULL, "vport_address");
+       struct vport *vport = container_of(t, struct vport, t);
+       const char *name = interface_name(iface);
+       struct sockaddr_un sa;
+       int fd, err;
+
+       fd = socket(AF_LOCAL, SOCK_DGRAM, 0);
+       if (fd < 0) {
+               pr_err("vport: failed to create socket: %m");
+               return -1;
+       }
+       memset(&sa, 0, sizeof(sa));
+       sa.sun_family = AF_LOCAL;
+       strncpy(sa.sun_path, name, sizeof(sa.sun_path) - 1);
+
+       unlink(name);
+
+       err = bind(fd, (struct sockaddr *) &sa, sizeof(sa));
+       if (err < 0) {
+               pr_err("vport: bind failed: %m");
+               close(fd);
+               return -1;
+       }
+
+       /* For client use, pre load the server path. */
+       memset(&sa, 0, sizeof(sa));
+       sa.sun_family = AF_LOCAL;
+       strncpy(sa.sun_path, vport_path, sizeof(sa.sun_path) - 1);
+       vport->address.sun = sa;
+       vport->address.len = sizeof(sa);
+
+       chmod(name, VPORT_FILEMODE);
+       fda->fd[FD_EVENT] = -1;
+       fda->fd[FD_GENERAL] = fd;
+       return 0;
+}
+
+static int vport_recv(struct transport *t, int fd, void *buf, int buflen,
+                   struct address *addr, struct hw_timestamp *hwts)
+{
+       int cnt;
+       struct vport *vport = container_of(t, struct vport, t);
+
+       addr->len = sizeof(addr->sun);
+       cnt = recvfrom(fd, buf, buflen, 0, &addr->sa, &addr->len);
+       if (cnt <= 0) {
+               pr_err("vport: recvfrom failed: %m");
+               return cnt;
+       }
+       vport->address = *addr;
+       return cnt;
+}
+
+static int vport_send(struct transport *t, struct fdarray *fda,
+                   enum transport_event event, int peer, void *buf, int buflen,
+                   struct address *addr, struct hw_timestamp *hwts)
+{
+       int cnt, fd = fda->fd[FD_GENERAL];
+       struct vport *vport = container_of(t, struct vport, t);
+
+       if (!addr)
+               addr = &vport->address;
+
+       cnt = sendto(fd, buf, buflen, 0, &addr->sa, addr->len);
+       if (cnt < 1) {
+               return -errno;
+       }
+       return cnt;
+}
+
+static void vport_release(struct transport *t)
+{
+       struct vport *vport = container_of(t, struct vport, t);
+
+       free(vport);
+}
+
+struct transport *vport_transport_create(void)
+{
+       struct vport *vport;
+
+       vport = calloc(1, sizeof(*vport));
+       if (!vport)
+               return NULL;
+       vport->t.close   = vport_close;
+       vport->t.open    = vport_open;
+       vport->t.recv    = vport_recv;
+       vport->t.send    = vport_send;
+       vport->t.release = vport_release;
+       return &vport->t;
+}
diff --git a/vport.h b/vport.h
new file mode 100644
index 0000000..bf77ddd
--- /dev/null
+++ b/vport.h
@@ -0,0 +1,26 @@
+/**
+ * @file vport.h
+ * @note Copyright (C) 2022 SyncMonk Technologies <servi...@syncmonk.net>
+ * @note SPDX-License-Identifier: GPL-2.0+
+ *
+ * Also, for each author's protection and ours, we want to make certain that
+ * everyone understands that there is no warranty for this free software.
+ * If the software is modified by someone else and passed on, we want its
+ * recipients to know that what they have is not the original, so that any
+ * problems introduced by others will not reflect on the original authors'
+ * reputations.
+ */
+#ifndef HAVE_VPORT_H
+#define HAVE_VPORT_H
+
+#include "config.h"
+#include "fd.h"
+#include "transport.h"
+
+/**
+ * Allocate an instance of a VPORT transport.
+ * @return Pointer to a new transport instance on success, NULL otherwise.
+ */
+struct transport *vport_transport_create(void);
+
+#endif
-- 
2.17.1



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

Reply via email to