All of lore.kernel.org
 help / color / mirror / Atom feed
From: Denys Dmytriyenko <denis@denix.org>
To: danishanwar@ti.com
Cc: meta-arago@lists.yoctoproject.org, reatmon@ti.com,
	denys@konsulko.com, c-shilwant@ti.com
Subject: Re: [meta-arago][master][PATCH v2] linuxptp: Add support for HSR in Linux ptp
Date: Mon, 2 Mar 2026 15:40:51 -0500	[thread overview]
Message-ID: <20260302204051.GF11121@denix.org> (raw)
In-Reply-To: <20260302103515.848554-1-danishanwar@ti.com>

On Mon, Mar 02, 2026 at 04:05:15PM +0530, MD Danish Anwar via lists.yoctoproject.org wrote:
> Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> ---
> These patches have been posted to upstream linuxptp mailing list.
> https://lists.nwtime.org/sympa/arc/linuxptp-devel/2025-11/msg00013.html
> They are currently under review.
> 
> v1 -> v2:
>   Added Upstream Status in each patch.
> 
>  .../linuxptp/linuxptp-arago.inc               |  19 +
>  ...age-of-non-PTP-packets-during-socket.patch |  81 +++
>  ...d-dataset_comparison-type-IEC62439-3.patch | 217 +++++++
>  .../0003-Add-PASSIVE_SLAVE-state.patch        | 275 +++++++++
>  .../0004-rtnl-Add-rtnl_get_hsr_devices.patch  | 120 ++++
>  .../0005-port-Add-paired_port-option.patch    | 175 ++++++
>  ...-a-state-engine-for-redundant-master.patch | 519 ++++++++++++++++
>  ...ouble-attached-clock-hybrid-clock-HC.patch | 444 ++++++++++++++
>  ...orward-packets-both-ways-in-DAC-mode.patch |  37 ++
>  ...guard-in-case-PASSIVE_SLAVE-attempts.patch |  37 ++
>  ...w-to-forward-packets-in-LISTEN-state.patch |  61 ++
>  .../linuxptp/0011-raw-Add-HSR-handling.patch  | 567 ++++++++++++++++++
>  ...Add-sample-configs-for-the-HSR-setup.patch |  96 +++
>  ...2p_dst_mac-to-avoid-IEEE-802.1-reser.patch |  47 ++
>  .../linuxptp/linuxptp_%.bbappend              |   4 +
>  15 files changed, 2699 insertions(+)
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp-arago.inc
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0001-raw-Prevent-leakage-of-non-PTP-packets-during-socket.patch
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0002-port-Add-dataset_comparison-type-IEC62439-3.patch
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0003-Add-PASSIVE_SLAVE-state.patch
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0004-rtnl-Add-rtnl_get_hsr_devices.patch
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0005-port-Add-paired_port-option.patch
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0006-fsm-Add-a-state-engine-for-redundant-master.patch
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0007-p2p_hc-Add-a-double-attached-clock-hybrid-clock-HC.patch
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0008-tc-Allow-to-forward-packets-both-ways-in-DAC-mode.patch
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0009-port-Add-a-safe-guard-in-case-PASSIVE_SLAVE-attempts.patch
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0010-tc-Allow-to-forward-packets-in-LISTEN-state.patch
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0011-raw-Add-HSR-handling.patch
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0012-configs-Add-sample-configs-for-the-HSR-setup.patch
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0013-configs-Change-p2p_dst_mac-to-avoid-IEEE-802.1-reser.patch
>  create mode 100644 meta-arago-distro/recipes-connectivity/linuxptp/linuxptp_%.bbappend
> 
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp-arago.inc b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp-arago.inc
> new file mode 100644
> index 00000000..17819cf3
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp-arago.inc
> @@ -0,0 +1,19 @@
> +PR:append = ".arago6"

This is the first submission, why is it .arago6?


> +
> +FILESEXTRAPATHS:prepend := "${THISDIR}/linuxptp:"
> +
> +SRC_URI:append = " \

There's absolutely no reason to suee :append here vs. a simple +=


> +    file://0001-raw-Prevent-leakage-of-non-PTP-packets-during-socket.patch \
> +    file://0002-port-Add-dataset_comparison-type-IEC62439-3.patch \
> +    file://0003-Add-PASSIVE_SLAVE-state.patch \
> +    file://0004-rtnl-Add-rtnl_get_hsr_devices.patch \
> +    file://0005-port-Add-paired_port-option.patch \
> +    file://0006-fsm-Add-a-state-engine-for-redundant-master.patch \
> +    file://0007-p2p_hc-Add-a-double-attached-clock-hybrid-clock-HC.patch \
> +    file://0008-tc-Allow-to-forward-packets-both-ways-in-DAC-mode.patch \
> +    file://0009-port-Add-a-safe-guard-in-case-PASSIVE_SLAVE-attempts.patch \
> +    file://0010-tc-Allow-to-forward-packets-in-LISTEN-state.patch \
> +    file://0011-raw-Add-HSR-handling.patch \
> +    file://0012-configs-Add-sample-configs-for-the-HSR-setup.patch \
> +    file://0013-configs-Change-p2p_dst_mac-to-avoid-IEEE-802.1-reser.patch \
> +"
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0001-raw-Prevent-leakage-of-non-PTP-packets-during-socket.patch b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0001-raw-Prevent-leakage-of-non-PTP-packets-during-socket.patch
> new file mode 100644
> index 00000000..ea0f968c
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0001-raw-Prevent-leakage-of-non-PTP-packets-during-socket.patch
> @@ -0,0 +1,81 @@
> +From 5afe386619bfcded393fd5a9ea5d7c9bbcf05823 Mon Sep 17 00:00:00 2001
> +From: Cliff Spradlin <cspradlin@google.com>
> +Date: Thu, 2 Oct 2025 18:37:54 -0700
> +Subject: [PATCH 01/13] raw: Prevent leakage of non-PTP packets during socket
> + init
> +
> +There were two problems with the socket configuration sequencing:
> +
> +1) Calling socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) causes all
> +ethernet frames from -all- interfaces to be queued to the socket,
> +until bind() is later called.
> +
> +2) The BPF filter is installed -after- bind() is called, so ethernet
> +frames that should be rejected could be queued before the BPF filter
> +is installed.
> +
> +This patch reorders the raw socket initialization so that all
> +configuration happens before bind() is called.
> +
> +Signed-off-by: Cliff Spradlin <cspradlin@google.com>
> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> +---
> +Upstream-Status:
> +Submitted
> +  Submitted to upstream, waiting approval
> +  https://lists.nwtime.org/sympa/arc/linuxptp-devel/2025-11/msg00007.html

While there's no specific requirement to have this before the --- line, it 
still must be in a single line. Same comment to all patches.


