1881 lines
54 KiB
Diff
1881 lines
54 KiB
Diff
commit dc03af081e36dd2d0aa90f55e6ee6ecf6c5a31e7
|
|
Author: Luke Howard (via linuxptp-devel Mailing List) <linuxptp-devel@lists.nwtime.org>
|
|
Date: Mon Sep 16 06:45:20 2024 +0000
|
|
|
|
makefile: use $(TRANSP) in ts2phc
|
|
|
|
ts2phc explicitly included the transport object files, rather than using the
|
|
$(TRANSP) variable defined earlier in the makefile.
|
|
|
|
Signed-off-by: Luke Howard <lukeh@padl.com>
|
|
|
|
diff --git a/makefile b/makefile
|
|
index 3c2406b..a12f061 100644
|
|
--- a/makefile
|
|
+++ b/makefile
|
|
@@ -89,7 +89,7 @@ timemaster: phc.o print.o rtnl.o sk.o timemaster.o util.o version.o
|
|
|
|
ts2phc: config.o clockadj.o hash.o interface.o msg.o phc.o pmc_agent.o \
|
|
pmc_common.o print.o $(SECURITY) $(SERVOS) sk.o $(TS2PHC) tlv.o transport.o \
|
|
- raw.o udp.o udp6.o uds.o util.o version.o
|
|
+ $(TRANSP) util.o version.o
|
|
|
|
tz2alt: config.o hash.o interface.o lstab.o msg.o phc.o pmc_common.o print.o \
|
|
$(SECURITY) sk.o tlv.o $(TRANSP) tz2alt.o util.o version.o
|
|
|
|
commit fd952e62cb0ed4d5bdb6bf4bb86e558ba95b3546
|
|
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
Date: Mon Apr 13 14:52:14 2026 +0200
|
|
|
|
clock: Add function to read clock.
|
|
|
|
Add a function to directly read a timestamp from the controlled clock.
|
|
This will be needed to provide an estimate of the transmit timestamp in
|
|
two-step sync messages.
|
|
|
|
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
|
|
diff --git a/clock.c b/clock.c
|
|
index 7c30066..d0ff9a7 100644
|
|
--- a/clock.c
|
|
+++ b/clock.c
|
|
@@ -1967,6 +1967,20 @@ struct tsproc *clock_get_tsproc(struct clock *c)
|
|
return c->tsproc;
|
|
}
|
|
|
|
+int clock_read_time(struct clock *c, tmv_t *ts)
|
|
+{
|
|
+ struct timespec tspec;
|
|
+
|
|
+ if (clock_gettime(c->clkid, &tspec) < 0) {
|
|
+ pr_err("failed to read clock: %m");
|
|
+ return -errno;
|
|
+ }
|
|
+
|
|
+ *ts = timespec_to_tmv(tspec);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
int clock_switch_phc(struct clock *c, int phc_index)
|
|
{
|
|
struct servo *servo;
|
|
diff --git a/clock.h b/clock.h
|
|
index ce9ae91..fed4d5a 100644
|
|
--- a/clock.h
|
|
+++ b/clock.h
|
|
@@ -318,6 +318,15 @@ UInteger16 clock_steps_removed(struct clock *c);
|
|
*/
|
|
struct tsproc *clock_get_tsproc(struct clock *c);
|
|
|
|
+/**
|
|
+ * Read the clock and return the timestamp as a tmv_t.
|
|
+ *
|
|
+ * @param c The clock instance.
|
|
+ * @param ts Pointer where to save the timestamp.
|
|
+ * @return Zero on success, negative error code otherwise.
|
|
+ */
|
|
+int clock_read_time(struct clock *c, tmv_t *ts);
|
|
+
|
|
/**
|
|
* Switch to a new PTP Hardware Clock, for use with the "jbod" mode.
|
|
* @param c The clock instance.
|
|
|
|
commit bbb16dbac6d03a1477422a459e0df26a11298fbd
|
|
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
Date: Mon Apr 13 12:00:39 2026 +0200
|
|
|
|
port: Add option to estimate two-step sync timestamp.
|
|
|
|
The PTP specification says that the origin timestamp in two-step sync
|
|
messages should either be zero or a valid timestamp accurate to +/- 1
|
|
second. ptp4l always set it to zero.
|
|
|
|
Add an option to provide an estimated timestamp by directly reading the
|
|
clock when the message is formed. This will be needed by the PROFINET
|
|
transport, where sync and follow-up messages are expected to contain the
|
|
same timestamp (to which the provided correction is applied).
|
|
|
|
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
|
|
diff --git a/config.c b/config.c
|
|
index fedc3a0..d7f433e 100644
|
|
--- a/config.c
|
|
+++ b/config.c
|
|
@@ -283,6 +283,7 @@ struct config_item config_tab[] = {
|
|
GLOB_ITEM_INT("dscp_general", 0, 0, 63),
|
|
GLOB_ITEM_INT("domainNumber", 0, 0, 255),
|
|
PORT_ITEM_INT("egressLatency", 0, INT_MIN, INT_MAX),
|
|
+ PORT_ITEM_INT("estimate_sync_timestamp", 0, 0, 1),
|
|
PORT_ITEM_INT("fault_badpeernet_interval", 16, INT32_MIN, INT32_MAX),
|
|
PORT_ITEM_INT("fault_reset_interval", 4, INT8_MIN, INT8_MAX),
|
|
GLOB_ITEM_DBL("first_step_threshold", 0.00002, 0.0, DBL_MAX),
|
|
diff --git a/configs/default.cfg b/configs/default.cfg
|
|
index 768eef8..c0e885c 100644
|
|
--- a/configs/default.cfg
|
|
+++ b/configs/default.cfg
|
|
@@ -39,6 +39,7 @@ G.8275.portDS.localPriority 128
|
|
allowedLostResponses 3
|
|
asCapable auto
|
|
BMCA ptp
|
|
+estimate_sync_timestamp 0
|
|
inhibit_announce 0
|
|
inhibit_delay_req 0
|
|
ignore_source_id 0
|
|
diff --git a/msg.c b/msg.c
|
|
index 7c236c3..4caabd5 100644
|
|
--- a/msg.c
|
|
+++ b/msg.c
|
|
@@ -450,6 +450,7 @@ int msg_pre_send(struct ptp_message *m)
|
|
|
|
switch (type) {
|
|
case SYNC:
|
|
+ timestamp_pre_send(&m->sync.originTimestamp);
|
|
break;
|
|
case DELAY_REQ:
|
|
clock_gettime(CLOCK_MONOTONIC, &m->ts.host);
|
|
diff --git a/port.c b/port.c
|
|
index 05aabea..4f1dd40 100644
|
|
--- a/port.c
|
|
+++ b/port.c
|
|
@@ -1830,6 +1830,17 @@ int port_tx_sync(struct port *p, struct address *dst, uint16_t sequence_id)
|
|
|
|
if (p->timestamping != TS_ONESTEP && p->timestamping != TS_P2P1STEP) {
|
|
msg->header.flagField[0] |= TWO_STEP;
|
|
+ if (p->estimate_sync_timestamp) {
|
|
+ tmv_t estxtm;
|
|
+
|
|
+ /* Estimate the transmit time. */
|
|
+ if (clock_read_time(p->clock, &estxtm)) {
|
|
+ err = -1;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ msg->sync.originTimestamp = tmv_to_Timestamp(estxtm);
|
|
+ }
|
|
}
|
|
|
|
if (dst) {
|
|
@@ -3582,6 +3593,8 @@ struct port *port_open(const char *phc_device,
|
|
if (p->phc_index < 0)
|
|
p->phc_index = phc_index;
|
|
p->jbod = config_get_int(cfg, interface_name(interface), "boundary_clock_jbod");
|
|
+ p->estimate_sync_timestamp =
|
|
+ config_get_int(cfg, interface_name(interface), "estimate_sync_timestamp");
|
|
p->master_only = config_get_int(cfg, interface_name(interface), "serverOnly");
|
|
p->bmca = config_get_int(cfg, interface_name(interface), "BMCA");
|
|
p->trp = transport_create(cfg, config_get_int(cfg,
|
|
diff --git a/port_private.h b/port_private.h
|
|
index e3c6679..e0417af 100644
|
|
--- a/port_private.h
|
|
+++ b/port_private.h
|
|
@@ -77,6 +77,7 @@ struct port {
|
|
enum fsm_event (*event)(struct port *p, int fd_index);
|
|
|
|
int jbod;
|
|
+ int estimate_sync_timestamp;
|
|
struct foreign_clock *best;
|
|
enum syfu_state syfu;
|
|
struct ptp_message *last_syncfup;
|
|
diff --git a/ptp4l.8 b/ptp4l.8
|
|
index 28cc4c9..06e3943 100644
|
|
--- a/ptp4l.8
|
|
+++ b/ptp4l.8
|
|
@@ -248,6 +248,12 @@ time at the reference plane and the reported transmit time stamp. This
|
|
value will be added to egress time stamps obtained from the hardware.
|
|
The default is 0.
|
|
|
|
+.TP
|
|
+.B estimate_sync_timestamp
|
|
+Enables ptp4l to provide in two-step sync messages an estimated transmit time
|
|
+stamp instead of zero.
|
|
+The default is 0 (disabled).
|
|
+
|
|
.TP
|
|
.B fault_badpeernet_interval
|
|
The time in seconds between the detection of a peer network misconfiguration
|
|
|
|
commit eee58e0c8116c1e713c09b208dd1f2918274922f
|
|
Author: Richard Cochran <richardcochran@gmail.com>
|
|
Date: Tue Jan 10 10:51:36 2017 +0100
|
|
|
|
Add PROFINET transport.
|
|
|
|
Add support for PTP over PROFINET (IEC 61158 Type 10), described in
|
|
IEEE 1588-2019 annex H, IEC 61158-6-10 and other standards. The protocol
|
|
is called Precision Transparent Clock Protocol (PTCP) and has its own
|
|
message format.
|
|
|
|
The layer converts between the PTP and PTCP packet formats. In addition
|
|
it also maintains a fair amount of state in order to translate between the
|
|
two protocols. In general PTCP provides less information than does PTP,
|
|
and it distributes the various fields differently among the message types.
|
|
|
|
A new option is added to configure the PTCP-specific subdomainUUID.
|
|
|
|
Co-authored-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
|
|
diff --git a/config.c b/config.c
|
|
index d7f433e..ecd612b 100644
|
|
--- a/config.c
|
|
+++ b/config.c
|
|
@@ -219,6 +219,7 @@ static struct config_enum nw_trans_enu[] = {
|
|
{ "L2", TRANS_IEEE_802_3 },
|
|
{ "UDPv4", TRANS_UDP_IPV4 },
|
|
{ "UDPv6", TRANS_UDP_IPV6 },
|
|
+ { "PN", TRANS_PROFINET },
|
|
{ NULL, 0 },
|
|
};
|
|
|
|
@@ -362,6 +363,8 @@ struct config_item config_tab[] = {
|
|
PORT_ITEM_INT("spp", -1, -1, UINT8_MAX),
|
|
GLOB_ITEM_DBL("step_threshold", 0.0, 0.0, DBL_MAX),
|
|
GLOB_ITEM_INT("step_window", 0, 0, INT_MAX),
|
|
+ PORT_ITEM_STR("subdomainUUID",
|
|
+ "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"),
|
|
GLOB_ITEM_INT("summary_interval", 0, INT_MIN, INT_MAX),
|
|
PORT_ITEM_INT("syncReceiptTimeout", 0, 0, UINT8_MAX),
|
|
GLOB_ITEM_INT("tc_spanning_tree", 0, 0, 1),
|
|
diff --git a/configs/default.cfg b/configs/default.cfg
|
|
index c0e885c..8bce8e6 100644
|
|
--- a/configs/default.cfg
|
|
+++ b/configs/default.cfg
|
|
@@ -109,6 +109,7 @@ ptp_dst_ipv6 FF0E:0:0:0:0:0:0:181
|
|
p2p_dst_ipv6 FF02:0:0:0:0:0:0:6B
|
|
ptp_dst_mac 01:1B:19:00:00:00
|
|
p2p_dst_mac 01:80:C2:00:00:0E
|
|
+subdomainUUID 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
|
|
udp_ttl 1
|
|
udp6_scope 0x0E
|
|
uds_address /var/run/ptp4l
|
|
diff --git a/makefile b/makefile
|
|
index a12f061..6f04160 100644
|
|
--- a/makefile
|
|
+++ b/makefile
|
|
@@ -26,7 +26,7 @@ PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster ts2phc tz2alt
|
|
SECURITY = sad.o
|
|
FILTERS = filter.o mave.o mmedian.o
|
|
SERVOS = linreg.o ntpshm.o nullf.o pi.o refclock_sock.o servo.o
|
|
-TRANSP = raw.o transport.o udp.o udp6.o uds.o
|
|
+TRANSP = pn.o pn-ptcp.o raw.o transport.o udp.o udp6.o uds.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
|
|
OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \
|
|
diff --git a/missing.h b/missing.h
|
|
index c6be8fd..83e163d 100644
|
|
--- a/missing.h
|
|
+++ b/missing.h
|
|
@@ -54,6 +54,9 @@
|
|
/* ETH_P_PRP is defined in if_ether.h from kernel 3.13 */
|
|
#define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */
|
|
#endif
|
|
+#ifndef ETH_P_PROFINET
|
|
+#define ETH_P_PROFINET 0x8892
|
|
+#endif
|
|
|
|
#ifndef HAVE_ONESTEP_SYNC
|
|
enum _missing_hwtstamp_tx_types {
|
|
diff --git a/pn-ptcp.c b/pn-ptcp.c
|
|
new file mode 100644
|
|
index 0000000..30943d2
|
|
--- /dev/null
|
|
+++ b/pn-ptcp.c
|
|
@@ -0,0 +1,635 @@
|
|
+/**
|
|
+ * @file pn-ptcp.c
|
|
+ * @brief Implements PTCP message types.
|
|
+ * @note Copyright (C) 2016 linutronix GmbH
|
|
+ * @note The PTP message construction was based on code from port.c.
|
|
+ * Copyright (C) 2011 Richard Cochran <richardcochran@gmail.com>
|
|
+ * @note SPDX-License-Identifier: GPL-2.0+
|
|
+ */
|
|
+#include <errno.h>
|
|
+#include <linux/if_ether.h>
|
|
+
|
|
+#include "missing.h"
|
|
+#include "pn-ptcp.h"
|
|
+#include "print.h"
|
|
+#include "util.h"
|
|
+
|
|
+#define FID_SYFU_CLOCK 0x0020 /* Two-step Sync */
|
|
+#define FID_SYFU_TIME 0x0021
|
|
+#define FID_SYNC_CLOCK 0x0080 /* One-step Sync */
|
|
+#define FID_SYNC_TIME 0x0081
|
|
+#define FID_ANNO_CLOCK 0xFF00 /* Announce */
|
|
+#define FID_ANNO_TIME 0xFF01
|
|
+#define FID_FUP_CLOCK 0xFF20 /* FollowUp */
|
|
+#define FID_FUP_TIME 0xFF21
|
|
+#define FID_DELAY_REQ 0xFF40 /* Peer Delay Request */
|
|
+#define FID_DELAY_RESP 0xFF41 /* Two-step Peer Delay Response */
|
|
+#define FID_DELAY_RESP_FU 0xFF42 /* Peer Delay Response FollowUp */
|
|
+#define FID_DELAY_RESP_1 0xFF43 /* One-step Peer Delay Response */
|
|
+
|
|
+#define MAC_SYFU_CLOCK 0x20
|
|
+#define MAC_SYFU_TIME 0x21
|
|
+#define MAC_ANNO_CLOCK 0x00
|
|
+#define MAC_ANNO_TIME 0x01
|
|
+#define MAC_FUP_CLOCK 0x40
|
|
+#define MAC_FUP_TIME 0x41
|
|
+#define MAC_PEER 0x0E
|
|
+
|
|
+#define PRIMARY_MASTER (1 << 0)
|
|
+#define MASTER_ACTIVE (1 << 7)
|
|
+#define MASTER_PRIO_1 (PRIMARY_MASTER | MASTER_ACTIVE)
|
|
+
|
|
+enum controlField {
|
|
+ CTL_SYNC,
|
|
+ CTL_DELAY_REQ,
|
|
+ CTL_FOLLOW_UP,
|
|
+ CTL_DELAY_RESP,
|
|
+ CTL_MANAGEMENT,
|
|
+ CTL_OTHER,
|
|
+};
|
|
+
|
|
+static void tlv_set(void *buf, int tag, int len)
|
|
+{
|
|
+ uint16_t word;
|
|
+
|
|
+ word = htons((tag << 9) | len);
|
|
+ memcpy(buf, &word, sizeof(word));
|
|
+}
|
|
+
|
|
+#define TLV_SET(x, type) \
|
|
+ tlv_set(&(x).TLVHeader, type, sizeof(x) - 2)
|
|
+
|
|
+static const unsigned char pn_ptcp_mac[MAC_LEN] = {
|
|
+ 0x01, 0x0E, 0xCF, 0x00, 0x04, 0x00
|
|
+};
|
|
+
|
|
+static const unsigned char pn_peer_mac[MAC_LEN] = {
|
|
+ 0x01, 0x80, 0xC2, 0x00, 0x00, 0x0E
|
|
+};
|
|
+
|
|
+static void mac_to_pid(eth_addr mac, struct PortIdentity *pid)
|
|
+{
|
|
+ pid->clockIdentity.id[0] = mac[0];
|
|
+ pid->clockIdentity.id[1] = mac[1];
|
|
+ pid->clockIdentity.id[2] = mac[2];
|
|
+ pid->clockIdentity.id[3] = 0xFF;
|
|
+ pid->clockIdentity.id[4] = 0xFE;
|
|
+ pid->clockIdentity.id[5] = mac[3];
|
|
+ pid->clockIdentity.id[6] = mac[4];
|
|
+ pid->clockIdentity.id[7] = mac[5];
|
|
+}
|
|
+
|
|
+static void ns_to_Timestamp(uint64_t ns, struct Timestamp *ts)
|
|
+{
|
|
+ *ts = tmv_to_Timestamp(nanoseconds_to_tmv(ns));
|
|
+}
|
|
+
|
|
+static void pid_to_mac(struct PortIdentity *pid, eth_addr mac)
|
|
+{
|
|
+ mac[0] = pid->clockIdentity.id[0];
|
|
+ mac[1] = pid->clockIdentity.id[1];
|
|
+ mac[2] = pid->clockIdentity.id[2];
|
|
+ mac[3] = pid->clockIdentity.id[5];
|
|
+ mac[4] = pid->clockIdentity.id[6];
|
|
+ mac[5] = pid->clockIdentity.id[7];
|
|
+}
|
|
+
|
|
+static void ptp_to_ptcp_subdomain(struct pn_info *info,
|
|
+ struct PortIdentity *pid,
|
|
+ struct PTCPSubdomain *s)
|
|
+{
|
|
+ TLV_SET(*s, PTCP_SUBDOMAIN);
|
|
+ pid_to_mac(pid, s->MasterSourceAddress);
|
|
+ memcpy(s->SubdomainUUID, info->subdomain, SUBD_LEN);
|
|
+}
|
|
+
|
|
+static void ptp_to_ptcp_master(struct pn_info *info, struct PTCPMaster *m)
|
|
+{
|
|
+ TLV_SET(*m, PTCP_MASTER);
|
|
+ m->MasterPriority1 = MASTER_PRIO_1;
|
|
+ m->MasterPriority2 = info->priority2;
|
|
+ m->ClockClass = info->quality.clockClass;
|
|
+ m->ClockAccuracy = info->quality.clockAccuracy;
|
|
+ m->ClockVariance = info->quality.offsetScaledLogVariance;
|
|
+}
|
|
+
|
|
+static unsigned char *setup_ethernet_header(unsigned char *ptr,
|
|
+ const unsigned char *dst_mac,
|
|
+ unsigned char last_mac_byte)
|
|
+{
|
|
+ struct eth_hdr *hdr;
|
|
+
|
|
+ ptr -= sizeof(*hdr);
|
|
+ hdr = (struct eth_hdr *) ptr;
|
|
+ memcpy(&hdr->dst, dst_mac, MAC_LEN);
|
|
+ hdr->dst[MAC_LEN - 1] = last_mac_byte;
|
|
+ hdr->type = htons(ETH_P_PROFINET);
|
|
+
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
+static uint64_t Timestamp_to_ns(struct Timestamp *ts)
|
|
+{
|
|
+ uint64_t sec, nsec, result;
|
|
+ uint32_t lsb = ntohl(ts->seconds_lsb);
|
|
+ uint16_t msb = ntohs(ts->seconds_msb);
|
|
+
|
|
+ sec = ((uint64_t)lsb) | (((uint64_t)msb) << 32);
|
|
+ nsec = ntohl(ts->nanoseconds);
|
|
+ result = sec * NS_PER_SEC + nsec;
|
|
+ return result;
|
|
+}
|
|
+
|
|
+static tmv_t ptcp_time_to_tmv(struct PTCPTime *t)
|
|
+{
|
|
+ struct Timestamp ts;
|
|
+
|
|
+ ts.seconds_msb = t->EpochNumber;
|
|
+ ts.seconds_lsb = t->Seconds;
|
|
+ ts.nanoseconds = t->NanoSeconds;
|
|
+
|
|
+ return nanoseconds_to_tmv(Timestamp_to_ns(&ts));
|
|
+}
|
|
+
|
|
+static unsigned char *ptp_sync_to_ptcp(struct ptp_message *msg,
|
|
+ struct PTCP_PDU *pdu,
|
|
+ struct pn_info *info, int *len)
|
|
+{
|
|
+ unsigned char *ptr = (unsigned char *) pdu;
|
|
+ struct PTCP_SYNC_PDU *sync = &pdu->sync;
|
|
+
|
|
+ /* Only two-step sync is supported. */
|
|
+ if (!(msg->header.flagField[0] & TWO_STEP))
|
|
+ return NULL;
|
|
+
|
|
+ *len = sizeof(struct eth_hdr) + sizeof(pdu->frame_id) + sizeof(*sync);
|
|
+
|
|
+ ptr = setup_ethernet_header(ptr, pn_ptcp_mac, MAC_SYFU_TIME);
|
|
+ pdu->frame_id = htons(FID_SYFU_TIME);
|
|
+
|
|
+ sync->hdr.SequenceID = msg->header.sequenceId;
|
|
+
|
|
+ ptp_to_ptcp_subdomain(info, &msg->header.sourcePortIdentity,
|
|
+ &sync->pdu.subdomain);
|
|
+
|
|
+ TLV_SET(sync->pdu.time, PTCP_TIME);
|
|
+ sync->pdu.time.EpochNumber =
|
|
+ msg->sync.originTimestamp.seconds_msb;
|
|
+ sync->pdu.time.Seconds =
|
|
+ msg->sync.originTimestamp.seconds_lsb;
|
|
+ sync->pdu.time.NanoSeconds =
|
|
+ msg->sync.originTimestamp.nanoseconds;
|
|
+
|
|
+ info->time = sync->pdu.time;
|
|
+
|
|
+ TLV_SET(sync->pdu.tmext, PTCP_TIME_EXTENSION);
|
|
+ sync->pdu.tmext.Flags = info->tmext_flags;
|
|
+ sync->pdu.tmext.CurrentUTCOffset = info->utc_offset;
|
|
+
|
|
+ ptp_to_ptcp_master(info, &sync->pdu.master);
|
|
+
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
+static unsigned char *ptp_fup_to_ptcp(struct ptp_message *msg,
|
|
+ struct PTCP_PDU *pdu,
|
|
+ struct pn_info *info, int *len)
|
|
+{
|
|
+ unsigned char *ptr = (unsigned char *) pdu;
|
|
+ struct PTCP_FUP_PDU *fup = &pdu->fup;
|
|
+ tmv_t actx, estx;
|
|
+ int32_t corr;
|
|
+
|
|
+ *len = sizeof(struct eth_hdr) + sizeof(pdu->frame_id) + sizeof(*fup);
|
|
+
|
|
+ ptr = setup_ethernet_header(ptr, pn_ptcp_mac, MAC_FUP_TIME);
|
|
+ pdu->frame_id = htons(FID_FUP_TIME);
|
|
+
|
|
+ fup->hdr.SequenceID = msg->header.sequenceId;
|
|
+
|
|
+ ptp_to_ptcp_subdomain(info, &msg->header.sourcePortIdentity,
|
|
+ &fup->pdu.subdomain);
|
|
+
|
|
+ fup->pdu.time = info->time;
|
|
+
|
|
+ estx = ptcp_time_to_tmv(&fup->pdu.time);
|
|
+ actx = nanoseconds_to_tmv(Timestamp_to_ns(&msg->follow_up.preciseOriginTimestamp));
|
|
+ corr = tmv_to_nanoseconds(tmv_sub(actx, estx));
|
|
+
|
|
+ if (corr > INT32_MAX) {
|
|
+ corr = INT32_MAX;
|
|
+ } else if (corr < INT32_MIN) {
|
|
+ corr = INT32_MIN;
|
|
+ }
|
|
+
|
|
+ fup->hdr.Delay1ns_FUP = htonl((uint32_t) corr);
|
|
+
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
+static unsigned char *ptp_anno_to_ptcp(struct ptp_message *msg,
|
|
+ struct PTCP_PDU *pdu,
|
|
+ struct pn_info *info, int *len)
|
|
+{
|
|
+ unsigned char *ptr = (unsigned char *) pdu;
|
|
+ struct PTCP_ANNOUNCE_PDU *anno = &pdu->anno;
|
|
+
|
|
+ *len = sizeof(struct eth_hdr) + sizeof(pdu->frame_id) + sizeof(*anno);
|
|
+
|
|
+ ptr = setup_ethernet_header(ptr, pn_ptcp_mac, MAC_ANNO_TIME);
|
|
+ pdu->frame_id = htons(FID_ANNO_TIME);
|
|
+
|
|
+ anno->hdr.SequenceID = msg->header.sequenceId;
|
|
+
|
|
+ ptp_to_ptcp_subdomain(info, &msg->header.sourcePortIdentity,
|
|
+ &anno->pdu.subdomain);
|
|
+
|
|
+ /*
|
|
+ * We will need quality, priority2, and friends in the PTCP
|
|
+ * Sync message, so we remember the latest values here.
|
|
+ */
|
|
+ info->quality = msg->announce.grandmasterClockQuality;
|
|
+ info->priority2 = msg->announce.grandmasterPriority2;
|
|
+ info->utc_offset = msg->announce.currentUtcOffset;
|
|
+
|
|
+ if (field_is_set(msg, 1, LEAP_61)) {
|
|
+ info->tmext_flags = htons(1 << 8);
|
|
+ } else if (field_is_set(msg, 1, LEAP_59)) {
|
|
+ info->tmext_flags = htons(2 << 8);
|
|
+ } else {
|
|
+ info->tmext_flags = 0;
|
|
+ }
|
|
+
|
|
+ ptp_to_ptcp_master(info, &anno->pdu.master);
|
|
+
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
+static unsigned char *ptp_req_to_ptcp(struct ptp_message *msg,
|
|
+ struct PTCP_PDU *pdu,
|
|
+ struct pn_info *info, int *len)
|
|
+{
|
|
+ unsigned char *ptr = (unsigned char *) pdu;
|
|
+ struct PTCP_DELAY_REQ_PDU *req = &pdu->req;
|
|
+
|
|
+ *len = sizeof(struct eth_hdr) + sizeof(pdu->frame_id) + sizeof(*req);
|
|
+
|
|
+ ptr = setup_ethernet_header(ptr, pn_peer_mac, MAC_PEER);
|
|
+ pdu->frame_id = htons(FID_DELAY_REQ);
|
|
+
|
|
+ req->hdr.SequenceID = msg->header.sequenceId;
|
|
+
|
|
+ TLV_SET(req->pdu.param, PTCP_DELAY_PARAMETER);
|
|
+ pid_to_mac(&msg->header.sourcePortIdentity,
|
|
+ req->pdu.param.PortMACAddress);
|
|
+ /*
|
|
+ * The requesting port ID in the PTCP responses does not
|
|
+ * include the port number. Remember the local port number
|
|
+ * so that we can fudge it back into the responses.
|
|
+ */
|
|
+ info->portNumber = ntohs(msg->header.sourcePortIdentity.portNumber);
|
|
+
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
+static unsigned char *ptp_resp_to_ptcp(struct ptp_message *msg,
|
|
+ struct PTCP_PDU *pdu,
|
|
+ struct pn_info *info, int *len)
|
|
+{
|
|
+ unsigned char *ptr = (unsigned char *) pdu;
|
|
+ struct PTCP_DELAY_RSP_PDU *resp = &pdu->resp;
|
|
+
|
|
+ *len = sizeof(struct eth_hdr) + sizeof(pdu->frame_id) + sizeof(*resp);
|
|
+
|
|
+ ptr = setup_ethernet_header(ptr, pn_peer_mac, MAC_PEER);
|
|
+ pdu->frame_id = htons(FID_DELAY_RESP);
|
|
+
|
|
+ resp->hdr.SequenceID = msg->header.sequenceId;
|
|
+
|
|
+ TLV_SET(resp->pdu.delay_param, PTCP_DELAY_PARAMETER);
|
|
+ pid_to_mac(&msg->pdelay_resp.requestingPortIdentity,
|
|
+ resp->pdu.delay_param.PortMACAddress);
|
|
+
|
|
+ TLV_SET(resp->pdu.port_param, PTCP_PORT_PARAMETER);
|
|
+ /*
|
|
+ * We leave T2PortRxDelay and T3PortTxDelay as zero, since
|
|
+ * the time stamps are already corrected for ingress/egress
|
|
+ * latency at the port layer.
|
|
+ */
|
|
+
|
|
+ TLV_SET(resp->pdu.time, PTCP_PORT_TIME);
|
|
+ /*
|
|
+ * The port layer always sends the pdelay_resp_fup immediately
|
|
+ * after the pdelay_resp. Remember timestamp T2 so that we can
|
|
+ * calculate the local residence time for the PTCP follow up.
|
|
+ */
|
|
+ info->tx_t2 = Timestamp_to_ns(&msg->pdelay_resp.requestReceiptTimestamp);
|
|
+ resp->pdu.time.T2TimeStamp = htonl(info->tx_t2);
|
|
+
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
+static unsigned char *ptp_resfup_to_ptcp(struct ptp_message *msg,
|
|
+ struct PTCP_PDU *pdu,
|
|
+ struct pn_info *info, int *len)
|
|
+{
|
|
+ unsigned char *ptr = (unsigned char *) pdu;
|
|
+ struct PTCP_DELAY_FU_RSP_PDU *rfp = &pdu->resfup;
|
|
+ uint64_t diff;
|
|
+
|
|
+ *len = sizeof(struct eth_hdr) + sizeof(pdu->frame_id) + sizeof(*rfp);
|
|
+
|
|
+ ptr = setup_ethernet_header(ptr, pn_peer_mac, MAC_PEER);
|
|
+ pdu->frame_id = htons(FID_DELAY_RESP_FU);
|
|
+
|
|
+ rfp->hdr.SequenceID = msg->header.sequenceId;
|
|
+
|
|
+ diff = Timestamp_to_ns(&msg->pdelay_resp_fup.responseOriginTimestamp);
|
|
+ diff -= info->tx_t2;
|
|
+ rfp->hdr.Delay1ns = htonl(diff);
|
|
+
|
|
+ TLV_SET(rfp->pdu.param, PTCP_DELAY_PARAMETER);
|
|
+ pid_to_mac(&msg->pdelay_resp.requestingPortIdentity,
|
|
+ rfp->pdu.param.PortMACAddress);
|
|
+
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
+unsigned char *ptp_to_ptcp(struct ptp_message *msg, struct PTCP_PDU *pdu,
|
|
+ struct pn_info *info, int *len)
|
|
+{
|
|
+ memset(pdu, 0, sizeof(*pdu));
|
|
+
|
|
+ switch (msg_type(msg)) {
|
|
+ case SYNC:
|
|
+ return ptp_sync_to_ptcp(msg, pdu, info, len);
|
|
+ case FOLLOW_UP:
|
|
+ return ptp_fup_to_ptcp(msg, pdu, info, len);
|
|
+ case PDELAY_REQ:
|
|
+ return ptp_req_to_ptcp(msg, pdu, info, len);
|
|
+ case PDELAY_RESP:
|
|
+ return ptp_resp_to_ptcp(msg, pdu, info, len);
|
|
+ case PDELAY_RESP_FOLLOW_UP:
|
|
+ return ptp_resfup_to_ptcp(msg, pdu, info, len);
|
|
+ case ANNOUNCE:
|
|
+ return ptp_anno_to_ptcp(msg, pdu, info, len);
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static int ptcp_sync_to_ptp(struct PTCP_SYNC_PDU *pdu, int len,
|
|
+ struct ptp_message *msg, struct pn_info *info,
|
|
+ int two_step)
|
|
+{
|
|
+ int pdulen = sizeof(struct sync_msg);
|
|
+
|
|
+ if (len < sizeof(*pdu))
|
|
+ return -EBADMSG;
|
|
+
|
|
+ msg->hwts.type = info->timestamping;
|
|
+
|
|
+ msg->header.tsmt = SYNC | info->transportSpecific;
|
|
+ msg->header.ver = PTP_VERSION;
|
|
+ msg->header.messageLength = pdulen;
|
|
+ msg->header.domainNumber = info->domain;
|
|
+
|
|
+ mac_to_pid(pdu->pdu.subdomain.MasterSourceAddress,
|
|
+ &msg->header.sourcePortIdentity);
|
|
+
|
|
+ msg->header.sequenceId = ntohs(pdu->hdr.SequenceID);
|
|
+ msg->header.control = CTL_SYNC;
|
|
+ msg->header.logMessageInterval = info->logSyncInterval;
|
|
+
|
|
+ if (two_step)
|
|
+ msg->header.flagField[0] |= TWO_STEP;
|
|
+
|
|
+ if (msg_pre_send(msg)) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* Remember fields to be reported in the announce message. */
|
|
+ info->tmext_flags = pdu->pdu.tmext.Flags;
|
|
+ info->utc_offset = pdu->pdu.tmext.CurrentUTCOffset;
|
|
+ info->priority2 = pdu->pdu.master.MasterPriority2;
|
|
+ info->quality.clockClass = pdu->pdu.master.ClockClass;
|
|
+ info->quality.clockAccuracy = pdu->pdu.master.ClockAccuracy;
|
|
+ info->quality.offsetScaledLogVariance = pdu->pdu.master.ClockVariance;
|
|
+
|
|
+ return pdulen;
|
|
+}
|
|
+
|
|
+static int ptcp_anno_to_ptp(struct PTCP_ANNOUNCE_PDU *pdu, int len,
|
|
+ struct ptp_message *msg, struct pn_info *info)
|
|
+{
|
|
+ int pdulen = sizeof(struct announce_msg);
|
|
+ uint16_t flags;
|
|
+
|
|
+ if (len < sizeof(*pdu))
|
|
+ return -EBADMSG;
|
|
+
|
|
+ msg->hwts.type = info->timestamping;
|
|
+
|
|
+ msg->header.tsmt = ANNOUNCE | info->transportSpecific;
|
|
+ msg->header.ver = PTP_VERSION;
|
|
+ msg->header.messageLength = pdulen;
|
|
+ msg->header.domainNumber = info->domain;
|
|
+
|
|
+ mac_to_pid(pdu->pdu.subdomain.MasterSourceAddress,
|
|
+ &msg->header.sourcePortIdentity);
|
|
+
|
|
+ msg->header.sequenceId = ntohs(pdu->hdr.SequenceID);
|
|
+ msg->header.control = CTL_OTHER;
|
|
+ msg->header.logMessageInterval = info->logAnnounceInterval;
|
|
+
|
|
+ flags = ntohs(info->tmext_flags);
|
|
+ if (flags & (1 << 8)) {
|
|
+ msg->header.flagField[1] |= LEAP_61;
|
|
+ } else if (flags & (2 << 8)) {
|
|
+ msg->header.flagField[1] |= LEAP_59;
|
|
+ }
|
|
+
|
|
+ msg->announce.currentUtcOffset = ntohs(info->utc_offset);
|
|
+ msg->announce.grandmasterPriority1 = 128;
|
|
+ msg->announce.grandmasterClockQuality = info->quality;
|
|
+ msg->announce.grandmasterClockQuality.offsetScaledLogVariance =
|
|
+ ntohs(info->quality.offsetScaledLogVariance);
|
|
+ msg->announce.grandmasterPriority2 = info->priority2;
|
|
+ msg->announce.grandmasterIdentity =
|
|
+ msg->header.sourcePortIdentity.clockIdentity;
|
|
+ msg->announce.stepsRemoved = 1;
|
|
+ msg->announce.timeSource = INTERNAL_OSCILLATOR;
|
|
+
|
|
+ return msg_pre_send(msg) ? -1 : pdulen;
|
|
+}
|
|
+
|
|
+static int ptcp_fup_to_ptp(struct PTCP_FUP_PDU *pdu, int len,
|
|
+ struct ptp_message *msg, struct pn_info *info)
|
|
+{
|
|
+ int pdulen = sizeof(struct follow_up_msg);
|
|
+ tmv_t actx, corr, estx;
|
|
+
|
|
+ if (len < sizeof(*pdu))
|
|
+ return -EBADMSG;
|
|
+
|
|
+ msg->hwts.type = info->timestamping;
|
|
+
|
|
+ msg->header.tsmt = FOLLOW_UP | info->transportSpecific;
|
|
+ msg->header.ver = PTP_VERSION;
|
|
+ msg->header.messageLength = pdulen;
|
|
+ msg->header.domainNumber = info->domain;
|
|
+
|
|
+ mac_to_pid(pdu->pdu.subdomain.MasterSourceAddress,
|
|
+ &msg->header.sourcePortIdentity);
|
|
+
|
|
+ msg->header.sequenceId = ntohs(pdu->hdr.SequenceID);
|
|
+ msg->header.control = CTL_FOLLOW_UP;
|
|
+ msg->header.logMessageInterval = info->logSyncInterval;
|
|
+
|
|
+ estx = ptcp_time_to_tmv(&pdu->pdu.time);
|
|
+ corr = nanoseconds_to_tmv(ntohl(pdu->hdr.Delay1ns_FUP));
|
|
+ actx = tmv_add(estx, corr);
|
|
+
|
|
+ msg->follow_up.preciseOriginTimestamp = tmv_to_Timestamp(actx);
|
|
+
|
|
+ return msg_pre_send(msg) ? -1 : pdulen;
|
|
+}
|
|
+
|
|
+static int ptcp_req_to_ptp(struct PTCP_DELAY_REQ_PDU *pdu, int len,
|
|
+ struct ptp_message *msg, struct pn_info *info)
|
|
+{
|
|
+ int pdulen = sizeof(struct pdelay_req_msg);
|
|
+
|
|
+ if (len < sizeof(*pdu))
|
|
+ return -EBADMSG;
|
|
+
|
|
+ msg->hwts.type = info->timestamping;
|
|
+
|
|
+ msg->header.tsmt = PDELAY_REQ | info->transportSpecific;
|
|
+ msg->header.ver = PTP_VERSION;
|
|
+ msg->header.messageLength = pdulen;
|
|
+ msg->header.domainNumber = info->domain;
|
|
+
|
|
+ mac_to_pid(pdu->pdu.param.PortMACAddress,
|
|
+ &msg->header.sourcePortIdentity);
|
|
+
|
|
+ msg->header.sourcePortIdentity.portNumber = 1;
|
|
+
|
|
+ msg->header.sequenceId = ntohs(pdu->hdr.SequenceID);
|
|
+ msg->header.control = CTL_OTHER;
|
|
+ msg->header.logMessageInterval = 0x7f;
|
|
+
|
|
+ /* Assume this peer is sending responses to our requests as well. */
|
|
+ info->peer_portid = msg->header.sourcePortIdentity;
|
|
+
|
|
+ return msg_pre_send(msg) ? -1 : pdulen;
|
|
+}
|
|
+
|
|
+static int ptcp_resp_to_ptp(struct PTCP_DELAY_RSP_PDU *pdu, int len,
|
|
+ struct ptp_message *msg, struct pn_info *info,
|
|
+ int two_step)
|
|
+{
|
|
+ int pdulen = sizeof(struct pdelay_resp_msg);
|
|
+
|
|
+ if (len < sizeof(*pdu))
|
|
+ return -EBADMSG;
|
|
+
|
|
+ msg->hwts.type = info->timestamping;
|
|
+
|
|
+ msg->header.tsmt = PDELAY_RESP | info->transportSpecific;
|
|
+ msg->header.ver = PTP_VERSION;
|
|
+ msg->header.messageLength = pdulen;
|
|
+ msg->header.domainNumber = info->domain;
|
|
+ /*
|
|
+ * PTCP doesn't tell us the peer's port ID. Assume this
|
|
+ * message came from the sender of the last received request.
|
|
+ */
|
|
+ msg->header.sourcePortIdentity = info->peer_portid;
|
|
+ msg->header.sequenceId = ntohs(pdu->hdr.SequenceID);
|
|
+ msg->header.control = CTL_OTHER;
|
|
+ msg->header.logMessageInterval = 0x7f;
|
|
+
|
|
+ if (two_step)
|
|
+ msg->header.flagField[0] |= TWO_STEP;
|
|
+
|
|
+ mac_to_pid(pdu->pdu.delay_param.PortMACAddress,
|
|
+ &msg->pdelay_resp.requestingPortIdentity);
|
|
+
|
|
+ msg->pdelay_resp.requestingPortIdentity.portNumber = info->portNumber;
|
|
+
|
|
+ /*
|
|
+ * Remember T2 from the payload. The follow-up will deliver
|
|
+ * (T3-T2) instead of T3. In addition, the payload values are
|
|
+ * only 32 bits wide. We will just construct phony T2 and T3
|
|
+ * values without the missing upper bits.
|
|
+ */
|
|
+ info->rx_t2 = ntohl(pdu->pdu.time.T2TimeStamp);
|
|
+ ns_to_Timestamp(info->rx_t2, &msg->pdelay_resp.requestReceiptTimestamp);
|
|
+
|
|
+ return msg_pre_send(msg) ? -1 : pdulen;
|
|
+}
|
|
+
|
|
+static int ptcp_resfup_to_ptp(struct PTCP_DELAY_FU_RSP_PDU *pdu, int len,
|
|
+ struct ptp_message *msg, struct pn_info *info)
|
|
+{
|
|
+ int pdulen = sizeof(struct pdelay_resp_fup_msg);
|
|
+ uint64_t diff;
|
|
+
|
|
+ if (len < sizeof(*pdu))
|
|
+ return -EBADMSG;
|
|
+
|
|
+ msg->hwts.type = info->timestamping;
|
|
+
|
|
+ msg->header.tsmt = PDELAY_RESP_FOLLOW_UP | info->transportSpecific;
|
|
+ msg->header.ver = PTP_VERSION;
|
|
+ msg->header.messageLength = pdulen;
|
|
+ msg->header.domainNumber = info->domain;
|
|
+ msg->header.sourcePortIdentity = info->peer_portid;
|
|
+ msg->header.sequenceId = ntohs(pdu->hdr.SequenceID);
|
|
+ msg->header.control = CTL_OTHER;
|
|
+ msg->header.logMessageInterval = 0x7f;
|
|
+
|
|
+ mac_to_pid(pdu->pdu.param.PortMACAddress,
|
|
+ &msg->pdelay_resp_fup.requestingPortIdentity);
|
|
+
|
|
+ msg->pdelay_resp_fup.requestingPortIdentity.portNumber = info->portNumber;
|
|
+
|
|
+ diff = ntohl(pdu->hdr.Delay1ns);
|
|
+ diff += info->rx_t2;
|
|
+ ns_to_Timestamp(diff, &msg->pdelay_resp_fup.responseOriginTimestamp);
|
|
+
|
|
+ return msg_pre_send(msg) ? -1 : pdulen;
|
|
+}
|
|
+
|
|
+int ptcp_to_ptp(struct PTCP_PDU *pdu, int len, struct ptp_message *msg,
|
|
+ struct pn_info *info)
|
|
+{
|
|
+ if (len < sizeof(pdu->frame_id))
|
|
+ return -EBADMSG;
|
|
+ len -= sizeof(pdu->frame_id);
|
|
+
|
|
+ switch (ntohs(pdu->frame_id)) {
|
|
+ case FID_SYFU_TIME:
|
|
+ return ptcp_sync_to_ptp(&pdu->sync, len, msg, info, 1);
|
|
+ case FID_SYNC_TIME:
|
|
+ return ptcp_sync_to_ptp(&pdu->sync, len, msg, info, 0);
|
|
+ case FID_ANNO_TIME:
|
|
+ return ptcp_anno_to_ptp(&pdu->anno, len, msg, info);
|
|
+ case FID_FUP_TIME:
|
|
+ return ptcp_fup_to_ptp(&pdu->fup, len, msg, info);
|
|
+ case FID_DELAY_REQ:
|
|
+ return ptcp_req_to_ptp(&pdu->req, len, msg, info);
|
|
+ case FID_DELAY_RESP:
|
|
+ return ptcp_resp_to_ptp(&pdu->resp, len, msg, info, 1);
|
|
+ case FID_DELAY_RESP_FU:
|
|
+ return ptcp_resfup_to_ptp(&pdu->resfup, len, msg, info);
|
|
+ case FID_DELAY_RESP_1:
|
|
+ return ptcp_resp_to_ptp(&pdu->resp, len, msg, info, 0);
|
|
+ case FID_SYFU_CLOCK:
|
|
+ case FID_SYNC_CLOCK:
|
|
+ case FID_ANNO_CLOCK:
|
|
+ case FID_FUP_CLOCK:
|
|
+ pr_debug("ignoring PTCP message %x", ntohs(pdu->frame_id));
|
|
+ return 0;
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
diff --git a/pn-ptcp.h b/pn-ptcp.h
|
|
new file mode 100644
|
|
index 0000000..f2a5cd4
|
|
--- /dev/null
|
|
+++ b/pn-ptcp.h
|
|
@@ -0,0 +1,252 @@
|
|
+/**
|
|
+ * @file pn-ptcp.h
|
|
+ * @brief Implements PTCP message types.
|
|
+ * @note Copyright (C) 2016 linutronix GmbH
|
|
+ * @note SPDX-License-Identifier: GPL-2.0+
|
|
+ */
|
|
+#ifndef HAVE_PN_PTCP_H
|
|
+#define HAVE_PN_PTCP_H
|
|
+
|
|
+#include <stdint.h>
|
|
+
|
|
+#include "address.h"
|
|
+#include "ddt.h"
|
|
+#include "ether.h"
|
|
+#include "msg.h"
|
|
+#include "transport_private.h"
|
|
+
|
|
+#define SUBD_LEN 16
|
|
+
|
|
+struct PTCP_Header_Sync {
|
|
+ uint32_t reserved_1;
|
|
+ uint32_t reserved_2;
|
|
+ uint32_t Delay10ns;
|
|
+ uint16_t SequenceID;
|
|
+ uint8_t Delay1ns_Byte;
|
|
+ /* Ensure 32 bit alignment. */
|
|
+ uint8_t Padding;
|
|
+ uint32_t Delay1ns;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_Header_FUP {
|
|
+ uint8_t Padding[12];
|
|
+ uint16_t SequenceID;
|
|
+ uint8_t Padding2[2];
|
|
+ int32_t Delay1ns_FUP;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_Header_Announce {
|
|
+ uint8_t Padding[12];
|
|
+ uint16_t SequenceID;
|
|
+ uint8_t Padding2[6];
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_Header_Delay {
|
|
+ uint8_t Padding[12];
|
|
+ uint16_t SequenceID;
|
|
+ uint8_t Padding2[2];
|
|
+ uint32_t Delay1ns;
|
|
+} PACKED;
|
|
+
|
|
+enum ptcp_tlv_type {
|
|
+ PTCP_END,
|
|
+ PTCP_SUBDOMAIN,
|
|
+ PTCP_TIME,
|
|
+ PTCP_TIME_EXTENSION,
|
|
+ PTCP_MASTER,
|
|
+ PTCP_PORT_PARAMETER,
|
|
+ PTCP_DELAY_PARAMETER,
|
|
+ PTCP_PORT_TIME,
|
|
+};
|
|
+
|
|
+struct PTCPSubdomain {
|
|
+ uint16_t TLVHeader;
|
|
+ eth_addr MasterSourceAddress;
|
|
+ uint8_t SubdomainUUID[SUBD_LEN];
|
|
+} PACKED;
|
|
+
|
|
+struct PTCPTime {
|
|
+ uint16_t TLVHeader;
|
|
+ uint16_t EpochNumber;
|
|
+ uint32_t Seconds;
|
|
+ uint32_t NanoSeconds;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCPTimeExtension {
|
|
+ uint16_t TLVHeader;
|
|
+ uint16_t Flags;
|
|
+ int16_t CurrentUTCOffset;
|
|
+ /* Ensure 32 bit alignment. */
|
|
+ uint16_t Padding;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCPMaster {
|
|
+ uint16_t TLVHeader;
|
|
+ uint8_t MasterPriority1;
|
|
+ uint8_t MasterPriority2;
|
|
+ uint8_t ClockClass;
|
|
+ uint8_t ClockAccuracy;
|
|
+ uint16_t ClockVariance;
|
|
+ /* Ensure 32 bit alignment. */
|
|
+ /* Padding */
|
|
+} PACKED;
|
|
+
|
|
+struct PTCPDelayParameter {
|
|
+ uint16_t TLVHeader;
|
|
+ uint8_t PortMACAddress[6];
|
|
+} PACKED;
|
|
+
|
|
+struct PTCPPortParameter {
|
|
+ uint16_t TLVHeader;
|
|
+ uint8_t Padding[2];
|
|
+ uint32_t T2PortRxDelay;
|
|
+ uint32_t T3PortTxDelay;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCPPortTime {
|
|
+ uint16_t TLVHeader;
|
|
+ uint8_t Padding[2];
|
|
+ uint32_t T2TimeStamp;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_RTSyncPDU {
|
|
+ struct PTCPSubdomain subdomain;
|
|
+ struct PTCPTime time;
|
|
+ struct PTCPTimeExtension tmext;
|
|
+ struct PTCPMaster master;
|
|
+ /* PTCPOption */
|
|
+ uint16_t End;
|
|
+ /*
|
|
+ * Shall be existent, if initially transmitted in the RED
|
|
+ * period and may be omitted, if initially transmitted in the
|
|
+ * ORANGE or GREEN period:
|
|
+ */
|
|
+ /* APDU_Status */
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_FollowUpPDU {
|
|
+ struct PTCPSubdomain subdomain;
|
|
+ struct PTCPTime time;
|
|
+ /* PTCPOption */
|
|
+ uint16_t End;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_AnnouncePDU {
|
|
+ struct PTCPSubdomain subdomain;
|
|
+ struct PTCPMaster master;
|
|
+ /* PTCPOption */
|
|
+ uint16_t End;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_DelayReqPDU {
|
|
+ struct PTCPDelayParameter param;
|
|
+ /* PTCPOption */
|
|
+ uint16_t End;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_DelayResPDU {
|
|
+ struct PTCPDelayParameter delay_param;
|
|
+ struct PTCPPortParameter port_param;
|
|
+ struct PTCPPortTime time;
|
|
+ /* PTCPOption */
|
|
+ uint16_t End;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_DelayFuResPDU {
|
|
+ struct PTCPDelayParameter param;
|
|
+ /* PTCPOption */
|
|
+ uint16_t End;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_SYNC_PDU {
|
|
+ struct PTCP_Header_Sync hdr;
|
|
+ struct PTCP_RTSyncPDU pdu;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_FUP_PDU {
|
|
+ struct PTCP_Header_FUP hdr;
|
|
+ struct PTCP_FollowUpPDU pdu;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_ANNOUNCE_PDU {
|
|
+ struct PTCP_Header_Announce hdr;
|
|
+ struct PTCP_AnnouncePDU pdu;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_DELAY_REQ_PDU {
|
|
+ struct PTCP_Header_Delay hdr;
|
|
+ struct PTCP_DelayReqPDU pdu;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_DELAY_RSP_PDU {
|
|
+ struct PTCP_Header_Delay hdr;
|
|
+ struct PTCP_DelayResPDU pdu;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_DELAY_FU_RSP_PDU {
|
|
+ struct PTCP_Header_Delay hdr;
|
|
+ struct PTCP_DelayFuResPDU pdu;
|
|
+} PACKED;
|
|
+
|
|
+struct PTCP_PDU {
|
|
+ uint16_t frame_id;
|
|
+ union {
|
|
+ struct PTCP_SYNC_PDU sync;
|
|
+ struct PTCP_FUP_PDU fup;
|
|
+ struct PTCP_ANNOUNCE_PDU anno;
|
|
+ struct PTCP_DELAY_REQ_PDU req;
|
|
+ struct PTCP_DELAY_RSP_PDU resp;
|
|
+ struct PTCP_DELAY_FU_RSP_PDU resfup;
|
|
+ } PACKED;
|
|
+} PACKED;
|
|
+
|
|
+/*
|
|
+ * Head room fits a VLAN Ethernet header, and 'pdu.sync' is 64 bit aligned.
|
|
+ */
|
|
+#define PN_HEADROOM 22
|
|
+
|
|
+struct pn_storage {
|
|
+ unsigned char reserved[PN_HEADROOM];
|
|
+ struct PTCP_PDU pdu;
|
|
+} PACKED;
|
|
+
|
|
+struct pn_info {
|
|
+ struct transport t;
|
|
+ struct address src_addr;
|
|
+ int vlan;
|
|
+
|
|
+ /* Peer port ID is not provided by PTCP response or follow-up! */
|
|
+ struct PortIdentity peer_portid;
|
|
+
|
|
+ /* Transmit conversion helpers */
|
|
+ uint64_t tx_t2;
|
|
+
|
|
+ /* Receive conversion helpers */
|
|
+ enum timestamp_type timestamping;
|
|
+ UInteger16 portNumber;
|
|
+ uint64_t rx_t2;
|
|
+
|
|
+ /* The following fields are in network byte order. */
|
|
+
|
|
+ /* Transmit conversion helpers */
|
|
+ unsigned char subdomain[SUBD_LEN];
|
|
+ struct ClockQuality quality;
|
|
+ struct PTCPTime time;
|
|
+ uint8_t priority2;
|
|
+ int16_t utc_offset;
|
|
+ uint16_t tmext_flags;
|
|
+
|
|
+ /* Receive conversion helpers */
|
|
+ uint8_t domain;
|
|
+ UInteger8 transportSpecific;
|
|
+ Integer8 logSyncInterval;
|
|
+ Integer8 logAnnounceInterval;
|
|
+};
|
|
+
|
|
+unsigned char *ptp_to_ptcp(struct ptp_message *src, struct PTCP_PDU *dst,
|
|
+ struct pn_info *info, int *len);
|
|
+
|
|
+int ptcp_to_ptp(struct PTCP_PDU *src, int len, struct ptp_message *dst,
|
|
+ struct pn_info *info);
|
|
+
|
|
+#endif
|
|
diff --git a/pn.c b/pn.c
|
|
new file mode 100644
|
|
index 0000000..e5de0b3
|
|
--- /dev/null
|
|
+++ b/pn.c
|
|
@@ -0,0 +1,413 @@
|
|
+/**
|
|
+ * @file pn.c
|
|
+ * @note Derived from raw.c.
|
|
+ * @note Copyright (C) 2012 Richard Cochran <richardcochran@gmail.com>
|
|
+ * @note SPDX-License-Identifier: GPL-2.0+
|
|
+ */
|
|
+#include <errno.h>
|
|
+#include <fcntl.h>
|
|
+#include <linux/filter.h>
|
|
+#include <linux/if_ether.h>
|
|
+#include <net/ethernet.h>
|
|
+#include <net/if.h>
|
|
+#include <netinet/in.h>
|
|
+#include <netpacket/packet.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+#include <sys/ioctl.h>
|
|
+#include <sys/socket.h>
|
|
+#include <sys/types.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include <linux/errqueue.h>
|
|
+#include <linux/net_tstamp.h>
|
|
+#include <linux/sockios.h>
|
|
+
|
|
+#include "address.h"
|
|
+#include "config.h"
|
|
+#include "contain.h"
|
|
+#include "ether.h"
|
|
+#include "missing.h"
|
|
+#include "pn.h"
|
|
+#include "pn-ptcp.h"
|
|
+#include "print.h"
|
|
+#include "sk.h"
|
|
+#include "transport_private.h"
|
|
+#include "util.h"
|
|
+
|
|
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
|
+
|
|
+/*
|
|
+ * tcpdump -d \
|
|
+ * '(ether[12:2]=0x8100 and ether[12+4:2]=0x8892 and '\
|
|
+ * ' (ether[14+4:2]=0x0020 or ether[14+4:2]=0x0021 or '\
|
|
+ * ' ether[14+4:2]=0x0080 or ether[14+4:2]=0x0081 or '\
|
|
+ * ' ether[14+4:2]=0xFF40 or ether[14+4:2]=0xFF41 or ether[14+4:2]=0xFF43)) or '\
|
|
+ * '(ether[12:2]=0x8892 and '\
|
|
+ * ' (ether[14:2]=0x0020 or ether[14:2]=0x0021 or '\
|
|
+ * ' ether[14:2]=0x0080 or ether[14:2]=0x0081 or '\
|
|
+ * ' ether[14:2]=0xFF40 or ether[14:2]=0xFF41 or ether[14:2]=0xFF43))'
|
|
+ *
|
|
+ * (000) ldh [12]
|
|
+ * (001) jeq #0x8100 jt 2 jf 6
|
|
+ * (002) ldh [16]
|
|
+ * (003) jeq #0x8892 jt 4 jf 16
|
|
+ * (004) ldh [18]
|
|
+ * (005) jeq #0x20 jt 15 jf 9
|
|
+ * (006) jeq #0x8892 jt 7 jf 16
|
|
+ * (007) ldh [14]
|
|
+ * (008) jeq #0x20 jt 15 jf 9
|
|
+ * (009) jeq #0x21 jt 15 jf 10
|
|
+ * (010) jeq #0x80 jt 15 jf 11
|
|
+ * (011) jeq #0x81 jt 15 jf 12
|
|
+ * (012) jeq #0xff40 jt 15 jf 13
|
|
+ * (013) jeq #0xff41 jt 15 jf 14
|
|
+ * (014) jeq #0xff43 jt 15 jf 16
|
|
+ * (015) ret #262144
|
|
+ * (016) ret #0
|
|
+ */
|
|
+static struct sock_filter pn_event_filter[] = {
|
|
+ { 0x28, 0, 0, 0x0000000c },
|
|
+ { 0x15, 0, 4, 0x00008100 },
|
|
+ { 0x28, 0, 0, 0x00000010 },
|
|
+ { 0x15, 0, 12, 0x00008892 },
|
|
+ { 0x28, 0, 0, 0x00000012 },
|
|
+ { 0x15, 9, 3, 0x00000020 },
|
|
+ { 0x15, 0, 9, 0x00008892 },
|
|
+ { 0x28, 0, 0, 0x0000000e },
|
|
+ { 0x15, 6, 0, 0x00000020 },
|
|
+ { 0x15, 5, 0, 0x00000021 },
|
|
+ { 0x15, 4, 0, 0x00000080 },
|
|
+ { 0x15, 3, 0, 0x00000081 },
|
|
+ { 0x15, 2, 0, 0x0000ff40 },
|
|
+ { 0x15, 1, 0, 0x0000ff41 },
|
|
+ { 0x15, 0, 1, 0x0000ff43 },
|
|
+ { 0x6, 0, 0, 0x00040000 },
|
|
+ { 0x6, 0, 0, 0x00000000 },
|
|
+};
|
|
+
|
|
+/*
|
|
+ * tcpdump -d \
|
|
+ * '(ether[12:2]=0x8100 and ether[12+4:2]=0x8892 and '\
|
|
+ * ' (ether[14+4:2]=0xFF00 or ether[14+4:2]=0xFF01 or ether[14+4:2]=0xFF20 or '\
|
|
+ * ' ether[14+4:2]=0xFF21 or ether[14+4:2]=0xFF42)) or '\
|
|
+ * '(ether[12:2]=0x8892 and '\
|
|
+ * ' (ether[14:2]=0xFF00 or ether[14:2]=0xFF01 or ether[14:2]=0xFF20 or '\
|
|
+ * ' ether[14:2]=0xFF21 or ether[14:2]=0xFF42))'
|
|
+ *
|
|
+ * (000) ldh [12]
|
|
+ * (001) jeq #0x8100 jt 2 jf 6
|
|
+ * (002) ldh [16]
|
|
+ * (003) jeq #0x8892 jt 4 jf 14
|
|
+ * (004) ldh [18]
|
|
+ * (005) jeq #0xff00 jt 13 jf 9
|
|
+ * (006) jeq #0x8892 jt 7 jf 14
|
|
+ * (007) ldh [14]
|
|
+ * (008) jeq #0xff00 jt 13 jf 9
|
|
+ * (009) jeq #0xff01 jt 13 jf 10
|
|
+ * (010) jeq #0xff20 jt 13 jf 11
|
|
+ * (011) jeq #0xff21 jt 13 jf 12
|
|
+ * (012) jeq #0xff42 jt 13 jf 14
|
|
+ * (013) ret #262144
|
|
+ * (014) ret #0
|
|
+ */
|
|
+static struct sock_filter pn_general_filter[] = {
|
|
+ { 0x28, 0, 0, 0x0000000c },
|
|
+ { 0x15, 0, 4, 0x00008100 },
|
|
+ { 0x28, 0, 0, 0x00000010 },
|
|
+ { 0x15, 0, 10, 0x00008892 },
|
|
+ { 0x28, 0, 0, 0x00000012 },
|
|
+ { 0x15, 7, 3, 0x0000ff00 },
|
|
+ { 0x15, 0, 7, 0x00008892 },
|
|
+ { 0x28, 0, 0, 0x0000000e },
|
|
+ { 0x15, 4, 0, 0x0000ff00 },
|
|
+ { 0x15, 3, 0, 0x0000ff01 },
|
|
+ { 0x15, 2, 0, 0x0000ff20 },
|
|
+ { 0x15, 1, 0, 0x0000ff21 },
|
|
+ { 0x15, 0, 1, 0x0000ff42 },
|
|
+ { 0x6, 0, 0, 0x00040000 },
|
|
+ { 0x6, 0, 0, 0x00000000 },
|
|
+};
|
|
+
|
|
+static int pn_configure(int fd, int event, int index, int enable)
|
|
+{
|
|
+ struct packet_mreq mreq;
|
|
+ struct sock_fprog prg;
|
|
+ int option;
|
|
+
|
|
+ if (event) {
|
|
+ prg.len = ARRAY_SIZE(pn_event_filter);
|
|
+ prg.filter = pn_event_filter;
|
|
+ } else {
|
|
+ prg.len = ARRAY_SIZE(pn_general_filter);
|
|
+ prg.filter = pn_general_filter;
|
|
+ }
|
|
+
|
|
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &prg, sizeof(prg))) {
|
|
+ pr_err("setsockopt SO_ATTACH_FILTER failed: %m");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ option = enable ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP;
|
|
+
|
|
+ memset(&mreq, 0, sizeof (mreq));
|
|
+ mreq.mr_ifindex = index;
|
|
+ mreq.mr_type = PACKET_MR_ALLMULTI;
|
|
+ mreq.mr_alen = 0;
|
|
+ if (!setsockopt(fd, SOL_PACKET, option, &mreq, sizeof(mreq))) {
|
|
+ return 0;
|
|
+ }
|
|
+ pr_warning("setsockopt PACKET_MR_ALLMULTI failed: %m");
|
|
+
|
|
+ mreq.mr_ifindex = index;
|
|
+ mreq.mr_type = PACKET_MR_PROMISC;
|
|
+ mreq.mr_alen = 0;
|
|
+ memset(&mreq.mr_address, 0, sizeof (mreq.mr_address));
|
|
+ if (!setsockopt(fd, SOL_PACKET, option, &mreq, sizeof(mreq))) {
|
|
+ return 0;
|
|
+ }
|
|
+ pr_warning("setsockopt PACKET_MR_PROMISC failed: %m");
|
|
+
|
|
+ pr_err("all socket options failed");
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static int pn_close(struct transport *t, struct fdarray *fda)
|
|
+{
|
|
+ close(fda->fd[0]);
|
|
+ close(fda->fd[1]);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int open_socket(struct interface *iface, int event,
|
|
+ enum timestamp_type ts_type)
|
|
+{
|
|
+ const char *name = interface_label(iface);
|
|
+ struct sockaddr_ll addr;
|
|
+ int fd, index;
|
|
+
|
|
+ fd = socket(PF_PACKET, SOCK_RAW, 0);
|
|
+ if (fd < 0) {
|
|
+ pr_err("socket failed: %m");
|
|
+ goto no_socket;
|
|
+ }
|
|
+ index = sk_interface_index(fd, name);
|
|
+ if (index < 0)
|
|
+ goto no_option;
|
|
+
|
|
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))) {
|
|
+ pr_err("setsockopt SO_BINDTODEVICE failed: %m");
|
|
+ goto no_option;
|
|
+ }
|
|
+
|
|
+ if (event) {
|
|
+ if (sk_timestamping_init(fd, name, ts_type, TRANS_IEEE_802_3,
|
|
+ interface_get_vclock(iface)))
|
|
+ goto no_option;
|
|
+ } else {
|
|
+ if (sk_general_init(fd))
|
|
+ goto no_option;
|
|
+ }
|
|
+
|
|
+ if (pn_configure(fd, event, index, 1))
|
|
+ goto no_option;
|
|
+
|
|
+ memset(&addr, 0, sizeof(addr));
|
|
+ addr.sll_ifindex = index;
|
|
+ addr.sll_family = AF_PACKET;
|
|
+ addr.sll_protocol = htons(ETH_P_ALL);
|
|
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr))) {
|
|
+ pr_err("bind failed: %m");
|
|
+ goto no_option;
|
|
+ }
|
|
+
|
|
+ return fd;
|
|
+no_option:
|
|
+ close(fd);
|
|
+no_socket:
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static void addr_to_mac(void *mac, struct address *addr)
|
|
+{
|
|
+ memcpy(mac, &addr->sll.sll_addr, MAC_LEN);
|
|
+}
|
|
+
|
|
+static int parse_subdomain(const char *str, unsigned char subdomain[SUBD_LEN])
|
|
+{
|
|
+ unsigned char buf[SUBD_LEN];
|
|
+ int c = sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:"
|
|
+ "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
|
+ &buf[0], &buf[1], &buf[2], &buf[3],
|
|
+ &buf[4], &buf[5], &buf[6], &buf[7],
|
|
+ &buf[8], &buf[9], &buf[10], &buf[11],
|
|
+ &buf[12], &buf[13], &buf[14], &buf[15]);
|
|
+ if (c != SUBD_LEN) {
|
|
+ return -1;
|
|
+ }
|
|
+ memcpy(subdomain, buf, SUBD_LEN);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int pn_open(struct transport *t, struct interface *iface,
|
|
+ struct fdarray *fda, enum timestamp_type ts_type)
|
|
+{
|
|
+ struct pn_info *pn = container_of(t, struct pn_info, t);
|
|
+ uint16_t log_variance;
|
|
+ const char *name;
|
|
+ int efd, gfd;
|
|
+ char *str;
|
|
+
|
|
+ name = interface_label(iface);
|
|
+ str = config_get_string(t->cfg, name, "subdomainUUID");
|
|
+ if (parse_subdomain(str, pn->subdomain)) {
|
|
+ pr_err("invalid subdomainUUID %s", str);
|
|
+ return -1;
|
|
+ }
|
|
+ pn->quality.clockClass = config_get_int(t->cfg, NULL, "clockClass");
|
|
+ pn->quality.clockAccuracy = config_get_int(t->cfg, NULL, "clockAccuracy");
|
|
+ log_variance = config_get_int(t->cfg, NULL, "offsetScaledLogVariance");
|
|
+ pn->quality.offsetScaledLogVariance = htons(log_variance);
|
|
+ pn->priority2 = config_get_int(t->cfg, NULL, "priority2");
|
|
+ pn->utc_offset = htons(CURRENT_UTC_OFFSET);
|
|
+
|
|
+ pn->timestamping = config_get_int(t->cfg, NULL, "time_stamping");
|
|
+ pn->domain = config_get_int(t->cfg, NULL, "domainNumber");
|
|
+ pn->transportSpecific = config_get_int(t->cfg, name, "transportSpecific");
|
|
+ pn->logSyncInterval = config_get_int(t->cfg, name, "logSyncInterval");
|
|
+ pn->logAnnounceInterval = config_get_int(t->cfg, name, "logAnnounceInterval");
|
|
+
|
|
+ if (sk_interface_macaddr(name, &pn->src_addr))
|
|
+ goto no_mac;
|
|
+
|
|
+ efd = open_socket(iface, 1, ts_type);
|
|
+ if (efd < 0)
|
|
+ goto no_event;
|
|
+
|
|
+ gfd = open_socket(iface, 0, ts_type);
|
|
+ if (gfd < 0)
|
|
+ goto no_general;
|
|
+
|
|
+ fda->fd[FD_EVENT] = efd;
|
|
+ fda->fd[FD_GENERAL] = gfd;
|
|
+ return 0;
|
|
+
|
|
+no_general:
|
|
+ close(efd);
|
|
+no_event:
|
|
+no_mac:
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static int pn_recv(struct transport *t, int fd, void *buf, int buflen,
|
|
+ struct address *addr, struct hw_timestamp *hwts)
|
|
+{
|
|
+ struct pn_info *pn = container_of(t, struct pn_info, t);
|
|
+ struct pn_storage rx_data;
|
|
+ struct PTCP_PDU *src = &rx_data.pdu;
|
|
+ unsigned char *ptr = (unsigned char *) src;
|
|
+ int cnt, hlen, length = sizeof(*src);
|
|
+ struct ptp_message *dst = buf;
|
|
+ struct eth_hdr *hdr;
|
|
+
|
|
+ if (pn->vlan) {
|
|
+ hlen = sizeof(struct vlan_hdr);
|
|
+ } else {
|
|
+ hlen = sizeof(struct eth_hdr);
|
|
+ }
|
|
+ ptr -= hlen;
|
|
+ length += hlen;
|
|
+ hdr = (struct eth_hdr *) ptr;
|
|
+
|
|
+ cnt = sk_receive(fd, ptr, length, addr, hwts, MSG_DONTWAIT);
|
|
+ if (cnt >= 0) {
|
|
+ cnt -= hlen;
|
|
+ }
|
|
+ if (cnt < 0) {
|
|
+ return cnt;
|
|
+ }
|
|
+
|
|
+ if (pn->vlan) {
|
|
+ if (ETH_P_PROFINET == ntohs(hdr->type)) {
|
|
+ pr_notice("pn: disabling VLAN mode");
|
|
+ pn->vlan = 0;
|
|
+ }
|
|
+ } else {
|
|
+ if (ETH_P_8021Q == ntohs(hdr->type)) {
|
|
+ pr_notice("pn: switching to VLAN mode");
|
|
+ pn->vlan = 1;
|
|
+ }
|
|
+ }
|
|
+ cnt = ptcp_to_ptp(src, cnt, dst, pn);
|
|
+ return cnt;
|
|
+}
|
|
+
|
|
+static int pn_send(struct transport *t, struct fdarray *fda,
|
|
+ enum transport_event event, int peer, void *buf, int buflen,
|
|
+ struct address *addr, struct hw_timestamp *hwts)
|
|
+{
|
|
+ struct pn_info *pn = container_of(t, struct pn_info, t);
|
|
+ struct pn_storage tx_data;
|
|
+ struct PTCP_PDU *dst = &tx_data.pdu;
|
|
+ struct ptp_message *src = buf;
|
|
+ unsigned char pkt[1600], *ptr;
|
|
+ struct eth_hdr *hdr;
|
|
+ int fd, length;
|
|
+ ssize_t cnt;
|
|
+
|
|
+ fd = event ? fda->fd[FD_EVENT] : fda->fd[FD_GENERAL];
|
|
+ ptr = ptp_to_ptcp(src, dst, pn, &length);
|
|
+ if (!ptr) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ hdr = (struct eth_hdr *) ptr;
|
|
+ addr_to_mac(&hdr->src, &pn->src_addr);
|
|
+ cnt = send(fd, ptr, length, 0);
|
|
+ if (cnt < 1) {
|
|
+ pr_err("send failed: %d %m", errno);
|
|
+ return cnt;
|
|
+ }
|
|
+ /*
|
|
+ * Get the time stamp right away.
|
|
+ */
|
|
+ return event == TRANS_EVENT ?
|
|
+ sk_receive(fd, pkt, sizeof(pkt), NULL, hwts, MSG_ERRQUEUE) : cnt;
|
|
+}
|
|
+
|
|
+static void pn_release(struct transport *t)
|
|
+{
|
|
+ struct pn_info *pn = container_of(t, struct pn_info, t);
|
|
+ free(pn);
|
|
+}
|
|
+
|
|
+static int pn_physical_addr(struct transport *t, uint8_t *addr)
|
|
+{
|
|
+ struct pn_info *pn = container_of(t, struct pn_info, t);
|
|
+ addr_to_mac(addr, &pn->src_addr);
|
|
+ return MAC_LEN;
|
|
+}
|
|
+
|
|
+static int pn_protocol_addr(struct transport *t, uint8_t *addr)
|
|
+{
|
|
+ struct pn_info *pn = container_of(t, struct pn_info, t);
|
|
+ addr_to_mac(addr, &pn->src_addr);
|
|
+ return MAC_LEN;
|
|
+}
|
|
+
|
|
+struct transport *pn_transport_create(void)
|
|
+{
|
|
+ struct pn_info *pn;
|
|
+ pn = calloc(1, sizeof(*pn));
|
|
+ if (!pn)
|
|
+ return NULL;
|
|
+ pn->t.close = pn_close;
|
|
+ pn->t.open = pn_open;
|
|
+ pn->t.recv = pn_recv;
|
|
+ pn->t.send = pn_send;
|
|
+ pn->t.release = pn_release;
|
|
+ pn->t.physical_addr = pn_physical_addr;
|
|
+ pn->t.protocol_addr = pn_protocol_addr;
|
|
+ return &pn->t;
|
|
+}
|
|
diff --git a/pn.h b/pn.h
|
|
new file mode 100644
|
|
index 0000000..d913f31
|
|
--- /dev/null
|
|
+++ b/pn.h
|
|
@@ -0,0 +1,19 @@
|
|
+/**
|
|
+ * @file pn.h
|
|
+ * @brief Implements transport over PN.
|
|
+ * @note Derived from raw.h.
|
|
+ * @note Copyright (C) 2012 Richard Cochran <richardcochran@gmail.com>
|
|
+ * @note SPDX-License-Identifier: GPL-2.0+
|
|
+ */
|
|
+#ifndef HAVE_PN_H
|
|
+#define HAVE_PN_H
|
|
+
|
|
+#include "transport.h"
|
|
+
|
|
+/**
|
|
+ * Allocate an instance of a PN Ethernet transport.
|
|
+ * @return Pointer to a new transport instance on success, NULL otherwise.
|
|
+ */
|
|
+struct transport *pn_transport_create(void);
|
|
+
|
|
+#endif
|
|
diff --git a/ptp4l.8 b/ptp4l.8
|
|
index 06e3943..d581614 100644
|
|
--- a/ptp4l.8
|
|
+++ b/ptp4l.8
|
|
@@ -251,7 +251,7 @@ The default is 0.
|
|
.TP
|
|
.B estimate_sync_timestamp
|
|
Enables ptp4l to provide in two-step sync messages an estimated transmit time
|
|
-stamp instead of zero.
|
|
+stamp instead of zero. The PN transport needs this option to be enabled.
|
|
The default is 0 (disabled).
|
|
|
|
.TP
|
|
@@ -373,7 +373,8 @@ greater than this value the port is marked as not 802.1AS capable.
|
|
|
|
.TP
|
|
.B network_transport
|
|
-Select the network transport. Possible values are UDPv4, UDPv6 and L2.
|
|
+Select the network transport. Possible values are UDPv4, UDPv6, L2 (Ethernet),
|
|
+and PN (PROFINET).
|
|
The default is UDPv4.
|
|
|
|
.TP
|
|
@@ -530,6 +531,12 @@ delay messages (logMinDelayReqInterval or logMinPdelayReqInterval). Weighting
|
|
is useful with larger network jitters (e.g. software time stamping).
|
|
The default is filter.
|
|
|
|
+.TP
|
|
+.B subdomainUUID
|
|
+Specifies the UUID of the PTCP subdomain used with the PN transport.
|
|
+The default is 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 (which indicates
|
|
+an unknown subdomain).
|
|
+
|
|
.TP
|
|
.B udp_ttl
|
|
Specifies the Time to live (TTL) value for IPv4 multicast messages and the hop
|
|
diff --git a/transport.c b/transport.c
|
|
index 9366fbf..ae5c06c 100644
|
|
--- a/transport.c
|
|
+++ b/transport.c
|
|
@@ -21,6 +21,7 @@
|
|
|
|
#include "transport.h"
|
|
#include "transport_private.h"
|
|
+#include "pn.h"
|
|
#include "raw.h"
|
|
#include "udp.h"
|
|
#include "udp6.h"
|
|
@@ -117,7 +118,9 @@ struct transport *transport_create(struct config *cfg,
|
|
break;
|
|
case TRANS_DEVICENET:
|
|
case TRANS_CONTROLNET:
|
|
+ break;
|
|
case TRANS_PROFINET:
|
|
+ t = pn_transport_create();
|
|
break;
|
|
}
|
|
if (t) {
|
|
|
|
commit 20b0c2d27da5c7689cf4d9d8510d885b0582c500
|
|
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
Date: Wed Aug 19 11:35:49 2026 +0200
|
|
|
|
SQUASH: pn-ptcp: ignore out-of-order DelayFuRes
|
|
|
|
diff --git a/pn-ptcp.c b/pn-ptcp.c
|
|
index 30943d2..ff22838 100644
|
|
--- a/pn-ptcp.c
|
|
+++ b/pn-ptcp.c
|
|
@@ -562,6 +562,7 @@ static int ptcp_resp_to_ptp(struct PTCP_DELAY_RSP_PDU *pdu, int len,
|
|
* only 32 bits wide. We will just construct phony T2 and T3
|
|
* values without the missing upper bits.
|
|
*/
|
|
+ info->sequenceId = msg->header.sequenceId;
|
|
info->rx_t2 = ntohl(pdu->pdu.time.T2TimeStamp);
|
|
ns_to_Timestamp(info->rx_t2, &msg->pdelay_resp.requestReceiptTimestamp);
|
|
|
|
@@ -577,6 +578,11 @@ static int ptcp_resfup_to_ptp(struct PTCP_DELAY_FU_RSP_PDU *pdu, int len,
|
|
if (len < sizeof(*pdu))
|
|
return -EBADMSG;
|
|
|
|
+ if (info->sequenceId != ntohs(pdu->hdr.SequenceID)) {
|
|
+ pr_warning("ignoring out-of-order PTCP DelayFuRes message");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
msg->hwts.type = info->timestamping;
|
|
|
|
msg->header.tsmt = PDELAY_RESP_FOLLOW_UP | info->transportSpecific;
|
|
diff --git a/pn-ptcp.h b/pn-ptcp.h
|
|
index f2a5cd4..a5f657c 100644
|
|
--- a/pn-ptcp.h
|
|
+++ b/pn-ptcp.h
|
|
@@ -224,6 +224,7 @@ struct pn_info {
|
|
/* Receive conversion helpers */
|
|
enum timestamp_type timestamping;
|
|
UInteger16 portNumber;
|
|
+ UInteger16 sequenceId;
|
|
uint64_t rx_t2;
|
|
|
|
/* The following fields are in network byte order. */
|
|
|
|
commit 7b782a4d133c7f827cacf2c5cf67e028b00906ea
|
|
Author: Richard Cochran <richardcochran@gmail.com>
|
|
Date: Tue Jan 10 10:51:55 2017 +0100
|
|
|
|
port: Ensure that PN transport is configured correctly.
|
|
|
|
Make sure that ports using the PROFINET transport have the delay
|
|
mechanism set to P2P and enabled the estimated timestamp in sync
|
|
messages.
|
|
|
|
Co-authored-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
|
|
diff --git a/port.c b/port.c
|
|
index 4f1dd40..b24268e 100644
|
|
--- a/port.c
|
|
+++ b/port.c
|
|
@@ -3723,6 +3723,18 @@ struct port *port_open(const char *phc_device,
|
|
pr_err("%s: spp needs at least PTPv2.1", p->log_name);
|
|
goto err_uc_service;
|
|
}
|
|
+ if (transport_type(p->trp) == TRANS_PROFINET) {
|
|
+ if (p->delayMechanism != DM_P2P) {
|
|
+ pr_err("%s: PN transport requires P2P mechanism",
|
|
+ p->log_name);
|
|
+ goto err_uc_service;
|
|
+ }
|
|
+ if (!p->estimate_sync_timestamp) {
|
|
+ pr_err("%s: PN transport requires estimate_sync_timestamp",
|
|
+ p->log_name);
|
|
+ goto err_uc_service;
|
|
+ }
|
|
+ }
|
|
|
|
/* Set fault timeouts to a default value */
|
|
for (i = 0; i < FT_CNT; i++) {
|
|
|
|
commit 86dff249d3a93a6f42294e6f3ad41c1f1d316ef6
|
|
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
Date: Tue Apr 14 13:20:39 2026 +0200
|
|
|
|
config: Add rh_ prefix to new options.
|
|
|
|
Add rh_ prefix to the new PN-specific options and describe them as
|
|
experimental to make it clear they are downstream-only (at least for
|
|
now) and subject to change.
|
|
|
|
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
|
|
diff --git a/config.c b/config.c
|
|
index ecd612b..687288f 100644
|
|
--- a/config.c
|
|
+++ b/config.c
|
|
@@ -284,7 +284,7 @@ struct config_item config_tab[] = {
|
|
GLOB_ITEM_INT("dscp_general", 0, 0, 63),
|
|
GLOB_ITEM_INT("domainNumber", 0, 0, 255),
|
|
PORT_ITEM_INT("egressLatency", 0, INT_MIN, INT_MAX),
|
|
- PORT_ITEM_INT("estimate_sync_timestamp", 0, 0, 1),
|
|
+ PORT_ITEM_INT("rh_estimate_sync_timestamp", 0, 0, 1),
|
|
PORT_ITEM_INT("fault_badpeernet_interval", 16, INT32_MIN, INT32_MAX),
|
|
PORT_ITEM_INT("fault_reset_interval", 4, INT8_MIN, INT8_MAX),
|
|
GLOB_ITEM_DBL("first_step_threshold", 0.00002, 0.0, DBL_MAX),
|
|
@@ -363,7 +363,7 @@ struct config_item config_tab[] = {
|
|
PORT_ITEM_INT("spp", -1, -1, UINT8_MAX),
|
|
GLOB_ITEM_DBL("step_threshold", 0.0, 0.0, DBL_MAX),
|
|
GLOB_ITEM_INT("step_window", 0, 0, INT_MAX),
|
|
- PORT_ITEM_STR("subdomainUUID",
|
|
+ PORT_ITEM_STR("rh_subdomainUUID",
|
|
"00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"),
|
|
GLOB_ITEM_INT("summary_interval", 0, INT_MIN, INT_MAX),
|
|
PORT_ITEM_INT("syncReceiptTimeout", 0, 0, UINT8_MAX),
|
|
diff --git a/configs/default.cfg b/configs/default.cfg
|
|
index 8bce8e6..495ca43 100644
|
|
--- a/configs/default.cfg
|
|
+++ b/configs/default.cfg
|
|
@@ -39,7 +39,7 @@ G.8275.portDS.localPriority 128
|
|
allowedLostResponses 3
|
|
asCapable auto
|
|
BMCA ptp
|
|
-estimate_sync_timestamp 0
|
|
+rh_estimate_sync_timestamp 0
|
|
inhibit_announce 0
|
|
inhibit_delay_req 0
|
|
ignore_source_id 0
|
|
@@ -109,7 +109,7 @@ ptp_dst_ipv6 FF0E:0:0:0:0:0:0:181
|
|
p2p_dst_ipv6 FF02:0:0:0:0:0:0:6B
|
|
ptp_dst_mac 01:1B:19:00:00:00
|
|
p2p_dst_mac 01:80:C2:00:00:0E
|
|
-subdomainUUID 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
|
|
+rh_subdomainUUID 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
|
|
udp_ttl 1
|
|
udp6_scope 0x0E
|
|
uds_address /var/run/ptp4l
|
|
diff --git a/pn.c b/pn.c
|
|
index e5de0b3..cb064cb 100644
|
|
--- a/pn.c
|
|
+++ b/pn.c
|
|
@@ -260,7 +260,7 @@ static int pn_open(struct transport *t, struct interface *iface,
|
|
char *str;
|
|
|
|
name = interface_label(iface);
|
|
- str = config_get_string(t->cfg, name, "subdomainUUID");
|
|
+ str = config_get_string(t->cfg, name, "rh_subdomainUUID");
|
|
if (parse_subdomain(str, pn->subdomain)) {
|
|
pr_err("invalid subdomainUUID %s", str);
|
|
return -1;
|
|
diff --git a/port.c b/port.c
|
|
index b24268e..148af1b 100644
|
|
--- a/port.c
|
|
+++ b/port.c
|
|
@@ -3594,7 +3594,7 @@ struct port *port_open(const char *phc_device,
|
|
p->phc_index = phc_index;
|
|
p->jbod = config_get_int(cfg, interface_name(interface), "boundary_clock_jbod");
|
|
p->estimate_sync_timestamp =
|
|
- config_get_int(cfg, interface_name(interface), "estimate_sync_timestamp");
|
|
+ config_get_int(cfg, interface_name(interface), "rh_estimate_sync_timestamp");
|
|
p->master_only = config_get_int(cfg, interface_name(interface), "serverOnly");
|
|
p->bmca = config_get_int(cfg, interface_name(interface), "BMCA");
|
|
p->trp = transport_create(cfg, config_get_int(cfg,
|
|
diff --git a/ptp4l.8 b/ptp4l.8
|
|
index d581614..cf6b364 100644
|
|
--- a/ptp4l.8
|
|
+++ b/ptp4l.8
|
|
@@ -249,7 +249,10 @@ value will be added to egress time stamps obtained from the hardware.
|
|
The default is 0.
|
|
|
|
.TP
|
|
-.B estimate_sync_timestamp
|
|
+.B rh_estimate_sync_timestamp
|
|
+This is a RHEL-specific experimental option which can be renamed,
|
|
+removed, or the functionality changed, in a future minor update.
|
|
+
|
|
Enables ptp4l to provide in two-step sync messages an estimated transmit time
|
|
stamp instead of zero. The PN transport needs this option to be enabled.
|
|
The default is 0 (disabled).
|
|
@@ -374,7 +377,8 @@ greater than this value the port is marked as not 802.1AS capable.
|
|
.TP
|
|
.B network_transport
|
|
Select the network transport. Possible values are UDPv4, UDPv6, L2 (Ethernet),
|
|
-and PN (PROFINET).
|
|
+and PN (PROFINET). The PN transport is experimental in RHEL and it can be
|
|
+removed or changed in a future minor update.
|
|
The default is UDPv4.
|
|
|
|
.TP
|
|
@@ -532,7 +536,10 @@ is useful with larger network jitters (e.g. software time stamping).
|
|
The default is filter.
|
|
|
|
.TP
|
|
-.B subdomainUUID
|
|
+.B rh_subdomainUUID
|
|
+This is a RHEL-specific experimental option which can be renamed,
|
|
+removed, or the functionality changed, in a future minor update.
|
|
+
|
|
Specifies the UUID of the PTCP subdomain used with the PN transport.
|
|
The default is 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 (which indicates
|
|
an unknown subdomain).
|