> +
> + raw.c | 22 +++++++++++-----------
> + 1 file changed, 11 insertions(+), 11 deletions(-)
> +
> +diff --git a/raw.c b/raw.c
> +index 94c59ad..c809233 100644
> +--- a/raw.c
> ++++ b/raw.c
> +@@ -241,7 +241,7 @@ static int open_socket(const char *name, int event, unsigned char *local_addr,
> + 	struct sockaddr_ll addr;
> + 	int fd, index;
> + 
> +-	fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
> ++	fd = socket(AF_PACKET, SOCK_RAW, 0);
> + 	if (fd < 0) {
> + 		pr_err("socket failed: %m");
> + 		goto no_socket;
> +@@ -250,6 +250,16 @@ static int open_socket(const char *name, int event, unsigned char *local_addr,
> + 	if (index < 0)
> + 		goto no_option;
> + 
> ++	if (socket_priority > 0 &&
> ++	    setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &socket_priority,
> ++		       sizeof(socket_priority))) {
> ++		pr_err("setsockopt SO_PRIORITY failed: %m");
> ++		goto no_option;
> ++	}
> ++	if (raw_configure(fd, event, index, local_addr, ptp_dst_mac,
> ++			  p2p_dst_mac, 1))
> ++		goto no_option;
> ++
> + 	memset(&addr, 0, sizeof(addr));
> + 	addr.sll_ifindex = index;
> + 	addr.sll_family = AF_PACKET;
> +@@ -263,16 +273,6 @@ static int open_socket(const char *name, int event, unsigned char *local_addr,
> + 		goto no_option;
> + 	}
> + 
> +-	if (socket_priority > 0 &&
> +-	    setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &socket_priority,
> +-		       sizeof(socket_priority))) {
> +-		pr_err("setsockopt SO_PRIORITY failed: %m");
> +-		goto no_option;
> +-	}
> +-	if (raw_configure(fd, event, index, local_addr, ptp_dst_mac,
> +-			  p2p_dst_mac, 1))
> +-		goto no_option;
> +-
> + 	return fd;
> + no_option:
> + 	close(fd);
> +-- 
> +2.34.1
> +
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0002-port-Add-dataset_comparison-type-IEC62439-3.patch b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0002-port-Add-dataset_comparison-type-IEC62439-3.patch
> new file mode 100644
> index 00000000..c6405ae0
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0002-port-Add-dataset_comparison-type-IEC62439-3.patch
> @@ -0,0 +1,217 @@
> +From 43a4a63b8c64993ef8662035e86d4805923eb6cc Mon Sep 17 00:00:00 2001
> +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Date: Thu, 18 Sep 2025 12:58:52 +0200
> +Subject: [PATCH 02/13] port: Add dataset_comparison type IEC62439-3
> +
> +For IEC62439-3 the clock comparison is mostly the same as with IEEE1588.
> +The specification adds an additional comparison step if both messages
> +(from the same master) are identical. The suggestion is to use for
> +instance the port with the smaller value in the correction field but
> +also don't flip between the two ports if port with the smaller
> +correction value changes frequently.
> +
> +Instead pick the first port, and stay with it.
> +Use this dataset_comparison field to:
> +- Make the clock aware that it is in a DAC (doubly attached mode)
> +- Check for two interfaces
> +- Check that the interfaces belong to the same PTP clock domain. Not
> +  strictly required but makes it easier. Otherwise the manual
> +  synchronisation of the two clocks is required.
> +
> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> +---
> +Upstream-Status:
> +Submitted
> +  Submitted to upstream, waiting approval
> +  https://lists.nwtime.org/sympa/arc/linuxptp-devel/2025-11/msg00008.html
> +
> + bmc.h    |  1 +
> + clock.c  | 45 +++++++++++++++++++++++++++++++++++++++++++--
> + clock.h  |  7 +++++++
> + config.c |  1 +
> + ptp4l.8  |  4 ++--
> + ptp4l.c  | 13 ++++++++++++-
> + 6 files changed, 66 insertions(+), 5 deletions(-)
> +
> +diff --git a/bmc.h b/bmc.h
> +index 413d907..b59a234 100644
> +--- a/bmc.h
> ++++ b/bmc.h
> +@@ -32,6 +32,7 @@
> + enum {
> + 	DS_CMP_IEEE1588,
> + 	DS_CMP_G8275,
> ++	DS_CMP_IEC62439_3,
> + };
> + 
> + /**
> +diff --git a/clock.c b/clock.c
> +index 17484d8..40c21ec 100644
> +--- a/clock.c
> ++++ b/clock.c
> +@@ -151,6 +151,7 @@ struct clock {
> + 	struct time_zone tz[MAX_TIME_ZONES];
> + 	struct ClockIdentity ext_gm_identity;
> + 	int ext_gm_steps_removed;
> ++	bool use_DAC;
> + };
> + 
> + struct clock the_clock;
> +@@ -1389,11 +1390,26 @@ struct clock *clock_create(enum clock_type type, struct config *config,
> + 	}
> + 	c->servo_state = SERVO_UNLOCKED;
> + 	c->servo_type = servo;
> +-	if (config_get_int(config, NULL, "dataset_comparison") == DS_CMP_G8275) {
> ++
> ++	switch (config_get_int(config, NULL, "dataset_comparison")) {
> ++	case DS_CMP_IEEE1588:
> ++		c->dscmp = dscmp;
> ++		break;
> ++
> ++	case DS_CMP_G8275:
> + 		c->dscmp = telecom_dscmp;
> +-	} else {
> ++		break;
> ++
> ++	case DS_CMP_IEC62439_3:
> + 		c->dscmp = dscmp;
> ++		c->use_DAC = true;
> ++		break;
> ++
> ++	default:
> ++		pr_err("Bad dataset_comparison");
> ++		return NULL;
> + 	}
> ++
> + 	c->tsproc = tsproc_create(config_get_int(config, NULL, "tsproc_mode"),
> + 				  config_get_int(config, NULL, "delay_filter"),
> + 				  config_get_int(config, NULL, "delay_filter_length"));
> +@@ -1475,6 +1491,26 @@ struct clock *clock_create(enum clock_type type, struct config *config,
> + 		return NULL;
> + 	}
> + 
> ++	if (c->use_DAC) {
> ++		int phc_idx;
> ++
> ++		iface = STAILQ_FIRST(&config->interfaces);
> ++		if (interface_tsinfo_valid(iface)) {
> ++			phc_idx = interface_phc_index(iface);
> ++
> ++			STAILQ_FOREACH(iface, &config->interfaces, list) {
> ++				if (interface_tsinfo_valid(iface)) {
> ++					if (interface_phc_index(iface) != phc_idx) {
> ++						pr_err("The network devices not share the PMC\n");
> ++					}
> ++				} else {
> ++					pr_err("Could not verify PHC device of the network devices\n");
> ++				}
> ++			}
> ++		} else {
> ++			pr_err("Could not verify PHC device of the network devices\n");
> ++		}
> ++	}
> + 	/* Create the ports. */
> + 	STAILQ_FOREACH(iface, &config->interfaces, list) {
> + 		if (clock_add_port(c, phc_device, phc_index, timestamping, iface)) {
> +@@ -2359,6 +2395,11 @@ enum clock_type clock_type(struct clock *c)
> + 	return c->type;
> + }
> + 
> ++bool clock_type_is_DAC(struct clock *c)
> ++{
> ++	return c->use_DAC;
> ++}
> ++
> + void clock_check_ts(struct clock *c, uint64_t ts)
> + {
> + 	if (c->sanity_check && clockcheck_sample(c->sanity_check, ts)) {
> +diff --git a/clock.h b/clock.h
> +index ce9ae91..5d410b3 100644
> +--- a/clock.h
> ++++ b/clock.h
> +@@ -382,6 +382,13 @@ struct clock_description *clock_description(struct clock *c);
> +  */
> + enum clock_type clock_type(struct clock *c);
> + 
> ++/**
> ++ * Check if the clock is doubly attached clock.
> ++ * @param c  The clock instance.
> ++ * @return   True if the clock is DAC, false otherwise.
> ++ */
> ++bool clock_type_is_DAC(struct clock *c);
> ++
> + /**
> +  * Perform a sanity check on a time stamp made by a clock.
> +  * @param c  The clock instance.
> +diff --git a/config.c b/config.c
> +index 222f4cd..6f4fd9c 100644
> +--- a/config.c
> ++++ b/config.c
> +@@ -176,6 +176,7 @@ static struct config_enum clock_type_enu[] = {
> + static struct config_enum dataset_comp_enu[] = {
> + 	{ "ieee1588", DS_CMP_IEEE1588 },
> + 	{ "G.8275.x", DS_CMP_G8275    },
> ++	{ "IEC62439-3", DS_CMP_IEC62439_3},
> + 	{ NULL, 0 },
> + };
> + 
> +diff --git a/ptp4l.8 b/ptp4l.8
> +index 6e3f456..dd40731 100644
> +--- a/ptp4l.8
> ++++ b/ptp4l.8
> +@@ -657,8 +657,8 @@ The default is "OC".
> + .TP
> + .B dataset_comparison
> + Specifies the method to be used when comparing data sets during the
> +-Best Master Clock Algorithm.  The possible values are "ieee1588" and
> +-"G.8275.x".  The default is "ieee1588".
> ++Best Master Clock Algorithm.  The possible values are "ieee1588",
> ++"G.8275.x" and "IEC62439-3". The default is "ieee1588".
> + 
> + .TP
> + .B domainNumber
> +diff --git a/ptp4l.c b/ptp4l.c
> +index ac2ef96..10b8962 100644
> +--- a/ptp4l.c
> ++++ b/ptp4l.c
> +@@ -23,6 +23,7 @@
> + #include <string.h>
> + #include <unistd.h>
> + 
> ++#include "bmc.h"
> + #include "clock.h"
> + #include "config.h"
> + #include "ntpshm.h"
> +@@ -75,6 +76,7 @@ int main(int argc, char *argv[])
> + 	enum clock_type type = CLOCK_TYPE_ORDINARY;
> + 	int c, err = -1, index, cmd_line_print_level;
> + 	struct clock *clock = NULL;
> ++	bool use_DAC = false;
> + 	struct option *opts;
> + 	struct config *cfg;
> + 
> +@@ -211,10 +213,19 @@ int main(int argc, char *argv[])
> + 		goto out;
> + 	}
> + 
> ++	if (config_get_int(cfg, NULL, "dataset_comparison") == DS_CMP_IEC62439_3) {
> ++		use_DAC = true;
> ++	}
> ++
> + 	type = config_get_int(cfg, NULL, "clock_type");
> + 	switch (type) {
> + 	case CLOCK_TYPE_ORDINARY:
> +-		if (cfg->n_interfaces > 1) {
> ++		if (use_DAC) {
> ++			if (cfg->n_interfaces != 2) {
> ++				fprintf(stderr, "IEC62439-3 dataset comparison requires two interfaces\n");
> ++				goto out;
> ++			}
> ++		} else if (cfg->n_interfaces > 1) {
> + 			type = CLOCK_TYPE_BOUNDARY;
> + 		}
> + 		break;
> +-- 
> +2.34.1
> +
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0003-Add-PASSIVE_SLAVE-state.patch b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0003-Add-PASSIVE_SLAVE-state.patch
> new file mode 100644
> index 00000000..c65d7471
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0003-Add-PASSIVE_SLAVE-state.patch
> @@ -0,0 +1,275 @@
> +From b79ffcdde80fe7f464b00a2fd20f6c587ab6e4ab Mon Sep 17 00:00:00 2001
> +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Date: Thu, 2 Oct 2025 10:09:32 +0200
> +Subject: [PATCH 03/13] Add PASSIVE_SLAVE state.
> +
> +Add PASSIVE_SLAVE which is defined in IEC 62439-3 as an extension to IEC
> +61588. It also renames PASSIVE to PASSIVE_MASTER for clarity but lets
> +ignore this part.
> +
> +The PASSIVE_SLAVE acts as SLAVE but does not participate in clock
> +adjustments. It will send Delay_Req or Pdelay_Req and respond to those
> +packets.
> +In management interface the PASSIVE_SLAVE should be exposed as SLAVE.
> +
> +Add PS_PASSIVE_SLAVE to port_state and EV_RS_PSLAVE to fsm_event. Update
> +existing state engines to avoid "unhandled switch case".
> +
> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> +---
> +Upstream-Status:
> +Submitted
> +  Submitted to upstream, waiting approval
> +  https://lists.nwtime.org/sympa/arc/linuxptp-devel/2025-11/msg00009.html
> +
> + clock.c           |  3 +++
> + e2e_tc.c          |  2 ++
> + fsm.c             |  3 +++
> + fsm.h             |  2 ++
> + p2p_tc.c          |  4 ++++
> + port.c            | 14 ++++++++++++--
> + port_signaling.c  |  1 +
> + tc.c              |  2 ++
> + unicast_service.c |  1 +
> + util.c            |  2 ++
> + 10 files changed, 32 insertions(+), 2 deletions(-)
> +
> +diff --git a/clock.c b/clock.c
> +index 40c21ec..d777378 100644
> +--- a/clock.c
> ++++ b/clock.c
> +@@ -2373,6 +2373,9 @@ static void handle_state_decision_event(struct clock *c)
> + 			clock_update_slave(c);
> + 			event = EV_RS_SLAVE;
> + 			break;
> ++		case PS_PASSIVE_SLAVE:
> ++			event = EV_RS_PSLAVE;
> ++			break;
> + 		default:
> + 			event = EV_FAULT_DETECTED;
> + 			break;
> +diff --git a/e2e_tc.c b/e2e_tc.c
> +index 82b454a..53cf4eb 100644
> +--- a/e2e_tc.c
> ++++ b/e2e_tc.c
> +@@ -75,6 +75,8 @@ void e2e_dispatch(struct port *p, enum fsm_event event, int mdiff)
> + 	case PS_SLAVE:
> + 		port_set_announce_tmo(p);
> + 		break;
> ++	case PS_PASSIVE_SLAVE:
> ++		break;
> + 	};
> + }
> + 
> +diff --git a/fsm.c b/fsm.c
> +index ce6efad..9e8c4d8 100644
> +--- a/fsm.c
> ++++ b/fsm.c
> +@@ -214,6 +214,9 @@ enum port_state ptp_fsm(enum port_state state, enum fsm_event event, int mdiff)
> + 			break;
> + 		}
> + 		break;
> ++
> ++	case PS_PASSIVE_SLAVE:
> ++		break;
> + 	}
> + 
> + 	return next;
> +diff --git a/fsm.h b/fsm.h
> +index 857af05..e07d9a9 100644
> +--- a/fsm.h
> ++++ b/fsm.h
> +@@ -31,6 +31,7 @@ enum port_state {
> + 	PS_PASSIVE,
> + 	PS_UNCALIBRATED,
> + 	PS_SLAVE,
> ++	PS_PASSIVE_SLAVE, /* Added to IEC 61588:2021, Table 27 (via 62439-3) */
> + 	PS_GRAND_MASTER, /*non-standard extension*/
> + };
> + 
> +@@ -53,6 +54,7 @@ enum fsm_event {
> + 	EV_RS_GRAND_MASTER,
> + 	EV_RS_SLAVE,
> + 	EV_RS_PASSIVE,
> ++	EV_RS_PSLAVE,
> + };
> + 
> + enum bmca_select {
> +diff --git a/p2p_tc.c b/p2p_tc.c
> +index a8ec63b..7fda10e 100644
> +--- a/p2p_tc.c
> ++++ b/p2p_tc.c
> +@@ -40,6 +40,8 @@ static int p2p_delay_request(struct port *p)
> + 	case PS_SLAVE:
> + 	case PS_GRAND_MASTER:
> + 		break;
> ++	case PS_PASSIVE_SLAVE:
> ++		break;
> + 	}
> + 	return port_delay_request(p);
> + }
> +@@ -92,6 +94,8 @@ void p2p_dispatch(struct port *p, enum fsm_event event, int mdiff)
> + 	case PS_SLAVE:
> + 		port_set_announce_tmo(p);
> + 		break;
> ++	case PS_PASSIVE_SLAVE:
> ++		break;
> + 	};
> + }
> + 
> +diff --git a/port.c b/port.c
> +index a1d0f2c..549d72b 100644
> +--- a/port.c
> ++++ b/port.c
> +@@ -1023,6 +1023,8 @@ static int port_management_fill_response(struct port *target,
> + 		pds->portIdentity            = target->portIdentity;
> + 		if (target->state == PS_GRAND_MASTER) {
> + 			pds->portState = PS_MASTER;
> ++		} else if (target->state == PS_PASSIVE_SLAVE) {
> ++			pds->portState = PS_SLAVE;
> + 		} else {
> + 			pds->portState = target->state;
> + 		}
> +@@ -1089,6 +1091,8 @@ static int port_management_fill_response(struct port *target,
> + 		ppn->portIdentity = target->portIdentity;
> + 		if (target->state == PS_GRAND_MASTER)
> + 			ppn->port_state = PS_MASTER;
> ++		else if (target->state == PS_PASSIVE_SLAVE)
> ++			ppn->port_state = PS_SLAVE;
> + 		else
> + 			ppn->port_state = target->state;
> + 		ppn->timestamping = target->timestamping;
> +@@ -1922,6 +1926,7 @@ int port_is_enabled(struct port *p)
> + 	case PS_PASSIVE:
> + 	case PS_UNCALIBRATED:
> + 	case PS_SLAVE:
> ++	case PS_PASSIVE_SLAVE:
> + 		break;
> + 	}
> + 	return 1;
> +@@ -2242,6 +2247,7 @@ int process_announce(struct port *p, struct ptp_message *m)
> + 	case PS_PASSIVE:
> + 	case PS_UNCALIBRATED:
> + 	case PS_SLAVE:
> ++	case PS_PASSIVE_SLAVE:
> + 		result = update_current_master(p, m);
> + 		break;
> + 	}
> +@@ -2289,7 +2295,7 @@ static int process_cmlds(struct port *p)
> + 		p->nrate.ratio = 1.0 + (double) cmlds->scaledNeighborRateRatio / POW2_41;
> + 		p->asCapable = cmlds->as_capable;
> + 		p->cmlds.timer_count = 0;
> +-		if (p->state == PS_UNCALIBRATED || p->state == PS_SLAVE) {
> ++		if (p->state == PS_UNCALIBRATED || p->state == PS_SLAVE || p->state == PS_PASSIVE_SLAVE) {
> + 			const tmv_t tx = tmv_zero();
> + 			clock_peer_delay(p->clock, p->peer_delay, tx, tx, p->nrate.ratio);
> + 		}
> +@@ -2437,6 +2443,7 @@ void process_follow_up(struct port *p, struct ptp_message *m)
> + 	case PS_MASTER:
> + 	case PS_GRAND_MASTER:
> + 	case PS_PASSIVE:
> ++	case PS_PASSIVE_SLAVE:
> + 		return;
> + 	case PS_UNCALIBRATED:
> + 	case PS_SLAVE:
> +@@ -2668,7 +2675,7 @@ calc:
> + 
> + 	p->peerMeanPathDelay = tmv_to_TimeInterval(p->peer_delay);
> + 
> +-	if (p->state == PS_UNCALIBRATED || p->state == PS_SLAVE) {
> ++	if (p->state == PS_UNCALIBRATED || p->state == PS_SLAVE || p->state == PS_PASSIVE_SLAVE) {
> + 		clock_peer_delay(p->clock, p->peer_delay, t1, t2,
> + 				 p->nrate.ratio);
> + 	}
> +@@ -2752,6 +2759,7 @@ void process_sync(struct port *p, struct ptp_message *m)
> + 	case PS_MASTER:
> + 	case PS_GRAND_MASTER:
> + 	case PS_PASSIVE:
> ++	case PS_PASSIVE_SLAVE:
> + 		return;
> + 	case PS_UNCALIBRATED:
> + 	case PS_SLAVE:
> +@@ -2895,6 +2903,7 @@ static void port_e2e_transition(struct port *p, enum port_state next)
> + 		sad_set_last_seqid(clock_config(p->clock), p->spp, -1);
> + 		/* fall through */
> + 	case PS_SLAVE:
> ++	case PS_PASSIVE_SLAVE:
> + 		port_set_announce_tmo(p);
> + 		port_set_delay_tmo(p);
> + 		break;
> +@@ -2943,6 +2952,7 @@ static void port_p2p_transition(struct port *p, enum port_state next)
> + 		sad_set_last_seqid(clock_config(p->clock), p->spp, -1);
> + 		/* fall through */
> + 	case PS_SLAVE:
> ++	case PS_PASSIVE_SLAVE:
> + 		port_set_announce_tmo(p);
> + 		break;
> + 	};
> +diff --git a/port_signaling.c b/port_signaling.c
> +index cf28756..fb42fe6 100644
> +--- a/port_signaling.c
> ++++ b/port_signaling.c
> +@@ -147,6 +147,7 @@ int process_signaling(struct port *p, struct ptp_message *m)
> + 	case PS_PASSIVE:
> + 	case PS_UNCALIBRATED:
> + 	case PS_SLAVE:
> ++	case PS_PASSIVE_SLAVE:
> + 		break;
> + 	}
> + 
> +diff --git a/tc.c b/tc.c
> +index 27ba66f..0fd1bc4 100644
> +--- a/tc.c
> ++++ b/tc.c
> +@@ -88,6 +88,7 @@ static int tc_blocked(struct port *q, struct port *p, struct ptp_message *m)
> + 		break;
> + 	case PS_UNCALIBRATED:
> + 	case PS_SLAVE:
> ++	case PS_PASSIVE_SLAVE:
> + 		break;
> + 	}
> + 	/* Egress state */
> +@@ -102,6 +103,7 @@ static int tc_blocked(struct port *q, struct port *p, struct ptp_message *m)
> + 		return 1;
> + 	case PS_UNCALIBRATED:
> + 	case PS_SLAVE:
> ++	case PS_PASSIVE_SLAVE:
> + 		/* Delay_Req swims against the stream. */
> + 		if (msg_type(m) != DELAY_REQ) {
> + 			return 1;
> +diff --git a/unicast_service.c b/unicast_service.c
> +index d7a4ecd..7b5196b 100644
> +--- a/unicast_service.c
> ++++ b/unicast_service.c
> +@@ -532,6 +532,7 @@ int unicast_service_timer(struct port *p)
> + 	case PS_PASSIVE:
> + 	case PS_UNCALIBRATED:
> + 	case PS_SLAVE:
> ++	case PS_PASSIVE_SLAVE:
> + 		break;
> + 	case PS_MASTER:
> + 	case PS_GRAND_MASTER:
> +diff --git a/util.c b/util.c
> +index 4e9263b..4deebe8 100644
> +--- a/util.c
> ++++ b/util.c
> +@@ -60,6 +60,7 @@ const char *ps_str[] = {
> + 	"PASSIVE",
> + 	"UNCALIBRATED",
> + 	"SLAVE",
> ++	"PASSIVE_SLAVE",
> + 	"GRAND_MASTER",
> + };
> + 
> +@@ -81,6 +82,7 @@ const char *ev_str[] = {
> + 	"RS_GRAND_MASTER",
> + 	"RS_SLAVE",
> + 	"RS_PASSIVE",
> ++	"RS_PASSIVE_SLAVE",
> + };
> + 
> + const char *ts_str(enum timestamp_type ts)
> +-- 
> +2.34.1
> +
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0004-rtnl-Add-rtnl_get_hsr_devices.patch b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0004-rtnl-Add-rtnl_get_hsr_devices.patch
> new file mode 100644
> index 00000000..2b238acc
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0004-rtnl-Add-rtnl_get_hsr_devices.patch
> @@ -0,0 +1,120 @@
> +From 0dc4e53ae6fb958c5d04108d8511e25cbf3aabfb Mon Sep 17 00:00:00 2001
> +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Date: Wed, 22 Oct 2025 15:32:22 +0200
> +Subject: [PATCH 04/13] rtnl: Add rtnl_get_hsr_devices()
> +
> +Extend rtnl_linkinfo_parse() by supporting HSR. It will return the
> +two interface numbers in one 32bit int by using the lower 16bit for
> +slave1 and the upper 16bit for slave2.
> +
> +Provide rtnl_get_hsr_devices() which uses it and returns the resolved
> +interface name.
> +
> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> +---
> +Upstream-Status:
> +Submitted
> +  Submitted to upstream, waiting approval
> +  https://lists.nwtime.org/sympa/arc/linuxptp-devel/2025-11/msg00014.html
> +
> + rtnl.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
> + rtnl.h |  9 +++++++++
> + 2 files changed, 60 insertions(+)
> +
> +diff --git a/rtnl.c b/rtnl.c
> +index 1037c44..2e377ae 100644
> +--- a/rtnl.c
> ++++ b/rtnl.c
> +@@ -207,6 +207,7 @@ static int rtnl_linkinfo_parse(int master_index, struct rtattr *rta)
> + {
> + 	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
> + 	struct rtattr *bond[IFLA_BOND_MAX+1];
> ++	struct rtattr *hsr[IFLA_HSR_MAX+1];
> + 	int index = -1;
> + 	char *kind;
> + 
> +@@ -227,6 +228,24 @@ static int rtnl_linkinfo_parse(int master_index, struct rtattr *rta)
> + 			}
> + 		} else if (kind && !strncmp(kind, "team", 4)) {
> + 			index = get_team_active_iface(master_index);
> ++
> ++		} else if (kind && !strncmp(kind, "hsr", 3) &&
> ++			   linkinfo[IFLA_INFO_DATA]) {
> ++			unsigned int slave1, slave2;
> ++
> ++			if (rtnl_nested_rtattr_parse(hsr, IFLA_HSR_MAX,
> ++						     linkinfo[IFLA_INFO_DATA]) < 0)
> ++				return -1;
> ++
> ++			if (!hsr[IFLA_HSR_SLAVE1] || !hsr[IFLA_HSR_SLAVE2])
> ++				return -1;
> ++
> ++			slave1 = rta_getattr_u32(hsr[IFLA_HSR_SLAVE1]);
> ++			slave2 = rta_getattr_u32(hsr[IFLA_HSR_SLAVE2]);
> ++
> ++			if (slave1 > 0xffff || slave2 > 0xffff)
> ++				return -1;
> ++			index = slave1 | slave2 << 16;
> + 		}
> + 	}
> + 	return index;
> +@@ -552,3 +571,35 @@ no_info:
> + 	nl_close(fd);
> + 	return ret;
> + }
> ++
> ++int rtnl_get_hsr_devices(const char *hsr_device, char slaveA[IF_NAMESIZE], char slaveB[IF_NAMESIZE])
> ++{
> ++	int err, fd;
> ++	unsigned int hsr_slaves = 0;
> ++
> ++	fd = rtnl_open();
> ++	if (fd < 0)
> ++		return fd;
> ++
> ++	err = rtnl_link_query(fd, hsr_device);
> ++	if (err) {
> ++		goto no_info;
> ++	}
> ++	err = -1;
> ++
> ++	rtnl_link_status(fd, hsr_device, rtnl_get_ts_device_callback, &hsr_slaves);
> ++	if (hsr_slaves == 0)
> ++		goto no_info;
> ++
> ++	if (!if_indextoname(hsr_slaves & 0xffff, slaveA))
> ++		goto no_info;
> ++
> ++	if (!if_indextoname(hsr_slaves >> 16, slaveB))
> ++		goto no_info;
> ++
> ++	err = 0;
> ++
> ++no_info:
> ++	rtnl_close(fd);
> ++	return err;
> ++}
> +diff --git a/rtnl.h b/rtnl.h
> +index 96fee29..d10db32 100644
> +--- a/rtnl.h
> ++++ b/rtnl.h
> +@@ -68,6 +68,15 @@ int rtnl_link_status(int fd, const char *device, rtnl_callback cb, void *ctx);
> +  */
> + int rtnl_iface_has_vclock(const char *device, int phc_index);
> + 
> ++/**
> ++ * Return both slave portts of a HSR device.
> ++ * param device	The name of the HSR deviec
> ++ * @param slaveA	The name of the SlaveA device
> ++ * @param slaveB	The name of the SlaveB device
> ++ * @return		Zero on success, non-zero otherwise.
> ++ */
> ++int rtnl_get_hsr_devices(const char *hsr_device, char slaveA[IF_NAMESIZE], char slaveB[IF_NAMESIZE]);
> ++
> + /**
> +  * Open a RT netlink socket for monitoring link state.
> +  * @return    A valid socket, or -1 on error.
> +-- 
> +2.34.1
> +
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0005-port-Add-paired_port-option.patch b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0005-port-Add-paired_port-option.patch
> new file mode 100644
> index 00000000..22265db7
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0005-port-Add-paired_port-option.patch
> @@ -0,0 +1,175 @@
> +From 6817d7ab3549a59d55fe6de888e2ee7e72c6c8c9 Mon Sep 17 00:00:00 2001
> +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Date: Thu, 2 Oct 2025 10:34:10 +0200
> +Subject: [PATCH 05/13] port: Add paired_port option.
> +
> +Add an option to pair ports. This is for a HSR network to find the other
> +port.
> +
> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> +---
> +Upstream-Status:
> +Submitted
> +  Submitted to upstream, waiting approval
> +  https://lists.nwtime.org/sympa/arc/linuxptp-devel/2025-11/msg00010.html
> +
> + clock.c        |  1 +
> + config.c       |  1 +
> + makefile       |  2 +-
> + port.c         | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
> + port.h         | 15 +++++++++++++++
> + port_private.h |  1 +
> + ptp4l.8        |  4 ++++
> + 7 files changed, 71 insertions(+), 1 deletion(-)
> +
> +diff --git a/clock.c b/clock.c
> +index d777378..b628c34 100644
> +--- a/clock.c
> ++++ b/clock.c
> +@@ -1051,6 +1051,7 @@ static int clock_add_port(struct clock *c, const char *phc_device,
> + 		return -1;
> + 	}
> + 	LIST_FOREACH(piter, &c->ports, list) {
> ++		port_pair_redundant_ports(p, piter);
> + 		lastp = piter;
> + 	}
> + 	if (lastp) {
> +diff --git a/config.c b/config.c
> +index 6f4fd9c..a882bc7 100644
> +--- a/config.c
> ++++ b/config.c
> +@@ -293,6 +293,7 @@ struct config_item config_tab[] = {
> + 	GLOB_ITEM_INT("G.8275.defaultDS.localPriority", 128, 1, UINT8_MAX),
> + 	PORT_ITEM_INT("G.8275.portDS.localPriority", 128, 1, UINT8_MAX),
> + 	GLOB_ITEM_INT("gmCapable", 1, 0, 1),
> ++	PORT_ITEM_STR("hsr_device", ""),
> + 	GLOB_ITEM_ENU("hwts_filter", HWTS_FILTER_NORMAL, hwts_filter_enu),
> + 	PORT_ITEM_INT("hybrid_e2e", 0, 0, 1),
> + 	PORT_ITEM_INT("ignore_source_id", 0, 0, 1),
> +diff --git a/makefile b/makefile
> +index 07199aa..50c2d5a 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	= raw.o transport.o udp.o udp6.o uds.o rtnl.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/port.c b/port.c
> +index 549d72b..586e365 100644
> +--- a/port.c
> ++++ b/port.c
> +@@ -3793,6 +3793,54 @@ err_port:
> + 	return NULL;
> + }
> + 
> ++void port_pair_redundant_ports(struct port *p1, struct port *p2)
> ++{
> ++	const char *p1_name = interface_name(p1->iface);
> ++	const char *p2_name = interface_name(p2->iface);
> ++	const char *p1_hsr, *p2_hsr;
> ++	struct config *cfg;
> ++	char hsr_slave_A[IF_NAMESIZE];
> ++	char hsr_slave_B[IF_NAMESIZE];
> ++	int ret;
> ++
> ++	cfg = clock_config(p1->clock);
> ++
> ++	/* Do the two ports belong to the same hsr device? */
> ++	p1_hsr = config_get_string(cfg, p1_name, "hsr_device");
> ++	if (!strlen(p1_hsr))
> ++		return;
> ++
> ++	p2_hsr = config_get_string(cfg, p2_name, "hsr_device");
> ++	if (strcmp(p1_hsr, p2_hsr))
> ++		return;
> ++
> ++	ret = rtnl_get_hsr_devices(p1_hsr, hsr_slave_A, hsr_slave_B);
> ++	if (ret) {
> ++		pr_err("Failed to query HSR attributes on %s", p1_hsr);
> ++		return;
> ++	}
> ++
> ++	if (!strcmp(hsr_slave_A, p1_name) && !strcmp(hsr_slave_B, p2_name)) {
> ++		p1->paired_port = p2;
> ++		p2->paired_port = p1;
> ++	} else if (!strcmp(hsr_slave_A, p2_name) && !strcmp(hsr_slave_B, p1_name)) {
> ++		p1->paired_port = p2;
> ++		p2->paired_port = p1;
> ++	} else {
> ++		pr_err("On HSR dev %s ports %s/%s don't match %s/%s\n",
> ++		       p1_hsr, hsr_slave_A, hsr_slave_B, p1_name, p2_name);
> ++		return;
> ++	}
> ++
> ++	pr_notice("Pairing redundant ports %s and %s on %s.",
> ++		  p1_name, p2_name, p1_hsr);
> ++}
> ++
> ++struct port *port_paired_port(struct port *port)
> ++{
> ++	return port->paired_port;
> ++}
> ++
> + enum port_state port_state(struct port *port)
> + {
> + 	return port->state;
> +diff --git a/port.h b/port.h
> +index cc03859..f103006 100644
> +--- a/port.h
> ++++ b/port.h
> +@@ -366,4 +366,19 @@ void tc_cleanup(void);
> +  */
> + void port_update_unicast_state(struct port *p);
> + 
> ++/**
> ++ * Pair two ports which are redundant (as per HSR)
> ++ *
> ++ * @param p1  A port instance.
> ++ * @param p2  A port instance.
> ++ */
> ++void port_pair_redundant_ports(struct port *p1, struct port *p2);
> ++
> ++/**
> ++ * Return the paired (rundant port) of this port instance (as per HSR)
> ++ *
> ++ * @param port  A port instance.
> ++ */
> ++struct port *port_paired_port(struct port *port);
> ++
> + #endif
> +diff --git a/port_private.h b/port_private.h
> +index aa9a095..79b1d31 100644
> +--- a/port_private.h
> ++++ b/port_private.h
> +@@ -174,6 +174,7 @@ struct port {
> + 		int port;
> + 	} cmlds;
> + 	struct ProfileIdentity profileIdentity;
> ++	struct port *paired_port;
> + };
> + 
> + #define portnum(p) (p->portIdentity.portNumber)
> +diff --git a/ptp4l.8 b/ptp4l.8
> +index dd40731..c8c00ef 100644
> +--- a/ptp4l.8
> ++++ b/ptp4l.8
> +@@ -754,6 +754,10 @@ The server sends its interface rate using interface rate TLV
> + as per G.8275.2 Annex D.
> + The default is 0 (does not support interface rate tlv).
> + 
> ++.TP
> ++.B hsr_device
> ++If the device belongs to the hsr network, specifiy the HSR parent. Default is empty.
> ++
> + .TP
> + .B hwts_filter
> + Select the hardware time stamp filter setting mode.
> +-- 
> +2.34.1
> +
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0006-fsm-Add-a-state-engine-for-redundant-master.patch b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0006-fsm-Add-a-state-engine-for-redundant-master.patch
> new file mode 100644
> index 00000000..dd94c963
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0006-fsm-Add-a-state-engine-for-redundant-master.patch
> @@ -0,0 +1,519 @@
> +From 52baa3911eb6e7a103b6de6f4c2adf964ab4fca3 Mon Sep 17 00:00:00 2001
> +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Date: Thu, 2 Oct 2025 10:37:06 +0200
> +Subject: [PATCH 06/13] fsm: Add a state engine for redundant master
> +
> +The FSM ptp_red_m_fsm() and ptp_red_slave_fsm() (slave only) are based
> +on ptp_fsm() and ptp_slave_fsm() but add the additional PASSIVE_SLAVE
> +handling.
> +This state machine is selected once redundant_master has been specified
> +for the bmca option.
> +
> +The bmc_state_decision() will return PASSIVE_SLAVE if a redundant port
> +is available and has the best clock.
> +
> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> +---
> +Upstream-Status:
> +Submitted
> +  Submitted to upstream, waiting approval
> +  https://lists.nwtime.org/sympa/arc/linuxptp-devel/2025-11/msg00016.html
> +
> + bmc.c    |   6 +
> + config.c |   1 +
> + fsm.c    | 395 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> + fsm.h    |  19 +++
> + port.c   |   2 +
> + 5 files changed, 423 insertions(+)
> +
> +diff --git a/bmc.c b/bmc.c
> +index ebc0789..5ce0b0c 100644
> +--- a/bmc.c
> ++++ b/bmc.c
> +@@ -130,6 +130,7 @@ enum port_state bmc_state_decision(struct clock *c, struct port *r,
> + 				   int (*compare)(struct dataset *a, struct dataset *b))
> + {
> + 	struct dataset *clock_ds, *clock_best, *port_best;
> ++	struct port *paired_port;
> + 	enum port_state ps;
> + 
> + 	clock_ds = clock_default_ds(c);
> +@@ -167,6 +168,11 @@ enum port_state bmc_state_decision(struct clock *c, struct port *r,
> + 		return PS_SLAVE; /*S1*/
> + 	}
> + 
> ++	paired_port = port_paired_port(r);
> ++	if (paired_port && clock_best_port(c) == paired_port) {
> ++		return PS_PASSIVE_SLAVE;
> ++	}
> ++
> + 	if (compare(clock_best, port_best) == A_BETTER_TOPO) {
> + 		return PS_PASSIVE; /*P2*/
> + 	} else {
> +diff --git a/config.c b/config.c
> +index a882bc7..4cdb37f 100644
> +--- a/config.c
> ++++ b/config.c
> +@@ -249,6 +249,7 @@ static struct config_enum as_capable_enu[] = {
> + static struct config_enum bmca_enu[] = {
> + 	{ "ptp",  BMCA_PTP  },
> + 	{ "noop", BMCA_NOOP },
> ++	{ "redundant_master", BMCA_RED_MASTER },
> + 	{ NULL, 0 },
> + };
> + 
> +diff --git a/fsm.c b/fsm.c
> +index 9e8c4d8..af72fea 100644
> +--- a/fsm.c
> ++++ b/fsm.c
> +@@ -338,3 +338,398 @@ enum port_state ptp_slave_fsm(enum port_state state, enum fsm_event event,
> + 
> + 	return next;
> + }
> ++
> ++enum port_state ptp_red_m_fsm(enum port_state state, enum fsm_event event, int mdiff)
> ++{
> ++	enum port_state next = state;
> ++
> ++	if (EV_INITIALIZE == event || EV_POWERUP == event)
> ++		return PS_INITIALIZING;
> ++
> ++	switch (state) {
> ++	case PS_INITIALIZING:
> ++		switch (event) {
> ++		case EV_FAULT_DETECTED:
> ++			next = PS_FAULTY;
> ++			break;
> ++		case EV_INIT_COMPLETE:
> ++			next = PS_LISTENING;
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++
> ++	case PS_FAULTY:
> ++		switch (event) {
> ++		case EV_DESIGNATED_DISABLED:
> ++			next = PS_DISABLED;
> ++			break;
> ++		case EV_FAULT_CLEARED:
> ++			next = PS_INITIALIZING;
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++
> ++	case PS_DISABLED:
> ++		if (EV_DESIGNATED_ENABLED == event)
> ++			next = PS_INITIALIZING;
> ++		break;
> ++
> ++	case PS_LISTENING:
> ++		switch (event) {
> ++		case EV_DESIGNATED_DISABLED:
> ++			next = PS_DISABLED;
> ++			break;
> ++		case EV_FAULT_DETECTED:
> ++			next = PS_FAULTY;
> ++			break;
> ++		case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES:
> ++			next = PS_MASTER;
> ++			break;
> ++		case EV_RS_MASTER:
> ++			next = PS_PRE_MASTER;
> ++			break;
> ++		case EV_RS_GRAND_MASTER:
> ++			next = PS_GRAND_MASTER;
> ++			break;
> ++		case EV_RS_SLAVE:
> ++			next = PS_UNCALIBRATED;
> ++			break;
> ++		case EV_RS_PASSIVE:
> ++			next = PS_PASSIVE;
> ++			break;
> ++		case EV_RS_PSLAVE:
> ++			next = PS_PASSIVE_SLAVE;
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++
> ++	case PS_PRE_MASTER:
> ++		switch (event) {
> ++		case EV_DESIGNATED_DISABLED:
> ++			next = PS_DISABLED;
> ++			break;
> ++		case EV_FAULT_DETECTED:
> ++			next = PS_FAULTY;
> ++			break;
> ++		case EV_QUALIFICATION_TIMEOUT_EXPIRES:
> ++			next = PS_MASTER;
> ++			break;
> ++		case EV_RS_SLAVE:
> ++			next = PS_UNCALIBRATED;
> ++			break;
> ++		case EV_RS_PASSIVE:
> ++			next = PS_PASSIVE;
> ++			break;
> ++		case EV_RS_PSLAVE:
> ++			next = PS_PASSIVE_SLAVE;
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++
> ++	case PS_MASTER:
> ++	case PS_GRAND_MASTER:
> ++		switch (event) {
> ++		case EV_DESIGNATED_DISABLED:
> ++			next = PS_DISABLED;
> ++			break;
> ++		case EV_FAULT_DETECTED:
> ++			next = PS_FAULTY;
> ++			break;
> ++		case EV_RS_SLAVE:
> ++			next = PS_UNCALIBRATED;
> ++			break;
> ++		case EV_RS_PASSIVE:
> ++			next = PS_PASSIVE;
> ++			break;
> ++		case EV_RS_PSLAVE:
> ++			next = PS_PASSIVE_SLAVE;
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++
> ++	case PS_PASSIVE:
> ++		switch (event) {
> ++		case EV_DESIGNATED_DISABLED:
> ++			next = PS_DISABLED;
> ++			break;
> ++		case EV_FAULT_DETECTED:
> ++			next = PS_FAULTY;
> ++			break;
> ++		case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES:
> ++			next = PS_MASTER;
> ++			break;
> ++		case EV_RS_MASTER:
> ++			next = PS_PRE_MASTER;
> ++			break;
> ++		case EV_RS_GRAND_MASTER:
> ++			next = PS_GRAND_MASTER;
> ++			break;
> ++		case EV_RS_SLAVE:
> ++			next = PS_UNCALIBRATED;
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++
> ++	case PS_UNCALIBRATED:
> ++		switch (event) {
> ++		case EV_DESIGNATED_DISABLED:
> ++			next = PS_DISABLED;
> ++			break;
> ++		case EV_FAULT_DETECTED:
> ++			next = PS_FAULTY;
> ++			break;
> ++		case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES:
> ++			next = PS_MASTER;
> ++			break;
> ++		case EV_MASTER_CLOCK_SELECTED:
> ++			next = PS_SLAVE;
> ++			break;
> ++		case EV_RS_MASTER:
> ++			next = PS_PRE_MASTER;
> ++			break;
> ++		case EV_RS_GRAND_MASTER:
> ++			next = PS_GRAND_MASTER;
> ++			break;
> ++		case EV_RS_SLAVE:
> ++			next = PS_UNCALIBRATED;
> ++			break;
> ++		case EV_RS_PASSIVE:
> ++			next = PS_PASSIVE;
> ++			break;
> ++		case EV_RS_PSLAVE:
> ++			next = PS_PASSIVE_SLAVE;
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++
> ++	case PS_SLAVE:
> ++		switch (event) {
> ++		case EV_DESIGNATED_DISABLED:
> ++			next = PS_DISABLED;
> ++			break;
> ++		case EV_FAULT_DETECTED:
> ++			next = PS_FAULTY;
> ++			break;
> ++		case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES:
> ++			next = PS_MASTER;
> ++			break;
> ++		case EV_SYNCHRONIZATION_FAULT:
> ++			next = PS_UNCALIBRATED;
> ++			break;
> ++		case EV_RS_MASTER:
> ++			next = PS_PRE_MASTER;
> ++			break;
> ++		case EV_RS_GRAND_MASTER:
> ++			next = PS_GRAND_MASTER;
> ++			break;
> ++		case EV_RS_SLAVE:
> ++			if (mdiff)
> ++				next = PS_UNCALIBRATED;
> ++			break;
> ++		case EV_RS_PASSIVE:
> ++			next = PS_PASSIVE;
> ++			break;
> ++		case EV_RS_PSLAVE:
> ++			next = PS_PASSIVE_SLAVE;
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++
> ++	case PS_PASSIVE_SLAVE:
> ++		switch (event) {
> ++		case EV_DESIGNATED_DISABLED:
> ++			next = PS_DISABLED;
> ++			break;
> ++		case EV_FAULT_DETECTED:
> ++			next = PS_FAULTY;
> ++			break;
> ++		case EV_RS_MASTER:
> ++		case EV_RS_GRAND_MASTER:
> ++		case EV_RS_SLAVE:
> ++			next = PS_UNCALIBRATED;
> ++			break;
> ++		case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES:
> ++			next = PS_MASTER;
> ++			break;
> ++		case EV_NONE:
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++	}
> ++
> ++	return next;
> ++}
> ++
> ++enum port_state ptp_red_slave_fsm(enum port_state state, enum fsm_event event,
> ++				  int mdiff)
> ++{
> ++	enum port_state next = state;
> ++
> ++	if (EV_INITIALIZE == event || EV_POWERUP == event)
> ++		return PS_INITIALIZING;
> ++
> ++	switch (state) {
> ++	case PS_INITIALIZING:
> ++		switch (event) {
> ++		case EV_FAULT_DETECTED:
> ++			next = PS_FAULTY;
> ++			break;
> ++		case EV_INIT_COMPLETE:
> ++			next = PS_LISTENING;
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++
> ++	case PS_FAULTY:
> ++		switch (event) {
> ++		case EV_DESIGNATED_DISABLED:
> ++			next = PS_DISABLED;
> ++			break;
> ++		case EV_FAULT_CLEARED:
> ++			next = PS_INITIALIZING;
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++
> ++	case PS_DISABLED:
> ++		if (EV_DESIGNATED_ENABLED == event)
> ++			next = PS_INITIALIZING;
> ++		break;
> ++
> ++	case PS_LISTENING:
> ++		switch (event) {
> ++		case EV_DESIGNATED_DISABLED:
> ++			next = PS_DISABLED;
> ++			break;
> ++		case EV_FAULT_DETECTED:
> ++			next = PS_FAULTY;
> ++			break;
> ++		case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES:
> ++		case EV_RS_MASTER:
> ++		case EV_RS_GRAND_MASTER:
> ++		case EV_RS_PASSIVE:
> ++			next = PS_LISTENING;
> ++			break;
> ++		case EV_RS_SLAVE:
> ++			next = PS_UNCALIBRATED;
> ++			break;
> ++		case EV_RS_PSLAVE:
> ++			next = PS_PASSIVE_SLAVE;
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++
> ++	case PS_UNCALIBRATED:
> ++		switch (event) {
> ++		case EV_DESIGNATED_DISABLED:
> ++			next = PS_DISABLED;
> ++			break;
> ++		case EV_FAULT_DETECTED:
> ++			next = PS_FAULTY;
> ++			break;
> ++		case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES:
> ++		case EV_RS_MASTER:
> ++		case EV_RS_GRAND_MASTER:
> ++		case EV_RS_PASSIVE:
> ++			next = PS_LISTENING;
> ++			break;
> ++		case EV_MASTER_CLOCK_SELECTED:
> ++			next = PS_SLAVE;
> ++			break;
> ++		case EV_RS_PSLAVE:
> ++			next = PS_PASSIVE_SLAVE;
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++
> ++	case PS_SLAVE:
> ++		switch (event) {
> ++		case EV_DESIGNATED_DISABLED:
> ++			next = PS_DISABLED;
> ++			break;
> ++		case EV_FAULT_DETECTED:
> ++			next = PS_FAULTY;
> ++			break;
> ++		case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES:
> ++		case EV_RS_MASTER:
> ++		case EV_RS_GRAND_MASTER:
> ++		case EV_RS_PASSIVE:
> ++			next = PS_LISTENING;
> ++			break;
> ++		case EV_SYNCHRONIZATION_FAULT:
> ++			next = PS_UNCALIBRATED;
> ++			break;
> ++		case EV_RS_SLAVE:
> ++			if (mdiff)
> ++				next = PS_UNCALIBRATED;
> ++			break;
> ++		case EV_RS_PSLAVE:
> ++			next = PS_PASSIVE_SLAVE;
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++
> ++	case PS_PASSIVE_SLAVE:
> ++		switch (event) {
> ++		case EV_DESIGNATED_DISABLED:
> ++			next = PS_DISABLED;
> ++			break;
> ++		case EV_FAULT_DETECTED:
> ++			next = PS_FAULTY;
> ++			break;
> ++		case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES:
> ++		case EV_RS_MASTER:
> ++		case EV_RS_GRAND_MASTER:
> ++		case EV_RS_PASSIVE:
> ++			next = PS_LISTENING;
> ++			break;
> ++		case EV_SYNCHRONIZATION_FAULT:
> ++			next = PS_UNCALIBRATED;
> ++			break;
> ++		case EV_RS_SLAVE:
> ++			next = PS_UNCALIBRATED;
> ++			break;
> ++		case EV_RS_PSLAVE:
> ++			next = PS_PASSIVE_SLAVE;
> ++			break;
> ++		case EV_NONE:
> ++			break;
> ++		default:
> ++			break;
> ++		}
> ++		break;
> ++
> ++	default:
> ++		break;
> ++	}
> ++
> ++	return next;
> ++}
> +diff --git a/fsm.h b/fsm.h
> +index e07d9a9..60f2805 100644
> +--- a/fsm.h
> ++++ b/fsm.h
> +@@ -60,6 +60,7 @@ enum fsm_event {
> + enum bmca_select {
> + 	BMCA_PTP,
> + 	BMCA_NOOP,
> ++	BMCA_RED_MASTER,
> + };
> + 
> + /**
> +@@ -81,4 +82,22 @@ enum port_state ptp_fsm(enum port_state state, enum fsm_event event, int mdiff);
> + enum port_state ptp_slave_fsm(enum port_state state, enum fsm_event event,
> + 			      int mdiff);
> + 
> ++/**
> ++ * Run the state machine for a TC+OC setup on a redundant port setup.
> ++ * @param state  The current state of the port.
> ++ * @param event  The event to be processed.
> ++ * @param mdiff  Whether a new master has been selected.
> ++ * @return       The new state for the port.
> ++ */
> ++enum port_state ptp_red_m_fsm(enum port_state state, enum fsm_event event, int mdiff);
> ++
> ++/**
> ++ * Run the state machine for a TC+OC setup on a redundant port setup for a salve only clock.
> ++ * @param state  The current state of the port.
> ++ * @param event  The event to be processed.
> ++ * @param mdiff  Whether a new master has been selected.
> ++ * @return       The new state for the port.
> ++ */
> ++enum port_state ptp_red_slave_fsm(enum port_state state, enum fsm_event event, int mdiff);
> ++
> + #endif
> +diff --git a/port.c b/port.c
> +index 586e365..7ade639 100644
> +--- a/port.c
> ++++ b/port.c
> +@@ -3628,6 +3628,8 @@ struct port *port_open(const char *phc_device,
> + 			pr_err("Please enable at least one of serverOnly or clientOnly when BMCA == noop.\n");
> + 			goto err_transport;
> + 		}
> ++	} else if (p->bmca == BMCA_RED_MASTER) {
> ++		p->state_machine = clock_slave_only(clock) ? ptp_red_slave_fsm : ptp_red_m_fsm;
> + 	} else {
> + 		p->state_machine = clock_slave_only(clock) ? ptp_slave_fsm : ptp_fsm;
> + 	}
> +-- 
> +2.34.1
> +
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0007-p2p_hc-Add-a-double-attached-clock-hybrid-clock-HC.patch b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0007-p2p_hc-Add-a-double-attached-clock-hybrid-clock-HC.patch
> new file mode 100644
> index 00000000..212e28b0
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0007-p2p_hc-Add-a-double-attached-clock-hybrid-clock-HC.patch
> @@ -0,0 +1,444 @@
> +From 13b25f731684d21102a8a288ab74d6bfcfe24640 Mon Sep 17 00:00:00 2001
> +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Date: Thu, 2 Oct 2025 10:39:45 +0200
> +Subject: [PATCH 07/13] p2p_hc: Add a double attached clock, hybrid clock (HC).
> +MIME-Version: 1.0
> +Content-Type: text/plain; charset=UTF-8
> +Content-Transfer-Encoding: 8bit
> +
> +The double attached clock, hybrid clock, is described in IEC 62439-3 for
> +the two PRP/ HSR scenario with two ports.
> +
> +                ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
> +                ┃                             ┃
> +                ┃    Doubly Attached Node     ┃
> +                ┃    ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄     ┃
> +                ┃           ╭────╮            ┃
> +                ┃           │ OC │            ┃
> +                ┃           ╰────╯            ┃
> +                ┃             ║               ┃
> +                ┃             ║               ┃
> +                ┃           ╭────╮            ┃
> +                ┃    ╔══════│ TC │══════╗     ┃
> +                ┃    ║      ╰────╯      ║     ┃
> +                ┃ ╭──────╮           ╭──────╮ ┃
> +                ┃ │Port A│           │Port B│ ┃
> +                ┃ ╰──────╯           ╰──────╯ ┃
> +                ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
> +
> +The TC has three ports: One for each networking port and one for the OC.
> +The TC forwards SYNC packets from port A to B and to the OC to consume
> +it. It sends PDELAY_* messages and responds to them.
> +The OC receives usually two SYNC message which the TC received on both
> +ports. It keeps the "better" one and ignores the other. Therefore it
> +synchronises only against one of the two ports. In master mode the OC
> +sends a SYNC message on both ports.
> +
> +Add a HC which implements a TC and OC. Use it if the OC is set and clock
> +type is doubly attached.
> +The clock is assigned as a special case of OC/ BC. The PTP specification
> +mentions clockType for OC/ BC as 0/ 1 but the code is using 0x8000/
> +0x4000. I don't where this is used so it pretends to be a OC.
> +
> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> +---
> +Upstream-Status:
> +Submitted
> +  Submitted to upstream, waiting approval
> +  https://lists.nwtime.org/sympa/arc/linuxptp-devel/2025-11/msg00011.html
> +
> + makefile       |   2 +-
> + p2p_hc.c       | 331 +++++++++++++++++++++++++++++++++++++++++++++++++
> + port.c         |   9 +-
> + port_private.h |   3 +
> + 4 files changed, 342 insertions(+), 3 deletions(-)
> + create mode 100644 p2p_hc.c
> +
> +diff --git a/makefile b/makefile
> +index 50c2d5a..e42e147 100644
> +--- a/makefile
> ++++ b/makefile
> +@@ -31,7 +31,7 @@ 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 \
> +  e2e_tc.o fault.o $(FILTERS) fsm.o hash.o interface.o monitor.o msg.o phc.o \
> +- pmc_common.o port.o port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o rtnl.o \
> ++ pmc_common.o port.o port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o p2p_hc.o rtnl.o \
> +  $(SECURITY) $(SERVOS) sk.o stats.o tc.o $(TRANSP) telecom.o tlv.o tsproc.o \
> +  unicast_client.o unicast_fsm.o unicast_service.o util.o version.o
> + 
> +diff --git a/p2p_hc.c b/p2p_hc.c
> +new file mode 100644
> +index 0000000..4c0dbd8
> +--- /dev/null
> ++++ b/p2p_hc.c
> +@@ -0,0 +1,331 @@
> ++/**
> ++ * @file p2p_hc.c
> ++ * @brief Implements a Hybrid Clock (Transparent Clock + Ordinary Clock).
> ++ * @note Copyright (C) 2025 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> ++ * @note Based on TC/OC which is
> ++ * @note Copyright (C) 2018 Richard Cochran <richardcochran@gmail.com>
> ++ * @note SPDX-License-Identifier: GPL-2.0+
> ++ */
> ++#include <errno.h>
> ++
> ++#include "port.h"
> ++#include "port_private.h"
> ++#include "print.h"
> ++#include "rtnl.h"
> ++#include "sad.h"
> ++#include "tc.h"
> ++
> ++static int p2p_hc_delay_request(struct port *p)
> ++{
> ++	switch (p->state) {
> ++	case PS_INITIALIZING:
> ++	case PS_FAULTY:
> ++	case PS_DISABLED:
> ++		return 0;
> ++	case PS_LISTENING:
> ++	case PS_PRE_MASTER:
> ++	case PS_MASTER:
> ++	case PS_PASSIVE:
> ++	case PS_UNCALIBRATED:
> ++	case PS_SLAVE:
> ++	case PS_GRAND_MASTER:
> ++	case PS_PASSIVE_SLAVE:
> ++		break;
> ++	}
> ++	return port_delay_request(p);
> ++}
> ++
> ++static int port_set_sync_tx_tmo(struct port *p)
> ++{
> ++	return set_tmo_log(p->fda.fd[FD_SYNC_TX_TIMER], 1, p->logSyncInterval);
> ++}
> ++
> ++static void flush_peer_delay(struct port *p)
> ++{
> ++	if (p->peer_delay_req) {
> ++		msg_put(p->peer_delay_req);
> ++		p->peer_delay_req = NULL;
> ++	}
> ++	if (p->peer_delay_resp) {
> ++		msg_put(p->peer_delay_resp);
> ++		p->peer_delay_resp = NULL;
> ++	}
> ++	if (p->peer_delay_fup) {
> ++		msg_put(p->peer_delay_fup);
> ++		p->peer_delay_fup = NULL;
> ++	}
> ++}
> ++
> ++void p2p_hc_dispatch(struct port *p, enum fsm_event event, int mdiff)
> ++{
> ++	if (clock_slave_only(p->clock)) {
> ++		if (event == EV_RS_GRAND_MASTER) {
> ++			const char *n = p->log_name;
> ++			pr_warning("%s: master state recommended in slave only mode", n);
> ++			pr_warning("%s: defaultDS.priority1 probably misconfigured", n);
> ++		}
> ++	}
> ++
> ++	if (!port_state_update(p, event, mdiff)) {
> ++		return;
> ++	}
> ++
> ++	if (!portnum(p)) {
> ++		/* UDS needs no timers. */
> ++		return;
> ++	}
> ++	/* port_p2p_transition */
> ++	port_clr_tmo(p->fda.fd[FD_ANNOUNCE_TIMER]);
> ++	port_clr_tmo(p->fda.fd[FD_SYNC_RX_TIMER]);
> ++	/* Leave FD_DELAY_TIMER running. */
> ++	port_clr_tmo(p->fda.fd[FD_QUALIFICATION_TIMER]);
> ++	port_clr_tmo(p->fda.fd[FD_MANNO_TIMER]);
> ++	port_clr_tmo(p->fda.fd[FD_SYNC_TX_TIMER]);
> ++
> ++	/*
> ++	 * Handle the side effects of the state transition.
> ++	 */
> ++	switch (p->state) {
> ++	case PS_INITIALIZING:
> ++		break;
> ++	case PS_FAULTY:
> ++	case PS_DISABLED:
> ++		port_disable(p);
> ++		sad_set_last_seqid(clock_config(p->clock), p->spp, -1);
> ++		break;
> ++	case PS_LISTENING:
> ++		port_set_announce_tmo(p);
> ++		port_set_delay_tmo(p);
> ++		break;
> ++	case PS_PRE_MASTER:
> ++		port_set_qualification_tmo(p);
> ++		break;
> ++	case PS_MASTER:
> ++	case PS_GRAND_MASTER:
> ++		if (!p->inhibit_announce) {
> ++			set_tmo_log(p->fda.fd[FD_MANNO_TIMER], 1, -10); /*~1ms*/
> ++		}
> ++		port_set_sync_tx_tmo(p);
> ++		sad_set_last_seqid(clock_config(p->clock), p->spp, -1);
> ++		break;
> ++	case PS_PASSIVE:
> ++		port_set_announce_tmo(p);
> ++		break;
> ++	case PS_UNCALIBRATED:
> ++		flush_last_sync(p);
> ++		flush_peer_delay(p);
> ++		sad_set_last_seqid(clock_config(p->clock), p->spp, -1);
> ++		/* fall through */
> ++	case PS_SLAVE:
> ++	case PS_PASSIVE_SLAVE:
> ++		port_set_announce_tmo(p);
> ++		break;
> ++	};
> ++}
> ++
> ++static int port_set_manno_tmo(struct port *p)
> ++{
> ++	return set_tmo_log(p->fda.fd[FD_MANNO_TIMER], 1, p->logAnnounceInterval);
> ++}
> ++
> ++enum fsm_event p2p_hc_event(struct port *p, int fd_index)
> ++{
> ++	int cnt, err, fd = p->fda.fd[fd_index];
> ++	enum fsm_event event = EV_NONE;
> ++	struct ptp_message *msg, *dup;
> ++
> ++	switch (fd_index) {
> ++	case FD_ANNOUNCE_TIMER:
> ++	case FD_SYNC_RX_TIMER:
> ++		pr_debug("%s: %s timeout", p->log_name,
> ++			 fd_index == FD_SYNC_RX_TIMER ? "rx sync" : "announce");
> ++		if (p->best) {
> ++			fc_clear(p->best);
> ++		}
> ++
> ++		if (fd_index == FD_SYNC_RX_TIMER) {
> ++			p->service_stats.sync_timeout++;
> ++		} else {
> ++			p->service_stats.announce_timeout++;
> ++		}
> ++
> ++		if (p->inhibit_announce) {
> ++			port_clr_tmo(p->fda.fd[FD_ANNOUNCE_TIMER]);
> ++		} else {
> ++			port_set_announce_tmo(p);
> ++		}
> ++
> ++		if (p->inhibit_announce) {
> ++			return EV_NONE;
> ++		}
> ++		return EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES;
> ++
> ++	case FD_DELAY_TIMER:
> ++		pr_debug("%s: delay timeout", p->log_name);
> ++		port_set_delay_tmo(p);
> ++		tc_prune(p);
> ++		return p2p_hc_delay_request(p) ? EV_FAULT_DETECTED : EV_NONE;
> ++
> ++	case FD_QUALIFICATION_TIMER:
> ++		pr_debug("%s: qualification timeout", p->log_name);
> ++		p->service_stats.qualification_timeout++;
> ++		return EV_QUALIFICATION_TIMEOUT_EXPIRES;
> ++
> ++	case FD_MANNO_TIMER:
> ++		pr_debug("%s: master tx announce timeout", p->log_name);
> ++		port_set_manno_tmo(p);
> ++		p->service_stats.master_announce_timeout++;
> ++		clock_update_leap_status(p->clock);
> ++		return port_tx_announce(p, NULL, p->seqnum.announce++) ?
> ++			EV_FAULT_DETECTED : EV_NONE;
> ++
> ++	case FD_SYNC_TX_TIMER:
> ++		pr_debug("%s: master sync timeout", p->log_name);
> ++		port_set_sync_tx_tmo(p);
> ++		p->service_stats.master_sync_timeout++;
> ++		return port_tx_sync(p, NULL, p->seqnum.sync++) ?
> ++			EV_FAULT_DETECTED : EV_NONE;
> ++
> ++	case FD_UNICAST_REQ_TIMER:
> ++	case FD_UNICAST_SRV_TIMER:
> ++		pr_err("unexpected timer expiration");
> ++		return EV_NONE;
> ++
> ++	case FD_RTNL:
> ++		pr_debug("%s: received link status notification", p->log_name);
> ++		rtnl_link_status(fd, p->name, port_link_status, p);
> ++		if (p->link_status == (LINK_UP|LINK_STATE_CHANGED)) {
> ++			return EV_FAULT_CLEARED;
> ++		} else if ((p->link_status == (LINK_DOWN|LINK_STATE_CHANGED)) ||
> ++			   (p->link_status & TS_LABEL_CHANGED)) {
> ++			return EV_FAULT_DETECTED;
> ++		} else {
> ++			return EV_NONE;
> ++		}
> ++	}
> ++
> ++	msg = msg_allocate();
> ++	if (!msg) {
> ++		return EV_FAULT_DETECTED;
> ++	}
> ++	msg->hwts.type = p->timestamping;
> ++
> ++	cnt = transport_recv(p->trp, fd, msg);
> ++	if (cnt <= 0) {
> ++		pr_err("%s: recv message failed", p->log_name);
> ++		msg_put(msg);
> ++		return EV_FAULT_DETECTED;
> ++	}
> ++
> ++	if (msg_sots_valid(msg)) {
> ++		ts_add(&msg->hwts.ts, -p->rx_timestamp_offset);
> ++	}
> ++	if (msg_unicast(msg)) {
> ++		pl_warning(600, "cannot switch unicast messages! type %s",
> ++			   msg_type_string(msg_type(msg)));
> ++		msg_put(msg);
> ++		return EV_NONE;
> ++	}
> ++
> ++	dup = msg_duplicate(msg, cnt);
> ++	if (!dup) {
> ++		msg_put(msg);
> ++		return EV_NONE;
> ++	}
> ++	msg_tlv_copy(dup, msg);
> ++	if (tc_ignore(p, dup)) {
> ++		msg_put(dup);
> ++		dup = NULL;
> ++	} else {
> ++		err = sad_process_auth(clock_config(p->clock), p->spp, dup, msg);
> ++		if (err) {
> ++			switch (err) {
> ++			case -EBADMSG:
> ++				pr_err("%s: auth: bad message", p->log_name);
> ++				break;
> ++			case -EPROTO:
> ++				pr_debug("%s: auth: ignoring message", p->log_name);
> ++				break;
> ++			}
> ++			msg_put(msg);
> ++			if (dup) {
> ++				msg_put(dup);
> ++			}
> ++			return EV_NONE;
> ++		}
> ++	}
> ++
> ++	switch (msg_type(msg)) {
> ++	case SYNC:
> ++		if (tc_fwd_sync(p, msg)) {
> ++			event = EV_FAULT_DETECTED;
> ++			break;
> ++		}
> ++		if (dup) {
> ++			process_sync(p, dup);
> ++		}
> ++		break;
> ++	case DELAY_REQ:
> ++		break;
> ++	case PDELAY_REQ:
> ++		if (dup && process_pdelay_req(p, dup)) {
> ++			event = EV_FAULT_DETECTED;
> ++		}
> ++		break;
> ++	case PDELAY_RESP:
> ++		if (dup && process_pdelay_resp(p, dup)) {
> ++			event = EV_FAULT_DETECTED;
> ++		}
> ++		break;
> ++	case FOLLOW_UP:
> ++		if (tc_fwd_folup(p, msg)) {
> ++			event = EV_FAULT_DETECTED;
> ++			break;
> ++		}
> ++		if (dup) {
> ++			process_follow_up(p, dup);
> ++		}
> ++		break;
> ++	case DELAY_RESP:
> ++		break;
> ++	case PDELAY_RESP_FOLLOW_UP:
> ++		if (dup) {
> ++			process_pdelay_resp_fup(p, dup);
> ++		}
> ++		break;
> ++	case ANNOUNCE:
> ++		if (tc_forward(p, msg)) {
> ++			event = EV_FAULT_DETECTED;
> ++			break;
> ++		}
> ++		if (dup && process_announce(p, dup)) {
> ++			event = EV_STATE_DECISION_EVENT;
> ++		}
> ++		break;
> ++	case SIGNALING:
> ++		if (tc_forward(p, msg)) {
> ++			event = EV_FAULT_DETECTED;
> ++			break;
> ++		}
> ++		if (dup && process_signaling(p, dup)) {
> ++			event = EV_FAULT_DETECTED;
> ++		}
> ++		break;
> ++
> ++	case MANAGEMENT:
> ++		if (tc_forward(p, msg)) {
> ++			event = EV_FAULT_DETECTED;
> ++			break;
> ++		}
> ++		if (dup && clock_manage(p->clock, p, dup)) {
> ++			event = EV_STATE_DECISION_EVENT;
> ++		}
> ++		break;
> ++	}
> ++
> ++	msg_put(msg);
> ++	if (dup) {
> ++		msg_put(dup);
> ++	}
> ++	return event;
> ++}
> +diff --git a/port.c b/port.c
> +index 7ade639..50a3a75 100644
> +--- a/port.c
> ++++ b/port.c
> +@@ -3592,8 +3592,13 @@ struct port *port_open(const char *phc_device,
> + 	switch (type) {
> + 	case CLOCK_TYPE_ORDINARY:
> + 	case CLOCK_TYPE_BOUNDARY:
> +-		p->dispatch = bc_dispatch;
> +-		p->event = bc_event;
> ++		if (clock_type_is_DAC(clock)) {
> ++			p->dispatch = p2p_hc_dispatch;
> ++			p->event = p2p_hc_event;
> ++		} else {
> ++			p->dispatch = bc_dispatch;
> ++			p->event = bc_event;
> ++		}
> + 		break;
> + 	case CLOCK_TYPE_P2P:
> + 		p->dispatch = p2p_dispatch;
> +diff --git a/port_private.h b/port_private.h
> +index 79b1d31..507c296 100644
> +--- a/port_private.h
> ++++ b/port_private.h
> +@@ -185,6 +185,9 @@ enum fsm_event e2e_event(struct port *p, int fd_index);
> + void p2p_dispatch(struct port *p, enum fsm_event event, int mdiff);
> + enum fsm_event p2p_event(struct port *p, int fd_index);
> + 
> ++void p2p_hc_dispatch(struct port *p, enum fsm_event event, int mdiff);
> ++enum fsm_event p2p_hc_event(struct port *p, int fd_index);
> ++
> + int clear_fault_asap(struct fault_interval *faint);
> + void delay_req_prune(struct port *p);
> + void fc_clear(struct foreign_clock *fc);
> +-- 
> +2.34.1
> +
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0008-tc-Allow-to-forward-packets-both-ways-in-DAC-mode.patch b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0008-tc-Allow-to-forward-packets-both-ways-in-DAC-mode.patch
> new file mode 100644
> index 00000000..0f1f1720
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0008-tc-Allow-to-forward-packets-both-ways-in-DAC-mode.patch
> @@ -0,0 +1,37 @@
> +From 104fcfab6c17c3dfd2d72e1da51e631355d4f204 Mon Sep 17 00:00:00 2001
> +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Date: Thu, 2 Oct 2025 10:45:44 +0200
> +Subject: [PATCH 08/13] tc: Allow to forward packets both ways in DAC mode
> +
> +In a HSR/ DAC setup, the TC should forward SYNC, FOLLOW-UP packets in
> +both directions.
> +
> +Allow to forward packets both ways in DAC mode.
> +
> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> +---
> +Upstream-Status:
> +Submitted
> +  Submitted to upstream, waiting approval
> +  https://lists.nwtime.org/sympa/arc/linuxptp-devel/2025-11/msg00018.html
> +
> + tc.c | 2 +-
> + 1 file changed, 1 insertion(+), 1 deletion(-)
> +
> +diff --git a/tc.c b/tc.c
> +index 0fd1bc4..109947d 100644
> +--- a/tc.c
> ++++ b/tc.c
> +@@ -105,7 +105,7 @@ static int tc_blocked(struct port *q, struct port *p, struct ptp_message *m)
> + 	case PS_SLAVE:
> + 	case PS_PASSIVE_SLAVE:
> + 		/* Delay_Req swims against the stream. */
> +-		if (msg_type(m) != DELAY_REQ) {
> ++		if (!clock_type_is_DAC(p->clock) && msg_type(m) != DELAY_REQ) {
> + 			return 1;
> + 		}
> + 		break;
> +-- 
> +2.34.1
> +
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0009-port-Add-a-safe-guard-in-case-PASSIVE_SLAVE-attempts.patch b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0009-port-Add-a-safe-guard-in-case-PASSIVE_SLAVE-attempts.patch
> new file mode 100644
> index 00000000..c979b42b
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0009-port-Add-a-safe-guard-in-case-PASSIVE_SLAVE-attempts.patch
> @@ -0,0 +1,37 @@
> +From e8a1ada27bd5065fcbe04d1f56db05060cfe967f Mon Sep 17 00:00:00 2001
> +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Date: Thu, 2 Oct 2025 10:46:17 +0200
> +Subject: [PATCH 09/13] port: Add a safe guard in case PASSIVE_SLAVE attempts
> + to sync
> +
> +Add a error message in case port_synchronize() attempts to synchronize
> +the lock on a port in PASSIVE_SLAVE state.
> +
> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> +---
> +Upstream-Status:
> +Submitted
> +  Submitted to upstream, waiting approval
> +  https://lists.nwtime.org/sympa/arc/linuxptp-devel/2025-11/msg00015.html
> +
> + port.c | 3 +++
> + 1 file changed, 3 insertions(+)
> +
> +diff --git a/port.c b/port.c
> +index 50a3a75..68d77ad 100644
> +--- a/port.c
> ++++ b/port.c
> +@@ -1434,6 +1434,9 @@ static void port_synchronize(struct port *p,
> + 			     clock_parent_identity(p->clock), seqid,
> + 			     t1, tmv_add(c1, c2), t2);
> + 		break;
> ++	case PS_PASSIVE_SLAVE:
> ++		pr_err("Port in passive slave attempts to synchronize");
> ++		return;
> + 	default:
> + 		break;
> + 	}
> +-- 
> +2.34.1
> +
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0010-tc-Allow-to-forward-packets-in-LISTEN-state.patch b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0010-tc-Allow-to-forward-packets-in-LISTEN-state.patch
> new file mode 100644
> index 00000000..2618868b
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0010-tc-Allow-to-forward-packets-in-LISTEN-state.patch
> @@ -0,0 +1,61 @@
> +From 04c95905fd0d323930e2fe443c59a127e301c818 Mon Sep 17 00:00:00 2001
> +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Date: Thu, 2 Oct 2025 13:01:53 +0200
> +Subject: [PATCH 10/13] tc: Allow to forward packets in LISTEN state
> +
> +In the HSR/ DAC network, the port which receives ANNOUNCE messages
> +enters SLAVE state. The other port remains in LISTEN state since it does
> +not receive any PTP packets.
> +The tc_blocked() forbids to forward a packet if the EGRESS port is in
> +listen state.
> +
> +Allow to forward packets if the EGRESS port is in LISTEN state. If the
> +packets make their way through the ring, the port will eventually switch
> +to PASSIVE_SLAVE state.
> +
> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> +---
> +Upstream-Status:
> +Submitted
> +  Submitted to upstream, waiting approval
> +  https://lists.nwtime.org/sympa/arc/linuxptp-devel/2025-11/msg00019.html
> +
> + tc.c | 4 ++--
> + 1 file changed, 2 insertions(+), 2 deletions(-)
> +
> +diff --git a/tc.c b/tc.c
> +index 109947d..15b83c4 100644
> +--- a/tc.c
> ++++ b/tc.c
> +@@ -75,7 +75,6 @@ static int tc_blocked(struct port *q, struct port *p, struct ptp_message *m)
> + 	case PS_INITIALIZING:
> + 	case PS_FAULTY:
> + 	case PS_DISABLED:
> +-	case PS_LISTENING:
> + 	case PS_PRE_MASTER:
> + 	case PS_PASSIVE:
> + 		return 1;
> +@@ -86,6 +85,7 @@ static int tc_blocked(struct port *q, struct port *p, struct ptp_message *m)
> + 			return 1;
> + 		}
> + 		break;
> ++	case PS_LISTENING:
> + 	case PS_UNCALIBRATED:
> + 	case PS_SLAVE:
> + 	case PS_PASSIVE_SLAVE:
> +@@ -97,10 +97,10 @@ static int tc_blocked(struct port *q, struct port *p, struct ptp_message *m)
> + 	case PS_INITIALIZING:
> + 	case PS_FAULTY:
> + 	case PS_DISABLED:
> +-	case PS_LISTENING:
> + 	case PS_PRE_MASTER:
> + 	case PS_PASSIVE:
> + 		return 1;
> ++	case PS_LISTENING:
> + 	case PS_UNCALIBRATED:
> + 	case PS_SLAVE:
> + 	case PS_PASSIVE_SLAVE:
> +-- 
> +2.34.1
> +
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0011-raw-Add-HSR-handling.patch b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0011-raw-Add-HSR-handling.patch
> new file mode 100644
> index 00000000..8de6a039
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0011-raw-Add-HSR-handling.patch
> @@ -0,0 +1,567 @@
> +From 96629470ca54aba5049414c8fdd67427d8cdec9a Mon Sep 17 00:00:00 2001
> +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Date: Wed, 22 Oct 2025 15:42:28 +0200
> +Subject: [PATCH 11/13] raw: Add HSR handling
> +MIME-Version: 1.0
> +Content-Type: text/plain; charset=UTF-8
> +Content-Transfer-Encoding: 8bit
> +
> +In a HSR network each device as two port attached to the HSR ring. The
> +two ports are usually called port A and port B. The communication is
> +Ethernet based and the payload part is ETH_P_HSR. After the HSR header,
> +for PTP the payload is ETH_P_1588 as usual. So we have either
> +
> +     ┌─────────┬─────────┬─────────┬─────┐
> +     │ MAC DST │ MAC SRC │ HSR-TAG │ PTP │
> +     └─────────┴─────────┴─────────┴─────┘
> +or with VLAN enabled
> +     ┌─────────┬─────────┬──────────┬─────────┬─────┐
> +     │ MAC DST │ MAC SRC │ VLAN-TAG │ HSR-TAG │ PTP │
> +     └─────────┴─────────┴──────────┴─────────┴─────┘
> +
> +The kernel is supposed not to forward HSR packets with ETH_P_1588
> +payload. Also it must support socket option PACKET_HSR_BIND_PORT to bind
> +the hsr device to one of the two ports via PACKET_HSR_BIND_PORT_A or
> +PACKET_HSR_BIND_PORT_B. It needs to support PACKET_HSR_INFO with the
> +option PACKET_HSR_INFO_HAS_HDR for the control message in order to send
> +HSR with a HSR header. These changes are not merged into the upstream.
> +
> +This interface is used by ptp4l to receive both copies of a packets and
> +to send a packet on one of the two ports including a PTP timestamp.
> +
> +The clock is setup as TC which means the forwarding done by ptp4l will
> +properly update the correction header. The HSR header of a received
> +message is saved so it can be used while the packet is forwarded. This
> +is important to keep the MAC address of the sender but also to keep HSR
> +fields such as sequence number or port.
> +The PDELAY_* packets are not forwarded and instead responded to. Here
> +the HSR header is constructed by the HSR stack and the packet is sent
> +only on the request port.
> +
> +The added BPF filter is based on the existing one and adds HSR type
> +handling and ignores possible VLAN. The filter drops all packets which
> +are sent by "us". The sender is supposed to remove his packets from the
> +ring.
> +
> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> +---
> +Upstream-Status:
> +Submitted
> +  Submitted to upstream, waiting approval
> +  https://lists.nwtime.org/sympa/arc/linuxptp-devel/2025-11/msg00012.html
> +
> + ether.h   |   9 ++
> + missing.h |  13 +++
> + msg.c     |   3 +-
> + msg.h     |   6 ++
> + raw.c     | 312 +++++++++++++++++++++++++++++++++++++++++++++++-------
> + 5 files changed, 301 insertions(+), 42 deletions(-)
> +
> +diff --git a/ether.h b/ether.h
> +index 276eec4..577a07e 100644
> +--- a/ether.h
> ++++ b/ether.h
> +@@ -48,4 +48,13 @@ struct vlan_hdr {
> + 	uint16_t type;
> + } __attribute__((packed));
> + 
> ++struct hsr_hdr {
> ++	eth_addr dst;
> ++	eth_addr src;
> ++	uint16_t type;
> ++	uint16_t pathid_and_LSDU_size;
> ++	uint16_t sequence_nr;
> ++	uint16_t encap_type;
> ++} __attribute__((packed));
> ++
> + #endif
> +diff --git a/missing.h b/missing.h
> +index c6be8fd..583e035 100644
> +--- a/missing.h
> ++++ b/missing.h
> +@@ -137,6 +137,19 @@ enum {
> + #define PTP_PEROUT_REQUEST2 PTP_PEROUT_REQUEST
> + #endif
> + 
> ++#ifndef PACKET_HSR_BIND_PORT
> ++#define PACKET_HSR_BIND_PORT	25
> ++
> ++/* For HSR, bind port */
> ++#define PACKET_HSR_BIND_PORT_AB		0
> ++#define PACKET_HSR_BIND_PORT_A		1
> ++#define PACKET_HSR_BIND_PORT_B		2
> ++/* HSR, CMSG */
> ++#define PACKET_HSR_INFO			1
> ++#define PACKET_HSR_INFO_HAS_HDR		1
> ++
> ++#endif
> ++
> + #if LINUX_VERSION_CODE < KERNEL_VERSION(6,5,0)
> + 
> + /* from upcoming Linux kernel version 6.5 */
> +diff --git a/msg.c b/msg.c
> +index 7c236c3..9989456 100644
> +--- a/msg.c
> ++++ b/msg.c
> +@@ -32,7 +32,8 @@ int assume_two_step = 0;
> + uint8_t ptp_hdr_ver = PTP_VERSION;
> + 
> + /*
> +- * Head room fits a VLAN Ethernet header, and 'msg' is 64 bit aligned.
> ++ * Head room fits a VLAN Ethernet header or a HSR-Ethernet header, and 'msg' is
> ++ * 64 bit aligned.
> +  */
> + #define MSG_HEADROOM 24
> + 
> +diff --git a/msg.h b/msg.h
> +index 58c2287..c53e07b 100644
> +--- a/msg.h
> ++++ b/msg.h
> +@@ -29,6 +29,7 @@
> + #include "ddt.h"
> + #include "tlv.h"
> + #include "tmv.h"
> ++#include "ether.h"
> + 
> + /* Version definition for IEEE 1588-2019 */
> + #define PTP_MAJOR_VERSION	2
> +@@ -238,6 +239,11 @@ struct ptp_message {
> + 	 * pointers to the appended TLVs.
> + 	 */
> + 	TAILQ_HEAD(tlv_list, tlv_extra) tlv_list;
> ++	/**
> ++	 * Containing the HSR header
> ++	 */
> ++	struct hsr_hdr hsr_header;
> ++	int hsr_header_valid;
> + };
> + 
> + /**
> +diff --git a/raw.c b/raw.c
> +index c809233..eb01072 100644
> +--- a/raw.c
> ++++ b/raw.c
> +@@ -46,6 +46,7 @@
> + #include "sk.h"
> + #include "transport_private.h"
> + #include "util.h"
> ++#include "rtnl.h"
> + 
> + struct raw {
> + 	struct transport t;
> +@@ -53,10 +54,87 @@ struct raw {
> + 	struct address ptp_addr;
> + 	struct address p2p_addr;
> + 	int vlan;
> ++	int hsr_slave;
> + };
> + 
> + #define PRP_TRAILER_LEN 6
> + 
> ++/*
> ++ * tcpdump -d
> ++ *  'ether[12:2] == 0x892f && ether[18:2] == 0x88F7 and
> ++ *  (ether[18 +2:1] & 0x8 == 0x8) &&
> ++ *   not ether src 11:22:33:44:55:66'
> ++ *
> ++ * (000) ldh      [12]
> ++ * (001) jeq      #0x892f          jt 2	jf 12
> ++ * (002) ldh      [18]
> ++ * (003) jeq      #0x88f7          jt 4	jf 12
> ++ * (004) ldb      [20]
> ++ * (005) and      #0x8
> ++ * (006) jeq      #0x8             jt 7	jf 12
> ++ * (007) ld       [8]
> ++ * (008) jeq      #0x33445566      jt 9	jf 11
> ++ * (009) ldh      [6]
> ++ * (010) jeq      #0x1122          jt 12	jf 11
> ++ * (011) ret      #262144
> ++ * (012) ret      #0
> ++ */
> ++static struct sock_filter raw_filter_hsr_norm_general[] = {
> ++	{ 0x28, 0, 0, 0x0000000c },
> ++	{ 0x15, 0, 10, 0x0000892f },
> ++	{ 0x28, 0, 0, 0x00000012 },
> ++	{ 0x15, 0, 8, 0x000088f7 },
> ++	{ 0x30, 0, 0, 0x00000014 },
> ++	{ 0x54, 0, 0, 0x00000008 },
> ++	{ 0x15, 0, 5, 0x00000008 },
> ++	{ 0x20, 0, 0, 0x00000008 },
> ++	{ 0x15, 0, 2, 0x33445566 },
> ++	{ 0x28, 0, 0, 0x00000006 },
> ++	{ 0x15, 1, 0, 0x00001122 },
> ++	{ 0x6, 0, 0, 0x00040000 },
> ++	{ 0x6, 0, 0, 0x00000000 },
> ++};
> ++#define FILTER_HSR_GENERAL_SRC0 10
> ++#define FILTER_HSR_GENERAL_SRC2 8
> ++
> ++/*
> ++ * tcpdump -d
> ++ *  'ether[12:2] == 0x892f && ether[18:2] == 0x88F7 and
> ++ *   (ether[18 +2:1] & 0x8 != 0x8) &&
> ++ *   not ether src 11:22:33:44:55:66'
> ++ *
> ++ * (000) ldh      [12]
> ++ * (001) jeq      #0x892f          jt 2	jf 12
> ++ * (002) ldh      [18]
> ++ * (003) jeq      #0x88f7          jt 4	jf 12
> ++ * (004) ldb      [20]
> ++ * (005) and      #0x8
> ++ * (006) jeq      #0x8             jt 12	jf 7
> ++ * (007) ld       [8]
> ++ * (008) jeq      #0x33445566      jt 9	jf 11
> ++ * (009) ldh      [6]
> ++ * (010) jeq      #0x1122          jt 12	jf 11
> ++ * (011) ret      #262144
> ++ * (012) ret      #0
> ++ */
> ++static struct sock_filter raw_filter_hsr_norm_event[] = {
> ++	{ 0x28, 0, 0, 0x0000000c },
> ++	{ 0x15, 0, 10, 0x0000892f },
> ++	{ 0x28, 0, 0, 0x00000012 },
> ++	{ 0x15, 0, 8, 0x000088f7 },
> ++	{ 0x30, 0, 0, 0x00000014 },
> ++	{ 0x54, 0, 0, 0x00000008 },
> ++	{ 0x15, 5, 0, 0x00000008 },
> ++	{ 0x20, 0, 0, 0x00000008 },
> ++	{ 0x15, 0, 2, 0x33445566 },
> ++	{ 0x28, 0, 0, 0x00000006 },
> ++	{ 0x15, 1, 0, 0x00001122 },
> ++	{ 0x6, 0, 0, 0x00040000 },
> ++	{ 0x6, 0, 0, 0x00000000 },
> ++};
> ++#define FILTER_HSR_EVENT_SRC0 10
> ++#define FILTER_HSR_EVENT_SRC2 8
> ++
> + /*
> +  * tcpdump -d \
> +  * '((ether[12:2] == 0x8100 and ether[12 + 4 :2] == 0x88F7 and ether[14+4 :1] & 0x8 == 0x8) or '\
> +@@ -153,32 +231,59 @@ static struct sock_filter raw_filter_vlan_norm_event[] = {
> + 
> + static int raw_configure(int fd, int event, int index,
> + 			 unsigned char *local_addr, unsigned char *addr1,
> +-			 unsigned char *addr2, int enable)
> ++			 unsigned char *addr2, int enable, int hsr_slave)
> + {
> + 	int err1, err2, option;
> + 	struct packet_mreq mreq;
> + 	struct sock_fprog prg;
> + 
> +-	if (event) {
> +-		prg.len = ARRAY_SIZE(raw_filter_vlan_norm_event);
> +-		prg.filter = raw_filter_vlan_norm_event;
> +-
> +-		memcpy(&prg.filter[FILTER_EVENT_POS_SRC0].k, local_addr, 2);
> +-		memcpy(&prg.filter[FILTER_EVENT_POS_SRC2].k, local_addr + 2, 4);
> +-		prg.filter[FILTER_EVENT_POS_SRC0].k =
> +-			ntohs(prg.filter[FILTER_EVENT_POS_SRC0].k);
> +-		prg.filter[FILTER_EVENT_POS_SRC2].k =
> +-			ntohl(prg.filter[FILTER_EVENT_POS_SRC2].k);
> ++	if (hsr_slave) {
> ++		if (event) {
> ++			prg.len = ARRAY_SIZE(raw_filter_hsr_norm_event);
> ++			prg.filter = raw_filter_hsr_norm_event;
> ++
> ++			memcpy(&prg.filter[FILTER_HSR_EVENT_SRC0].k, local_addr, 2);
> ++			memcpy(&prg.filter[FILTER_HSR_EVENT_SRC2].k, local_addr + 2, 4);
> ++
> ++			prg.filter[FILTER_HSR_EVENT_SRC0].k =
> ++				ntohs(prg.filter[FILTER_HSR_EVENT_SRC0].k);
> ++			prg.filter[FILTER_HSR_EVENT_SRC2].k =
> ++				ntohl(prg.filter[FILTER_HSR_EVENT_SRC2].k);
> ++		} else {
> ++			prg.len = ARRAY_SIZE(raw_filter_hsr_norm_general);
> ++			prg.filter = raw_filter_hsr_norm_general;
> ++
> ++			memcpy(&prg.filter[FILTER_HSR_GENERAL_SRC0].k, local_addr, 2);
> ++			memcpy(&prg.filter[FILTER_HSR_GENERAL_SRC2].k, local_addr + 2, 4);
> ++
> ++			prg.filter[FILTER_HSR_GENERAL_SRC0].k =
> ++				ntohs(prg.filter[FILTER_HSR_GENERAL_SRC0].k);
> ++			prg.filter[FILTER_HSR_GENERAL_SRC2].k =
> ++				ntohl(prg.filter[FILTER_HSR_GENERAL_SRC2].k);
> ++		}
> + 	} else {
> +-		prg.len = ARRAY_SIZE(raw_filter_vlan_norm_general);
> +-		prg.filter = raw_filter_vlan_norm_general;
> +-
> +-		memcpy(&prg.filter[FILTER_GENERAL_POS_SRC0].k, local_addr, 2);
> +-		memcpy(&prg.filter[FILTER_GENERAL_POS_SRC2].k, local_addr + 2, 4);
> +-		prg.filter[FILTER_GENERAL_POS_SRC0].k =
> +-			ntohs(prg.filter[FILTER_GENERAL_POS_SRC0].k);
> +-		prg.filter[FILTER_GENERAL_POS_SRC2].k =
> +-			ntohl(prg.filter[FILTER_GENERAL_POS_SRC2].k);
> ++		if (event) {
> ++			prg.len = ARRAY_SIZE(raw_filter_vlan_norm_event);
> ++			prg.filter = raw_filter_vlan_norm_event;
> ++
> ++			memcpy(&prg.filter[FILTER_EVENT_POS_SRC0].k, local_addr, 2);
> ++			memcpy(&prg.filter[FILTER_EVENT_POS_SRC2].k, local_addr + 2, 4);
> ++			prg.filter[FILTER_EVENT_POS_SRC0].k =
> ++				ntohs(prg.filter[FILTER_EVENT_POS_SRC0].k);
> ++			prg.filter[FILTER_EVENT_POS_SRC2].k =
> ++				ntohl(prg.filter[FILTER_EVENT_POS_SRC2].k);
> ++		} else {
> ++			prg.len = ARRAY_SIZE(raw_filter_vlan_norm_general);
> ++			prg.filter = raw_filter_vlan_norm_general;
> ++
> ++			memcpy(&prg.filter[FILTER_GENERAL_POS_SRC0].k, local_addr, 2);
> ++			memcpy(&prg.filter[FILTER_GENERAL_POS_SRC2].k, local_addr + 2, 4);
> ++			prg.filter[FILTER_GENERAL_POS_SRC0].k =
> ++				ntohs(prg.filter[FILTER_GENERAL_POS_SRC0].k);
> ++			prg.filter[FILTER_GENERAL_POS_SRC2].k =
> ++				ntohl(prg.filter[FILTER_GENERAL_POS_SRC2].k);
> ++
> ++		}
> + 	}
> + 
> + 	if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &prg, sizeof(prg))) {
> +@@ -236,7 +341,7 @@ static int raw_close(struct transport *t, struct fdarray *fda)
> + 
> + static int open_socket(const char *name, int event, unsigned char *local_addr,
> + 		       unsigned char *ptp_dst_mac, unsigned char *p2p_dst_mac,
> +-		       int socket_priority)
> ++		       int socket_priority, int hsr_slave)
> + {
> + 	struct sockaddr_ll addr;
> + 	int fd, index;
> +@@ -256,8 +361,22 @@ static int open_socket(const char *name, int event, unsigned char *local_addr,
> + 		pr_err("setsockopt SO_PRIORITY failed: %m");
> + 		goto no_option;
> + 	}
> ++	if (hsr_slave) {
> ++		int option;
> ++
> ++		if (hsr_slave == 1)
> ++			option = PACKET_HSR_BIND_PORT_A;
> ++		else
> ++			option = PACKET_HSR_BIND_PORT_B;
> ++
> ++		if (setsockopt(fd, SOL_PACKET, PACKET_HSR_BIND_PORT, &option,
> ++				 sizeof(option))) {
> ++			pr_err("setsockopt(SOL_PACKET, PACKET_HSR_BIND_PORT) failed: %m");
> ++			goto no_option;
> ++		}
> ++	}
> + 	if (raw_configure(fd, event, index, local_addr, ptp_dst_mac,
> +-			  p2p_dst_mac, 1))
> ++			  p2p_dst_mac, 1, hsr_slave))
> + 		goto no_option;
> + 
> + 	memset(&addr, 0, sizeof(addr));
> +@@ -352,7 +471,7 @@ static int raw_open(struct transport *t, struct interface *iface,
> + 	unsigned char ptp_dst_mac[MAC_LEN];
> + 	unsigned char p2p_dst_mac[MAC_LEN];
> + 	int efd, gfd, socket_priority;
> +-	const char *name;
> ++	const char *name, *hsr_device;
> + 	char *str;
> + 
> + 	name = interface_label(iface);
> +@@ -369,18 +488,45 @@ static int raw_open(struct transport *t, struct interface *iface,
> + 	mac_to_addr(&raw->ptp_addr, ptp_dst_mac);
> + 	mac_to_addr(&raw->p2p_addr, p2p_dst_mac);
> + 
> ++	hsr_device = config_get_string(t->cfg, name, "hsr_device");
> ++	if (!strlen(hsr_device))
> ++		hsr_device = NULL;
> ++	if (hsr_device) {
> ++		char hsr_slave_A[IF_NAMESIZE];
> ++		char hsr_slave_B[IF_NAMESIZE];
> ++		int ret;
> ++
> ++		ret = rtnl_get_hsr_devices(hsr_device, hsr_slave_A, hsr_slave_B);
> ++		if (ret < 0) {
> ++			pr_err("Failed to query hsr device %s", hsr_device);
> ++			goto no_mac;
> ++		}
> ++		if (!strcmp(name, hsr_slave_A)) {
> ++			raw->hsr_slave = 1;
> ++		} else if (!strcmp(name, hsr_slave_B)) {
> ++			raw->hsr_slave = 2;
> ++		} else {
> ++			pr_err("HSR device %s has no slave %s", hsr_device, name);
> ++			goto no_mac;
> ++		}
> ++
> ++		pr_notice("raw: HSR interface %s/%s, port %c",
> ++			  hsr_device, name,
> ++			  raw->hsr_slave == 1 ? 'A' : 'B');
> ++	}
> ++
> + 	if (sk_interface_macaddr(name, &raw->src_addr))
> + 		goto no_mac;
> + 
> + 	socket_priority = config_get_int(t->cfg, "global", "socket_priority");
> + 
> +-	efd = open_socket(name, 1, raw->src_addr.sll.sll_addr, ptp_dst_mac,
> +-			  p2p_dst_mac, socket_priority);
> ++	efd = open_socket(hsr_device ?: name, 1, raw->src_addr.sll.sll_addr, ptp_dst_mac,
> ++			  p2p_dst_mac, socket_priority, raw->hsr_slave);
> + 	if (efd < 0)
> + 		goto no_event;
> + 
> +-	gfd = open_socket(name, 0, raw->src_addr.sll.sll_addr, p2p_dst_mac,
> +-			  p2p_dst_mac, socket_priority);
> ++	gfd = open_socket(hsr_device ?: name, 0, raw->src_addr.sll.sll_addr, p2p_dst_mac,
> ++			  p2p_dst_mac, socket_priority, raw->hsr_slave);
> + 	if (gfd < 0)
> + 		goto no_general;
> + 
> +@@ -412,7 +558,9 @@ static int raw_recv(struct transport *t, int fd, void *buf, int buflen,
> + 	struct eth_hdr *hdr;
> + 	int cnt, hlen;
> + 
> +-	if (raw->vlan) {
> ++	if (raw->hsr_slave) {
> ++		hlen = sizeof(struct hsr_hdr);
> ++	} else if (raw->vlan) {
> + 		hlen = sizeof(struct vlan_hdr);
> + 	} else {
> + 		hlen = sizeof(struct eth_hdr);
> +@@ -431,7 +579,21 @@ static int raw_recv(struct transport *t, int fd, void *buf, int buflen,
> + 	if (has_prp_trailer(buf, cnt))
> + 		cnt -= PRP_TRAILER_LEN;
> + 
> +-	if (raw->vlan) {
> ++	if (raw->hsr_slave) {
> ++		struct hsr_hdr *hsr_hdr = (struct hsr_hdr *) ptr;
> ++		struct ptp_message *m = buf;
> ++		unsigned int hsr_size;
> ++
> ++		hsr_size = ntohs(hsr_hdr->pathid_and_LSDU_size) & 0xfff;
> ++		if (hsr_size != cnt + 6) {
> ++			pr_notice("Dropping bad sized HSR packet (%d vs %d)", hsr_size, cnt);
> ++			return 0;
> ++		}
> ++
> ++		memcpy(&m->hsr_header, hsr_hdr, sizeof(struct hsr_hdr));
> ++		m->hsr_header_valid = 1;
> ++
> ++	} else if (raw->vlan) {
> + 		if (ETH_P_1588 == ntohs(hdr->type)) {
> + 			pr_notice("raw: disabling VLAN mode");
> + 			raw->vlan = 0;
> +@@ -445,14 +607,37 @@ static int raw_recv(struct transport *t, int fd, void *buf, int buflen,
> + 	return cnt;
> + }
> + 
> ++static unsigned int put_cmsg(struct msghdr *msg, unsigned int msg_off, int ctrl_tot,
> ++			     int level, int type, int len, void *data)
> ++{
> ++	struct cmsghdr *cm;
> ++	int cmlen = CMSG_LEN(len);
> ++
> ++	if (msg->msg_controllen + cmlen >= ctrl_tot)
> ++		return 0;
> ++
> ++	cm = msg->msg_control + msg_off;
> ++	cm->cmsg_level = level;
> ++	cm->cmsg_type = type;
> ++	cm->cmsg_len = cmlen;
> ++
> ++	memcpy(CMSG_DATA(cm), data, cmlen - sizeof(*cm));
> ++	cmlen = CMSG_SPACE(len);
> ++	if (cmlen > ctrl_tot - msg_off)
> ++		cmlen = ctrl_tot - msg_off;
> ++
> ++	msg->msg_controllen += cmlen;
> ++	return msg_off + cmlen;
> ++}
> ++
> + static int raw_send(struct transport *t, struct fdarray *fda,
> + 		    enum transport_event event, int peer, void *buf, int len,
> + 		    struct address *addr, struct hw_timestamp *hwts)
> + {
> + 	struct raw *raw = container_of(t, struct raw, t);
> ++	struct ptp_message *m = buf;
> + 	ssize_t cnt;
> + 	unsigned char pkt[1600], *ptr = buf;
> +-	struct eth_hdr *hdr;
> + 	int fd = -1;
> + 
> + 	switch (event) {
> +@@ -467,22 +652,67 @@ static int raw_send(struct transport *t, struct fdarray *fda,
> + 		break;
> + 	}
> + 
> +-	ptr -= sizeof(*hdr);
> +-	len += sizeof(*hdr);
> ++	if (raw->hsr_slave && m->hsr_header_valid) {
> ++		struct hsr_hdr *hdr;
> ++		unsigned int pathid;
> ++		struct msghdr msg;
> ++		char control[256];
> ++		struct iovec iov = { ptr, len };
> ++		unsigned int hsr_option;
> + 
> +-	if (!addr)
> +-		addr = peer ? &raw->p2p_addr : &raw->ptp_addr;
> ++		ptr -= sizeof(*hdr);
> ++		len += sizeof(*hdr);
> + 
> +-	hdr = (struct eth_hdr *) ptr;
> +-	addr_to_mac(&hdr->dst, addr);
> +-	addr_to_mac(&hdr->src, &raw->src_addr);
> ++		hdr = (struct hsr_hdr *)ptr;
> ++		memcpy(hdr, &m->hsr_header, sizeof(struct hsr_hdr));
> ++
> ++		/*
> ++		 * The sender might have used a larger padding than neccessary.
> ++		 * Sending a smaller packet requires to update the HSR header.
> ++		 */
> ++		pathid = ntohs(hdr->pathid_and_LSDU_size) & ~0x0fff;
> ++		hdr->pathid_and_LSDU_size = htons((len - sizeof(struct eth_hdr)) | pathid);
> ++
> ++		iov.iov_base = ptr;
> ++		iov.iov_len = len;
> + 
> +-	hdr->type = htons(ETH_P_1588);
> ++		memset(control, 0, sizeof(control));
> ++		memset(&msg, 0, sizeof(msg));
> + 
> +-	cnt = send(fd, ptr, len, 0);
> +-	if (cnt < 1) {
> +-		return -errno;
> ++		msg.msg_iov = &iov;
> ++		msg.msg_iovlen = 1;
> ++		msg.msg_control = control;
> ++
> ++		hsr_option = PACKET_HSR_INFO_HAS_HDR;
> ++
> ++		put_cmsg(&msg, 0, sizeof(control), SOL_PACKET, PACKET_HSR_INFO,
> ++			 sizeof(hsr_option), &hsr_option);
> ++
> ++		cnt = sendmsg(fd, &msg, 0);
> ++		if (cnt < 1) {
> ++			return -errno;
> ++		}
> ++	} else {
> ++		struct eth_hdr *hdr;
> ++
> ++		ptr -= sizeof(*hdr);
> ++		len += sizeof(*hdr);
> ++
> ++		if (!addr)
> ++			addr = peer ? &raw->p2p_addr : &raw->ptp_addr;
> ++
> ++		hdr = (struct eth_hdr *) ptr;
> ++		addr_to_mac(&hdr->dst, addr);
> ++		addr_to_mac(&hdr->src, &raw->src_addr);
> ++
> ++		hdr->type = htons(ETH_P_1588);
> ++
> ++		cnt = send(fd, ptr, len, 0);
> ++		if (cnt < 1) {
> ++			return -errno;
> ++		}
> + 	}
> ++
> + 	/*
> + 	 * Get the time stamp right away.
> + 	 */
> +-- 
> +2.34.1
> +
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0012-configs-Add-sample-configs-for-the-HSR-setup.patch b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0012-configs-Add-sample-configs-for-the-HSR-setup.patch
> new file mode 100644
> index 00000000..69833bdd
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0012-configs-Add-sample-configs-for-the-HSR-setup.patch
> @@ -0,0 +1,96 @@
> +From 65df5cc751c253ff08ecb14d03c59d8e8882f9d6 Mon Sep 17 00:00:00 2001
> +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Date: Thu, 2 Oct 2025 11:51:53 +0200
> +Subject: [PATCH 12/13] configs: Add sample configs for the HSR setup
> +
> +This is a sample config for the HSR/ DAC mode.
> +
> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> +Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> +---
> +Upstream-Status:
> +Submitted
> +  Submitted to upstream, waiting approval
> +  https://lists.nwtime.org/sympa/arc/linuxptp-devel/2025-11/msg00017.html
> +
> + configs/hsr-master.cfg | 30 ++++++++++++++++++++++++++++++
> + configs/hsr-slave.cfg  | 30 ++++++++++++++++++++++++++++++
> + 2 files changed, 60 insertions(+)
> + create mode 100644 configs/hsr-master.cfg
> + create mode 100644 configs/hsr-slave.cfg
> +
> +diff --git a/configs/hsr-master.cfg b/configs/hsr-master.cfg
> +new file mode 100644
> +index 0000000..477b3a2
> +--- /dev/null
> ++++ b/configs/hsr-master.cfg
> +@@ -0,0 +1,30 @@
> ++# HSR example. Two redundant ports, paired. The interfaces implement a HC
> ++# consisting of two TC and attached OC for internal synchronisation. Both
> ++# ports should use the PTP-clock.
> ++# Can become master.
> ++# See the file, default.cfg, for the complete list of available options.
> ++
> ++[global]
> ++clientOnly 0
> ++priority1 127
> ++priority2 128
> ++logAnnounceInterval 0
> ++logSyncInterval 0
> ++path_trace_enabled 1
> ++tc_spanning_tree 1
> ++network_transport L2
> ++delay_mechanism P2P
> ++
> ++profileIdentity 00:0c:cd:01:01:01
> ++dataset_comparison IEC62439-3
> ++use_syslog    0
> ++verbose       1
> ++logging_level 6
> ++
> ++[eth1]
> ++hsr_device hsr0
> ++BMCA redundant_master
> ++
> ++[eth2]
> ++hsr_device hsr0
> ++BMCA redundant_master
> +diff --git a/configs/hsr-slave.cfg b/configs/hsr-slave.cfg
> +new file mode 100644
> +index 0000000..4531011
> +--- /dev/null
> ++++ b/configs/hsr-slave.cfg
> +@@ -0,0 +1,30 @@
> ++# HSR example. Two redundant ports, paired. The interfaces implement a HC
> ++# consisting of two TC and attached OC for internal synchronisation. Both
> ++# ports should use the PTP-clock.
> ++# Slave only mode.
> ++# See the file, default.cfg, for the complete list of available options.
> ++
> ++[global]
> ++clientOnly 1
> ++priority1 255
> ++priority2 255
> ++logAnnounceInterval 0
> ++logSyncInterval 0
> ++path_trace_enabled 1
> ++tc_spanning_tree 1
> ++network_transport L2
> ++delay_mechanism P2P
> ++
> ++profileIdentity 00:0c:cd:01:01:01
> ++dataset_comparison IEC62439-3
> ++use_syslog    0
> ++verbose       1
> ++logging_level 6
> ++
> ++[eth1]
> ++hsr_device hsr0
> ++BMCA redundant_master
> ++
> ++[eth2]
> ++hsr_device hsr0
> ++BMCA redundant_master
> +-- 
> +2.34.1
> +
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0013-configs-Change-p2p_dst_mac-to-avoid-IEEE-802.1-reser.patch b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0013-configs-Change-p2p_dst_mac-to-avoid-IEEE-802.1-reser.patch
> new file mode 100644
> index 00000000..928eb11e
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp/0013-configs-Change-p2p_dst_mac-to-avoid-IEEE-802.1-reser.patch
> @@ -0,0 +1,47 @@
> +From 53546f24b647adb969316f082652004a67ab50c8 Mon Sep 17 00:00:00 2001
> +From: MD Danish Anwar <danishanwar@ti.com>
> +Date: Wed, 18 Feb 2026 15:45:27 +0530
> +Subject: [PATCH 13/13] configs: Change p2p_dst_mac to avoid IEEE 802.1
> + reserved range
> +
> +The default p2p_dst_mac (01:80:C2:00:00:0E) is in the IEEE 802.1 reserved
> +range which can cause issues with switches and HSR handling. Change it to
> +01:1B:19:00:00:01 which is in the PTP multicast address range.
> +
> +Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> +---
> +Upstream-Status:
> +Pending
> +  To be posted once Patch 1-12 gets integrated
> +
> + configs/hsr-master.cfg | 1 +
> + configs/hsr-slave.cfg  | 1 +
> + 2 files changed, 2 insertions(+)
> +
> +diff --git a/configs/hsr-master.cfg b/configs/hsr-master.cfg
> +index 477b3a2..6ac8f76 100644
> +--- a/configs/hsr-master.cfg
> ++++ b/configs/hsr-master.cfg
> +@@ -14,6 +14,7 @@ path_trace_enabled 1
> + tc_spanning_tree 1
> + network_transport L2
> + delay_mechanism P2P
> ++p2p_dst_mac 01:1B:19:00:00:01
> + 
> + profileIdentity 00:0c:cd:01:01:01
> + dataset_comparison IEC62439-3
> +diff --git a/configs/hsr-slave.cfg b/configs/hsr-slave.cfg
> +index 4531011..94ce948 100644
> +--- a/configs/hsr-slave.cfg
> ++++ b/configs/hsr-slave.cfg
> +@@ -14,6 +14,7 @@ path_trace_enabled 1
> + tc_spanning_tree 1
> + network_transport L2
> + delay_mechanism P2P
> ++p2p_dst_mac 01:1B:19:00:00:01
> + 
> + profileIdentity 00:0c:cd:01:01:01
> + dataset_comparison IEC62439-3
> +-- 
> +2.34.1
> +
> diff --git a/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp_%.bbappend b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp_%.bbappend
> new file mode 100644
> index 00000000..09958c5a
> --- /dev/null
> +++ b/meta-arago-distro/recipes-connectivity/linuxptp/linuxptp_%.bbappend
> @@ -0,0 +1,4 @@
> +LINUXPTP_ARAGO = ""
> +LINUXPTP_ARAGO:arago = "linuxptp-arago.inc"
> +
> +require ${LINUXPTP_ARAGO}
> 
> base-commit: 3230bc79957bd71775e0273ea1f4eab8d676123a
> -- 
> 2.34.1



  parent reply	other threads:[~2026-03-02 20:41 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-02 10:35 [meta-arago][master][PATCH v2] linuxptp: Add support for HSR in Linux ptp MD Danish Anwar
2026-03-02 10:54 ` PRC Automation
2026-03-02 14:47 ` Ryan Eatmon
2026-03-02 20:40 ` Denys Dmytriyenko [this message]
2026-03-03  5:24   ` MD Danish Anwar
2026-03-04 23:08     ` Denys Dmytriyenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260302204051.GF11121@denix.org \
    --to=denis@denix.org \
    --cc=c-shilwant@ti.com \
    --cc=danishanwar@ti.com \
    --cc=denys@konsulko.com \
    --cc=meta-arago@lists.yoctoproject.org \
    --cc=reatmon@ti.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.