Thread: [Linuxptp-devel] [PATCH v6 03/11] Introduce the power profile. (Page 3)
PTP IEEE 1588 stack for Linux
                
                Brought to you by:
                
                    rcochran
                    
                
            
            
        
        
        
    | 
      
      
      From: Richard C. <ric...@gm...> - 2023-02-20 20:58:15
       | 
| Signed-off-by: Richard Cochran <ric...@gm...>
---
 config.c            | 15 ++++++++++++-
 configs/default.cfg |  5 +++++
 port.c              | 53 +++++++++++++++++++++++++++++++++++++++++++++
 port_private.h      |  3 +++
 power_profile.h     | 31 ++++++++++++++++++++++++++
 ptp4l.8             | 38 ++++++++++++++++++++++++++++++++
 tlv.h               |  8 +++++++
 7 files changed, 152 insertions(+), 1 deletion(-)
 create mode 100644 power_profile.h
diff --git a/config.c b/config.c
index f028b88..cb4421f 100644
--- a/config.c
+++ b/config.c
@@ -31,6 +31,7 @@
 #include "config.h"
 #include "ether.h"
 #include "hash.h"
+#include "power_profile.h"
 #include "print.h"
 #include "util.h"
 
@@ -66,7 +67,7 @@ typedef union {
 	char *s;
 } any_t;
 
-#define CONFIG_LABEL_SIZE 32
+#define CONFIG_LABEL_SIZE 64
 
 #define CFG_ITEM_STATIC (1 << 0) /* statically allocated, not to be freed */
 #define CFG_ITEM_LOCKED (1 << 1) /* command line value, may not be changed */
@@ -190,6 +191,13 @@ static struct config_enum hwts_filter_enu[] = {
 	{ NULL, 0 },
 };
 
+static struct config_enum ieee_c37_238_enu[] = {
+	{ "none", IEEE_C37_238_VERSION_NONE },
+	{ "2011", IEEE_C37_238_VERSION_2011 },
+	{ "2017", IEEE_C37_238_VERSION_2017 },
+	{ NULL, 0 },
+};
+
 static struct config_enum nw_trans_enu[] = {
 	{ "L2",    TRANS_IEEE_802_3 },
 	{ "UDPv4", TRANS_UDP_IPV4   },
@@ -300,6 +308,11 @@ struct config_item config_tab[] = {
 	GLOB_ITEM_DBL("pi_proportional_exponent", -0.3, -DBL_MAX, DBL_MAX),
 	GLOB_ITEM_DBL("pi_proportional_norm_max", 0.7, DBL_MIN, 1.0),
 	GLOB_ITEM_DBL("pi_proportional_scale", 0.0, 0.0, DBL_MAX),
+	PORT_ITEM_ENU("power_profile.version", IEEE_C37_238_VERSION_NONE, ieee_c37_238_enu),
+	PORT_ITEM_INT("power_profile.2011.grandmasterTimeInaccuracy", 0xFFFFFFFF, 0, INT_MAX),
+	PORT_ITEM_INT("power_profile.2011.networkTimeInaccuracy", 0, 0, INT_MAX),
+	PORT_ITEM_INT("power_profile.2017.totalTimeInaccuracy", 0xFFFFFFFF, 0, INT_MAX),
+	PORT_ITEM_INT("power_profile.grandmasterID", 0, 0, 0xFFFF),
 	GLOB_ITEM_INT("priority1", 128, 0, UINT8_MAX),
 	GLOB_ITEM_INT("priority2", 128, 0, UINT8_MAX),
 	GLOB_ITEM_STR("productDescription", ";;"),
diff --git a/configs/default.cfg b/configs/default.cfg
index 297a99d..a21ec66 100644
--- a/configs/default.cfg
+++ b/configs/default.cfg
@@ -41,6 +41,11 @@ BMCA                    ptp
 inhibit_announce        0
 inhibit_delay_req       0
 ignore_source_id        0
+power_profile.2011.grandmasterTimeInaccuracy	0xFFFFFFFF
+power_profile.2011.networkTimeInaccuracy	0
+power_profile.2017.totalTimeInaccuracy		0xFFFFFFFF
+power_profile.grandmasterID			0
+power_profile.version				none
 #
 # Run time options
 #
diff --git a/port.c b/port.c
index 92f3ee3..23697a5 100644
--- a/port.c
+++ b/port.c
@@ -453,6 +453,46 @@ static int follow_up_info_append(struct ptp_message *m)
 	return 0;
 }
 
+static int ieee_c37_238_append(struct port *p, struct ptp_message *m)
+{
+	struct ieee_c37_238_2017_tlv *p17;
+	struct ieee_c37_238_2011_tlv *p11;
+	struct tlv_extra *extra;
+
+	switch (p->pwr.version) {
+	case IEEE_C37_238_VERSION_NONE:
+		return 0;
+	case IEEE_C37_238_VERSION_2011:
+		extra = msg_tlv_append(m, sizeof(*p11));
+		if (!extra) {
+			return -1;
+		}
+		p11 = (struct ieee_c37_238_2011_tlv *) extra->tlv;
+		p11->type = TLV_ORGANIZATION_EXTENSION;
+		p11->length = sizeof(*p11) - sizeof(p11->type) - sizeof(p11->length);
+		memcpy(p11->id, ieeec37_238_id, sizeof(ieeec37_238_id));
+		p11->subtype[2] = 1;
+		p11->grandmasterID = p->pwr.grandmasterID;
+		p11->grandmasterTimeInaccuracy = p->pwr.grandmasterTimeInaccuracy;
+		p11->networkTimeInaccuracy = p->pwr.networkTimeInaccuracy;
+		break;
+	case IEEE_C37_238_VERSION_2017:
+		extra = msg_tlv_append(m, sizeof(*p17));
+		if (!extra) {
+			return -1;
+		}
+		p17 = (struct ieee_c37_238_2017_tlv *) extra->tlv;
+		p17->type = TLV_ORGANIZATION_EXTENSION;
+		p17->length = sizeof(*p17) - sizeof(p17->type) - sizeof(p17->length);
+		memcpy(p17->id, ieeec37_238_id, sizeof(ieeec37_238_id));
+		p17->subtype[2] = 2;
+		p17->grandmasterID = p->pwr.grandmasterID;
+		p17->totalTimeInaccuracy = p->pwr.totalTimeInaccuracy;
+		break;
+	}
+	return 0;
+}
+
 static int net_sync_resp_append(struct port *p, struct ptp_message *m)
 {
 	struct timePropertiesDS tp = clock_time_properties(p->clock);
@@ -1622,6 +1662,9 @@ int port_tx_announce(struct port *p, struct address *dst, uint16_t sequence_id)
 	if (p->path_trace_enabled && path_trace_append(p, msg, dad)) {
 		pr_err("%s: append path trace failed", p->log_name);
 	}
+	if (ieee_c37_238_append(p, msg)) {
+		pr_err("%s: append power profile failed", p->log_name);
+	}
 
 	err = port_prepare_and_send(p, msg, TRANS_GENERAL);
 	if (err) {
@@ -3332,6 +3375,16 @@ struct port *port_open(const char *phc_device,
 	p->state = PS_INITIALIZING;
 	p->delayMechanism = config_get_int(cfg, p->name, "delay_mechanism");
 	p->versionNumber = PTP_MAJOR_VERSION;
+	p->pwr.version =
+		config_get_int(cfg, p->name, "power_profile.version");
+	p->pwr.grandmasterID =
+		config_get_int(cfg, p->name, "power_profile.grandmasterID");
+	p->pwr.grandmasterTimeInaccuracy =
+		config_get_int(cfg, p->name, "power_profile.2011.grandmasterTimeInaccuracy");
+	p->pwr.networkTimeInaccuracy =
+		config_get_int(cfg, p->name, "power_profile.2011.networkTimeInaccuracy");
+	p->pwr.totalTimeInaccuracy =
+		config_get_int(cfg, p->name, "power_profile.2017.totalTimeInaccuracy");
 	p->slave_event_monitor = clock_slave_monitor(clock);
 
 	if (!port_is_uds(p) && unicast_client_initialize(p)) {
diff --git a/port_private.h b/port_private.h
index e5837b9..3b02d2f 100644
--- a/port_private.h
+++ b/port_private.h
@@ -26,6 +26,7 @@
 #include "fsm.h"
 #include "monitor.h"
 #include "msg.h"
+#include "power_profile.h"
 #include "tmv.h"
 
 #define NSEC2SEC 1000000000LL
@@ -153,6 +154,8 @@ struct port {
 	LIST_HEAD(fm, foreign_clock) foreign_masters;
 	/* TC book keeping */
 	TAILQ_HEAD(tct, tc_txd) tc_transmitted;
+	/* power profile */
+	struct ieee_c37_238_settings_np pwr;
 	/* unicast client mode */
 	struct unicast_master_table *unicast_master_table;
 	/* unicast service mode */
diff --git a/power_profile.h b/power_profile.h
new file mode 100644
index 0000000..6a7c8a4
--- /dev/null
+++ b/power_profile.h
@@ -0,0 +1,31 @@
+/**
+ * @file power_profile.h
+ * @brief Implements power profile features.
+ * @note Copyright (C) 2021 Richard Cochran <ric...@gm...>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef HAVE_POWER_PROFILE_H
+#define HAVE_POWER_PROFILE_H
+
+#include "pdt.h"
+
+enum power_profile_version {
+	IEEE_C37_238_VERSION_NONE,
+	IEEE_C37_238_VERSION_2011,
+	IEEE_C37_238_VERSION_2017,
+};
+
+#endif
diff --git a/ptp4l.8 b/ptp4l.8
index 55070aa..bb678c9 100644
--- a/ptp4l.8
+++ b/ptp4l.8
@@ -360,6 +360,44 @@ free-running physical clock (created by writing to
 The default is -1, which means the index will be set to the PHC associated with
 the interface, or the device specified by the \fB-p\fP option.
 
+.TP
+.B power_profile.2011.grandmasterTimeInaccuracy
+Specifies the time inaccuracy of the GM in nanoseconds.  Relevant only
+when power_profile.version is 2011.  This value may be changed
+dynamically using the POWER_PROFILE_SETTINGS_NP management message.
+The default is 0xFFFFFFFF.
+
+.TP
+.B power_profile.2011.networkTimeInaccuracy
+Specifies the time inaccuracy of the network in nanoseconds.  Relevant
+only when power_profile.version is 2011.  This value may be changed
+dynamically using the POWER_PROFILE_SETTINGS_NP management message.
+The default is 0xFFFFFFFF.
+
+.TP
+.B power_profile.2017.totalTimeInaccuracy
+Specifies the sum of the GM, network, and local node inaccuracies in
+nanoseconds.  Relevant only when power_profile.version is 2017.  This
+value may be changed dynamically using the POWER_PROFILE_SETTINGS_NP
+management message.  The default is 0xFFFFFFFF meaning unknown
+inaccuracy.
+
+.TP
+.B power_profile.grandmasterID
+Specifies an optional, non-zero identification code for the GM.  Note
+that the code is an arbitrary, power profile specific integer, not
+necessarily related to the clockIdentity in any way.  This value may
+be changed dynamically using the POWER_PROFILE_SETTINGS_NP management
+message.  The default is 0 meaning unused.
+
+.TP
+.B power_profile.version
+Specifies the power profile version to be used.  Valid values are
+"none", "2011", or "2017".
+This value may be changed dynamically using the
+POWER_PROFILE_SETTINGS_NP management message.
+The default is "none".
+
 .TP
 .B ptp_dst_mac
 The MAC address to which PTP messages should be sent.
diff --git a/tlv.h b/tlv.h
index 3d838b7..b973295 100644
--- a/tlv.h
+++ b/tlv.h
@@ -324,6 +324,14 @@ struct ieee_c37_238_2017_tlv {
 	Octet         pad[2];
 } PACKED;
 
+struct ieee_c37_238_settings_np {
+	Enumeration16 version;
+	UInteger16    grandmasterID;
+	UInteger32    grandmasterTimeInaccuracy;
+	UInteger32    networkTimeInaccuracy;
+	UInteger32    totalTimeInaccuracy;
+} PACKED;
+
 struct msg_interval_req_tlv {
 	Enumeration16 type;
 	UInteger16    length;
-- 
2.30.2
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2023-02-20 20:58:17
       | 
| Signed-off-by: Richard Cochran <ric...@gm...>
---
 clock.c      | 70 +++++++++++++++++++++++++++++++++++++++++++++++++---
 pmc.c        | 17 +++++++++++++
 pmc_common.c | 27 +++++++++++++++++++-
 tlv.c        | 21 ++++++++++++++++
 util.h       |  7 ++++++
 5 files changed, 137 insertions(+), 5 deletions(-)
diff --git a/clock.c b/clock.c
index 2d5b3eb..cc85081 100644
--- a/clock.c
+++ b/clock.c
@@ -157,6 +157,32 @@ static int clock_resize_pollfd(struct clock *c, int new_nports);
 static void clock_remove_port(struct clock *c, struct port *p);
 static void clock_stats_display(struct clock_stats *s);
 
+uint8_t clock_alttime_offset_get_key(struct ptp_message *req)
+{
+	struct management_tlv_datum *mtd;
+	struct management_tlv *mgt =
+		(struct management_tlv *) req->management.suffix;
+
+	/*
+	 * The data field of incoming management request messages is
+	 * normally ignored.  Indeed it can even be empty.  However
+	 * the ALTERNATE_TIME_OFFSET requests are exceptional because
+	 * the key field selects one of the configured time zones.
+	 *
+	 * Provide the first time zone for an empty GET, and validate
+	 * the length of the request when non-empty.
+	 */
+	if (mgt->length == sizeof(mgt->id)) {
+		return 0;
+	}
+	if (mgt->length < sizeof(mgt->id) + sizeof(*mtd)) {
+		return MAX_TIME_ZONES;
+	}
+	mtd = (struct management_tlv_datum *) mgt->data;
+
+	return mtd->val;
+}
+
 static void remove_subscriber(struct clock_subscriber *s)
 {
 	LIST_REMOVE(s, list);
@@ -354,6 +380,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 					  struct ptp_message *req,
 					  struct ptp_message *rsp, int id)
 {
+	struct alternate_time_offset_properties *atop;
 	struct grandmaster_settings_np *gsn;
 	struct management_tlv_datum *mtd;
 	struct subscribe_events_np *sen;
@@ -363,6 +390,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 	struct PTPText *text;
 	uint16_t duration;
 	int datalen = 0;
+	uint8_t key;
 
 	extra = tlv_extra_alloc();
 	if (!extra) {
@@ -433,6 +461,24 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 		mtd->val = c->tds.flags & PTP_TIMESCALE;
 		datalen = sizeof(*mtd);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		key = clock_alttime_offset_get_key(req);
+		if (key >= MAX_TIME_ZONES) {
+			break;
+		}
+		atop = (struct alternate_time_offset_properties *) tlv->data;
+		atop->keyField = key;
+		/* Message alignment broken by design. */
+		memcpy(&atop->currentOffset, &c->tz[key].current_offset,
+		       sizeof(atop->currentOffset));
+		memcpy(&atop->jumpSeconds, &c->tz[key].jump_seconds,
+		       sizeof(atop->jumpSeconds));
+		memcpy(&atop->timeOfNextJump.seconds_lsb, &c->tz[key].next_jump_lsb,
+		       sizeof(atop->timeOfNextJump.seconds_lsb));
+		memcpy(&atop->timeOfNextJump.seconds_msb, &c->tz[key].next_jump_msb,
+		       sizeof(atop->timeOfNextJump.seconds_msb));
+		datalen = sizeof(*atop);
+		break;
 	case MID_TIME_STATUS_NP:
 		tsn = (struct time_status_np *) tlv->data;
 		tsn->master_offset = tmv_to_nanoseconds(c->master_offset);
@@ -511,11 +557,12 @@ static int clock_management_get_response(struct clock *c, struct port *p,
 static int clock_management_set(struct clock *c, struct port *p,
 				int id, struct ptp_message *req, int *changed)
 {
-	int respond = 0;
-	struct management_tlv *tlv;
-	struct management_tlv_datum *mtd;
+	struct alternate_time_offset_properties *atop;
 	struct grandmaster_settings_np *gsn;
+	struct management_tlv_datum *mtd;
 	struct subscribe_events_np *sen;
+	struct management_tlv *tlv;
+	int key, respond = 0;
 
 	tlv = (struct management_tlv *) req->management.suffix;
 
@@ -532,6 +579,22 @@ static int clock_management_set(struct clock *c, struct port *p,
 		*changed = 1;
 		respond = 1;
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		atop = (struct alternate_time_offset_properties *) tlv->data;
+		key = atop->keyField;
+		if (key < MAX_TIME_ZONES) {
+			/* Message alignment broken by design. */
+			memcpy(&c->tz[key].current_offset, &atop->currentOffset,
+			       sizeof(c->tz[key].current_offset));
+			memcpy(&c->tz[key].jump_seconds, &atop->jumpSeconds,
+			       sizeof(c->tz[key].jump_seconds));
+			memcpy(&c->tz[key].next_jump_lsb, &atop->timeOfNextJump.seconds_lsb,
+			       sizeof(c->tz[key].next_jump_lsb));
+			memcpy(&c->tz[key].next_jump_msb, &atop->timeOfNextJump.seconds_msb,
+			       sizeof(c->tz[key].next_jump_msb));
+			respond = 1;
+		}
+		break;
 	case MID_GRANDMASTER_SETTINGS_NP:
 		gsn = (struct grandmaster_settings_np *) tlv->data;
 		c->dds.clockQuality = gsn->clockQuality;
@@ -1533,7 +1596,6 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg)
 	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
 	case MID_ALTERNATE_TIME_OFFSET_NAME:
 	case MID_ALTERNATE_TIME_OFFSET_MAX_KEY:
-	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 	case MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET:
 	case MID_PRIMARY_DOMAIN:
 	case MID_TIME_STATUS_NP:
diff --git a/pmc.c b/pmc.c
index 793a790..fd8b978 100644
--- a/pmc.c
+++ b/pmc.c
@@ -157,6 +157,7 @@ static void pmc_show_signaling(struct ptp_message *msg, FILE *fp)
 
 static void pmc_show(struct ptp_message *msg, FILE *fp)
 {
+	struct alternate_time_offset_properties *atop;
 	struct ieee_c37_238_settings_np *pwr;
 	struct unicast_master_table_np *umtn;
 	struct grandmaster_settings_np *gsn;
@@ -176,6 +177,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
 	struct defaultDS *dds;
 	struct currentDS *cds;
 	struct parentDS *pds;
+	uint64_t next_jump;
 	struct portDS *p;
 	struct TLV *tlv;
 	uint8_t *buf;
@@ -359,6 +361,21 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
 		fprintf(fp, "TIMESCALE_PROPERTIES "
 			IFMT "ptpTimescale %d", mtd->val & PTP_TIMESCALE ? 1 : 0);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		atop = (struct alternate_time_offset_properties *) mgt->data;
+		next_jump = atop->timeOfNextJump.seconds_msb;
+		next_jump <<= 32;
+		next_jump |= atop->timeOfNextJump.seconds_lsb;
+		fprintf(fp, "ALTERNATE_TIME_OFFSET_PROPERTIES "
+			IFMT "keyField       %hhu"
+			IFMT "currentOffset  %d"
+			IFMT "jumpSeconds    %d"
+			IFMT "timeOfNextJump %" PRIu64,
+			atop->keyField,
+			align32(&atop->currentOffset),
+			align32(&atop->jumpSeconds),
+			next_jump);
+		break;
 	case MID_MASTER_ONLY:
 		mtd = (struct management_tlv_datum *) mgt->data;
 		fprintf(fp, "MASTER_ONLY "
diff --git a/pmc_common.c b/pmc_common.c
index bb7d087..7ab73aa 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -123,7 +123,7 @@ struct management_id idtab[] = {
 	{ "ALTERNATE_TIME_OFFSET_ENABLE", MID_ALTERNATE_TIME_OFFSET_ENABLE, not_supported },
 	{ "ALTERNATE_TIME_OFFSET_NAME", MID_ALTERNATE_TIME_OFFSET_NAME, not_supported },
 	{ "ALTERNATE_TIME_OFFSET_MAX_KEY", MID_ALTERNATE_TIME_OFFSET_MAX_KEY, not_supported },
-	{ "ALTERNATE_TIME_OFFSET_PROPERTIES", MID_ALTERNATE_TIME_OFFSET_PROPERTIES, not_supported },
+	{ "ALTERNATE_TIME_OFFSET_PROPERTIES", MID_ALTERNATE_TIME_OFFSET_PROPERTIES, do_set_action },
 	{ "MASTER_ONLY", MID_MASTER_ONLY, do_get_action },
 	{ "TRANSPARENT_CLOCK_DEFAULT_DATA_SET", MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET, not_supported },
 	{ "PRIMARY_DOMAIN", MID_PRIMARY_DOMAIN, not_supported },
@@ -170,6 +170,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 {
 	int cnt, code = idtab[index].code, freq_traceable, leap_59, leap_61,
 		ptp_timescale, time_traceable, utc_off_valid;
+	struct alternate_time_offset_properties atop;
 	struct ieee_c37_238_settings_np pwr;
 	struct grandmaster_settings_np gsn;
 	struct management_tlv_datum mtd;
@@ -177,6 +178,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 	struct port_ds_np pnp;
 	char onoff_port_state[4] = "off";
 	char onoff_time_status[4] = "off";
+	uint64_t jump;
 
 	mtd.reserved = 0;
 
@@ -205,6 +207,26 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 		}
 		pmc_send_set_action(pmc, code, &mtd, sizeof(mtd));
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		memset(&atop, 0, sizeof(atop));
+		cnt = sscanf(str, " %*s %*s "
+			     "keyField       %hhu "
+			     "currentOffset  %d "
+			     "jumpSeconds    %d "
+			     "timeOfNextJump %" SCNu64,
+			     &atop.keyField,
+			     &atop.currentOffset,
+			     &atop.jumpSeconds,
+			     &jump);
+		if (cnt != 4) {
+			fprintf(stderr, "%s SET needs 4 values\n",
+				idtab[index].name);
+			break;
+		}
+		atop.timeOfNextJump.seconds_lsb = jump & 0xffffffff;
+		atop.timeOfNextJump.seconds_msb = jump >> 32;
+		pmc_send_set_action(pmc, code, &atop, sizeof(atop));
+		break;
 	case MID_GRANDMASTER_SETTINGS_NP:
 		cnt = sscanf(str, " %*s %*s "
 			     "clockClass              %hhu "
@@ -575,6 +597,9 @@ static int pmc_tlv_datalen(struct pmc *pmc, int id)
 	case MID_TIME_STATUS_NP:
 		len += sizeof(struct time_status_np);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		len += sizeof(struct alternate_time_offset_properties);
+		break;
 	case MID_GRANDMASTER_SETTINGS_NP:
 		len += sizeof(struct grandmaster_settings_np);
 		break;
diff --git a/tlv.c b/tlv.c
index 81982c6..c9c97ba 100644
--- a/tlv.c
+++ b/tlv.c
@@ -168,6 +168,7 @@ static void alttime_offset_pre_send(struct tlv_extra *extra)
 static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 			 struct tlv_extra *extra)
 {
+	struct alternate_time_offset_properties *atop;
 	struct ieee_c37_238_settings_np *pwr;
 	struct unicast_master_table_np *umtn;
 	struct grandmaster_settings_np *gsn;
@@ -338,6 +339,17 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 		p->portIdentity.portNumber = ntohs(p->portIdentity.portNumber);
 		p->peerMeanPathDelay = net2host64(p->peerMeanPathDelay);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		atop = (struct alternate_time_offset_properties *) m->data;
+		if (data_len != sizeof(*atop)) {
+			goto bad_length;
+		}
+		/* Message alignment broken by design. */
+		net2host32_unaligned(&atop->currentOffset);
+		net2host32_unaligned(&atop->jumpSeconds);
+		flip16(&atop->timeOfNextJump.seconds_msb);
+		net2host32_unaligned(&atop->timeOfNextJump.seconds_lsb);
+		break;
 	case MID_TIME_STATUS_NP:
 		if (data_len != sizeof(struct time_status_np))
 			goto bad_length;
@@ -487,6 +499,7 @@ bad_length:
 
 static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)
 {
+	struct alternate_time_offset_properties *atop;
 	struct ieee_c37_238_settings_np *pwr;
 	struct unicast_master_table_np *umtn;
 	struct grandmaster_settings_np *gsn;
@@ -549,6 +562,14 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)
 		p->portIdentity.portNumber = htons(p->portIdentity.portNumber);
 		p->peerMeanPathDelay = host2net64(p->peerMeanPathDelay);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		atop = (struct alternate_time_offset_properties *) m->data;
+		/* Message alignment broken by design. */
+		host2net32_unaligned(&atop->currentOffset);
+		host2net32_unaligned(&atop->jumpSeconds);
+		flip16(&atop->timeOfNextJump.seconds_msb);
+		host2net32_unaligned(&atop->timeOfNextJump.seconds_lsb);
+		break;
 	case MID_TIME_STATUS_NP:
 		tsn = (struct time_status_np *) m->data;
 		tsn->master_offset = host2net64(tsn->master_offset);
diff --git a/util.h b/util.h
index 542f3b5..2bbde71 100644
--- a/util.h
+++ b/util.h
@@ -66,6 +66,13 @@ static inline uint16_t align16(void *p)
 	return v;
 }
 
+static inline uint32_t align32(void *p)
+{
+	uint32_t v;
+	memcpy(&v, p, sizeof(v));
+	return v;
+}
+
 char *bin2str_impl(Octet *data, int len, char *buf, int buf_len);
 
 /**
-- 
2.30.2
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2023-02-20 20:58:18
       | 
| Signed-off-by: Richard Cochran <ric...@gm...>
---
 clock.c | 19 ++++++++++++++++---
 tz.h    | 13 +++++++++++++
 2 files changed, 29 insertions(+), 3 deletions(-)
 create mode 100644 tz.h
diff --git a/clock.c b/clock.c
index 1e51dd8..2d5b3eb 100644
--- a/clock.c
+++ b/clock.c
@@ -42,6 +42,7 @@
 #include "rtnl.h"
 #include "tlv.h"
 #include "tsproc.h"
+#include "tz.h"
 #include "uds.h"
 #include "util.h"
 
@@ -79,6 +80,15 @@ struct clock_subscriber {
 	time_t expiration;
 };
 
+struct time_zone {
+	bool enabled;
+	int32_t current_offset;
+	int32_t jump_seconds;
+	uint16_t next_jump_msb;
+	uint32_t next_jump_lsb;
+	struct static_ptp_text display_name;
+};
+
 struct clock {
 	enum clock_type type;
 	struct config *config;
@@ -137,6 +147,7 @@ struct clock {
 	struct monitor *slave_event_monitor;
 	int step_window_counter;
 	int step_window;
+	struct time_zone tz[MAX_TIME_ZONES];
 };
 
 struct clock the_clock;
@@ -896,19 +907,17 @@ int clock_required_modes(struct clock *c)
 struct clock *clock_create(enum clock_type type, struct config *config,
 			   const char *phc_device)
 {
+	int conf_phc_index, i, max_adj = 0, phc_index, required_modes = 0, sfl, sw_ts;
 	enum servo_type servo = config_get_int(config, NULL, "clock_servo");
 	char ts_label[IF_NAMESIZE], phc[32], *tmp;
 	enum timestamp_type timestamping;
-	int phc_index, conf_phc_index, required_modes = 0;
 	struct clock *c = &the_clock;
-	int max_adj = 0, sw_ts;
 	const char *uds_ifname;
 	double fadj = 0.0;
 	struct port *p;
 	unsigned char oui[OUI_LEN];
 	struct interface *iface;
 	struct timespec ts;
-	int sfl;
 
 	clock_gettime(CLOCK_REALTIME, &ts);
 	srandom(ts.tv_sec ^ ts.tv_nsec);
@@ -1205,6 +1214,10 @@ struct clock *clock_create(enum clock_type type, struct config *config,
 	c->dad.pds.observedParentClockPhaseChangeRate    = 0x7fffffff;
 	c->dad.ptl = c->ptl;
 
+	for (i = 0; i < MAX_TIME_ZONES; i++) {
+		c->tz[i].display_name.max_symbols = MAX_TZ_DISPLAY_NAME;
+	}
+
 	clock_sync_interval(c, 0);
 
 	LIST_INIT(&c->subscribers);
diff --git a/tz.h b/tz.h
new file mode 100644
index 0000000..14f0dbb
--- /dev/null
+++ b/tz.h
@@ -0,0 +1,13 @@
+/**
+ * @file tz.h
+ * @brief Implements time zone constants.
+ * @note Copyright (C) 2021 Richard Cochran <ric...@gm...>
+ * @note SPDX-License-Identifier: GPL-2.0+
+ */
+#ifndef HAVE_TZ_H
+#define HAVE_TZ_H
+
+#define MAX_TZ_DISPLAY_NAME	10
+#define MAX_TIME_ZONES		4
+
+#endif
-- 
2.30.2
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2023-02-20 20:58:18
       | 
| Signed-off-by: Richard Cochran <ric...@gm...>
---
 clock.c      | 21 +++++++++++++++++++-
 pmc.c        |  9 +++++++++
 pmc_common.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 tlv.c        | 11 +++++++++++
 tlv.h        |  5 +++++
 5 files changed, 100 insertions(+), 2 deletions(-)
diff --git a/clock.c b/clock.c
index cc85081..67c0044 100644
--- a/clock.c
+++ b/clock.c
@@ -381,6 +381,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 					  struct ptp_message *rsp, int id)
 {
 	struct alternate_time_offset_properties *atop;
+	struct alternate_time_offset_name *aton;
 	struct grandmaster_settings_np *gsn;
 	struct management_tlv_datum *mtd;
 	struct subscribe_events_np *sen;
@@ -461,6 +462,16 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 		mtd->val = c->tds.flags & PTP_TIMESCALE;
 		datalen = sizeof(*mtd);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_NAME:
+		key = clock_alttime_offset_get_key(req);
+		if (key >= MAX_TIME_ZONES) {
+			break;
+		}
+		aton = (struct alternate_time_offset_name *) tlv->data;
+		aton->keyField = key;
+		ptp_text_copy(&aton->displayName, &c->tz[key].display_name);
+		datalen = sizeof(*aton) + aton->displayName.length;
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		key = clock_alttime_offset_get_key(req);
 		if (key >= MAX_TIME_ZONES) {
@@ -558,6 +569,7 @@ static int clock_management_set(struct clock *c, struct port *p,
 				int id, struct ptp_message *req, int *changed)
 {
 	struct alternate_time_offset_properties *atop;
+	struct alternate_time_offset_name *aton;
 	struct grandmaster_settings_np *gsn;
 	struct management_tlv_datum *mtd;
 	struct subscribe_events_np *sen;
@@ -579,6 +591,14 @@ static int clock_management_set(struct clock *c, struct port *p,
 		*changed = 1;
 		respond = 1;
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_NAME:
+		aton = (struct alternate_time_offset_name *) tlv->data;
+		key = aton->keyField;
+		if (key < MAX_TIME_ZONES &&
+		    !static_ptp_text_copy(&c->tz[key].display_name, &aton->displayName)) {
+			respond = 1;
+		}
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		atop = (struct alternate_time_offset_properties *) tlv->data;
 		key = atop->keyField;
@@ -1594,7 +1614,6 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg)
 	case MID_ACCEPTABLE_MASTER_TABLE:
 	case MID_ACCEPTABLE_MASTER_MAX_TABLE_SIZE:
 	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
-	case MID_ALTERNATE_TIME_OFFSET_NAME:
 	case MID_ALTERNATE_TIME_OFFSET_MAX_KEY:
 	case MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET:
 	case MID_PRIMARY_DOMAIN:
diff --git a/pmc.c b/pmc.c
index fd8b978..35dddba 100644
--- a/pmc.c
+++ b/pmc.c
@@ -158,6 +158,7 @@ static void pmc_show_signaling(struct ptp_message *msg, FILE *fp)
 static void pmc_show(struct ptp_message *msg, FILE *fp)
 {
 	struct alternate_time_offset_properties *atop;
+	struct alternate_time_offset_name *aton;
 	struct ieee_c37_238_settings_np *pwr;
 	struct unicast_master_table_np *umtn;
 	struct grandmaster_settings_np *gsn;
@@ -361,6 +362,14 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
 		fprintf(fp, "TIMESCALE_PROPERTIES "
 			IFMT "ptpTimescale %d", mtd->val & PTP_TIMESCALE ? 1 : 0);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_NAME:
+		aton = (struct alternate_time_offset_name *) mgt->data;
+		fprintf(fp, "ALTERNATE_TIME_OFFSET_NAME "
+			IFMT "keyField       %hhu"
+			IFMT "displayName    %s",
+			aton->keyField,
+			text2str(&aton->displayName));
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		atop = (struct alternate_time_offset_properties *) mgt->data;
 		next_jump = atop->timeOfNextJump.seconds_msb;
diff --git a/pmc_common.c b/pmc_common.c
index 7ab73aa..20f7b22 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -79,6 +79,7 @@ static void do_get_action(struct pmc *pmc, int action, int index, char *str);
 static void do_set_action(struct pmc *pmc, int action, int index, char *str);
 static void not_supported(struct pmc *pmc, int action, int index, char *str);
 static void null_management(struct pmc *pmc, int action, int index, char *str);
+static int send_set_aton(struct pmc *pmc, int id, uint8_t key, const char *name);
 
 static const char *action_string[] = {
 	"GET",
@@ -121,7 +122,7 @@ struct management_id idtab[] = {
 	{ "ACCEPTABLE_MASTER_TABLE", MID_ACCEPTABLE_MASTER_TABLE, not_supported },
 	{ "ACCEPTABLE_MASTER_MAX_TABLE_SIZE", MID_ACCEPTABLE_MASTER_MAX_TABLE_SIZE, not_supported },
 	{ "ALTERNATE_TIME_OFFSET_ENABLE", MID_ALTERNATE_TIME_OFFSET_ENABLE, not_supported },
-	{ "ALTERNATE_TIME_OFFSET_NAME", MID_ALTERNATE_TIME_OFFSET_NAME, not_supported },
+	{ "ALTERNATE_TIME_OFFSET_NAME", MID_ALTERNATE_TIME_OFFSET_NAME, do_set_action },
 	{ "ALTERNATE_TIME_OFFSET_MAX_KEY", MID_ALTERNATE_TIME_OFFSET_MAX_KEY, not_supported },
 	{ "ALTERNATE_TIME_OFFSET_PROPERTIES", MID_ALTERNATE_TIME_OFFSET_PROPERTIES, do_set_action },
 	{ "MASTER_ONLY", MID_MASTER_ONLY, do_get_action },
@@ -178,7 +179,9 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 	struct port_ds_np pnp;
 	char onoff_port_state[4] = "off";
 	char onoff_time_status[4] = "off";
+	char display_name[11] = {0};
 	uint64_t jump;
+	uint8_t key;
 
 	mtd.reserved = 0;
 
@@ -207,6 +210,19 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 		}
 		pmc_send_set_action(pmc, code, &mtd, sizeof(mtd));
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_NAME:
+		cnt = sscanf(str, " %*s %*s "
+			     "keyField       %hhu "
+			     "displayName    %10s ",
+			     &key,
+			     display_name);
+		if (cnt != 2) {
+			fprintf(stderr, "%s SET needs 2 values\n",
+				idtab[index].name);
+			break;
+		}
+		send_set_aton(pmc, code, key, display_name);
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		memset(&atop, 0, sizeof(atop));
 		cnt = sscanf(str, " %*s %*s "
@@ -597,6 +613,9 @@ static int pmc_tlv_datalen(struct pmc *pmc, int id)
 	case MID_TIME_STATUS_NP:
 		len += sizeof(struct time_status_np);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_NAME:
+		len += sizeof(struct alternate_time_offset_name);
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		len += sizeof(struct alternate_time_offset_properties);
 		break;
@@ -728,6 +747,41 @@ int pmc_send_set_action(struct pmc *pmc, int id, void *data, int datasize)
 	return 0;
 }
 
+static int send_set_aton(struct pmc *pmc, int id, uint8_t key, const char *name)
+{
+	struct alternate_time_offset_name *aton;
+	struct management_tlv *mgt;
+	struct ptp_message *msg;
+	struct tlv_extra *extra;
+	int datasize;
+
+	datasize = sizeof(*aton) + strlen(name);
+	if (datasize % 2) {
+		datasize++;
+	}
+	msg = pmc_message(pmc, SET);
+	if (!msg) {
+		return -1;
+	}
+	extra = msg_tlv_append(msg, sizeof(*mgt) + datasize);
+	if (!extra) {
+		msg_put(msg);
+		return -ENOMEM;
+	}
+	mgt = (struct management_tlv *) extra->tlv;
+	mgt->type = TLV_MANAGEMENT;
+	mgt->length = 2 + datasize;
+	mgt->id = id;
+	aton = (struct alternate_time_offset_name *) mgt->data;
+	aton->keyField = key;
+	ptp_text_set(&aton->displayName, name);
+
+	pmc_send(pmc, msg);
+	msg_put(msg);
+
+	return 0;
+}
+
 struct ptp_message *pmc_recv(struct pmc *pmc)
 {
 	struct ptp_message *msg;
diff --git a/tlv.c b/tlv.c
index c9c97ba..1d08560 100644
--- a/tlv.c
+++ b/tlv.c
@@ -169,6 +169,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 			 struct tlv_extra *extra)
 {
 	struct alternate_time_offset_properties *atop;
+	struct alternate_time_offset_name *aton;
 	struct ieee_c37_238_settings_np *pwr;
 	struct unicast_master_table_np *umtn;
 	struct grandmaster_settings_np *gsn;
@@ -339,6 +340,14 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 		p->portIdentity.portNumber = ntohs(p->portIdentity.portNumber);
 		p->peerMeanPathDelay = net2host64(p->peerMeanPathDelay);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_NAME:
+		aton = (struct alternate_time_offset_name *) m->data;
+		if (data_len < sizeof(*aton)) {
+			goto bad_length;
+		}
+		extra_len = sizeof(*aton);
+		extra_len += aton->displayName.length;
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		atop = (struct alternate_time_offset_properties *) m->data;
 		if (data_len != sizeof(*atop)) {
@@ -562,6 +571,8 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)
 		p->portIdentity.portNumber = htons(p->portIdentity.portNumber);
 		p->peerMeanPathDelay = host2net64(p->peerMeanPathDelay);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_NAME:
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		atop = (struct alternate_time_offset_properties *) m->data;
 		/* Message alignment broken by design. */
diff --git a/tlv.h b/tlv.h
index 15df2bb..8b51ffd 100644
--- a/tlv.h
+++ b/tlv.h
@@ -189,6 +189,11 @@ struct alternate_time_offset_indicator_tlv {
 	struct PTPText  displayName;
 } PACKED;
 
+struct alternate_time_offset_name {
+	UInteger8       keyField;
+	struct PTPText  displayName;
+} PACKED;
+
 struct alternate_time_offset_properties {
 	UInteger8       keyField;
 	/* Message alignment broken by design. */
-- 
2.30.2
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2023-02-20 20:58:16
       | 
| Signed-off-by: Richard Cochran <ric...@gm...>
---
 pmc.c        | 15 +++++++++++++++
 pmc_common.c | 37 +++++++++++++++++++++++++++++++++++++
 port.c       | 20 +++++++++++++++++++-
 tlv.c        | 20 ++++++++++++++++++++
 tlv.h        |  1 +
 5 files changed, 92 insertions(+), 1 deletion(-)
diff --git a/pmc.c b/pmc.c
index e218ca4..793a790 100644
--- a/pmc.c
+++ b/pmc.c
@@ -157,6 +157,7 @@ static void pmc_show_signaling(struct ptp_message *msg, FILE *fp)
 
 static void pmc_show(struct ptp_message *msg, FILE *fp)
 {
+	struct ieee_c37_238_settings_np *pwr;
 	struct unicast_master_table_np *umtn;
 	struct grandmaster_settings_np *gsn;
 	struct port_service_stats_np *pssp;
@@ -572,6 +573,20 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
 			phn->phc_index,
 			phn->flags);
 		break;
+	case MID_POWER_PROFILE_SETTINGS_NP:
+		pwr = (struct ieee_c37_238_settings_np *) mgt->data;
+		fprintf(fp, "POWER_PROFILE_SETTINGS_NP "
+			IFMT "version                   %hu"
+			IFMT "grandmasterID             0x%04hx"
+			IFMT "grandmasterTimeInaccuracy %u"
+			IFMT "networkTimeInaccuracy     %u"
+			IFMT "totalTimeInaccuracy       %u",
+			pwr->version,
+			pwr->grandmasterID,
+			pwr->grandmasterTimeInaccuracy,
+			pwr->networkTimeInaccuracy,
+			pwr->totalTimeInaccuracy);
+		break;
 	case MID_LOG_ANNOUNCE_INTERVAL:
 		mtd = (struct management_tlv_datum *) mgt->data;
 		fprintf(fp, "LOG_ANNOUNCE_INTERVAL "
diff --git a/pmc_common.c b/pmc_common.c
index 1dd89f6..bb7d087 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -28,6 +28,7 @@
 #include "tlv.h"
 #include "transport.h"
 #include "pmc_common.h"
+#include "power_profile.h"
 
 #define BAD_ACTION   -1
 #define BAD_ID       -1
@@ -154,6 +155,7 @@ struct management_id idtab[] = {
 	{ "PORT_SERVICE_STATS_NP", MID_PORT_SERVICE_STATS_NP, do_get_action },
 	{ "UNICAST_MASTER_TABLE_NP", MID_UNICAST_MASTER_TABLE_NP, do_get_action },
 	{ "PORT_HWCLOCK_NP", MID_PORT_HWCLOCK_NP, do_get_action },
+	{ "POWER_PROFILE_SETTINGS_NP", MID_POWER_PROFILE_SETTINGS_NP, do_set_action },
 };
 
 static void do_get_action(struct pmc *pmc, int action, int index, char *str)
@@ -168,6 +170,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 {
 	int cnt, code = idtab[index].code, freq_traceable, leap_59, leap_61,
 		ptp_timescale, time_traceable, utc_off_valid;
+	struct ieee_c37_238_settings_np pwr;
 	struct grandmaster_settings_np gsn;
 	struct management_tlv_datum mtd;
 	struct subscribe_events_np sen;
@@ -302,6 +305,37 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 		}
 		pmc_send_set_action(pmc, code, &pnp, sizeof(pnp));
 		break;
+	case MID_POWER_PROFILE_SETTINGS_NP:
+		cnt = sscanf(str, " %*s %*s "
+			     "version                   %hu "
+			     "grandmasterID             %hx "
+			     "grandmasterTimeInaccuracy %u "
+			     "networkTimeInaccuracy     %u "
+			     "totalTimeInaccuracy       %u ",
+			     &pwr.version,
+			     &pwr.grandmasterID,
+			     &pwr.grandmasterTimeInaccuracy,
+			     &pwr.networkTimeInaccuracy,
+			     &pwr.totalTimeInaccuracy);
+		if (cnt != 5) {
+			fprintf(stderr, "%s SET needs 5 values\n",
+				idtab[index].name);
+			break;
+		}
+		switch (pwr.version) {
+		case IEEE_C37_238_VERSION_NONE:
+		case IEEE_C37_238_VERSION_2011:
+		case IEEE_C37_238_VERSION_2017:
+			pmc_send_set_action(pmc, code, &pwr, sizeof(pwr));
+			break;
+		default:
+			fprintf(stderr, "\nusage: set PROFILE_SETTINGS_NP version "
+				"%hu (none), %hu (2011), or %hu (2017)\n\n",
+				IEEE_C37_238_VERSION_NONE,
+				IEEE_C37_238_VERSION_2011,
+				IEEE_C37_238_VERSION_2017);
+		}
+		break;
 	}
 }
 
@@ -573,6 +607,9 @@ static int pmc_tlv_datalen(struct pmc *pmc, int id)
 	case MID_PORT_HWCLOCK_NP:
 		len += sizeof(struct port_hwclock_np);
 		break;
+	case MID_POWER_PROFILE_SETTINGS_NP:
+		len += sizeof(struct ieee_c37_238_settings_np);
+		break;
 	case MID_LOG_ANNOUNCE_INTERVAL:
 	case MID_ANNOUNCE_RECEIPT_TIMEOUT:
 	case MID_LOG_SYNC_INTERVAL:
diff --git a/port.c b/port.c
index 23697a5..f2aabfb 100644
--- a/port.c
+++ b/port.c
@@ -877,6 +877,7 @@ static const Octet profile_id_8275_2[] = {0x00, 0x19, 0xA7, 0x02, 0x01, 0x02};
 static int port_management_fill_response(struct port *target,
 					 struct ptp_message *rsp, int id)
 {
+	struct ieee_c37_238_settings_np *pwr;
 	struct unicast_master_table_np *umtn;
 	struct unicast_master_address *ucma;
 	struct port_service_stats_np *pssn;
@@ -1123,6 +1124,11 @@ static int port_management_fill_response(struct port *target,
 			PORT_HWCLOCK_VCLOCK : 0;
 		datalen = sizeof(*phn);
 		break;
+	case MID_POWER_PROFILE_SETTINGS_NP:
+		pwr = (struct ieee_c37_238_settings_np *)tlv->data;
+		memcpy(pwr, &target->pwr, sizeof(*pwr));
+		datalen = sizeof(*pwr);
+		break;
 	default:
 		/* The caller should *not* respond to this message. */
 		tlv_extra_recycle(extra);
@@ -1164,9 +1170,10 @@ static int port_management_set(struct port *target,
 			       struct port *ingress, int id,
 			       struct ptp_message *req)
 {
-	int respond = 0;
+	struct ieee_c37_238_settings_np *pwr;
 	struct management_tlv *tlv;
 	struct port_ds_np *pdsnp;
+	int respond = 0;
 
 	tlv = (struct management_tlv *) req->management.suffix;
 
@@ -1176,6 +1183,17 @@ static int port_management_set(struct port *target,
 		target->neighborPropDelayThresh = pdsnp->neighborPropDelayThresh;
 		respond = 1;
 		break;
+	case MID_POWER_PROFILE_SETTINGS_NP:
+		pwr = (struct ieee_c37_238_settings_np *) tlv->data;
+		switch (pwr->version) {
+		case IEEE_C37_238_VERSION_NONE:
+		case IEEE_C37_238_VERSION_2011:
+		case IEEE_C37_238_VERSION_2017:
+			target->pwr = *pwr;
+			respond = 1;
+			break;
+		}
+		break;
 	}
 	if (respond && !port_management_get_response(target, ingress, id, req))
 		pr_err("%s: failed to send management set response", target->log_name);
diff --git a/tlv.c b/tlv.c
index fb807e8..66d797a 100644
--- a/tlv.c
+++ b/tlv.c
@@ -115,6 +115,7 @@ static bool tlv_array_invalid(struct TLV *tlv, size_t base_size, size_t item_siz
 static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 			 struct tlv_extra *extra)
 {
+	struct ieee_c37_238_settings_np *pwr;
 	struct unicast_master_table_np *umtn;
 	struct grandmaster_settings_np *gsn;
 	struct port_service_stats_np *pssn;
@@ -400,6 +401,16 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 		phn->phc_index = ntohl(phn->phc_index);
 		extra_len = sizeof(struct port_hwclock_np);
 		break;
+	case MID_POWER_PROFILE_SETTINGS_NP:
+		if (data_len < sizeof(struct ieee_c37_238_settings_np))
+			goto bad_length;
+		pwr = (struct ieee_c37_238_settings_np *)m->data;
+		NTOHS(pwr->version);
+		NTOHS(pwr->grandmasterID);
+		NTOHL(pwr->grandmasterTimeInaccuracy);
+		NTOHL(pwr->networkTimeInaccuracy);
+		NTOHL(pwr->totalTimeInaccuracy);
+		break;
 	case MID_SAVE_IN_NON_VOLATILE_STORAGE:
 	case MID_RESET_NON_VOLATILE_STORAGE:
 	case MID_INITIALIZE:
@@ -423,6 +434,7 @@ bad_length:
 
 static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)
 {
+	struct ieee_c37_238_settings_np *pwr;
 	struct unicast_master_table_np *umtn;
 	struct grandmaster_settings_np *gsn;
 	struct port_service_stats_np *pssn;
@@ -571,6 +583,14 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)
 		phn->portIdentity.portNumber = htons(phn->portIdentity.portNumber);
 		phn->phc_index = htonl(phn->phc_index);
 		break;
+	case MID_POWER_PROFILE_SETTINGS_NP:
+		pwr = (struct ieee_c37_238_settings_np *)m->data;
+		HTONS(pwr->version);
+		HTONS(pwr->grandmasterID);
+		HTONL(pwr->grandmasterTimeInaccuracy);
+		HTONL(pwr->networkTimeInaccuracy);
+		HTONL(pwr->totalTimeInaccuracy);
+		break;
 	}
 }
 
diff --git a/tlv.h b/tlv.h
index b973295..a08c74a 100644
--- a/tlv.h
+++ b/tlv.h
@@ -128,6 +128,7 @@ enum management_action {
 #define MID_PORT_SERVICE_STATS_NP			0xC007
 #define MID_UNICAST_MASTER_TABLE_NP			0xC008
 #define MID_PORT_HWCLOCK_NP				0xC009
+#define MID_POWER_PROFILE_SETTINGS_NP			0xC00A
 
 /* Management error ID values */
 #define MID_RESPONSE_TOO_BIG				0x0001
-- 
2.30.2
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2023-02-20 20:58:20
       | 
| If the local PTP management client enables a time zone, append the
matching TLV to outgoing Announce messages.
Signed-off-by: Richard Cochran <ric...@gm...>
---
 clock.c      | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 clock.h      |  8 ++++++
 pmc.c        |  8 ++++++
 pmc_common.c | 17 ++++++++++-
 port.c       |  3 ++
 5 files changed, 113 insertions(+), 3 deletions(-)
diff --git a/clock.c b/clock.c
index 67c0044..75d7c40 100644
--- a/clock.c
+++ b/clock.c
@@ -157,6 +157,43 @@ static int clock_resize_pollfd(struct clock *c, int new_nports);
 static void clock_remove_port(struct clock *c, struct port *p);
 static void clock_stats_display(struct clock_stats *s);
 
+static int clock_alttime_offset_append(struct clock *c, int key, struct ptp_message *m)
+{
+	struct alternate_time_offset_indicator_tlv *atoi;
+	struct tlv_extra *extra;
+	int tlv_len;
+
+	tlv_len = sizeof(*atoi) + c->tz[key].display_name.length;
+	if (tlv_len % 2) {
+		tlv_len++;
+	}
+	extra = msg_tlv_append(m, tlv_len);
+	if (!extra) {
+		return -1;
+	}
+	atoi = (struct alternate_time_offset_indicator_tlv *) extra->tlv;
+	atoi->type = TLV_ALTERNATE_TIME_OFFSET_INDICATOR;
+	atoi->length = tlv_len - sizeof(atoi->type) - sizeof(atoi->length);
+	atoi->keyField = key;
+
+	/* Message alignment broken by design. */
+	memcpy(&atoi->currentOffset, &c->tz[key].current_offset,
+	       sizeof(atoi->currentOffset));
+
+	memcpy(&atoi->jumpSeconds, &c->tz[key].jump_seconds,
+	       sizeof(atoi->jumpSeconds));
+
+	memcpy(&atoi->timeOfNextJump.seconds_lsb, &c->tz[key].next_jump_lsb,
+	       sizeof(atoi->timeOfNextJump.seconds_lsb));
+
+	memcpy(&atoi->timeOfNextJump.seconds_msb, &c->tz[key].next_jump_msb,
+	       sizeof(atoi->timeOfNextJump.seconds_msb));
+
+	ptp_text_copy(&atoi->displayName, &c->tz[key].display_name);
+
+	return 0;
+}
+
 uint8_t clock_alttime_offset_get_key(struct ptp_message *req)
 {
 	struct management_tlv_datum *mtd;
@@ -462,6 +499,16 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 		mtd->val = c->tds.flags & PTP_TIMESCALE;
 		datalen = sizeof(*mtd);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
+		key = clock_alttime_offset_get_key(req);
+		if (key >= MAX_TIME_ZONES) {
+			break;
+		}
+		mtd = (struct management_tlv_datum *) tlv->data;
+		mtd->val = key;
+		mtd->reserved = c->tz[key].enabled ? 1 : 0;
+		datalen = sizeof(*mtd);
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_NAME:
 		key = clock_alttime_offset_get_key(req);
 		if (key >= MAX_TIME_ZONES) {
@@ -574,7 +621,7 @@ static int clock_management_set(struct clock *c, struct port *p,
 	struct management_tlv_datum *mtd;
 	struct subscribe_events_np *sen;
 	struct management_tlv *tlv;
-	int key, respond = 0;
+	int k, key, respond = 0;
 
 	tlv = (struct management_tlv *) req->management.suffix;
 
@@ -591,6 +638,20 @@ static int clock_management_set(struct clock *c, struct port *p,
 		*changed = 1;
 		respond = 1;
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
+		mtd = (struct management_tlv_datum *) tlv->data;
+		key = mtd->val;
+		if (key == 0xff) {
+			for (k = 0; k < MAX_TIME_ZONES; k++) {
+				c->tz[k].enabled = mtd->reserved & 1 ? true : false;
+			}
+			respond = 1;
+		}
+		if (key < MAX_TIME_ZONES) {
+			c->tz[key].enabled = mtd->reserved & 1 ? true : false;
+			respond = 1;
+		}
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_NAME:
 		aton = (struct alternate_time_offset_name *) tlv->data;
 		key = aton->keyField;
@@ -896,6 +957,22 @@ static int forwarding(struct clock *c, struct port *p)
 
 /* public methods */
 
+int clock_append_timezones(struct clock *c, struct ptp_message *m)
+{
+	int err = 0, i;
+
+	for (i = 0; i < MAX_TIME_ZONES; i++) {
+		if (!c->tz[i].enabled) {
+			continue;
+		}
+		err = clock_alttime_offset_append(c, i, m);
+		if (err) {
+			break;
+		}
+	}
+	return err;
+}
+
 UInteger8 clock_class(struct clock *c)
 {
 	return c->dds.clockQuality.clockClass;
@@ -1613,7 +1690,6 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg)
 	case MID_GRANDMASTER_CLUSTER_TABLE:
 	case MID_ACCEPTABLE_MASTER_TABLE:
 	case MID_ACCEPTABLE_MASTER_MAX_TABLE_SIZE:
-	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
 	case MID_ALTERNATE_TIME_OFFSET_MAX_KEY:
 	case MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET:
 	case MID_PRIMARY_DOMAIN:
diff --git a/clock.h b/clock.h
index 0534f21..ce9ae91 100644
--- a/clock.h
+++ b/clock.h
@@ -43,6 +43,14 @@ enum clock_type {
 	CLOCK_TYPE_MANAGEMENT = 0x0800,
 };
 
+/**
+ * Appends the active time zone TLVs to a given message.
+ * @param c          The clock instance.
+ * @param m          The message that will receive the TLVs.
+ * @return           Zero on success, non-zero otherwise.
+ */
+int clock_append_timezones(struct clock *c, struct ptp_message *m);
+
 /**
  * Obtains a reference to the best foreign master of a clock.
  * @param c  The clock instance.
diff --git a/pmc.c b/pmc.c
index 35dddba..00e691f 100644
--- a/pmc.c
+++ b/pmc.c
@@ -362,6 +362,14 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
 		fprintf(fp, "TIMESCALE_PROPERTIES "
 			IFMT "ptpTimescale %d", mtd->val & PTP_TIMESCALE ? 1 : 0);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
+		mtd = (struct management_tlv_datum *) mgt->data;
+		fprintf(fp, "ALTERNATE_TIME_OFFSET_ENABLE "
+			IFMT "keyField       %hhu"
+			IFMT "enable         %d",
+			mtd->val,
+			mtd->reserved & 1 ? 1 : 0);
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_NAME:
 		aton = (struct alternate_time_offset_name *) mgt->data;
 		fprintf(fp, "ALTERNATE_TIME_OFFSET_NAME "
diff --git a/pmc_common.c b/pmc_common.c
index 20f7b22..f5f6ec4 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -121,7 +121,7 @@ struct management_id idtab[] = {
 	{ "GRANDMASTER_CLUSTER_TABLE", MID_GRANDMASTER_CLUSTER_TABLE, not_supported },
 	{ "ACCEPTABLE_MASTER_TABLE", MID_ACCEPTABLE_MASTER_TABLE, not_supported },
 	{ "ACCEPTABLE_MASTER_MAX_TABLE_SIZE", MID_ACCEPTABLE_MASTER_MAX_TABLE_SIZE, not_supported },
-	{ "ALTERNATE_TIME_OFFSET_ENABLE", MID_ALTERNATE_TIME_OFFSET_ENABLE, not_supported },
+	{ "ALTERNATE_TIME_OFFSET_ENABLE", MID_ALTERNATE_TIME_OFFSET_ENABLE, do_set_action },
 	{ "ALTERNATE_TIME_OFFSET_NAME", MID_ALTERNATE_TIME_OFFSET_NAME, do_set_action },
 	{ "ALTERNATE_TIME_OFFSET_MAX_KEY", MID_ALTERNATE_TIME_OFFSET_MAX_KEY, not_supported },
 	{ "ALTERNATE_TIME_OFFSET_PROPERTIES", MID_ALTERNATE_TIME_OFFSET_PROPERTIES, do_set_action },
@@ -182,6 +182,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 	char display_name[11] = {0};
 	uint64_t jump;
 	uint8_t key;
+	int enable;
 
 	mtd.reserved = 0;
 
@@ -210,6 +211,17 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 		}
 		pmc_send_set_action(pmc, code, &mtd, sizeof(mtd));
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
+		cnt = sscanf(str,  " %*s %*s keyField %hhu enable %d",
+			     &mtd.val, &enable);
+		if (cnt != 2) {
+			fprintf(stderr, "%s SET needs 2 values\n",
+				idtab[index].name);
+			break;
+		}
+		mtd.reserved = enable ? 1 : 0;
+		pmc_send_set_action(pmc, code, &mtd, sizeof(mtd));
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_NAME:
 		cnt = sscanf(str, " %*s %*s "
 			     "keyField       %hhu "
@@ -613,6 +625,9 @@ static int pmc_tlv_datalen(struct pmc *pmc, int id)
 	case MID_TIME_STATUS_NP:
 		len += sizeof(struct time_status_np);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
+		len += sizeof(struct management_tlv_datum);
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_NAME:
 		len += sizeof(struct alternate_time_offset_name);
 		break;
diff --git a/port.c b/port.c
index f2aabfb..3453716 100644
--- a/port.c
+++ b/port.c
@@ -1683,6 +1683,9 @@ int port_tx_announce(struct port *p, struct address *dst, uint16_t sequence_id)
 	if (ieee_c37_238_append(p, msg)) {
 		pr_err("%s: append power profile failed", p->log_name);
 	}
+	if (clock_append_timezones(p->clock, msg)) {
+		pr_err("%s: append time zones failed", p->log_name);
+	}
 
 	err = port_prepare_and_send(p, msg, TRANS_GENERAL);
 	if (err) {
-- 
2.30.2
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2023-02-20 20:58:20
       | 
| The function to set the alternate time offset name, a.k.a. time zone, will
be used by the time zone stand alone program.  Make the function into a
public PMC method.
Signed-off-by: Richard Cochran <ric...@gm...>
---
 pmc_common.c | 5 ++---
 pmc_common.h | 2 ++
 2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/pmc_common.c b/pmc_common.c
index f5f6ec4..a03f191 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -79,7 +79,6 @@ static void do_get_action(struct pmc *pmc, int action, int index, char *str);
 static void do_set_action(struct pmc *pmc, int action, int index, char *str);
 static void not_supported(struct pmc *pmc, int action, int index, char *str);
 static void null_management(struct pmc *pmc, int action, int index, char *str);
-static int send_set_aton(struct pmc *pmc, int id, uint8_t key, const char *name);
 
 static const char *action_string[] = {
 	"GET",
@@ -233,7 +232,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 				idtab[index].name);
 			break;
 		}
-		send_set_aton(pmc, code, key, display_name);
+		pmc_send_set_aton(pmc, code, key, display_name);
 		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		memset(&atop, 0, sizeof(atop));
@@ -762,7 +761,7 @@ int pmc_send_set_action(struct pmc *pmc, int id, void *data, int datasize)
 	return 0;
 }
 
-static int send_set_aton(struct pmc *pmc, int id, uint8_t key, const char *name)
+int pmc_send_set_aton(struct pmc *pmc, int id, uint8_t key, const char *name)
 {
 	struct alternate_time_offset_name *aton;
 	struct management_tlv *mgt;
diff --git a/pmc_common.h b/pmc_common.h
index 8bea2e0..6fb2fae 100644
--- a/pmc_common.h
+++ b/pmc_common.h
@@ -41,6 +41,8 @@ int pmc_send_get_action(struct pmc *pmc, int id);
 
 int pmc_send_set_action(struct pmc *pmc, int id, void *data, int datasize);
 
+int pmc_send_set_aton(struct pmc *pmc, int id, uint8_t key, const char *name);
+
 struct ptp_message *pmc_recv(struct pmc *pmc);
 
 int pmc_target(struct pmc *pmc, struct PortIdentity *pid);
-- 
2.30.2
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2023-02-20 20:58:22
       | 
| The ptp4l program supports up to four time zones via the
ALTERNATE_TIME_OFFSET_INDICATOR TLV.  Introduce a helper program that
leverages the local time zone database to monitor for changes in
daylight savings time and publishing them.
Signed-off-by: Richard Cochran <ric...@gm...>
---
 makefile |   7 +-
 tz2alt.8 | 126 ++++++++++++++++++
 tz2alt.c | 379 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 510 insertions(+), 2 deletions(-)
 create mode 100644 tz2alt.8
 create mode 100644 tz2alt.c
diff --git a/makefile b/makefile
index 0f8f185..3e3b8b3 100644
--- a/makefile
+++ b/makefile
@@ -22,7 +22,7 @@ CC	= $(CROSS_COMPILE)gcc
 VER     = -DVER=$(version)
 CFLAGS	= -Wall $(VER) $(incdefs) $(DEBUG) $(EXTRA_CFLAGS)
 LDLIBS	= -lm -lrt -pthread $(EXTRA_LDFLAGS)
-PRG	= ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster ts2phc
+PRG	= ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster ts2phc tz2alt
 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
@@ -35,7 +35,7 @@ OBJ	= bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \
  unicast_fsm.o unicast_service.o util.o version.o
 
 OBJECTS	= $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_agent.o \
- pmc_common.o sysoff.o timemaster.o $(TS2PHC)
+ pmc_common.o sysoff.o timemaster.o $(TS2PHC) tz2alt.o
 SRC	= $(OBJECTS:.o=.c)
 DEPEND	= $(OBJECTS:.o=.d)
 srcdir	:= $(dir $(lastword $(MAKEFILE_LIST)))
@@ -72,6 +72,9 @@ ts2phc: config.o clockadj.o hash.o interface.o msg.o phc.o pmc_agent.o \
  pmc_common.o print.o $(SERVOS) sk.o $(TS2PHC) tlv.o transport.o raw.o \
  udp.o udp6.o uds.o util.o version.o
 
+tz2alt: config.o hash.o interface.o lstab.o msg.o phc.o pmc_common.o print.o \
+ sk.o tlv.o $(TRANSP) tz2alt.o util.o version.o
+
 version.o: .version version.sh $(filter-out version.d,$(DEPEND))
 
 .version: force
diff --git a/tz2alt.8 b/tz2alt.8
new file mode 100644
index 0000000..66a6605
--- /dev/null
+++ b/tz2alt.8
@@ -0,0 +1,126 @@
+.TH TS2ALT 8 "February 2023" "linuxptp"
+.SH NAME
+tz2alt - Monitors daylight savings time changes and publishes them to PTP stack.
+
+.SH SYNOPSIS
+.B ts2alt
+[
+.B \-hmqv
+] [
+.BI \-f " conf"
+] [
+.BI \-k " key"
+] [
+.BI \-p " period"
+] [
+.BI \-w " window"
+] [
+.BI \-z " timezone"
+] [
+.I long-options
+]
+.I .\|.\|.
+
+.SH DESCRIPTION
+.B tz2alt
+leverages the local time zone database to monitor for changes in
+daylight savings time and publishes the pending changes to the PTP
+stack.
+
+.SH OPTIONS
+.TP
+.BI \-f " config"
+Read configuration from the specified file.
+No configuration file is read by default.
+.TP
+.BI \-h
+Displays the command line help summary.
+.TP
+.BI \-l " print-level"
+Sets the maximum syslog level of messages which should be printed or
+sent to the system logger. The default is 6 (LOG_INFO).
+.TP
+.B \-m
+Prints log messages to the standard output.
+.TP
+.B \-q
+Prevents sending log messages to the system logger.
+.TP
+.B \-v
+Prints the software version and exits.
+
+.SH LONG OPTIONS
+
+Each and every configuration file option (see below) may also appear
+as a "long" style command line argument.  For example, the use_syslog
+option may be set using either of these two forms.
+
+.RS
+\f(CW\-\-use_syslog 1   \-\-use_syslog=1\fP
+.RE
+
+Option values given on the command line override values in the global
+section of the configuration file.
+
+.SH CONFIGURATION FILE
+
+The configuration file is divided into sections. Each section starts with a
+line containing its name enclosed in brackets and it follows with settings.
+Each setting is placed on a separate line, it contains the name of the
+option and the value separated by whitespace characters. Empty lines and lines
+starting with # are ignored.
+
+.SH GLOBAL OPTIONS
+
+.TP
+.B domainNumber
+The domain attribute of the local clock.
+The default is 0.
+
+.TP
+.B leapfile
+The path to the current leap seconds definition file. In a Debian
+system this file is provided by the tzdata package and can be found at
+/usr/share/zoneinfo/leap-seconds.list. If a leapfile is configured it
+will be reloaded if modified. The default is an empty string, which
+causes the program to use a hard coded table that reflects the known
+leap seconds on the date of the software's release.
+
+.TP
+.B logging_level
+The maximum logging level of messages which should be printed.
+The default is 6 (LOG_INFO).
+
+.TP
+.B message_tag
+The tag which is added to all messages printed to the standard output
+or system log.  The default is an empty string (which cannot be set in
+the configuration file as the option requires an argument).
+
+.TP
+.B transportSpecific
+The transport specific field. Must be in the range 0 to 255.
+The default is 0.
+
+.TP
+.B use_syslog
+Print messages to the system log if enabled.  The default is 1 (enabled).
+
+.TP
+.B verbose
+Print messages to the standard output if enabled.  The default is 0 (disabled).
+
+.SH WARNING
+
+Be cautious when sharing the same configuration file between ptp4l,
+phc2sys, and tz2alt.  Keep in mind that values specified in the
+configuration file take precedence over the default values.  If an
+option which is common to the other programs is set in the
+configuration file, then the value will be applied to all the programs
+using the file, and this might not be what is expected.
+
+It is recommended to use separate configuration files for ptp4l,
+phc2sys, and tz2alt in order to avoid any unexpected behavior.
+
+.SH SEE ALSO
+.BR ptp4l (8)
diff --git a/tz2alt.c b/tz2alt.c
new file mode 100644
index 0000000..ed06420
--- /dev/null
+++ b/tz2alt.c
@@ -0,0 +1,379 @@
+/**
+ * @file tztool.c
+ * @note Copyright (C) 2021 Richard Cochran <ric...@gm...>
+ * @note SPDX-License-Identifier: GPL-2.0+
+ */
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "lstab.h"
+#include "pmc_common.h"
+#include "print.h"
+#include "version.h"
+#include "tz.h"
+
+#define DEFAULT_TZ	"PST8PDT"
+#define DEFAULT_PERIOD	3600
+#define DEFAULT_WINDOW	(3600 * 24 * 30 * 3)
+
+static int key_field, period = DEFAULT_PERIOD, window = DEFAULT_WINDOW;
+static char uds_local[MAX_IFNAME_SIZE + 1];
+static struct lstab *lstab;
+static struct config *cfg;
+
+struct tzinfo {
+	const char		*name;
+	char			display_name[MAX_TZ_DISPLAY_NAME + 1];
+	time_t			timestamp;
+
+	/* Following fields populated by get_offsets. */
+
+	time_t			local_utc_offset;
+	time_t			local_tai_offset;
+	int			tai_utc_offset;
+	enum lstab_result	tai_result;
+};
+
+static int get_offsets(struct tzinfo *tz);
+
+static bool offsets_equal(struct tzinfo *a, struct tzinfo *b)
+{
+	return a->local_utc_offset == b->local_utc_offset;
+}
+
+static bool find_next_discontinuity(struct tzinfo *tz, struct tzinfo *next)
+{
+	time_t i, j, n;
+	bool gt;
+
+	next->timestamp = tz->timestamp + window;
+	get_offsets(next);
+	if (offsets_equal(tz, next)) {
+		return false;
+	}
+
+	i = 0;
+	j = window;
+
+	while (1) {
+		next->timestamp = tz->timestamp + i;
+		get_offsets(next);
+		gt = offsets_equal(tz, next);
+		if (gt) {
+			n = j - i - 1;
+		} else {
+			j = i;
+			i = 0;
+			n = j - i - 1;
+		}
+		if (!n) {
+			if (gt) {
+				next->timestamp++;
+				get_offsets(next);
+			}
+			break;
+		}
+		i += (n + 1) / 2;
+	}
+
+	return true;
+}
+
+static int get_offsets(struct tzinfo *tz)
+{
+	struct tm tm = {0};
+	time_t t2;
+
+	tz->tai_result = lstab_utc2tai(lstab, tz->timestamp,
+				       &tz->tai_utc_offset);
+	if (tz->tai_result == LSTAB_UNKNOWN) {
+		pr_err("leap second table is stale");
+		return -1;
+	}
+
+	setenv("TZ", tz->name, 1);
+	tzset();
+	if (!localtime_r(&tz->timestamp, &tm)) {
+		return -1;
+	}
+
+	setenv("TZ", "UTC", 1);
+	tzset();
+	t2 = mktime(&tm);
+	tz->local_utc_offset = t2 - tz->timestamp;
+	tz->local_tai_offset = tz->local_utc_offset - tz->tai_utc_offset;
+
+	return 0;
+}
+
+static int get_unambiguous_time(struct tzinfo *tz)
+{
+	int err;
+
+	do {
+		tz->timestamp = time(NULL);
+		err = get_offsets(tz);
+	} while (tz->tai_result == LSTAB_AMBIGUOUS);
+
+	return err;
+}
+
+static void show_timezone_info(const char *label, struct tzinfo *tz)
+{
+	pr_debug("%s %s ts %ld local-utc %ld tai-utc %d local-tai %ld %s",
+		 label,
+		 tz->name,
+		 tz->timestamp,
+		 tz->local_utc_offset,
+		 tz->tai_utc_offset,
+		 tz->local_tai_offset,
+		 tz->tai_result == LSTAB_OK ? "valid" : "invalid");
+}
+
+/* Returns true if display name was truncated. */
+static bool tz_set_name(struct tzinfo *tz, const char *name)
+{
+	const char *suffix;
+	int len;
+
+	memset(tz->display_name, 0, sizeof(tz->display_name));
+	tz->name = name;
+
+	len = strlen(name);
+	if (len <= MAX_TZ_DISPLAY_NAME) {
+		strncpy(tz->display_name, name, sizeof(tz->display_name) - 1);
+		return false;
+	}
+
+	/*
+	 * The displayName field is limited to 10 characters, but
+	 * there are many valid time zone names like "Europe/Vienna".
+	 * Use the suffix if present.
+	 */
+	suffix = strchr(name, '/');
+	if (suffix) {
+		suffix++;
+		len = strlen(suffix);
+		if (len > 0 && len <= MAX_TZ_DISPLAY_NAME) {
+			strncpy(tz->display_name, suffix,
+				sizeof(tz->display_name) - 1);
+			return true;
+		}
+	}
+
+	/* No nice suffix to be found, so just truncate. */
+	strncpy(tz->display_name, name, sizeof(tz->display_name) - 1);
+
+	return true;
+}
+
+static int update_ptp_serivce(struct tzinfo *tz, struct tzinfo *next)
+{
+	struct alternate_time_offset_properties atop;
+	struct management_tlv_datum mtd;
+	uint64_t time_of_next_jump;
+	struct pmc *pmc;
+	int err;
+
+	pmc = pmc_create(cfg, TRANS_UDS, uds_local, 0,
+			 config_get_int(cfg, NULL, "domainNumber"),
+			 config_get_int(cfg, NULL, "transportSpecific") << 4, 1);
+	if (!pmc) {
+		return -1;
+	}
+	err = pmc_send_set_aton(pmc, MID_ALTERNATE_TIME_OFFSET_NAME,
+				key_field, tz->display_name);
+	if (err) {
+		return err;
+	}
+	memset(&atop, 0, sizeof(atop));
+	atop.keyField = key_field;
+	atop.currentOffset = tz->local_tai_offset;
+	if (next) {
+		atop.jumpSeconds = next->local_tai_offset - tz->local_tai_offset;
+		time_of_next_jump = (uint64_t) next->timestamp;
+		atop.timeOfNextJump.seconds_lsb = time_of_next_jump & 0xffffffff;
+		atop.timeOfNextJump.seconds_msb = time_of_next_jump >> 32;
+	}
+	err = pmc_send_set_action(pmc, MID_ALTERNATE_TIME_OFFSET_PROPERTIES,
+				  &atop, sizeof(atop));
+	if (err) {
+		return err;
+	}
+	mtd.val = key_field;
+	mtd.reserved = 1; /*enable field*/
+	err = pmc_send_set_action(pmc, MID_ALTERNATE_TIME_OFFSET_ENABLE,
+				  &mtd, sizeof(mtd));
+	if (err) {
+		return err;
+	}
+
+	pmc_destroy(pmc);
+	return 0;
+}
+
+static int do_tztool(const char *timezone)
+{
+	struct tzinfo nx, tz;
+	const char *leapfile;
+	bool pending;
+	char buf[64];
+	int err;
+
+	if (key_field > MAX_TIME_ZONES - 1) {
+		pr_err("key field %d exceeds maximum of %d", key_field,
+		       MAX_TIME_ZONES - 1);
+		return -1;
+	}
+
+	tz_set_name(&nx, timezone);
+	if (tz_set_name(&tz, timezone)) {
+		pr_info("truncating time zone display name from %s to %s",
+			tz.name, tz.display_name);
+	}
+
+	leapfile = config_get_string(cfg, NULL, "leapfile");
+	if (!leapfile) {
+		pr_err("please specify leap second table with --leapfile");
+		return -1;
+	}
+
+	while (is_running()) {
+
+		/* Read the leap seconds file again as it may have changed. */
+		lstab = lstab_create(leapfile);
+		if (!lstab) {
+			pr_err("failed to create leap second table");
+			return -1;
+		}
+
+		err = get_unambiguous_time(&tz);
+		if (err) {
+			return err;
+		}
+		show_timezone_info("current time  = ", &tz);
+
+		pending = find_next_discontinuity(&tz, &nx);
+		if (pending) {
+			setenv("TZ", nx.name, 1);
+			tzset();
+			if (ctime_r(&nx.timestamp, buf)) {
+				buf[strlen(buf) - 1] = 0;
+			}
+			show_timezone_info("discontinuity = ", &nx);
+			pr_info("next discontinuity %s %s", buf, nx.name);
+		} else {
+			pr_info("no discontinuity within %d second window", window);
+		}
+
+		lstab_destroy(lstab);
+		lstab = NULL;
+
+		err = update_ptp_serivce(&tz, pending ? &nx : NULL);
+		if (err) {
+			pr_err("failed to update PTP service");
+			return err;
+		}
+		sleep(period);
+	}
+	return 0;
+}
+
+static void usage(char *progname)
+{
+	fprintf(stderr,
+		"\nusage: %s [options]\n\n"
+		" -f [file] read configuration from 'file'\n"
+		" -h        prints this message and exits\n"
+		" -k [num]  key field for the ALTERNATE_TIME_OFFSET_INDICATOR TLV\n"
+		" -p [num]  period between updates in seconds, default %d\n"
+		" -v        prints the software version and exits\n"
+		" -w [num]  look ahead time window in seconds, default %d\n"
+		" -z zone   Time zone string, default '%s'\n"
+		"           See /usr/share/zoneinfo for valid strings\n"
+		"\n",
+		progname, DEFAULT_PERIOD, DEFAULT_WINDOW, DEFAULT_TZ);
+}
+
+int main(int argc, char *argv[])
+{
+	char *config = NULL, *progname, *timezone = DEFAULT_TZ;
+	int c, err = 0, index;
+	struct option *opts;
+
+	if (handle_term_signals()) {
+		return -1;
+	}
+	cfg = config_create();
+	if (!cfg) {
+		return -1;
+	}
+	opts = config_long_options(cfg);
+	print_set_verbose(1);
+	print_set_syslog(0);
+
+	/* Process the command line arguments. */
+	progname = strrchr(argv[0], '/');
+	progname = progname ? 1+progname : argv[0];
+	while (EOF != (c = getopt_long(argc, argv, "f:hk:p:vw:z:", opts, &index))) {
+		switch (c) {
+		case 0:
+			if (config_parse_option(cfg, opts[index].name, optarg)) {
+				config_destroy(cfg);
+				return -1;
+			}
+			break;
+		case 'f':
+			config = optarg;
+			break;
+		case 'k':
+			key_field = atoi(optarg);
+			break;
+		case 'p':
+			period = atoi(optarg);
+			break;
+		case 'v':
+			version_show(stdout);
+			config_destroy(cfg);
+			return 0;
+		case 'w':
+			window = atoi(optarg);
+			break;
+		case 'z':
+			timezone = optarg;
+			break;
+		case 'h':
+			usage(progname);
+			config_destroy(cfg);
+			return 0;
+		case '?':
+		default:
+			usage(progname);
+			config_destroy(cfg);
+			return -1;
+		}
+	}
+
+	print_set_syslog(0);
+	print_set_verbose(1);
+
+	if (config && (err = config_read(config, cfg))) {
+		goto out;
+	}
+
+	print_set_progname(progname);
+	print_set_tag(config_get_string(cfg, NULL, "message_tag"));
+	print_set_level(config_get_int(cfg, NULL, "logging_level"));
+	snprintf(uds_local, sizeof(uds_local), "/var/run/tztool.%d", getpid());
+
+	err = do_tztool(timezone);
+out:
+	config_destroy(cfg);
+	return err;
+}
-- 
2.30.2
 | 
| 
      
      
      From: Miroslav L. <mli...@re...> - 2023-02-21 09:05:01
       | 
| On Mon, Feb 20, 2023 at 12:57:48PM -0800, Richard Cochran wrote: > ChangeLog > ~~~~~~~~~ > v6: > Add new configuration options into default.cfg and man page > Make pmc SET command ready for 2106 (Miroslav) > Use SPDX tag for new file tz.h Looks good to me. Thanks, -- Miroslav Lichvar | 
| 
      
      
      From: Magnus A. <mag...@fi...> - 2023-02-23 05:34:33
       | 
| Hi, Could you please provide an example how to use linux vlan interfaces in order to live up to the required behaviour by the Power profile 2011 (receive 802.1Q tagged and untagged frames, send tagged frames - configurable VID, default 4). I tried to achieve this with linux vlan interfaces but failed, I foresee that others might have the same question if support is claimed. Clearly you have successfully tested this functionality. In general, good work and great to see power profile support being added in the official code base. BR, Magnus Armholt > -----Original Message----- > From: Miroslav Lichvar <mli...@re...> > Sent: tiistai 21. helmikuuta 2023 11.05 > To: Richard Cochran <ric...@gm...> > Cc: lin...@li... > Subject: Re: [Linuxptp-devel] [PATCH v6 00/11] Profile support for IEEE C37.238- > 2011 and IEEE C37.238-2017 > > BeSecure! This email comes from outside of ABB. Make sure you verify the > sender before clicking any links or downloading/opening attachments. > If this email looks suspicious, report it by clicking 'Report Phishing' button in > Outlook or raising a ticket on MyIS. > > > On Mon, Feb 20, 2023 at 12:57:48PM -0800, Richard Cochran wrote: > > ChangeLog > > ~~~~~~~~~ > > v6: > > Add new configuration options into default.cfg and man page Make pmc > > SET command ready for 2106 (Miroslav) Use SPDX tag for new file tz.h > > Looks good to me. > > Thanks, > > -- > Miroslav Lichvar > > > > _______________________________________________ > Linuxptp-devel mailing list > Lin...@li... > https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Flists.sour > ceforge.net%2Flists%2Flistinfo%2Flinuxptp- > devel&data=05%7C01%7Cmagnus.armholt%40fi.abb.com%7Ccff08f0bba844f40 > ed5208db13eb24b6%7C372ee9e09ce04033a64ac07073a91ecd%7C0%7C0%7C6 > 38125672876103996%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAi > LCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sd > ata=lzDukSknih0pel%2Bk384ccv3%2BI5iT82cbI%2Bkpgdnjl84%3D&reserved=0 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2021-11-09 20:12:11
       | 
| Signed-off-by: Richard Cochran <ric...@gm...>
---
 tlv.c | 36 ++++++++++++++++++++++++++++++++++++
 tlv.h | 24 ++++++++++++++++++++++++
 2 files changed, 60 insertions(+)
diff --git a/tlv.c b/tlv.c
index cc8d507..aadfe0a 100644
--- a/tlv.c
+++ b/tlv.c
@@ -35,6 +35,7 @@
 	(tlv->length < sizeof(struct type) - sizeof(struct TLV))
 
 uint8_t ieee8021_id[3] = { IEEE_802_1_COMMITTEE };
+uint8_t ieeec37_238_id[3] = { IEEE_C37_238_PROFILE };
 
 static TAILQ_HEAD(tlv_pool, tlv_extra) tlv_pool =
 	TAILQ_HEAD_INITIALIZER(tlv_pool);
@@ -558,6 +559,7 @@ static void nsm_resp_pre_send(struct tlv_extra *extra)
 
 static int org_post_recv(struct organization_tlv *org)
 {
+	struct ieee_c37_238_2017_tlv *p;
 	struct follow_up_info_tlv *f;
 
 	if (0 == memcmp(org->id, ieee8021_id, sizeof(ieee8021_id))) {
@@ -580,6 +582,24 @@ static int org_post_recv(struct organization_tlv *org)
 				goto bad_length;
 		}
 	}
+	if (0 == memcmp(org->id, ieeec37_238_id, sizeof(ieeec37_238_id))) {
+		if (org->subtype[0] || org->subtype[1]) {
+			return 0;
+		}
+		switch (org->subtype[2]) {
+		case 1:
+		case 2:
+			/* Layout of 2011 and 2017 messages is compatible. */
+			if (org->length + sizeof(struct TLV) !=
+			    sizeof(struct ieee_c37_238_2017_tlv))
+				goto bad_length;
+			p = (struct ieee_c37_238_2017_tlv *) org;
+			NTOHS(p->grandmasterID);
+			NTOHL(p->reserved1);
+			NTOHL(p->totalTimeInaccuracy);
+			break;
+		}
+	}
 	return 0;
 bad_length:
 	return -EBADMSG;
@@ -587,6 +607,7 @@ bad_length:
 
 static void org_pre_send(struct organization_tlv *org)
 {
+	struct ieee_c37_238_2017_tlv *p;
 	struct follow_up_info_tlv *f;
 
 	if (0 == memcmp(org->id, ieee8021_id, sizeof(ieee8021_id))) {
@@ -603,6 +624,21 @@ static void org_pre_send(struct organization_tlv *org)
 			break;
 		}
 	}
+	if (0 == memcmp(org->id, ieeec37_238_id, sizeof(ieeec37_238_id))) {
+		if (org->subtype[0] || org->subtype[1]) {
+			return;
+		}
+		switch (org->subtype[2]) {
+		case 1:
+		case 2:
+			/* Layout of 2011 and 2017 messages is compatible. */
+			p = (struct ieee_c37_238_2017_tlv *) org;
+			HTONS(p->grandmasterID);
+			HTONL(p->reserved1);
+			HTONL(p->totalTimeInaccuracy);
+			break;
+		}
+	}
 }
 
 static int slave_delay_timing_data_post_revc(struct tlv_extra *extra)
diff --git a/tlv.h b/tlv.h
index 97615fd..bb8c3af 100644
--- a/tlv.h
+++ b/tlv.h
@@ -207,6 +207,8 @@ struct nsm_resp_tlv_foot {
 /* Organizationally Unique Identifiers */
 #define IEEE_802_1_COMMITTEE 0x00, 0x80, 0xC2
 extern uint8_t ieee8021_id[3];
+#define IEEE_C37_238_PROFILE 0x1C, 0x12, 0x9D
+extern uint8_t ieeec37_238_id[3];
 
 struct organization_tlv {
 	Enumeration16 type;
@@ -294,6 +296,28 @@ struct follow_up_info_tlv {
 	Integer32     scaledLastGmPhaseChange;
 } PACKED;
 
+struct ieee_c37_238_2011_tlv {
+	Enumeration16 type;
+	UInteger16    length;
+	Octet         id[3];
+	Octet         subtype[3];
+	UInteger16    grandmasterID;
+	UInteger32    grandmasterTimeInaccuracy;
+	UInteger32    networkTimeInaccuracy;
+	Octet         pad[2];
+} PACKED;
+
+struct ieee_c37_238_2017_tlv {
+	Enumeration16 type;
+	UInteger16    length;
+	Octet         id[3];
+	Octet         subtype[3];
+	UInteger16    grandmasterID;
+	UInteger32    reserved1;
+	UInteger32    totalTimeInaccuracy;
+	Octet         pad[2];
+} PACKED;
+
 struct msg_interval_req_tlv {
 	Enumeration16 type;
 	UInteger16    length;
-- 
2.20.1
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2021-11-09 20:12:13
       | 
| Signed-off-by: Richard Cochran <ric...@gm...>
---
 pmc.c        | 15 +++++++++++++++
 pmc_common.c | 39 ++++++++++++++++++++++++++++++++++++++-
 port.c       | 20 +++++++++++++++++++-
 tlv.c        | 20 ++++++++++++++++++++
 tlv.h        |  1 +
 5 files changed, 93 insertions(+), 2 deletions(-)
diff --git a/pmc.c b/pmc.c
index a1ee787..28d96a8 100644
--- a/pmc.c
+++ b/pmc.c
@@ -139,6 +139,7 @@ static void pmc_show_signaling(struct ptp_message *msg, FILE *fp)
 
 static void pmc_show(struct ptp_message *msg, FILE *fp)
 {
+	struct ieee_c37_238_settings_np *pwr;
 	struct grandmaster_settings_np *gsn;
 	struct mgmt_clock_description *cd;
 	struct subscribe_events_np *sen;
@@ -495,6 +496,20 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
 			pcp->stats.txMsgType[SIGNALING],
 			pcp->stats.txMsgType[MANAGEMENT]);
 		break;
+	case MID_POWER_PROFILE_SETTINGS_NP:
+		pwr = (struct ieee_c37_238_settings_np *) mgt->data;
+		fprintf(fp, "POWER_PROFILE_SETTINGS_NP "
+			IFMT "version                   %hu"
+			IFMT "grandmasterID             0x%04hx"
+			IFMT "grandmasterTimeInaccuracy %u"
+			IFMT "networkTimeInaccuracy     %u"
+			IFMT "totalTimeInaccuracy       %u",
+			pwr->version,
+			pwr->grandmasterID,
+			pwr->grandmasterTimeInaccuracy,
+			pwr->networkTimeInaccuracy,
+			pwr->totalTimeInaccuracy);
+		break;
 	case MID_LOG_ANNOUNCE_INTERVAL:
 		mtd = (struct management_tlv_datum *) mgt->data;
 		fprintf(fp, "LOG_ANNOUNCE_INTERVAL "
diff --git a/pmc_common.c b/pmc_common.c
index 7a1dbb4..e40c3d8 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -28,6 +28,7 @@
 #include "tlv.h"
 #include "transport.h"
 #include "pmc_common.h"
+#include "power_profile.h"
 
 #define BAD_ACTION   -1
 #define BAD_ID       -1
@@ -130,8 +131,9 @@ struct management_id idtab[] = {
 	{ "DELAY_MECHANISM", MID_DELAY_MECHANISM, do_get_action },
 	{ "LOG_MIN_PDELAY_REQ_INTERVAL", MID_LOG_MIN_PDELAY_REQ_INTERVAL, do_get_action },
 	{ "PORT_DATA_SET_NP", MID_PORT_DATA_SET_NP, do_set_action },
-	{ "PORT_STATS_NP", MID_PORT_STATS_NP, do_get_action },
 	{ "PORT_PROPERTIES_NP", MID_PORT_PROPERTIES_NP, do_get_action },
+	{ "PORT_STATS_NP", MID_PORT_STATS_NP, do_get_action },
+	{ "POWER_PROFILE_SETTINGS_NP", MID_POWER_PROFILE_SETTINGS_NP, do_set_action },
 };
 
 static void do_get_action(struct pmc *pmc, int action, int index, char *str)
@@ -146,6 +148,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 {
 	int cnt, code = idtab[index].code, freq_traceable, leap_59, leap_61,
 		ptp_timescale, time_traceable, utc_off_valid;
+	struct ieee_c37_238_settings_np pwr;
 	struct grandmaster_settings_np gsn;
 	struct management_tlv_datum mtd;
 	struct subscribe_events_np sen;
@@ -278,6 +281,37 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 		}
 		pmc_send_set_action(pmc, code, &pnp, sizeof(pnp));
 		break;
+	case MID_POWER_PROFILE_SETTINGS_NP:
+		cnt = sscanf(str, " %*s %*s "
+			     "version                   %hu "
+			     "grandmasterID             %hx "
+			     "grandmasterTimeInaccuracy %u "
+			     "networkTimeInaccuracy     %u "
+			     "totalTimeInaccuracy       %u ",
+			     &pwr.version,
+			     &pwr.grandmasterID,
+			     &pwr.grandmasterTimeInaccuracy,
+			     &pwr.networkTimeInaccuracy,
+			     &pwr.totalTimeInaccuracy);
+		if (cnt != 5) {
+			fprintf(stderr, "%s SET needs 5 values\n",
+				idtab[index].name);
+			break;
+		}
+		switch (pwr.version) {
+		case IEEE_C37_238_VERSION_NONE:
+		case IEEE_C37_238_VERSION_2011:
+		case IEEE_C37_238_VERSION_2017:
+			pmc_send_set_action(pmc, code, &pwr, sizeof(pwr));
+			break;
+		default:
+			fprintf(stderr, "\nusage: set PROFILE_SETTINGS_NP version "
+				"%hu (none), %hu (2011), or %hu (2017)\n\n",
+				IEEE_C37_238_VERSION_NONE,
+				IEEE_C37_238_VERSION_2011,
+				IEEE_C37_238_VERSION_2017);
+		}
+		break;
 	}
 }
 
@@ -531,6 +565,9 @@ static int pmc_tlv_datalen(struct pmc *pmc, int id)
 	case MID_PORT_DATA_SET_NP:
 		len += sizeof(struct port_ds_np);
 		break;
+	case MID_POWER_PROFILE_SETTINGS_NP:
+		len += sizeof(struct ieee_c37_238_settings_np);
+		break;
 	case MID_LOG_ANNOUNCE_INTERVAL:
 	case MID_ANNOUNCE_RECEIPT_TIMEOUT:
 	case MID_LOG_SYNC_INTERVAL:
diff --git a/port.c b/port.c
index acd142f..d204dab 100644
--- a/port.c
+++ b/port.c
@@ -833,6 +833,7 @@ static const Octet profile_id_p2p[] = {0x00, 0x1B, 0x19, 0x00, 0x02, 0x00};
 static int port_management_fill_response(struct port *target,
 					 struct ptp_message *rsp, int id)
 {
+	struct ieee_c37_238_settings_np *pwr;
 	struct mgmt_clock_description *cd;
 	struct management_tlv_datum *mtd;
 	struct clock_description *desc;
@@ -1006,6 +1007,11 @@ static int port_management_fill_response(struct port *target,
 		psn->stats = target->stats;
 		datalen = sizeof(*psn);
 		break;
+	case MID_POWER_PROFILE_SETTINGS_NP:
+		pwr = (struct ieee_c37_238_settings_np *)tlv->data;
+		memcpy(pwr, &target->pwr, sizeof(*pwr));
+		datalen = sizeof(*pwr);
+		break;
 	default:
 		/* The caller should *not* respond to this message. */
 		tlv_extra_recycle(extra);
@@ -1047,9 +1053,10 @@ static int port_management_set(struct port *target,
 			       struct port *ingress, int id,
 			       struct ptp_message *req)
 {
-	int respond = 0;
+	struct ieee_c37_238_settings_np *pwr;
 	struct management_tlv *tlv;
 	struct port_ds_np *pdsnp;
+	int respond = 0;
 
 	tlv = (struct management_tlv *) req->management.suffix;
 
@@ -1059,6 +1066,17 @@ static int port_management_set(struct port *target,
 		target->neighborPropDelayThresh = pdsnp->neighborPropDelayThresh;
 		respond = 1;
 		break;
+	case MID_POWER_PROFILE_SETTINGS_NP:
+		pwr = (struct ieee_c37_238_settings_np *) tlv->data;
+		switch (pwr->version) {
+		case IEEE_C37_238_VERSION_NONE:
+		case IEEE_C37_238_VERSION_2011:
+		case IEEE_C37_238_VERSION_2017:
+			target->pwr = *pwr;
+			respond = 1;
+			break;
+		}
+		break;
 	}
 	if (respond && !port_management_get_response(target, ingress, id, req))
 		pr_err("%s: failed to send management set response", target->log_name);
diff --git a/tlv.c b/tlv.c
index aadfe0a..d9d2846 100644
--- a/tlv.c
+++ b/tlv.c
@@ -114,6 +114,7 @@ static bool tlv_array_invalid(struct TLV *tlv, size_t base_size, size_t item_siz
 static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 			 struct tlv_extra *extra)
 {
+	struct ieee_c37_238_settings_np *pwr;
 	struct grandmaster_settings_np *gsn;
 	struct mgmt_clock_description *cd;
 	struct subscribe_events_np *sen;
@@ -332,6 +333,16 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 		}
 		extra_len = sizeof(struct port_stats_np);
 		break;
+	case MID_POWER_PROFILE_SETTINGS_NP:
+		if (data_len < sizeof(struct ieee_c37_238_settings_np))
+			goto bad_length;
+		pwr = (struct ieee_c37_238_settings_np *)m->data;
+		NTOHS(pwr->version);
+		NTOHS(pwr->grandmasterID);
+		NTOHL(pwr->grandmasterTimeInaccuracy);
+		NTOHL(pwr->networkTimeInaccuracy);
+		NTOHL(pwr->totalTimeInaccuracy);
+		break;
 	case MID_SAVE_IN_NON_VOLATILE_STORAGE:
 	case MID_RESET_NON_VOLATILE_STORAGE:
 	case MID_INITIALIZE:
@@ -355,6 +366,7 @@ bad_length:
 
 static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)
 {
+	struct ieee_c37_238_settings_np *pwr;
 	struct grandmaster_settings_np *gsn;
 	struct mgmt_clock_description *cd;
 	struct subscribe_events_np *sen;
@@ -449,6 +461,14 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)
 			psn->stats.txMsgType[i] = __cpu_to_le64(psn->stats.txMsgType[i]);
 		}
 		break;
+	case MID_POWER_PROFILE_SETTINGS_NP:
+		pwr = (struct ieee_c37_238_settings_np *)m->data;
+		HTONS(pwr->version);
+		HTONS(pwr->grandmasterID);
+		HTONL(pwr->grandmasterTimeInaccuracy);
+		HTONL(pwr->networkTimeInaccuracy);
+		HTONL(pwr->totalTimeInaccuracy);
+		break;
 	}
 }
 
diff --git a/tlv.h b/tlv.h
index c15c49b..e7d2a2d 100644
--- a/tlv.h
+++ b/tlv.h
@@ -125,6 +125,7 @@ enum management_action {
 #define MID_PORT_DATA_SET_NP				0xC002
 #define MID_PORT_PROPERTIES_NP				0xC004
 #define MID_PORT_STATS_NP				0xC005
+#define MID_POWER_PROFILE_SETTINGS_NP			0xC007
 
 /* Management error ID values */
 #define MID_RESPONSE_TOO_BIG				0x0001
-- 
2.20.1
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2021-11-09 20:12:13
       | 
| Signed-off-by: Richard Cochran <ric...@gm...>
---
 clock.c      | 66 ++++++++++++++++++++++++++++++++++++++++++++++++----
 pmc.c        | 13 +++++++++++
 pmc_common.c | 24 ++++++++++++++++++-
 tlv.c        | 21 +++++++++++++++++
 util.h       |  7 ++++++
 5 files changed, 126 insertions(+), 5 deletions(-)
diff --git a/clock.c b/clock.c
index b5b729a..1e3ff3e 100644
--- a/clock.c
+++ b/clock.c
@@ -156,6 +156,32 @@ static int clock_resize_pollfd(struct clock *c, int new_nports);
 static void clock_remove_port(struct clock *c, struct port *p);
 static void clock_stats_display(struct clock_stats *s);
 
+uint8_t clock_alttime_offset_get_key(struct ptp_message *req)
+{
+	struct management_tlv_datum *mtd;
+	struct management_tlv *mgt =
+		(struct management_tlv *) req->management.suffix;
+
+	/*
+	 * The data field of incoming management request messages is
+	 * normally ignored.  Indeed it can even be empty.  However
+	 * the ALTERNATE_TIME_OFFSET requests are exceptional because
+	 * the key field selects one of the configured time zones.
+	 *
+	 * Provide the first time zone for an empty GET, and validate
+	 * the length of the request when non-empty.
+	 */
+	if (mgt->length == sizeof(mgt->id)) {
+		return 0;
+	}
+	if (mgt->length < sizeof(mgt->id) + sizeof(*mtd)) {
+		return MAX_TIME_ZONES;
+	}
+	mtd = (struct management_tlv_datum *) mgt->data;
+
+	return mtd->val;
+}
+
 static void remove_subscriber(struct clock_subscriber *s)
 {
 	LIST_REMOVE(s, list);
@@ -353,6 +379,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 					  struct ptp_message *req,
 					  struct ptp_message *rsp, int id)
 {
+	struct alternate_time_offset_properties *atop;
 	struct grandmaster_settings_np *gsn;
 	struct management_tlv_datum *mtd;
 	struct subscribe_events_np *sen;
@@ -362,6 +389,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 	struct PTPText *text;
 	uint16_t duration;
 	int datalen = 0;
+	uint8_t key;
 
 	extra = tlv_extra_alloc();
 	if (!extra) {
@@ -432,6 +460,22 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 		mtd->val = c->tds.flags & PTP_TIMESCALE;
 		datalen = sizeof(*mtd);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		key = clock_alttime_offset_get_key(req);
+		if (key >= MAX_TIME_ZONES) {
+			break;
+		}
+		atop = (struct alternate_time_offset_properties *) tlv->data;
+		atop->keyField = key;
+		/* Message alignment broken by design. */
+		memcpy(&atop->currentOffset, &c->tz[key].current_offset,
+		       sizeof(atop->currentOffset));
+		memcpy(&atop->jumpSeconds, &c->tz[key].jump_seconds,
+		       sizeof(atop->jumpSeconds));
+		memcpy(&atop->timeOfNextJump.seconds_lsb, &c->tz[key].next_jump,
+		       sizeof(atop->timeOfNextJump.seconds_lsb));
+		datalen = sizeof(*atop);
+		break;
 	case MID_TIME_STATUS_NP:
 		tsn = (struct time_status_np *) tlv->data;
 		tsn->master_offset = tmv_to_nanoseconds(c->master_offset);
@@ -510,11 +554,12 @@ static int clock_management_get_response(struct clock *c, struct port *p,
 static int clock_management_set(struct clock *c, struct port *p,
 				int id, struct ptp_message *req, int *changed)
 {
-	int respond = 0;
-	struct management_tlv *tlv;
-	struct management_tlv_datum *mtd;
+	struct alternate_time_offset_properties *atop;
 	struct grandmaster_settings_np *gsn;
+	struct management_tlv_datum *mtd;
 	struct subscribe_events_np *sen;
+	struct management_tlv *tlv;
+	int key, respond = 0;
 
 	tlv = (struct management_tlv *) req->management.suffix;
 
@@ -531,6 +576,20 @@ static int clock_management_set(struct clock *c, struct port *p,
 		*changed = 1;
 		respond = 1;
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		atop = (struct alternate_time_offset_properties *) tlv->data;
+		key = atop->keyField;
+		if (key < MAX_TIME_ZONES) {
+			/* Message alignment broken by design. */
+			memcpy(&c->tz[key].current_offset, &atop->currentOffset,
+			       sizeof(c->tz[key].current_offset));
+			memcpy(&c->tz[key].jump_seconds, &atop->jumpSeconds,
+			       sizeof(c->tz[key].jump_seconds));
+			memcpy(&c->tz[key].next_jump, &atop->timeOfNextJump.seconds_lsb,
+			       sizeof(c->tz[key].next_jump));
+			respond = 1;
+		}
+		break;
 	case MID_GRANDMASTER_SETTINGS_NP:
 		gsn = (struct grandmaster_settings_np *) tlv->data;
 		c->dds.clockQuality = gsn->clockQuality;
@@ -1529,7 +1588,6 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg)
 	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
 	case MID_ALTERNATE_TIME_OFFSET_NAME:
 	case MID_ALTERNATE_TIME_OFFSET_MAX_KEY:
-	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 	case MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET:
 	case MID_PRIMARY_DOMAIN:
 	case MID_TIME_STATUS_NP:
diff --git a/pmc.c b/pmc.c
index 28d96a8..e4dd566 100644
--- a/pmc.c
+++ b/pmc.c
@@ -139,6 +139,7 @@ static void pmc_show_signaling(struct ptp_message *msg, FILE *fp)
 
 static void pmc_show(struct ptp_message *msg, FILE *fp)
 {
+	struct alternate_time_offset_properties *atop;
 	struct ieee_c37_238_settings_np *pwr;
 	struct grandmaster_settings_np *gsn;
 	struct mgmt_clock_description *cd;
@@ -336,6 +337,18 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
 		fprintf(fp, "TIMESCALE_PROPERTIES "
 			IFMT "ptpTimescale %d", mtd->val & PTP_TIMESCALE ? 1 : 0);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		atop = (struct alternate_time_offset_properties *) mgt->data;
+		fprintf(fp, "ALTERNATE_TIME_OFFSET_PROPERTIES "
+			IFMT "keyField       %hhu"
+			IFMT "currentOffset  %d"
+			IFMT "jumpSeconds    %d"
+			IFMT "timeOfNextJump %u",
+			atop->keyField,
+			align32(&atop->currentOffset),
+			align32(&atop->jumpSeconds),
+			align32(&atop->timeOfNextJump.seconds_lsb));
+		break;
 	case MID_MASTER_ONLY:
 		mtd = (struct management_tlv_datum *) mgt->data;
 		fprintf(fp, "MASTER_ONLY "
diff --git a/pmc_common.c b/pmc_common.c
index e40c3d8..6ba8616 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -104,7 +104,7 @@ struct management_id idtab[] = {
 	{ "ALTERNATE_TIME_OFFSET_ENABLE", MID_ALTERNATE_TIME_OFFSET_ENABLE, not_supported },
 	{ "ALTERNATE_TIME_OFFSET_NAME", MID_ALTERNATE_TIME_OFFSET_NAME, not_supported },
 	{ "ALTERNATE_TIME_OFFSET_MAX_KEY", MID_ALTERNATE_TIME_OFFSET_MAX_KEY, not_supported },
-	{ "ALTERNATE_TIME_OFFSET_PROPERTIES", MID_ALTERNATE_TIME_OFFSET_PROPERTIES, not_supported },
+	{ "ALTERNATE_TIME_OFFSET_PROPERTIES", MID_ALTERNATE_TIME_OFFSET_PROPERTIES, do_set_action },
 	{ "MASTER_ONLY", MID_MASTER_ONLY, do_get_action },
 	{ "TRANSPARENT_CLOCK_DEFAULT_DATA_SET", MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET, not_supported },
 	{ "PRIMARY_DOMAIN", MID_PRIMARY_DOMAIN, not_supported },
@@ -148,6 +148,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 {
 	int cnt, code = idtab[index].code, freq_traceable, leap_59, leap_61,
 		ptp_timescale, time_traceable, utc_off_valid;
+	struct alternate_time_offset_properties atop;
 	struct ieee_c37_238_settings_np pwr;
 	struct grandmaster_settings_np gsn;
 	struct management_tlv_datum mtd;
@@ -181,6 +182,24 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 		}
 		pmc_send_set_action(pmc, code, &mtd, sizeof(mtd));
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		memset(&atop, 0, sizeof(atop));
+		cnt = sscanf(str, " %*s %*s "
+			     "keyField       %hhu "
+			     "currentOffset  %d "
+			     "jumpSeconds    %d "
+			     "timeOfNextJump %u ",
+			     &atop.keyField,
+			     &atop.currentOffset,
+			     &atop.jumpSeconds,
+			     &atop.timeOfNextJump.seconds_lsb);
+		if (cnt != 4) {
+			fprintf(stderr, "%s SET needs 4 values\n",
+				idtab[index].name);
+			break;
+		}
+		pmc_send_set_action(pmc, code, &atop, sizeof(atop));
+		break;
 	case MID_GRANDMASTER_SETTINGS_NP:
 		cnt = sscanf(str, " %*s %*s "
 			     "clockClass              %hhu "
@@ -551,6 +570,9 @@ static int pmc_tlv_datalen(struct pmc *pmc, int id)
 	case MID_TIME_STATUS_NP:
 		len += sizeof(struct time_status_np);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		len += sizeof(struct alternate_time_offset_properties);
+		break;
 	case MID_GRANDMASTER_SETTINGS_NP:
 		len += sizeof(struct grandmaster_settings_np);
 		break;
diff --git a/tlv.c b/tlv.c
index 03faa74..fdff99a 100644
--- a/tlv.c
+++ b/tlv.c
@@ -167,6 +167,7 @@ static void alttime_offset_pre_send(struct tlv_extra *extra)
 static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 			 struct tlv_extra *extra)
 {
+	struct alternate_time_offset_properties *atop;
 	struct ieee_c37_238_settings_np *pwr;
 	struct grandmaster_settings_np *gsn;
 	struct mgmt_clock_description *cd;
@@ -333,6 +334,17 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 		p->portIdentity.portNumber = ntohs(p->portIdentity.portNumber);
 		p->peerMeanPathDelay = net2host64(p->peerMeanPathDelay);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		atop = (struct alternate_time_offset_properties *) m->data;
+		if (data_len != sizeof(*atop)) {
+			goto bad_length;
+		}
+		/* Message alignment broken by design. */
+		net2host32_unaligned(&atop->currentOffset);
+		net2host32_unaligned(&atop->jumpSeconds);
+		flip16(&atop->timeOfNextJump.seconds_msb);
+		net2host32_unaligned(&atop->timeOfNextJump.seconds_lsb);
+		break;
 	case MID_TIME_STATUS_NP:
 		if (data_len != sizeof(struct time_status_np))
 			goto bad_length;
@@ -419,6 +431,7 @@ bad_length:
 
 static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)
 {
+	struct alternate_time_offset_properties *atop;
 	struct ieee_c37_238_settings_np *pwr;
 	struct grandmaster_settings_np *gsn;
 	struct mgmt_clock_description *cd;
@@ -476,6 +489,14 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)
 		p->portIdentity.portNumber = htons(p->portIdentity.portNumber);
 		p->peerMeanPathDelay = host2net64(p->peerMeanPathDelay);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		atop = (struct alternate_time_offset_properties *) m->data;
+		/* Message alignment broken by design. */
+		host2net32_unaligned(&atop->currentOffset);
+		host2net32_unaligned(&atop->jumpSeconds);
+		flip16(&atop->timeOfNextJump.seconds_msb);
+		host2net32_unaligned(&atop->timeOfNextJump.seconds_lsb);
+		break;
 	case MID_TIME_STATUS_NP:
 		tsn = (struct time_status_np *) m->data;
 		tsn->master_offset = host2net64(tsn->master_offset);
diff --git a/util.h b/util.h
index 739c8fd..0386409 100644
--- a/util.h
+++ b/util.h
@@ -64,6 +64,13 @@ static inline uint16_t align16(void *p)
 	return v;
 }
 
+static inline uint32_t align32(void *p)
+{
+	uint32_t v;
+	memcpy(&v, p, sizeof(v));
+	return v;
+}
+
 char *bin2str_impl(Octet *data, int len, char *buf, int buf_len);
 
 /**
-- 
2.20.1
 | 
| 
      
      
      From: Erez <ere...@gm...> - 2021-11-23 10:40:28
       | 
| Although 32 bits of seconds are 136 years, and as most users will find it
useless.
As we are compliant IEEE 1558, we need to support it, at least in ptp4l and
in printing the proper value in the pmc tool.
next_jump need to be 64 bits, to store all of the 48 bits required by the
standard
On Tue, 9 Nov 2021 at 21:14, Richard Cochran <ric...@gm...>
wrote:
> Signed-off-by: Richard Cochran <ric...@gm...>
> ---
>  clock.c      | 66 ++++++++++++++++++++++++++++++++++++++++++++++++----
>  pmc.c        | 13 +++++++++++
>  pmc_common.c | 24 ++++++++++++++++++-
>  tlv.c        | 21 +++++++++++++++++
>  util.h       |  7 ++++++
>  5 files changed, 126 insertions(+), 5 deletions(-)
>
> diff --git a/clock.c b/clock.c
> index b5b729a..1e3ff3e 100644
> --- a/clock.c
> +++ b/clock.c
> @@ -156,6 +156,32 @@ static int clock_resize_pollfd(struct clock *c, int
> new_nports);
>  static void clock_remove_port(struct clock *c, struct port *p);
>  static void clock_stats_display(struct clock_stats *s);
>
> +uint8_t clock_alttime_offset_get_key(struct ptp_message *req)
> +{
> +       struct management_tlv_datum *mtd;
> +       struct management_tlv *mgt =
> +               (struct management_tlv *) req->management.suffix;
> +
> +       /*
> +        * The data field of incoming management request messages is
> +        * normally ignored.  Indeed it can even be empty.  However
> +        * the ALTERNATE_TIME_OFFSET requests are exceptional because
> +        * the key field selects one of the configured time zones.
> +        *
> +        * Provide the first time zone for an empty GET, and validate
> +        * the length of the request when non-empty.
> +        */
> +       if (mgt->length == sizeof(mgt->id)) {
> +               return 0;
> +       }
> +       if (mgt->length < sizeof(mgt->id) + sizeof(*mtd)) {
> +               return MAX_TIME_ZONES;
> +       }
> +       mtd = (struct management_tlv_datum *) mgt->data;
> +
> +       return mtd->val;
> +}
> +
>  static void remove_subscriber(struct clock_subscriber *s)
>  {
>         LIST_REMOVE(s, list);
> @@ -353,6 +379,7 @@ static int clock_management_fill_response(struct clock
> *c, struct port *p,
>                                           struct ptp_message *req,
>                                           struct ptp_message *rsp, int id)
>  {
> +       struct alternate_time_offset_properties *atop;
>         struct grandmaster_settings_np *gsn;
>         struct management_tlv_datum *mtd;
>         struct subscribe_events_np *sen;
> @@ -362,6 +389,7 @@ static int clock_management_fill_response(struct clock
> *c, struct port *p,
>         struct PTPText *text;
>         uint16_t duration;
>         int datalen = 0;
> +       uint8_t key;
>
>         extra = tlv_extra_alloc();
>         if (!extra) {
> @@ -432,6 +460,22 @@ static int clock_management_fill_response(struct
> clock *c, struct port *p,
>                 mtd->val = c->tds.flags & PTP_TIMESCALE;
>                 datalen = sizeof(*mtd);
>                 break;
> +       case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
> +               key = clock_alttime_offset_get_key(req);
> +               if (key >= MAX_TIME_ZONES) {
> +                       break;
> +               }
> +               atop = (struct alternate_time_offset_properties *)
> tlv->data;
> +               atop->keyField = key;
> +               /* Message alignment broken by design. */
> +               memcpy(&atop->currentOffset, &c->tz[key].current_offset,
> +                      sizeof(atop->currentOffset));
> +               memcpy(&atop->jumpSeconds, &c->tz[key].jump_seconds,
> +                      sizeof(atop->jumpSeconds));
> +               memcpy(&atop->timeOfNextJump.seconds_lsb,
> &c->tz[key].next_jump,
> +                      sizeof(atop->timeOfNextJump.seconds_lsb));
>
Here you should copy  timeOfNextJump.seconds_msb.
i.e.
memcpy(&atop->timeOfNextJump.seconds_lsb, &c->tz[key].next_jump &
0xFFFFFFFF,
                      sizeof(atop->timeOfNextJump.seconds_lsb));
memcpy(&atop->timeOfNextJump. seconds_msb, &c->tz[key].next_jump >> 32,
                      sizeof(atop->timeOfNextJump. seconds_msb));
+               datalen = sizeof(*atop);
> +               break;
>         case MID_TIME_STATUS_NP:
>                 tsn = (struct time_status_np *) tlv->data;
>                 tsn->master_offset = tmv_to_nanoseconds(c->master_offset);
> @@ -510,11 +554,12 @@ static int clock_management_get_response(struct
> clock *c, struct port *p,
>  static int clock_management_set(struct clock *c, struct port *p,
>                                 int id, struct ptp_message *req, int
> *changed)
>  {
> -       int respond = 0;
> -       struct management_tlv *tlv;
> -       struct management_tlv_datum *mtd;
> +       struct alternate_time_offset_properties *atop;
>         struct grandmaster_settings_np *gsn;
> +       struct management_tlv_datum *mtd;
>         struct subscribe_events_np *sen;
> +       struct management_tlv *tlv;
> +       int key, respond = 0;
>
>         tlv = (struct management_tlv *) req->management.suffix;
>
> @@ -531,6 +576,20 @@ static int clock_management_set(struct clock *c,
> struct port *p,
>                 *changed = 1;
>                 respond = 1;
>                 break;
> +       case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
> +               atop = (struct alternate_time_offset_properties *)
> tlv->data;
> +               key = atop->keyField;
> +               if (key < MAX_TIME_ZONES) {
> +                       /* Message alignment broken by design. */
> +                       memcpy(&c->tz[key].current_offset,
> &atop->currentOffset,
> +                              sizeof(c->tz[key].current_offset));
> +                       memcpy(&c->tz[key].jump_seconds,
> &atop->jumpSeconds,
> +                              sizeof(c->tz[key].jump_seconds));
> +                       memcpy(&c->tz[key].next_jump,
> &atop->timeOfNextJump.seconds_lsb,
> +                              sizeof(c->tz[key].next_jump));
>
Here you should add timeOfNextJump.seconds_msb value
i.e.
uint16_t tmp16;
memcpy(&c->tz[key].next_jump, &atop->timeOfNextJump.seconds_lsb,
                                                        sizeof(uint32_t);
memcpy(&tmp16, &atop->timeOfNextJump.seconds_msb,
                                                        sizeof(uint16_t);
c->tz[key].next_jump |= tmp16 << 32;
> +                       respond = 1;
> +               }
> +               break;
>         case MID_GRANDMASTER_SETTINGS_NP:
>                 gsn = (struct grandmaster_settings_np *) tlv->data;
>                 c->dds.clockQuality = gsn->clockQuality;
> @@ -1529,7 +1588,6 @@ int clock_manage(struct clock *c, struct port *p,
> struct ptp_message *msg)
>         case MID_ALTERNATE_TIME_OFFSET_ENABLE:
>         case MID_ALTERNATE_TIME_OFFSET_NAME:
>         case MID_ALTERNATE_TIME_OFFSET_MAX_KEY:
> -       case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
>         case MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET:
>         case MID_PRIMARY_DOMAIN:
>         case MID_TIME_STATUS_NP:
> diff --git a/pmc.c b/pmc.c
> index 28d96a8..e4dd566 100644
> --- a/pmc.c
> +++ b/pmc.c
> @@ -139,6 +139,7 @@ static void pmc_show_signaling(struct ptp_message
> *msg, FILE *fp)
>
>  static void pmc_show(struct ptp_message *msg, FILE *fp)
>  {
> +       struct alternate_time_offset_properties *atop;
>         struct ieee_c37_238_settings_np *pwr;
>         struct grandmaster_settings_np *gsn;
>         struct mgmt_clock_description *cd;
> @@ -336,6 +337,18 @@ static void pmc_show(struct ptp_message *msg, FILE
> *fp)
>                 fprintf(fp, "TIMESCALE_PROPERTIES "
>                         IFMT "ptpTimescale %d", mtd->val & PTP_TIMESCALE ?
> 1 : 0);
>                 break;
> +       case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
> +               atop = (struct alternate_time_offset_properties *)
> mgt->data;
> +               fprintf(fp, "ALTERNATE_TIME_OFFSET_PROPERTIES "
> +                       IFMT "keyField       %hhu"
> +                       IFMT "currentOffset  %d"
> +                       IFMT "jumpSeconds    %d"
> +                       IFMT "timeOfNextJump %u",
> +                       atop->keyField,
> +                       align32(&atop->currentOffset),
> +                       align32(&atop->jumpSeconds),
> +                       align32(&atop->timeOfNextJump.seconds_lsb));
>
Here you print the entire value:
                           IFMT "timeOfNextJump %ju",
                           ...
                           align32(&atop->timeOfNextJump.seconds_lsb) ||
&atop->timeOfNextJump.seconds_msb << 32;
> +               break;
>         case MID_MASTER_ONLY:
>                 mtd = (struct management_tlv_datum *) mgt->data;
>                 fprintf(fp, "MASTER_ONLY "
> diff --git a/pmc_common.c b/pmc_common.c
> index e40c3d8..6ba8616 100644
> --- a/pmc_common.c
> +++ b/pmc_common.c
> @@ -104,7 +104,7 @@ struct management_id idtab[] = {
>         { "ALTERNATE_TIME_OFFSET_ENABLE",
> MID_ALTERNATE_TIME_OFFSET_ENABLE, not_supported },
>         { "ALTERNATE_TIME_OFFSET_NAME", MID_ALTERNATE_TIME_OFFSET_NAME,
> not_supported },
>         { "ALTERNATE_TIME_OFFSET_MAX_KEY",
> MID_ALTERNATE_TIME_OFFSET_MAX_KEY, not_supported },
> -       { "ALTERNATE_TIME_OFFSET_PROPERTIES",
> MID_ALTERNATE_TIME_OFFSET_PROPERTIES, not_supported },
> +       { "ALTERNATE_TIME_OFFSET_PROPERTIES",
> MID_ALTERNATE_TIME_OFFSET_PROPERTIES, do_set_action },
>         { "MASTER_ONLY", MID_MASTER_ONLY, do_get_action },
>         { "TRANSPARENT_CLOCK_DEFAULT_DATA_SET",
> MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET, not_supported },
>         { "PRIMARY_DOMAIN", MID_PRIMARY_DOMAIN, not_supported },
> @@ -148,6 +148,7 @@ static void do_set_action(struct pmc *pmc, int action,
> int index, char *str)
>  {
>         int cnt, code = idtab[index].code, freq_traceable, leap_59,
> leap_61,
>                 ptp_timescale, time_traceable, utc_off_valid;
> +       struct alternate_time_offset_properties atop;
>         struct ieee_c37_238_settings_np pwr;
>         struct grandmaster_settings_np gsn;
>         struct management_tlv_datum mtd;
> @@ -181,6 +182,24 @@ static void do_set_action(struct pmc *pmc, int
> action, int index, char *str)
>                 }
>                 pmc_send_set_action(pmc, code, &mtd, sizeof(mtd));
>                 break;
> +       case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
> +               memset(&atop, 0, sizeof(atop));
> +               cnt = sscanf(str, " %*s %*s "
> +                            "keyField       %hhu "
> +                            "currentOffset  %d "
> +                            "jumpSeconds    %d "
> +                            "timeOfNextJump %u ",
> +                            &atop.keyField,
> +                            &atop.currentOffset,
> +                            &atop.jumpSeconds,
> +                            &atop.timeOfNextJump.seconds_lsb);
>
As most users do not need 136 years or more, there is no need to set
timeOfNextJump.seconds_msb here, and leave it zero.
Users who wish, can use their own tool or change this tool by themself.
> +               if (cnt != 4) {
> +                       fprintf(stderr, "%s SET needs 4 values\n",
> +                               idtab[index].name);
> +                       break;
> +               }
> +               pmc_send_set_action(pmc, code, &atop, sizeof(atop));
> +               break;
>         case MID_GRANDMASTER_SETTINGS_NP:
>                 cnt = sscanf(str, " %*s %*s "
>                              "clockClass              %hhu "
> @@ -551,6 +570,9 @@ static int pmc_tlv_datalen(struct pmc *pmc, int id)
>         case MID_TIME_STATUS_NP:
>                 len += sizeof(struct time_status_np);
>                 break;
> +       case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
> +               len += sizeof(struct alternate_time_offset_properties);
> +               break;
>         case MID_GRANDMASTER_SETTINGS_NP:
>                 len += sizeof(struct grandmaster_settings_np);
>                 break;
> diff --git a/tlv.c b/tlv.c
> index 03faa74..fdff99a 100644
> --- a/tlv.c
> +++ b/tlv.c
> @@ -167,6 +167,7 @@ static void alttime_offset_pre_send(struct tlv_extra
> *extra)
>  static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
>                          struct tlv_extra *extra)
>  {
> +       struct alternate_time_offset_properties *atop;
>         struct ieee_c37_238_settings_np *pwr;
>         struct grandmaster_settings_np *gsn;
>         struct mgmt_clock_description *cd;
> @@ -333,6 +334,17 @@ static int mgt_post_recv(struct management_tlv *m,
> uint16_t data_len,
>                 p->portIdentity.portNumber =
> ntohs(p->portIdentity.portNumber);
>                 p->peerMeanPathDelay = net2host64(p->peerMeanPathDelay);
>                 break;
> +       case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
> +               atop = (struct alternate_time_offset_properties *) m->data;
> +               if (data_len != sizeof(*atop)) {
> +                       goto bad_length;
> +               }
> +               /* Message alignment broken by design. */
> +               net2host32_unaligned(&atop->currentOffset);
> +               net2host32_unaligned(&atop->jumpSeconds);
> +               flip16(&atop->timeOfNextJump.seconds_msb);
> +               net2host32_unaligned(&atop->timeOfNextJump.seconds_lsb);
> +               break;
>         case MID_TIME_STATUS_NP:
>                 if (data_len != sizeof(struct time_status_np))
>                         goto bad_length;
> @@ -419,6 +431,7 @@ bad_length:
>
>  static void mgt_pre_send(struct management_tlv *m, struct tlv_extra
> *extra)
>  {
> +       struct alternate_time_offset_properties *atop;
>         struct ieee_c37_238_settings_np *pwr;
>         struct grandmaster_settings_np *gsn;
>         struct mgmt_clock_description *cd;
> @@ -476,6 +489,14 @@ static void mgt_pre_send(struct management_tlv *m,
> struct tlv_extra *extra)
>                 p->portIdentity.portNumber =
> htons(p->portIdentity.portNumber);
>                 p->peerMeanPathDelay = host2net64(p->peerMeanPathDelay);
>                 break;
> +       case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
> +               atop = (struct alternate_time_offset_properties *) m->data;
> +               /* Message alignment broken by design. */
> +               host2net32_unaligned(&atop->currentOffset);
> +               host2net32_unaligned(&atop->jumpSeconds);
> +               flip16(&atop->timeOfNextJump.seconds_msb);
> +               host2net32_unaligned(&atop->timeOfNextJump.seconds_lsb);
> +               break;
>         case MID_TIME_STATUS_NP:
>                 tsn = (struct time_status_np *) m->data;
>                 tsn->master_offset = host2net64(tsn->master_offset);
> diff --git a/util.h b/util.h
> index 739c8fd..0386409 100644
> --- a/util.h
> +++ b/util.h
> @@ -64,6 +64,13 @@ static inline uint16_t align16(void *p)
>         return v;
>  }
>
> +static inline uint32_t align32(void *p)
> +{
> +       uint32_t v;
> +       memcpy(&v, p, sizeof(v));
> +       return v;
> +}
> +
>  char *bin2str_impl(Octet *data, int len, char *buf, int buf_len);
>
>  /**
> --
> 2.20.1
>
>
>
> _______________________________________________
> Linuxptp-devel mailing list
> Lin...@li...
> https://lists.sourceforge.net/lists/listinfo/linuxptp-devel
>
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2021-11-09 20:12:11
       | 
| Signed-off-by: Richard Cochran <ric...@gm...>
---
 config.c        | 15 +++++++++++++-
 port.c          | 53 +++++++++++++++++++++++++++++++++++++++++++++++++
 port_private.h  |  3 +++
 power_profile.h | 31 +++++++++++++++++++++++++++++
 tlv.h           |  8 ++++++++
 5 files changed, 109 insertions(+), 1 deletion(-)
 create mode 100644 power_profile.h
diff --git a/config.c b/config.c
index f3c52ba..0996896 100644
--- a/config.c
+++ b/config.c
@@ -30,6 +30,7 @@
 #include "config.h"
 #include "ether.h"
 #include "hash.h"
+#include "power_profile.h"
 #include "print.h"
 #include "util.h"
 
@@ -62,7 +63,7 @@ typedef union {
 	char *s;
 } any_t;
 
-#define CONFIG_LABEL_SIZE 32
+#define CONFIG_LABEL_SIZE 64
 
 #define CFG_ITEM_STATIC (1 << 0) /* statically allocated, not to be freed */
 #define CFG_ITEM_LOCKED (1 << 1) /* command line value, may not be changed */
@@ -184,6 +185,13 @@ static struct config_enum hwts_filter_enu[] = {
 	{ NULL, 0 },
 };
 
+static struct config_enum ieee_c37_238_enu[] = {
+	{ "none", IEEE_C37_238_VERSION_NONE },
+	{ "2011", IEEE_C37_238_VERSION_2011 },
+	{ "2017", IEEE_C37_238_VERSION_2017 },
+	{ NULL, 0 },
+};
+
 static struct config_enum nw_trans_enu[] = {
 	{ "L2",    TRANS_IEEE_802_3 },
 	{ "UDPv4", TRANS_UDP_IPV4   },
@@ -292,6 +300,11 @@ struct config_item config_tab[] = {
 	GLOB_ITEM_DBL("pi_proportional_exponent", -0.3, -DBL_MAX, DBL_MAX),
 	GLOB_ITEM_DBL("pi_proportional_norm_max", 0.7, DBL_MIN, 1.0),
 	GLOB_ITEM_DBL("pi_proportional_scale", 0.0, 0.0, DBL_MAX),
+	PORT_ITEM_ENU("power_profile.version", IEEE_C37_238_VERSION_NONE, ieee_c37_238_enu),
+	PORT_ITEM_INT("power_profile.2011.grandmasterTimeInaccuracy", 0xFFFFFFFF, 0, INT_MAX),
+	PORT_ITEM_INT("power_profile.2011.networkTimeInaccuracy", 0, 0, INT_MAX),
+	PORT_ITEM_INT("power_profile.2017.totalTimeInaccuracy", 0xFFFFFFFF, 0, INT_MAX),
+	PORT_ITEM_INT("power_profile.grandmasterID", 0, 0, 0xFFFF),
 	GLOB_ITEM_INT("priority1", 128, 0, UINT8_MAX),
 	GLOB_ITEM_INT("priority2", 128, 0, UINT8_MAX),
 	GLOB_ITEM_STR("productDescription", ";;"),
diff --git a/port.c b/port.c
index 7e51e77..acd142f 100644
--- a/port.c
+++ b/port.c
@@ -411,6 +411,46 @@ static int follow_up_info_append(struct ptp_message *m)
 	return 0;
 }
 
+static int ieee_c37_238_append(struct port *p, struct ptp_message *m)
+{
+	struct ieee_c37_238_2017_tlv *p17;
+	struct ieee_c37_238_2011_tlv *p11;
+	struct tlv_extra *extra;
+
+	switch (p->pwr.version) {
+	case IEEE_C37_238_VERSION_NONE:
+		return 0;
+	case IEEE_C37_238_VERSION_2011:
+		extra = msg_tlv_append(m, sizeof(*p11));
+		if (!extra) {
+			return -1;
+		}
+		p11 = (struct ieee_c37_238_2011_tlv *) extra->tlv;
+		p11->type = TLV_ORGANIZATION_EXTENSION;
+		p11->length = sizeof(*p11) - sizeof(p11->type) - sizeof(p11->length);
+		memcpy(p11->id, ieeec37_238_id, sizeof(ieeec37_238_id));
+		p11->subtype[2] = 1;
+		p11->grandmasterID = p->pwr.grandmasterID;
+		p11->grandmasterTimeInaccuracy = p->pwr.grandmasterTimeInaccuracy;
+		p11->networkTimeInaccuracy = p->pwr.networkTimeInaccuracy;
+		break;
+	case IEEE_C37_238_VERSION_2017:
+		extra = msg_tlv_append(m, sizeof(*p17));
+		if (!extra) {
+			return -1;
+		}
+		p17 = (struct ieee_c37_238_2017_tlv *) extra->tlv;
+		p17->type = TLV_ORGANIZATION_EXTENSION;
+		p17->length = sizeof(*p17) - sizeof(p17->type) - sizeof(p17->length);
+		memcpy(p17->id, ieeec37_238_id, sizeof(ieeec37_238_id));
+		p17->subtype[2] = 2;
+		p17->grandmasterID = p->pwr.grandmasterID;
+		p17->totalTimeInaccuracy = p->pwr.totalTimeInaccuracy;
+		break;
+	}
+	return 0;
+}
+
 static int net_sync_resp_append(struct port *p, struct ptp_message *m)
 {
 	struct timePropertiesDS tp = clock_time_properties(p->clock);
@@ -1501,6 +1541,9 @@ int port_tx_announce(struct port *p, struct address *dst, uint16_t sequence_id)
 	if (p->path_trace_enabled && path_trace_append(p, msg, dad)) {
 		pr_err("%s: append path trace failed", p->log_name);
 	}
+	if (ieee_c37_238_append(p, msg)) {
+		pr_err("%s: append power profile failed", p->log_name);
+	}
 
 	err = port_prepare_and_send(p, msg, TRANS_GENERAL);
 	if (err) {
@@ -3171,6 +3214,16 @@ struct port *port_open(const char *phc_device,
 	p->state = PS_INITIALIZING;
 	p->delayMechanism = config_get_int(cfg, p->name, "delay_mechanism");
 	p->versionNumber = PTP_MAJOR_VERSION;
+	p->pwr.version =
+		config_get_int(cfg, p->name, "power_profile.version");
+	p->pwr.grandmasterID =
+		config_get_int(cfg, p->name, "power_profile.grandmasterID");
+	p->pwr.grandmasterTimeInaccuracy =
+		config_get_int(cfg, p->name, "power_profile.2011.grandmasterTimeInaccuracy");
+	p->pwr.networkTimeInaccuracy =
+		config_get_int(cfg, p->name, "power_profile.2011.networkTimeInaccuracy");
+	p->pwr.totalTimeInaccuracy =
+		config_get_int(cfg, p->name, "power_profile.2017.totalTimeInaccuracy");
 	p->slave_event_monitor = clock_slave_monitor(clock);
 
 	if (!port_is_uds(p) && unicast_client_initialize(p)) {
diff --git a/port_private.h b/port_private.h
index 19eb944..98ae31b 100644
--- a/port_private.h
+++ b/port_private.h
@@ -26,6 +26,7 @@
 #include "fsm.h"
 #include "monitor.h"
 #include "msg.h"
+#include "power_profile.h"
 #include "tmv.h"
 
 #define NSEC2SEC 1000000000LL
@@ -150,6 +151,8 @@ struct port {
 	LIST_HEAD(fm, foreign_clock) foreign_masters;
 	/* TC book keeping */
 	TAILQ_HEAD(tct, tc_txd) tc_transmitted;
+	/* power profile */
+	struct ieee_c37_238_settings_np pwr;
 	/* unicast client mode */
 	struct unicast_master_table *unicast_master_table;
 	/* unicast service mode */
diff --git a/power_profile.h b/power_profile.h
new file mode 100644
index 0000000..6a7c8a4
--- /dev/null
+++ b/power_profile.h
@@ -0,0 +1,31 @@
+/**
+ * @file power_profile.h
+ * @brief Implements power profile features.
+ * @note Copyright (C) 2021 Richard Cochran <ric...@gm...>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef HAVE_POWER_PROFILE_H
+#define HAVE_POWER_PROFILE_H
+
+#include "pdt.h"
+
+enum power_profile_version {
+	IEEE_C37_238_VERSION_NONE,
+	IEEE_C37_238_VERSION_2011,
+	IEEE_C37_238_VERSION_2017,
+};
+
+#endif
diff --git a/tlv.h b/tlv.h
index bb8c3af..c15c49b 100644
--- a/tlv.h
+++ b/tlv.h
@@ -318,6 +318,14 @@ struct ieee_c37_238_2017_tlv {
 	Octet         pad[2];
 } PACKED;
 
+struct ieee_c37_238_settings_np {
+	Enumeration16 version;
+	UInteger16    grandmasterID;
+	UInteger32    grandmasterTimeInaccuracy;
+	UInteger32    networkTimeInaccuracy;
+	UInteger32    totalTimeInaccuracy;
+} PACKED;
+
 struct msg_interval_req_tlv {
 	Enumeration16 type;
 	UInteger16    length;
-- 
2.20.1
 | 
| 
      
      
      From: Olivier D. <oli...@or...> - 2021-11-10 15:33:16
       | 
| Hello Richard,
The 11/09/2021 12:11, Richard Cochran wrote:
> Signed-off-by: Richard Cochran <ric...@gm...>
> ---
>  config.c        | 15 +++++++++++++-
>  port.c          | 53 +++++++++++++++++++++++++++++++++++++++++++++++++
>  port_private.h  |  3 +++
>  power_profile.h | 31 +++++++++++++++++++++++++++++
>  tlv.h           |  8 ++++++++
>  5 files changed, 109 insertions(+), 1 deletion(-)
>  create mode 100644 power_profile.h
> 
> diff --git a/config.c b/config.c
> index f3c52ba..0996896 100644
> --- a/config.c
> +++ b/config.c
> @@ -30,6 +30,7 @@
>  #include "config.h"
>  #include "ether.h"
>  #include "hash.h"
> +#include "power_profile.h"
>  #include "print.h"
>  #include "util.h"
> 
> @@ -62,7 +63,7 @@ typedef union {
>         char *s;
>  } any_t;
> 
> -#define CONFIG_LABEL_SIZE 32
> +#define CONFIG_LABEL_SIZE 64
> 
>  #define CFG_ITEM_STATIC (1 << 0) /* statically allocated, not to be freed */
>  #define CFG_ITEM_LOCKED (1 << 1) /* command line value, may not be changed */
> @@ -184,6 +185,13 @@ static struct config_enum hwts_filter_enu[] = {
>         { NULL, 0 },
>  };
> 
> +static struct config_enum ieee_c37_238_enu[] = {
> +       { "none", IEEE_C37_238_VERSION_NONE },
> +       { "2011", IEEE_C37_238_VERSION_2011 },
> +       { "2017", IEEE_C37_238_VERSION_2017 },
> +       { NULL, 0 },
> +};
> +
>  static struct config_enum nw_trans_enu[] = {
>         { "L2",    TRANS_IEEE_802_3 },
>         { "UDPv4", TRANS_UDP_IPV4   },
> @@ -292,6 +300,11 @@ struct config_item config_tab[] = {
>         GLOB_ITEM_DBL("pi_proportional_exponent", -0.3, -DBL_MAX, DBL_MAX),
>         GLOB_ITEM_DBL("pi_proportional_norm_max", 0.7, DBL_MIN, 1.0),
>         GLOB_ITEM_DBL("pi_proportional_scale", 0.0, 0.0, DBL_MAX),
> +       PORT_ITEM_ENU("power_profile.version", IEEE_C37_238_VERSION_NONE, ieee_c37_238_enu),
> +       PORT_ITEM_INT("power_profile.2011.grandmasterTimeInaccuracy", 0xFFFFFFFF, 0, INT_MAX),
> +       PORT_ITEM_INT("power_profile.2011.networkTimeInaccuracy", 0, 0, INT_MAX),
> +       PORT_ITEM_INT("power_profile.2017.totalTimeInaccuracy", 0xFFFFFFFF, 0, INT_MAX),
> +       PORT_ITEM_INT("power_profile.grandmasterID", 0, 0, 0xFFFF),
>         GLOB_ITEM_INT("priority1", 128, 0, UINT8_MAX),
>         GLOB_ITEM_INT("priority2", 128, 0, UINT8_MAX),
>         GLOB_ITEM_STR("productDescription", ";;"),
> diff --git a/port.c b/port.c
> index 7e51e77..acd142f 100644
> --- a/port.c
> +++ b/port.c
> @@ -411,6 +411,46 @@ static int follow_up_info_append(struct ptp_message *m)
>         return 0;
>  }
> 
> +static int ieee_c37_238_append(struct port *p, struct ptp_message *m)
> +{
> +       struct ieee_c37_238_2017_tlv *p17;
> +       struct ieee_c37_238_2011_tlv *p11;
> +       struct tlv_extra *extra;
> +
> +       switch (p->pwr.version) {
> +       case IEEE_C37_238_VERSION_NONE:
> +               return 0;
> +       case IEEE_C37_238_VERSION_2011:
> +               extra = msg_tlv_append(m, sizeof(*p11));
> +               if (!extra) {
> +                       return -1;
> +               }
> +               p11 = (struct ieee_c37_238_2011_tlv *) extra->tlv;
> +               p11->type = TLV_ORGANIZATION_EXTENSION;
> +               p11->length = sizeof(*p11) - sizeof(p11->type) - sizeof(p11->length);
> +               memcpy(p11->id, ieeec37_238_id, sizeof(ieeec37_238_id));
> +               p11->subtype[2] = 1;
> +               p11->grandmasterID = p->pwr.grandmasterID;
> +               p11->grandmasterTimeInaccuracy = p->pwr.grandmasterTimeInaccuracy;
> +               p11->networkTimeInaccuracy = p->pwr.networkTimeInaccuracy;
> +               break;
> +       case IEEE_C37_238_VERSION_2017:
> +               extra = msg_tlv_append(m, sizeof(*p17));
> +               if (!extra) {
> +                       return -1;
> +               }
> +               p17 = (struct ieee_c37_238_2017_tlv *) extra->tlv;
> +               p17->type = TLV_ORGANIZATION_EXTENSION;
> +               p17->length = sizeof(*p17) - sizeof(p17->type) - sizeof(p17->length);
> +               memcpy(p17->id, ieeec37_238_id, sizeof(ieeec37_238_id));
> +               p17->subtype[2] = 2;
> +               p17->grandmasterID = p->pwr.grandmasterID;
> +               p17->totalTimeInaccuracy = p->pwr.totalTimeInaccuracy;
> +               break;
> +       }
> +       return 0;
> +}
> +
>  static int net_sync_resp_append(struct port *p, struct ptp_message *m)
>  {
>         struct timePropertiesDS tp = clock_time_properties(p->clock);
> @@ -1501,6 +1541,9 @@ int port_tx_announce(struct port *p, struct address *dst, uint16_t sequence_id)
>         if (p->path_trace_enabled && path_trace_append(p, msg, dad)) {
>                 pr_err("%s: append path trace failed", p->log_name);
>         }
> +       if (ieee_c37_238_append(p, msg)) {
> +               pr_err("%s: append power profile failed", p->log_name);
> +       }
> 
>         err = port_prepare_and_send(p, msg, TRANS_GENERAL);
>         if (err) {
> @@ -3171,6 +3214,16 @@ struct port *port_open(const char *phc_device,
>         p->state = PS_INITIALIZING;
>         p->delayMechanism = config_get_int(cfg, p->name, "delay_mechanism");
>         p->versionNumber = PTP_MAJOR_VERSION;
> +       p->pwr.version =
> +               config_get_int(cfg, p->name, "power_profile.version");
> +       p->pwr.grandmasterID =
> +               config_get_int(cfg, p->name, "power_profile.grandmasterID");
> +       p->pwr.grandmasterTimeInaccuracy =
> +               config_get_int(cfg, p->name, "power_profile.2011.grandmasterTimeInaccuracy");
> +       p->pwr.networkTimeInaccuracy =
> +               config_get_int(cfg, p->name, "power_profile.2011.networkTimeInaccuracy");
> +       p->pwr.totalTimeInaccuracy =
> +               config_get_int(cfg, p->name, "power_profile.2017.totalTimeInaccuracy");
>         p->slave_event_monitor = clock_slave_monitor(clock);
> 
>         if (!port_is_uds(p) && unicast_client_initialize(p)) {
> diff --git a/port_private.h b/port_private.h
> index 19eb944..98ae31b 100644
> --- a/port_private.h
> +++ b/port_private.h
> @@ -26,6 +26,7 @@
>  #include "fsm.h"
>  #include "monitor.h"
>  #include "msg.h"
> +#include "power_profile.h"
>  #include "tmv.h"
> 
>  #define NSEC2SEC 1000000000LL
> @@ -150,6 +151,8 @@ struct port {
>         LIST_HEAD(fm, foreign_clock) foreign_masters;
>         /* TC book keeping */
>         TAILQ_HEAD(tct, tc_txd) tc_transmitted;
> +       /* power profile */
> +       struct ieee_c37_238_settings_np pwr;
I had the feeling that power profiles attributes such as
totalTimeInaccuracy should be properties of the clock.
In case of a simple BC or jbod, all the ports need to share the
same inaccuracy.
This way, we don't rely on users to forward the inaccuracy between ports
with management tlvs. We also know that the totalTimeInaccuracy will
always be the sum of other fields. So i don't think we should allow
setting it through mgt.
For instance:
((T)TI is for (Total)TimeInaccuracy)
clock.internalTI = 0
clock.recv_TTI = 0
update_clockTTI():
	clock.totalTimeInaccuracy = cloc.recv_TTI + clock.internalTI
port received ieee_c37_238 tlv:
	-> clock.recv_TTI = tlv.TTI
	update_clockTTI()
port received MID_POWER_PROFILE_SETTINGS_NP
	-> BC ?
	    (clock.internalTI = distributionTI) :
	    clock.internalTI = grandMasterTI + networkTI
	update_clockTTI()
Olivier
 | 
| [Linuxptp-devel] [PATCH RFC v3 08/10] Implement the
 ALTERNATE_TIME_OFFSET_ENABLE management message.From: Richard C. <ric...@gm...> - 2021-11-09 20:12:14 | 
| If the local PTP management client enables a time zone, append the
matching TLV to outgoing Announce messages.
Signed-off-by: Richard Cochran <ric...@gm...>
---
 clock.c      | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 clock.h      |  8 ++++++
 pmc.c        |  8 ++++++
 pmc_common.c | 17 +++++++++++-
 port.c       |  3 ++
 5 files changed, 110 insertions(+), 3 deletions(-)
diff --git a/clock.c b/clock.c
index 5460c06..9ad418d 100644
--- a/clock.c
+++ b/clock.c
@@ -156,6 +156,40 @@ static int clock_resize_pollfd(struct clock *c, int new_nports);
 static void clock_remove_port(struct clock *c, struct port *p);
 static void clock_stats_display(struct clock_stats *s);
 
+static int clock_alttime_offset_append(struct clock *c, int key, struct ptp_message *m)
+{
+	struct alternate_time_offset_indicator_tlv *atoi;
+	struct tlv_extra *extra;
+	int tlv_len;
+
+	tlv_len = sizeof(*atoi) + c->tz[key].display_name.length;
+	if (tlv_len % 2) {
+		tlv_len++;
+	}
+	extra = msg_tlv_append(m, tlv_len);
+	if (!extra) {
+		return -1;
+	}
+	atoi = (struct alternate_time_offset_indicator_tlv *) extra->tlv;
+	atoi->type = TLV_ALTERNATE_TIME_OFFSET_INDICATOR;
+	atoi->length = tlv_len - sizeof(atoi->type) - sizeof(atoi->length);
+	atoi->keyField = key;
+
+	/* Message alignment broken by design. */
+	memcpy(&atoi->currentOffset, &c->tz[key].current_offset,
+	       sizeof(atoi->currentOffset));
+
+	memcpy(&atoi->jumpSeconds, &c->tz[key].jump_seconds,
+	       sizeof(atoi->jumpSeconds));
+
+	memcpy(&atoi->timeOfNextJump.seconds_lsb, &c->tz[key].next_jump,
+	       sizeof(atoi->timeOfNextJump.seconds_lsb));
+
+	ptp_text_copy(&atoi->displayName, &c->tz[key].display_name);
+
+	return 0;
+}
+
 uint8_t clock_alttime_offset_get_key(struct ptp_message *req)
 {
 	struct management_tlv_datum *mtd;
@@ -461,6 +495,16 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 		mtd->val = c->tds.flags & PTP_TIMESCALE;
 		datalen = sizeof(*mtd);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
+		key = clock_alttime_offset_get_key(req);
+		if (key >= MAX_TIME_ZONES) {
+			break;
+		}
+		mtd = (struct management_tlv_datum *) tlv->data;
+		mtd->val = key;
+		mtd->reserved = c->tz[key].enabled ? 1 : 0;
+		datalen = sizeof(*mtd);
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_NAME:
 		key = clock_alttime_offset_get_key(req);
 		if (key >= MAX_TIME_ZONES) {
@@ -571,7 +615,7 @@ static int clock_management_set(struct clock *c, struct port *p,
 	struct management_tlv_datum *mtd;
 	struct subscribe_events_np *sen;
 	struct management_tlv *tlv;
-	int key, respond = 0;
+	int k, key, respond = 0;
 
 	tlv = (struct management_tlv *) req->management.suffix;
 
@@ -588,6 +632,20 @@ static int clock_management_set(struct clock *c, struct port *p,
 		*changed = 1;
 		respond = 1;
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
+		mtd = (struct management_tlv_datum *) tlv->data;
+		key = mtd->val;
+		if (key == 0xff) {
+			for (k = 0; k < MAX_TIME_ZONES; k++) {
+				c->tz[k].enabled = mtd->reserved & 1 ? true : false;
+			}
+			respond = 1;
+		}
+		if (key < MAX_TIME_ZONES) {
+			c->tz[key].enabled = mtd->reserved & 1 ? true : false;
+			respond = 1;
+		}
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_NAME:
 		aton = (struct alternate_time_offset_name *) tlv->data;
 		key = aton->keyField;
@@ -889,6 +947,22 @@ static int forwarding(struct clock *c, struct port *p)
 
 /* public methods */
 
+int clock_append_timezones(struct clock *c, struct ptp_message *m)
+{
+	int err = 0, i;
+
+	for (i = 0; i < MAX_TIME_ZONES; i++) {
+		if (!c->tz[i].enabled) {
+			continue;
+		}
+		err = clock_alttime_offset_append(c, i, m);
+		if (err) {
+			break;
+		}
+	}
+	return err;
+}
+
 UInteger8 clock_class(struct clock *c)
 {
 	return c->dds.clockQuality.clockClass;
@@ -1605,7 +1679,6 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg)
 	case MID_GRANDMASTER_CLUSTER_TABLE:
 	case MID_ACCEPTABLE_MASTER_TABLE:
 	case MID_ACCEPTABLE_MASTER_MAX_TABLE_SIZE:
-	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
 	case MID_ALTERNATE_TIME_OFFSET_MAX_KEY:
 	case MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET:
 	case MID_PRIMARY_DOMAIN:
diff --git a/clock.h b/clock.h
index 0534f21..ce9ae91 100644
--- a/clock.h
+++ b/clock.h
@@ -43,6 +43,14 @@ enum clock_type {
 	CLOCK_TYPE_MANAGEMENT = 0x0800,
 };
 
+/**
+ * Appends the active time zone TLVs to a given message.
+ * @param c          The clock instance.
+ * @param m          The message that will receive the TLVs.
+ * @return           Zero on success, non-zero otherwise.
+ */
+int clock_append_timezones(struct clock *c, struct ptp_message *m);
+
 /**
  * Obtains a reference to the best foreign master of a clock.
  * @param c  The clock instance.
diff --git a/pmc.c b/pmc.c
index 3f90236..7c711d4 100644
--- a/pmc.c
+++ b/pmc.c
@@ -338,6 +338,14 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
 		fprintf(fp, "TIMESCALE_PROPERTIES "
 			IFMT "ptpTimescale %d", mtd->val & PTP_TIMESCALE ? 1 : 0);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
+		mtd = (struct management_tlv_datum *) mgt->data;
+		fprintf(fp, "ALTERNATE_TIME_OFFSET_ENABLE "
+			IFMT "keyField       %hhu"
+			IFMT "enable         %d",
+			mtd->val,
+			mtd->reserved & 1 ? 1 : 0);
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_NAME:
 		aton = (struct alternate_time_offset_name *) mgt->data;
 		fprintf(fp, "ALTERNATE_TIME_OFFSET_NAME "
diff --git a/pmc_common.c b/pmc_common.c
index 77d50d4..d811b04 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -102,7 +102,7 @@ struct management_id idtab[] = {
 	{ "GRANDMASTER_CLUSTER_TABLE", MID_GRANDMASTER_CLUSTER_TABLE, not_supported },
 	{ "ACCEPTABLE_MASTER_TABLE", MID_ACCEPTABLE_MASTER_TABLE, not_supported },
 	{ "ACCEPTABLE_MASTER_MAX_TABLE_SIZE", MID_ACCEPTABLE_MASTER_MAX_TABLE_SIZE, not_supported },
-	{ "ALTERNATE_TIME_OFFSET_ENABLE", MID_ALTERNATE_TIME_OFFSET_ENABLE, not_supported },
+	{ "ALTERNATE_TIME_OFFSET_ENABLE", MID_ALTERNATE_TIME_OFFSET_ENABLE, do_set_action },
 	{ "ALTERNATE_TIME_OFFSET_NAME", MID_ALTERNATE_TIME_OFFSET_NAME, do_set_action },
 	{ "ALTERNATE_TIME_OFFSET_MAX_KEY", MID_ALTERNATE_TIME_OFFSET_MAX_KEY, not_supported },
 	{ "ALTERNATE_TIME_OFFSET_PROPERTIES", MID_ALTERNATE_TIME_OFFSET_PROPERTIES, do_set_action },
@@ -159,6 +159,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 	char onoff_time_status[4] = "off";
 	char display_name[11] = {0};
 	uint8_t key;
+	int enable;
 
 	switch (action) {
 	case GET:
@@ -185,6 +186,17 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 		}
 		pmc_send_set_action(pmc, code, &mtd, sizeof(mtd));
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
+		cnt = sscanf(str,  " %*s %*s keyField %hhu enable %d",
+			     &mtd.val, &enable);
+		if (cnt != 2) {
+			fprintf(stderr, "%s SET needs 2 values\n",
+				idtab[index].name);
+			break;
+		}
+		mtd.reserved = enable ? 1 : 0;
+		pmc_send_set_action(pmc, code, &mtd, sizeof(mtd));
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_NAME:
 		cnt = sscanf(str, " %*s %*s "
 			     "keyField       %hhu "
@@ -586,6 +598,9 @@ static int pmc_tlv_datalen(struct pmc *pmc, int id)
 	case MID_TIME_STATUS_NP:
 		len += sizeof(struct time_status_np);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
+		len += sizeof(struct management_tlv_datum);
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_NAME:
 		len += sizeof(struct alternate_time_offset_name);
 		break;
diff --git a/port.c b/port.c
index d204dab..d046ef6 100644
--- a/port.c
+++ b/port.c
@@ -1562,6 +1562,9 @@ int port_tx_announce(struct port *p, struct address *dst, uint16_t sequence_id)
 	if (ieee_c37_238_append(p, msg)) {
 		pr_err("%s: append power profile failed", p->log_name);
 	}
+	if (clock_append_timezones(p->clock, msg)) {
+		pr_err("%s: append time zones failed", p->log_name);
+	}
 
 	err = port_prepare_and_send(p, msg, TRANS_GENERAL);
 	if (err) {
-- 
2.20.1
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2021-11-09 20:12:16
       | 
| Signed-off-by: Richard Cochran <ric...@gm...>
---
 clock.c | 22 +++++++++++++++++-----
 tz.h    | 26 ++++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 5 deletions(-)
 create mode 100644 tz.h
diff --git a/clock.c b/clock.c
index bbfd1a8..b5b729a 100644
--- a/clock.c
+++ b/clock.c
@@ -42,6 +42,7 @@
 #include "rtnl.h"
 #include "tlv.h"
 #include "tsproc.h"
+#include "tz.h"
 #include "uds.h"
 #include "util.h"
 
@@ -79,6 +80,14 @@ struct clock_subscriber {
 	time_t expiration;
 };
 
+struct time_zone {
+	bool enabled;
+	int32_t current_offset;
+	int32_t jump_seconds;
+	uint32_t next_jump;
+	struct static_ptp_text display_name;
+};
+
 struct clock {
 	enum clock_type type;
 	struct config *config;
@@ -137,6 +146,7 @@ struct clock {
 	struct monitor *slave_event_monitor;
 	int step_window_counter;
 	int step_window;
+	struct time_zone tz[MAX_TIME_ZONES];
 };
 
 struct clock the_clock;
@@ -894,18 +904,16 @@ int clock_required_modes(struct clock *c)
 struct clock *clock_create(enum clock_type type, struct config *config,
 			   const char *phc_device)
 {
+	int fadj = 0, i, max_adj = 0, phc_index, required_modes = 0, sfl, sw_ts;
 	enum servo_type servo = config_get_int(config, NULL, "clock_servo");
 	char ts_label[IF_NAMESIZE], phc[32], *tmp;
 	enum timestamp_type timestamping;
-	int fadj = 0, max_adj = 0, sw_ts;
-	int phc_index, required_modes = 0;
 	struct clock *c = &the_clock;
-	const char *uds_ifname;
-	struct port *p;
 	unsigned char oui[OUI_LEN];
 	struct interface *iface;
+	const char *uds_ifname;
 	struct timespec ts;
-	int sfl;
+	struct port *p;
 
 	clock_gettime(CLOCK_REALTIME, &ts);
 	srandom(ts.tv_sec ^ ts.tv_nsec);
@@ -1203,6 +1211,10 @@ struct clock *clock_create(enum clock_type type, struct config *config,
 	c->dad.pds.observedParentClockPhaseChangeRate    = 0x7fffffff;
 	c->dad.ptl = c->ptl;
 
+	for (i = 0; i < MAX_TIME_ZONES; i++) {
+		c->tz[i].display_name.max_symbols = MAX_TZ_DISPLAY_NAME;
+	}
+
 	clock_sync_interval(c, 0);
 
 	LIST_INIT(&c->subscribers);
diff --git a/tz.h b/tz.h
new file mode 100644
index 0000000..986f976
--- /dev/null
+++ b/tz.h
@@ -0,0 +1,26 @@
+/**
+ * @file tz.h
+ * @brief Implements time zone constants.
+ * @note Copyright (C) 2021 Richard Cochran <ric...@gm...>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef HAVE_TZ_H
+#define HAVE_TZ_H
+
+#define MAX_TZ_DISPLAY_NAME	10
+#define MAX_TIME_ZONES		4
+
+#endif
-- 
2.20.1
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2021-11-09 20:12:11
       | 
| Signed-off-by: Richard Cochran <ric...@gm...>
---
 tlv.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tlv.h | 26 ++++++++++++++++++++++++++
 2 files changed, 84 insertions(+)
diff --git a/tlv.c b/tlv.c
index d9d2846..03faa74 100644
--- a/tlv.c
+++ b/tlv.c
@@ -77,6 +77,22 @@ static uint16_t flip16(void *p)
 	return v;
 }
 
+static void host2net32_unaligned(void *p)
+{
+	int32_t v;
+	memcpy(&v, p, sizeof(v));
+	v = htonl(v);
+	memcpy(p, &v, sizeof(v));
+}
+
+static void net2host32_unaligned(void *p)
+{
+	int32_t v;
+	memcpy(&v, p, sizeof(v));
+	v = ntohl(v);
+	memcpy(p, &v, sizeof(v));
+}
+
 static int64_t host2net64_unaligned(void *p)
 {
 	int64_t v;
@@ -111,6 +127,43 @@ static bool tlv_array_invalid(struct TLV *tlv, size_t base_size, size_t item_siz
 	return (tlv->length == expected_length) ? false : true;
 }
 
+static int alttime_offset_post_recv(struct tlv_extra *extra)
+{
+	struct TLV *tlv = extra->tlv;
+	struct alternate_time_offset_indicator_tlv *atoi =
+		(struct alternate_time_offset_indicator_tlv *) tlv;
+
+	if (tlv->length < sizeof(struct alternate_time_offset_indicator_tlv) +
+	    atoi->displayName.length - sizeof(struct TLV)) {
+		return -EBADMSG;
+	}
+
+	NTOHS(atoi->type);
+	NTOHS(atoi->length);
+	/* Message alignment broken by design. */
+	net2host32_unaligned(&atoi->currentOffset);
+	net2host32_unaligned(&atoi->jumpSeconds);
+	flip16(&atoi->timeOfNextJump.seconds_msb);
+	net2host32_unaligned(&atoi->timeOfNextJump.seconds_lsb);
+
+	return 0;
+}
+
+static void alttime_offset_pre_send(struct tlv_extra *extra)
+{
+	struct alternate_time_offset_indicator_tlv *atoi;
+
+	atoi = (struct alternate_time_offset_indicator_tlv *) extra->tlv;
+
+	HTONS(atoi->type);
+	HTONS(atoi->length);
+	/* Message alignment broken by design. */
+	host2net32_unaligned(&atoi->currentOffset);
+	host2net32_unaligned(&atoi->jumpSeconds);
+	flip16(&atoi->timeOfNextJump.seconds_msb);
+	host2net32_unaligned(&atoi->timeOfNextJump.seconds_lsb);
+}
+
 static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 			 struct tlv_extra *extra)
 {
@@ -914,6 +967,8 @@ int tlv_post_recv(struct tlv_extra *extra)
 		}
 		break;
 	case TLV_ALTERNATE_TIME_OFFSET_INDICATOR:
+		result = alttime_offset_post_recv(extra);
+		break;
 	case TLV_AUTHENTICATION_2008:
 	case TLV_AUTHENTICATION_CHALLENGE:
 	case TLV_SECURITY_ASSOCIATION_UPDATE:
@@ -977,7 +1032,10 @@ void tlv_pre_send(struct TLV *tlv, struct tlv_extra *extra)
 		unicast_negotiation_pre_send(tlv);
 		break;
 	case TLV_PATH_TRACE:
+		break;
 	case TLV_ALTERNATE_TIME_OFFSET_INDICATOR:
+		alttime_offset_pre_send(extra);
+		break;
 	case TLV_AUTHENTICATION_2008:
 	case TLV_AUTHENTICATION_CHALLENGE:
 	case TLV_SECURITY_ASSOCIATION_UPDATE:
diff --git a/tlv.h b/tlv.h
index e7d2a2d..61fd817 100644
--- a/tlv.h
+++ b/tlv.h
@@ -169,6 +169,32 @@ struct grant_unicast_xmit_tlv {
 	uint8_t         flags;
 } PACKED;
 
+struct alternate_time_offset_indicator_tlv {
+	Enumeration16   type;
+	UInteger16      length;
+	UInteger8       keyField;
+	/* Message alignment broken by design. */
+	Integer32       currentOffset;
+	Integer32       jumpSeconds;
+	struct {
+		uint16_t   seconds_msb; /* 16 bits + */
+		uint32_t   seconds_lsb; /* 32 bits = 48 bits*/
+	} PACKED timeOfNextJump;
+	struct PTPText  displayName;
+} PACKED;
+
+struct alternate_time_offset_properties {
+	UInteger8       keyField;
+	/* Message alignment broken by design. */
+	Integer32       currentOffset;
+	Integer32       jumpSeconds;
+	struct {
+		uint16_t   seconds_msb; /* 16 bits + */
+		uint32_t   seconds_lsb; /* 32 bits = 48 bits*/
+	} PACKED timeOfNextJump;
+	uint8_t pad;
+} PACKED;
+
 struct management_tlv {
 	Enumeration16 type;
 	UInteger16    length;
-- 
2.20.1
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2021-11-09 20:12:13
       | 
| Signed-off-by: Richard Cochran <ric...@gm...>
---
 clock.c      | 21 +++++++++++++++++++-
 pmc.c        |  9 +++++++++
 pmc_common.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 tlv.c        | 11 +++++++++++
 tlv.h        |  5 +++++
 5 files changed, 100 insertions(+), 2 deletions(-)
diff --git a/clock.c b/clock.c
index 1e3ff3e..5460c06 100644
--- a/clock.c
+++ b/clock.c
@@ -380,6 +380,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 					  struct ptp_message *rsp, int id)
 {
 	struct alternate_time_offset_properties *atop;
+	struct alternate_time_offset_name *aton;
 	struct grandmaster_settings_np *gsn;
 	struct management_tlv_datum *mtd;
 	struct subscribe_events_np *sen;
@@ -460,6 +461,16 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 		mtd->val = c->tds.flags & PTP_TIMESCALE;
 		datalen = sizeof(*mtd);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_NAME:
+		key = clock_alttime_offset_get_key(req);
+		if (key >= MAX_TIME_ZONES) {
+			break;
+		}
+		aton = (struct alternate_time_offset_name *) tlv->data;
+		aton->keyField = key;
+		ptp_text_copy(&aton->displayName, &c->tz[key].display_name);
+		datalen = sizeof(*aton) + aton->displayName.length;
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		key = clock_alttime_offset_get_key(req);
 		if (key >= MAX_TIME_ZONES) {
@@ -555,6 +566,7 @@ static int clock_management_set(struct clock *c, struct port *p,
 				int id, struct ptp_message *req, int *changed)
 {
 	struct alternate_time_offset_properties *atop;
+	struct alternate_time_offset_name *aton;
 	struct grandmaster_settings_np *gsn;
 	struct management_tlv_datum *mtd;
 	struct subscribe_events_np *sen;
@@ -576,6 +588,14 @@ static int clock_management_set(struct clock *c, struct port *p,
 		*changed = 1;
 		respond = 1;
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_NAME:
+		aton = (struct alternate_time_offset_name *) tlv->data;
+		key = aton->keyField;
+		if (key < MAX_TIME_ZONES &&
+		    !static_ptp_text_copy(&c->tz[key].display_name, &aton->displayName)) {
+			respond = 1;
+		}
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		atop = (struct alternate_time_offset_properties *) tlv->data;
 		key = atop->keyField;
@@ -1586,7 +1606,6 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg)
 	case MID_ACCEPTABLE_MASTER_TABLE:
 	case MID_ACCEPTABLE_MASTER_MAX_TABLE_SIZE:
 	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
-	case MID_ALTERNATE_TIME_OFFSET_NAME:
 	case MID_ALTERNATE_TIME_OFFSET_MAX_KEY:
 	case MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET:
 	case MID_PRIMARY_DOMAIN:
diff --git a/pmc.c b/pmc.c
index e4dd566..3f90236 100644
--- a/pmc.c
+++ b/pmc.c
@@ -140,6 +140,7 @@ static void pmc_show_signaling(struct ptp_message *msg, FILE *fp)
 static void pmc_show(struct ptp_message *msg, FILE *fp)
 {
 	struct alternate_time_offset_properties *atop;
+	struct alternate_time_offset_name *aton;
 	struct ieee_c37_238_settings_np *pwr;
 	struct grandmaster_settings_np *gsn;
 	struct mgmt_clock_description *cd;
@@ -337,6 +338,14 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
 		fprintf(fp, "TIMESCALE_PROPERTIES "
 			IFMT "ptpTimescale %d", mtd->val & PTP_TIMESCALE ? 1 : 0);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_NAME:
+		aton = (struct alternate_time_offset_name *) mgt->data;
+		fprintf(fp, "ALTERNATE_TIME_OFFSET_NAME "
+			IFMT "keyField       %hhu"
+			IFMT "displayName    %s",
+			aton->keyField,
+			text2str(&aton->displayName));
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		atop = (struct alternate_time_offset_properties *) mgt->data;
 		fprintf(fp, "ALTERNATE_TIME_OFFSET_PROPERTIES "
diff --git a/pmc_common.c b/pmc_common.c
index 6ba8616..77d50d4 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -60,6 +60,7 @@ static void do_get_action(struct pmc *pmc, int action, int index, char *str);
 static void do_set_action(struct pmc *pmc, int action, int index, char *str);
 static void not_supported(struct pmc *pmc, int action, int index, char *str);
 static void null_management(struct pmc *pmc, int action, int index, char *str);
+static int send_set_aton(struct pmc *pmc, int id, uint8_t key, const char *name);
 
 static const char *action_string[] = {
 	"GET",
@@ -102,7 +103,7 @@ struct management_id idtab[] = {
 	{ "ACCEPTABLE_MASTER_TABLE", MID_ACCEPTABLE_MASTER_TABLE, not_supported },
 	{ "ACCEPTABLE_MASTER_MAX_TABLE_SIZE", MID_ACCEPTABLE_MASTER_MAX_TABLE_SIZE, not_supported },
 	{ "ALTERNATE_TIME_OFFSET_ENABLE", MID_ALTERNATE_TIME_OFFSET_ENABLE, not_supported },
-	{ "ALTERNATE_TIME_OFFSET_NAME", MID_ALTERNATE_TIME_OFFSET_NAME, not_supported },
+	{ "ALTERNATE_TIME_OFFSET_NAME", MID_ALTERNATE_TIME_OFFSET_NAME, do_set_action },
 	{ "ALTERNATE_TIME_OFFSET_MAX_KEY", MID_ALTERNATE_TIME_OFFSET_MAX_KEY, not_supported },
 	{ "ALTERNATE_TIME_OFFSET_PROPERTIES", MID_ALTERNATE_TIME_OFFSET_PROPERTIES, do_set_action },
 	{ "MASTER_ONLY", MID_MASTER_ONLY, do_get_action },
@@ -156,6 +157,8 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 	struct port_ds_np pnp;
 	char onoff_port_state[4] = "off";
 	char onoff_time_status[4] = "off";
+	char display_name[11] = {0};
+	uint8_t key;
 
 	switch (action) {
 	case GET:
@@ -182,6 +185,19 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 		}
 		pmc_send_set_action(pmc, code, &mtd, sizeof(mtd));
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_NAME:
+		cnt = sscanf(str, " %*s %*s "
+			     "keyField       %hhu "
+			     "displayName    %10s ",
+			     &key,
+			     display_name);
+		if (cnt != 2) {
+			fprintf(stderr, "%s SET needs 2 values\n",
+				idtab[index].name);
+			break;
+		}
+		send_set_aton(pmc, code, key, display_name);
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		memset(&atop, 0, sizeof(atop));
 		cnt = sscanf(str, " %*s %*s "
@@ -570,6 +586,9 @@ static int pmc_tlv_datalen(struct pmc *pmc, int id)
 	case MID_TIME_STATUS_NP:
 		len += sizeof(struct time_status_np);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_NAME:
+		len += sizeof(struct alternate_time_offset_name);
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		len += sizeof(struct alternate_time_offset_properties);
 		break;
@@ -683,6 +702,41 @@ int pmc_send_set_action(struct pmc *pmc, int id, void *data, int datasize)
 	return 0;
 }
 
+static int send_set_aton(struct pmc *pmc, int id, uint8_t key, const char *name)
+{
+	struct alternate_time_offset_name *aton;
+	struct management_tlv *mgt;
+	struct ptp_message *msg;
+	struct tlv_extra *extra;
+	int datasize;
+
+	datasize = sizeof(*aton) + strlen(name);
+	if (datasize % 2) {
+		datasize++;
+	}
+	msg = pmc_message(pmc, SET);
+	if (!msg) {
+		return -1;
+	}
+	extra = msg_tlv_append(msg, sizeof(*mgt) + datasize);
+	if (!extra) {
+		msg_put(msg);
+		return -ENOMEM;
+	}
+	mgt = (struct management_tlv *) extra->tlv;
+	mgt->type = TLV_MANAGEMENT;
+	mgt->length = 2 + datasize;
+	mgt->id = id;
+	aton = (struct alternate_time_offset_name *) mgt->data;
+	aton->keyField = key;
+	ptp_text_set(&aton->displayName, name);
+
+	pmc_send(pmc, msg);
+	msg_put(msg);
+
+	return 0;
+}
+
 struct ptp_message *pmc_recv(struct pmc *pmc)
 {
 	struct ptp_message *msg;
diff --git a/tlv.c b/tlv.c
index fdff99a..243906a 100644
--- a/tlv.c
+++ b/tlv.c
@@ -168,6 +168,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 			 struct tlv_extra *extra)
 {
 	struct alternate_time_offset_properties *atop;
+	struct alternate_time_offset_name *aton;
 	struct ieee_c37_238_settings_np *pwr;
 	struct grandmaster_settings_np *gsn;
 	struct mgmt_clock_description *cd;
@@ -334,6 +335,14 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 		p->portIdentity.portNumber = ntohs(p->portIdentity.portNumber);
 		p->peerMeanPathDelay = net2host64(p->peerMeanPathDelay);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_NAME:
+		aton = (struct alternate_time_offset_name *) m->data;
+		if (data_len < sizeof(*aton)) {
+			goto bad_length;
+		}
+		extra_len = sizeof(*aton);
+		extra_len += aton->displayName.length;
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		atop = (struct alternate_time_offset_properties *) m->data;
 		if (data_len != sizeof(*atop)) {
@@ -489,6 +498,8 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)
 		p->portIdentity.portNumber = htons(p->portIdentity.portNumber);
 		p->peerMeanPathDelay = host2net64(p->peerMeanPathDelay);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_NAME:
+		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		atop = (struct alternate_time_offset_properties *) m->data;
 		/* Message alignment broken by design. */
diff --git a/tlv.h b/tlv.h
index 61fd817..7a14b1d 100644
--- a/tlv.h
+++ b/tlv.h
@@ -183,6 +183,11 @@ struct alternate_time_offset_indicator_tlv {
 	struct PTPText  displayName;
 } PACKED;
 
+struct alternate_time_offset_name {
+	UInteger8       keyField;
+	struct PTPText  displayName;
+} PACKED;
+
 struct alternate_time_offset_properties {
 	UInteger8       keyField;
 	/* Message alignment broken by design. */
-- 
2.20.1
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2021-11-09 20:12:16
       | 
| The ptp4l program supports up to four time zones via the
ALTERNATE_TIME_OFFSET_INDICATOR TLV.  Introduce a helper program that
leverages the local time zone database to monitor for changes in
daylight savings time and publishing them.
Signed-off-by: Richard Cochran <ric...@gm...>
---
 makefile |   7 +-
 tztool.c | 377 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 382 insertions(+), 2 deletions(-)
 create mode 100644 tztool.c
diff --git a/makefile b/makefile
index 33e7ca0..811468d 100644
--- a/makefile
+++ b/makefile
@@ -22,7 +22,7 @@ CC	= $(CROSS_COMPILE)gcc
 VER     = -DVER=$(version)
 CFLAGS	= -Wall $(VER) $(incdefs) $(DEBUG) $(EXTRA_CFLAGS)
 LDLIBS	= -lm -lrt -pthread $(EXTRA_LDFLAGS)
-PRG	= ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster ts2phc
+PRG	= ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster ts2phc tztool
 FILTERS	= filter.o mave.o mmedian.o
 SERVOS	= linreg.o ntpshm.o nullf.o pi.o servo.o
 TRANSP	= raw.o transport.o udp.o udp6.o uds.o
@@ -35,7 +35,7 @@ OBJ	= bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \
  unicast_fsm.o unicast_service.o util.o version.o
 
 OBJECTS	= $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_agent.o \
- pmc_common.o sysoff.o timemaster.o $(TS2PHC)
+ pmc_common.o sysoff.o timemaster.o $(TS2PHC) tztool.o
 SRC	= $(OBJECTS:.o=.c)
 DEPEND	= $(OBJECTS:.o=.d)
 srcdir	:= $(dir $(lastword $(MAKEFILE_LIST)))
@@ -71,6 +71,9 @@ timemaster: phc.o print.o rtnl.o sk.o timemaster.o util.o version.o
 ts2phc: config.o clockadj.o hash.o interface.o phc.o print.o $(SERVOS) sk.o \
  $(TS2PHC) util.o version.o
 
+tztool: config.o hash.o interface.o lstab.o msg.o phc.o pmc_common.o print.o \
+ sk.o tlv.o $(TRANSP) tztool.o util.o version.o
+
 version.o: .version version.sh $(filter-out version.d,$(DEPEND))
 
 .version: force
diff --git a/tztool.c b/tztool.c
new file mode 100644
index 0000000..a7a2a69
--- /dev/null
+++ b/tztool.c
@@ -0,0 +1,377 @@
+/**
+ * @file tztool.c
+ * @note Copyright (C) 2021 Richard Cochran <ric...@gm...>
+ * @note SPDX-License-Identifier: GPL-2.0+
+ */
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "lstab.h"
+#include "pmc_common.h"
+#include "print.h"
+#include "version.h"
+#include "tz.h"
+
+#define DEFAULT_TZ	"PST8PDT"
+#define DEFAULT_PERIOD	3600
+#define DEFAULT_WINDOW	(3600 * 24 * 30 * 3)
+
+static int key_field, period = DEFAULT_PERIOD, window = DEFAULT_WINDOW;
+static char uds_local[MAX_IFNAME_SIZE + 1];
+static struct lstab *lstab;
+static struct config *cfg;
+
+struct tzinfo {
+	const char		*name;
+	char			display_name[MAX_TZ_DISPLAY_NAME + 1];
+	time_t			timestamp;
+
+	/* Following fields populated by get_offsets. */
+
+	time_t			local_utc_offset;
+	time_t			local_tai_offset;
+	int			tai_utc_offset;
+	enum lstab_result	tai_result;
+};
+
+static int get_offsets(struct tzinfo *tz);
+
+static bool offsets_equal(struct tzinfo *a, struct tzinfo *b)
+{
+	return a->local_utc_offset == b->local_utc_offset;
+}
+
+static bool find_next_discontinuity(struct tzinfo *tz, struct tzinfo *next)
+{
+	time_t i, j, n;
+	bool gt;
+
+	next->timestamp = tz->timestamp + window;
+	get_offsets(next);
+	if (offsets_equal(tz, next)) {
+		return false;
+	}
+
+	i = 0;
+	j = window;
+
+	while (1) {
+		next->timestamp = tz->timestamp + i;
+		get_offsets(next);
+		gt = offsets_equal(tz, next);
+		if (gt) {
+			n = j - i - 1;
+		} else {
+			j = i;
+			i = 0;
+			n = j - i - 1;
+		}
+		if (!n) {
+			if (gt) {
+				next->timestamp++;
+				get_offsets(next);
+			}
+			break;
+		}
+		i += (n + 1) / 2;
+	}
+
+	return true;
+}
+
+static int get_offsets(struct tzinfo *tz)
+{
+	struct tm tm = {0};
+	time_t t2;
+
+	tz->tai_result = lstab_utc2tai(lstab, tz->timestamp,
+				       &tz->tai_utc_offset);
+	if (tz->tai_result == LSTAB_UNKNOWN) {
+		pr_err("leap second table is stale");
+		return -1;
+	}
+
+	setenv("TZ", tz->name, 1);
+	tzset();
+	if (!localtime_r(&tz->timestamp, &tm)) {
+		return -1;
+	}
+
+	setenv("TZ", "UTC", 1);
+	tzset();
+	t2 = mktime(&tm);
+	tz->local_utc_offset = t2 - tz->timestamp;
+	tz->local_tai_offset = tz->local_utc_offset - tz->tai_utc_offset;
+
+	return 0;
+}
+
+static int get_unambiguous_time(struct tzinfo *tz)
+{
+	int err;
+
+	do {
+		tz->timestamp = time(NULL);
+		err = get_offsets(tz);
+	} while (tz->tai_result == LSTAB_AMBIGUOUS);
+
+	return err;
+}
+
+static void show_timezone_info(const char *label, struct tzinfo *tz)
+{
+	pr_debug("%s %s ts %ld local-utc %ld tai-utc %d local-tai %ld %s",
+		 label,
+		 tz->name,
+		 tz->timestamp,
+		 tz->local_utc_offset,
+		 tz->tai_utc_offset,
+		 tz->local_tai_offset,
+		 tz->tai_result == LSTAB_OK ? "valid" : "invalid");
+}
+
+/* Returns true if display name was truncated. */
+static bool tz_set_name(struct tzinfo *tz, const char *name)
+{
+	const char *suffix;
+	int len;
+
+	memset(tz->display_name, 0, sizeof(tz->display_name));
+	tz->name = name;
+
+	len = strlen(name);
+	if (len <= MAX_TZ_DISPLAY_NAME) {
+		strncpy(tz->display_name, name, sizeof(tz->display_name) - 1);
+		return false;
+	}
+
+	/*
+	 * The displayName field is limited to 10 characters, but
+	 * there are many valid time zone names like "Europe/Vienna".
+	 * Use the suffix if present.
+	 */
+	suffix = strchr(name, '/');
+	if (suffix) {
+		suffix++;
+		len = strlen(suffix);
+		if (len > 0 && len <= MAX_TZ_DISPLAY_NAME) {
+			strncpy(tz->display_name, suffix,
+				sizeof(tz->display_name) - 1);
+			return true;
+		}
+	}
+
+	/* No nice suffix to be found, so just truncate. */
+	strncpy(tz->display_name, name, sizeof(tz->display_name) - 1);
+
+	return true;
+}
+
+static int update_ptp_serivce(struct tzinfo *tz, struct tzinfo *next)
+{
+	struct alternate_time_offset_properties atop;
+	struct management_tlv_datum mtd;
+	struct pmc *pmc;
+	int err;
+
+	pmc = pmc_create(cfg, TRANS_UDS, uds_local, 0,
+			 config_get_int(cfg, NULL, "domainNumber"),
+			 config_get_int(cfg, NULL, "transportSpecific") << 4, 1);
+	if (!pmc) {
+		return -1;
+	}
+	err = pmc_send_set_aton(pmc, MID_ALTERNATE_TIME_OFFSET_NAME,
+				key_field, tz->display_name);
+	if (err) {
+		return err;
+	}
+	memset(&atop, 0, sizeof(atop));
+	atop.keyField = key_field;
+	atop.currentOffset = tz->local_tai_offset;
+	if (next) {
+		atop.jumpSeconds = next->local_tai_offset - tz->local_tai_offset;
+		atop.timeOfNextJump.seconds_lsb = next->timestamp;
+	}
+	err = pmc_send_set_action(pmc, MID_ALTERNATE_TIME_OFFSET_PROPERTIES,
+				  &atop, sizeof(atop));
+	if (err) {
+		return err;
+	}
+	mtd.val = key_field;
+	mtd.reserved = 1; /*enable field*/
+	err = pmc_send_set_action(pmc, MID_ALTERNATE_TIME_OFFSET_ENABLE,
+				  &mtd, sizeof(mtd));
+	if (err) {
+		return err;
+	}
+
+	pmc_destroy(pmc);
+	return 0;
+}
+
+static int do_tztool(const char *timezone)
+{
+	struct tzinfo nx, tz;
+	const char *leapfile;
+	bool pending;
+	char buf[64];
+	int err;
+
+	if (key_field > MAX_TIME_ZONES - 1) {
+		pr_err("key field %d exceeds maximum of %d", key_field,
+		       MAX_TIME_ZONES - 1);
+		return -1;
+	}
+
+	tz_set_name(&nx, timezone);
+	if (tz_set_name(&tz, timezone)) {
+		pr_info("truncating time zone display name from %s to %s",
+			tz.name, tz.display_name);
+	}
+
+	leapfile = config_get_string(cfg, NULL, "leapfile");
+	if (!leapfile) {
+		pr_err("please specify leap second table with --leapfile");
+		return -1;
+	}
+
+	while (is_running()) {
+
+		/* Read the leap seconds file again as it may have changed. */
+		lstab = lstab_create(leapfile);
+		if (!lstab) {
+			pr_err("failed to create leap second table");
+			return -1;
+		}
+
+		err = get_unambiguous_time(&tz);
+		if (err) {
+			return err;
+		}
+		show_timezone_info("current time  = ", &tz);
+
+		pending = find_next_discontinuity(&tz, &nx);
+		if (pending) {
+			setenv("TZ", nx.name, 1);
+			tzset();
+			if (ctime_r(&nx.timestamp, buf)) {
+				buf[strlen(buf) - 1] = 0;
+			}
+			show_timezone_info("discontinuity = ", &nx);
+			pr_info("next discontinuity %s %s", buf, nx.name);
+		} else {
+			pr_info("no discontinuity within %d second window", window);
+		}
+
+		lstab_destroy(lstab);
+		lstab = NULL;
+
+		err = update_ptp_serivce(&tz, pending ? &nx : NULL);
+		if (err) {
+			pr_err("failed to update PTP service");
+			return err;
+		}
+		puts("");
+		sleep(period);
+	}
+	return 0;
+}
+
+static void usage(char *progname)
+{
+	fprintf(stderr,
+		"\nusage: %s [options]\n\n"
+		" -f [file] read configuration from 'file'\n"
+		" -h        prints this message and exits\n"
+		" -k [num]  key field for the ALTERNATE_TIME_OFFSET_INDICATOR TLV\n"
+		" -p [num]  period between updates in seconds, default %d\n"
+		" -v        prints the software version and exits\n"
+		" -w [num]  look ahead time window in seconds, default %d\n"
+		" -z zone   Time zone string, default '%s'\n"
+		"           See /usr/share/zoneinfo for valid strings\n"
+		"\n",
+		progname, DEFAULT_PERIOD, DEFAULT_WINDOW, DEFAULT_TZ);
+}
+
+int main(int argc, char *argv[])
+{
+	char *config = NULL, *progname, *timezone = DEFAULT_TZ;
+	int c, err = 0, index;
+	struct option *opts;
+
+	if (handle_term_signals()) {
+		return -1;
+	}
+	cfg = config_create();
+	if (!cfg) {
+		return -1;
+	}
+	opts = config_long_options(cfg);
+	print_set_verbose(1);
+	print_set_syslog(0);
+
+	/* Process the command line arguments. */
+	progname = strrchr(argv[0], '/');
+	progname = progname ? 1+progname : argv[0];
+	while (EOF != (c = getopt_long(argc, argv, "f:hk:p:vw:z:", opts, &index))) {
+		switch (c) {
+		case 0:
+			if (config_parse_option(cfg, opts[index].name, optarg)) {
+				config_destroy(cfg);
+				return -1;
+			}
+			break;
+		case 'f':
+			config = optarg;
+			break;
+		case 'k':
+			key_field = atoi(optarg);
+			break;
+		case 'p':
+			period = atoi(optarg);
+			break;
+		case 'v':
+			version_show(stdout);
+			config_destroy(cfg);
+			return 0;
+		case 'w':
+			window = atoi(optarg);
+			break;
+		case 'z':
+			timezone = optarg;
+			break;
+		case 'h':
+			usage(progname);
+			config_destroy(cfg);
+			return 0;
+		case '?':
+		default:
+			usage(progname);
+			config_destroy(cfg);
+			return -1;
+		}
+	}
+
+	print_set_syslog(0);
+	print_set_verbose(1);
+
+	if (config && (err = config_read(config, cfg))) {
+		goto out;
+	}
+
+	print_set_progname(progname);
+	print_set_tag(config_get_string(cfg, NULL, "message_tag"));
+	print_set_level(config_get_int(cfg, NULL, "logging_level"));
+	snprintf(uds_local, sizeof(uds_local), "/var/run/tztool.%d", getpid());
+
+	err = do_tztool(timezone);
+out:
+	config_destroy(cfg);
+	return err;
+}
-- 
2.20.1
 | 
| 
      
      
      From: Richard C. <ric...@gm...> - 2021-11-09 20:12:17
       | 
| The function to set the alternate time offset name, a.k.a. time zone, will
be used by the time zone stand alone program.  Make the function into a
public PMC method.
Signed-off-by: Richard Cochran <ric...@gm...>
---
 pmc_common.c | 5 ++---
 pmc_common.h | 2 ++
 2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/pmc_common.c b/pmc_common.c
index d811b04..7bef6a6 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -60,7 +60,6 @@ static void do_get_action(struct pmc *pmc, int action, int index, char *str);
 static void do_set_action(struct pmc *pmc, int action, int index, char *str);
 static void not_supported(struct pmc *pmc, int action, int index, char *str);
 static void null_management(struct pmc *pmc, int action, int index, char *str);
-static int send_set_aton(struct pmc *pmc, int id, uint8_t key, const char *name);
 
 static const char *action_string[] = {
 	"GET",
@@ -208,7 +207,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 				idtab[index].name);
 			break;
 		}
-		send_set_aton(pmc, code, key, display_name);
+		pmc_send_set_aton(pmc, code, key, display_name);
 		break;
 	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 		memset(&atop, 0, sizeof(atop));
@@ -717,7 +716,7 @@ int pmc_send_set_action(struct pmc *pmc, int id, void *data, int datasize)
 	return 0;
 }
 
-static int send_set_aton(struct pmc *pmc, int id, uint8_t key, const char *name)
+int pmc_send_set_aton(struct pmc *pmc, int id, uint8_t key, const char *name)
 {
 	struct alternate_time_offset_name *aton;
 	struct management_tlv *mgt;
diff --git a/pmc_common.h b/pmc_common.h
index 8bea2e0..6fb2fae 100644
--- a/pmc_common.h
+++ b/pmc_common.h
@@ -41,6 +41,8 @@ int pmc_send_get_action(struct pmc *pmc, int id);
 
 int pmc_send_set_action(struct pmc *pmc, int id, void *data, int datasize);
 
+int pmc_send_set_aton(struct pmc *pmc, int id, uint8_t key, const char *name);
+
 struct ptp_message *pmc_recv(struct pmc *pmc);
 
 int pmc_target(struct pmc *pmc, struct PortIdentity *pid);
-- 
2.20.1
 | 
| 
      
      
      From: Geva, E. <ere...@si...> - 2021-11-10 10:30:19
       | 
| Hi,
According to IEEE 1588 timeOfNextJump is 48 bits.
The alternate_time_offset_properties structure reserve the full 48 bits.
The pre and the post function do full handle.
But the rest handle only the low 32 bits.
I can understand the PMC set use 32 bits only, but the rest?
Erez
-----Original Message-----
From: Richard Cochran <ric...@gm...> 
Sent: Tuesday, 9 November 2021 21:12
To: lin...@li...
Subject: [Linuxptp-devel] [PATCH RFC v3 06/10] Add the ALTERNATE_TIME_OFFSET_PROPERTIES management message.
Signed-off-by: Richard Cochran <ric...@gm...>
---
 clock.c      | 66 ++++++++++++++++++++++++++++++++++++++++++++++++----
 pmc.c        | 13 +++++++++++
 pmc_common.c | 24 ++++++++++++++++++-
 tlv.c        | 21 +++++++++++++++++
 util.h       |  7 ++++++
 5 files changed, 126 insertions(+), 5 deletions(-)
diff --git a/clock.c b/clock.c
index b5b729a..1e3ff3e 100644
--- a/clock.c
+++ b/clock.c
@@ -156,6 +156,32 @@ static int clock_resize_pollfd(struct clock *c, int new_nports);  static void clock_remove_port(struct clock *c, struct port *p);  static void clock_stats_display(struct clock_stats *s);
 
+uint8_t clock_alttime_offset_get_key(struct ptp_message *req) {
+	struct management_tlv_datum *mtd;
+	struct management_tlv *mgt =
+		(struct management_tlv *) req->management.suffix;
+
+	/*
+	 * The data field of incoming management request messages is
+	 * normally ignored.  Indeed it can even be empty.  However
+	 * the ALTERNATE_TIME_OFFSET requests are exceptional because
+	 * the key field selects one of the configured time zones.
+	 *
+	 * Provide the first time zone for an empty GET, and validate
+	 * the length of the request when non-empty.
+	 */
+	if (mgt->length == sizeof(mgt->id)) {
+		return 0;
+	}
+	if (mgt->length < sizeof(mgt->id) + sizeof(*mtd)) {
+		return MAX_TIME_ZONES;
+	}
+	mtd = (struct management_tlv_datum *) mgt->data;
+
+	return mtd->val;
+}
+
 static void remove_subscriber(struct clock_subscriber *s)  {
 	LIST_REMOVE(s, list);
@@ -353,6 +379,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 					  struct ptp_message *req,
 					  struct ptp_message *rsp, int id)  {
+	struct alternate_time_offset_properties *atop;
 	struct grandmaster_settings_np *gsn;
 	struct management_tlv_datum *mtd;
 	struct subscribe_events_np *sen;
@@ -362,6 +389,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 	struct PTPText *text;
 	uint16_t duration;
 	int datalen = 0;
+	uint8_t key;
 
 	extra = tlv_extra_alloc();
 	if (!extra) {
@@ -432,6 +460,22 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
 		mtd->val = c->tds.flags & PTP_TIMESCALE;
 		datalen = sizeof(*mtd);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		key = clock_alttime_offset_get_key(req);
+		if (key >= MAX_TIME_ZONES) {
+			break;
+		}
+		atop = (struct alternate_time_offset_properties *) tlv->data;
+		atop->keyField = key;
+		/* Message alignment broken by design. */
+		memcpy(&atop->currentOffset, &c->tz[key].current_offset,
+		       sizeof(atop->currentOffset));
+		memcpy(&atop->jumpSeconds, &c->tz[key].jump_seconds,
+		       sizeof(atop->jumpSeconds));
+		memcpy(&atop->timeOfNextJump.seconds_lsb, &c->tz[key].next_jump,
+		       sizeof(atop->timeOfNextJump.seconds_lsb));
+		datalen = sizeof(*atop);
+		break;
 	case MID_TIME_STATUS_NP:
 		tsn = (struct time_status_np *) tlv->data;
 		tsn->master_offset = tmv_to_nanoseconds(c->master_offset);
@@ -510,11 +554,12 @@ static int clock_management_get_response(struct clock *c, struct port *p,  static int clock_management_set(struct clock *c, struct port *p,
 				int id, struct ptp_message *req, int *changed)  {
-	int respond = 0;
-	struct management_tlv *tlv;
-	struct management_tlv_datum *mtd;
+	struct alternate_time_offset_properties *atop;
 	struct grandmaster_settings_np *gsn;
+	struct management_tlv_datum *mtd;
 	struct subscribe_events_np *sen;
+	struct management_tlv *tlv;
+	int key, respond = 0;
 
 	tlv = (struct management_tlv *) req->management.suffix;
 
@@ -531,6 +576,20 @@ static int clock_management_set(struct clock *c, struct port *p,
 		*changed = 1;
 		respond = 1;
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		atop = (struct alternate_time_offset_properties *) tlv->data;
+		key = atop->keyField;
+		if (key < MAX_TIME_ZONES) {
+			/* Message alignment broken by design. */
+			memcpy(&c->tz[key].current_offset, &atop->currentOffset,
+			       sizeof(c->tz[key].current_offset));
+			memcpy(&c->tz[key].jump_seconds, &atop->jumpSeconds,
+			       sizeof(c->tz[key].jump_seconds));
+			memcpy(&c->tz[key].next_jump, &atop->timeOfNextJump.seconds_lsb,
+			       sizeof(c->tz[key].next_jump));
+			respond = 1;
+		}
+		break;
 	case MID_GRANDMASTER_SETTINGS_NP:
 		gsn = (struct grandmaster_settings_np *) tlv->data;
 		c->dds.clockQuality = gsn->clockQuality; @@ -1529,7 +1588,6 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg)
 	case MID_ALTERNATE_TIME_OFFSET_ENABLE:
 	case MID_ALTERNATE_TIME_OFFSET_NAME:
 	case MID_ALTERNATE_TIME_OFFSET_MAX_KEY:
-	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
 	case MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET:
 	case MID_PRIMARY_DOMAIN:
 	case MID_TIME_STATUS_NP:
diff --git a/pmc.c b/pmc.c
index 28d96a8..e4dd566 100644
--- a/pmc.c
+++ b/pmc.c
@@ -139,6 +139,7 @@ static void pmc_show_signaling(struct ptp_message *msg, FILE *fp)
 
 static void pmc_show(struct ptp_message *msg, FILE *fp)  {
+	struct alternate_time_offset_properties *atop;
 	struct ieee_c37_238_settings_np *pwr;
 	struct grandmaster_settings_np *gsn;
 	struct mgmt_clock_description *cd;
@@ -336,6 +337,18 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
 		fprintf(fp, "TIMESCALE_PROPERTIES "
 			IFMT "ptpTimescale %d", mtd->val & PTP_TIMESCALE ? 1 : 0);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		atop = (struct alternate_time_offset_properties *) mgt->data;
+		fprintf(fp, "ALTERNATE_TIME_OFFSET_PROPERTIES "
+			IFMT "keyField       %hhu"
+			IFMT "currentOffset  %d"
+			IFMT "jumpSeconds    %d"
+			IFMT "timeOfNextJump %u",
+			atop->keyField,
+			align32(&atop->currentOffset),
+			align32(&atop->jumpSeconds),
+			align32(&atop->timeOfNextJump.seconds_lsb));
+		break;
 	case MID_MASTER_ONLY:
 		mtd = (struct management_tlv_datum *) mgt->data;
 		fprintf(fp, "MASTER_ONLY "
diff --git a/pmc_common.c b/pmc_common.c index e40c3d8..6ba8616 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -104,7 +104,7 @@ struct management_id idtab[] = {
 	{ "ALTERNATE_TIME_OFFSET_ENABLE", MID_ALTERNATE_TIME_OFFSET_ENABLE, not_supported },
 	{ "ALTERNATE_TIME_OFFSET_NAME", MID_ALTERNATE_TIME_OFFSET_NAME, not_supported },
 	{ "ALTERNATE_TIME_OFFSET_MAX_KEY", MID_ALTERNATE_TIME_OFFSET_MAX_KEY, not_supported },
-	{ "ALTERNATE_TIME_OFFSET_PROPERTIES", MID_ALTERNATE_TIME_OFFSET_PROPERTIES, not_supported },
+	{ "ALTERNATE_TIME_OFFSET_PROPERTIES", 
+MID_ALTERNATE_TIME_OFFSET_PROPERTIES, do_set_action },
 	{ "MASTER_ONLY", MID_MASTER_ONLY, do_get_action },
 	{ "TRANSPARENT_CLOCK_DEFAULT_DATA_SET", MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET, not_supported },
 	{ "PRIMARY_DOMAIN", MID_PRIMARY_DOMAIN, not_supported }, @@ -148,6 +148,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)  {
 	int cnt, code = idtab[index].code, freq_traceable, leap_59, leap_61,
 		ptp_timescale, time_traceable, utc_off_valid;
+	struct alternate_time_offset_properties atop;
 	struct ieee_c37_238_settings_np pwr;
 	struct grandmaster_settings_np gsn;
 	struct management_tlv_datum mtd;
@@ -181,6 +182,24 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
 		}
 		pmc_send_set_action(pmc, code, &mtd, sizeof(mtd));
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		memset(&atop, 0, sizeof(atop));
+		cnt = sscanf(str, " %*s %*s "
+			     "keyField       %hhu "
+			     "currentOffset  %d "
+			     "jumpSeconds    %d "
+			     "timeOfNextJump %u ",
+			     &atop.keyField,
+			     &atop.currentOffset,
+			     &atop.jumpSeconds,
+			     &atop.timeOfNextJump.seconds_lsb);
+		if (cnt != 4) {
+			fprintf(stderr, "%s SET needs 4 values\n",
+				idtab[index].name);
+			break;
+		}
+		pmc_send_set_action(pmc, code, &atop, sizeof(atop));
+		break;
 	case MID_GRANDMASTER_SETTINGS_NP:
 		cnt = sscanf(str, " %*s %*s "
 			     "clockClass              %hhu "
@@ -551,6 +570,9 @@ static int pmc_tlv_datalen(struct pmc *pmc, int id)
 	case MID_TIME_STATUS_NP:
 		len += sizeof(struct time_status_np);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		len += sizeof(struct alternate_time_offset_properties);
+		break;
 	case MID_GRANDMASTER_SETTINGS_NP:
 		len += sizeof(struct grandmaster_settings_np);
 		break;
diff --git a/tlv.c b/tlv.c
index 03faa74..fdff99a 100644
--- a/tlv.c
+++ b/tlv.c
@@ -167,6 +167,7 @@ static void alttime_offset_pre_send(struct tlv_extra *extra)  static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 			 struct tlv_extra *extra)
 {
+	struct alternate_time_offset_properties *atop;
 	struct ieee_c37_238_settings_np *pwr;
 	struct grandmaster_settings_np *gsn;
 	struct mgmt_clock_description *cd;
@@ -333,6 +334,17 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
 		p->portIdentity.portNumber = ntohs(p->portIdentity.portNumber);
 		p->peerMeanPathDelay = net2host64(p->peerMeanPathDelay);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		atop = (struct alternate_time_offset_properties *) m->data;
+		if (data_len != sizeof(*atop)) {
+			goto bad_length;
+		}
+		/* Message alignment broken by design. */
+		net2host32_unaligned(&atop->currentOffset);
+		net2host32_unaligned(&atop->jumpSeconds);
+		flip16(&atop->timeOfNextJump.seconds_msb);
+		net2host32_unaligned(&atop->timeOfNextJump.seconds_lsb);
+		break;
 	case MID_TIME_STATUS_NP:
 		if (data_len != sizeof(struct time_status_np))
 			goto bad_length;
@@ -419,6 +431,7 @@ bad_length:
 
 static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)  {
+	struct alternate_time_offset_properties *atop;
 	struct ieee_c37_238_settings_np *pwr;
 	struct grandmaster_settings_np *gsn;
 	struct mgmt_clock_description *cd;
@@ -476,6 +489,14 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)
 		p->portIdentity.portNumber = htons(p->portIdentity.portNumber);
 		p->peerMeanPathDelay = host2net64(p->peerMeanPathDelay);
 		break;
+	case MID_ALTERNATE_TIME_OFFSET_PROPERTIES:
+		atop = (struct alternate_time_offset_properties *) m->data;
+		/* Message alignment broken by design. */
+		host2net32_unaligned(&atop->currentOffset);
+		host2net32_unaligned(&atop->jumpSeconds);
+		flip16(&atop->timeOfNextJump.seconds_msb);
+		host2net32_unaligned(&atop->timeOfNextJump.seconds_lsb);
+		break;
 	case MID_TIME_STATUS_NP:
 		tsn = (struct time_status_np *) m->data;
 		tsn->master_offset = host2net64(tsn->master_offset); diff --git a/util.h b/util.h index 739c8fd..0386409 100644
--- a/util.h
+++ b/util.h
@@ -64,6 +64,13 @@ static inline uint16_t align16(void *p)
 	return v;
 }
 
+static inline uint32_t align32(void *p) {
+	uint32_t v;
+	memcpy(&v, p, sizeof(v));
+	return v;
+}
+
 char *bin2str_impl(Octet *data, int len, char *buf, int buf_len);
 
 /**
--
2.20.1
_______________________________________________
Linuxptp-devel mailing list
Lin...@li...
https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Flists.sourceforge.net%2Flists%2Flistinfo%2Flinuxptp-devel&data=04%7C01%7Cerez.geva.ext%40siemens.com%7C7c711b996e574f6328dc08d9a3bd6d6d%7C38ae3bcd95794fd4addab42e1495d55a%7C1%7C0%7C637720856242186481%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=O69Ic1RW3cdZ8%2FnbeI1HkkQS9FXcrlcN3%2BtQ141Ccl0%3D&reserved=0
 | 
| 
      
      
      From: Носиков В. А. <v.n...@pr...> - 2021-11-10 11:10:06
       | 
| On 10.11.2021 01:11, Richard Cochran wrote:
ChangeLog:
v3 First public release
v2 never published
v1 never published
The Power Profiles need two TLVs:
1. IEEE_C37_238 TLV
   - New configuration option to enable the TLV.
   - New configuration options for grandmasterID and default
     totalTimeInaccuracy.
   - New custom management message for changing totalTimeInaccuracy at
     run time. This message allows switching the TLV from the 2017
     format into the 2011 format by specifying grandmasterTimeInaccuracy
     and networkTimeInaccuracy.
2. ALTERNATE_TIME_OFFSET_INDICATOR TLV
   - New custom management message for enabling the TLV and setting
     the TLV values.
   - New helper program that computes the values from the local time
     and the Linux time zone data base and then sends the values to
     ptp4l. This method assumes that the local Linux system time and
     the PHC clock are synchronized.
Comments and feedback are much appreciated!
Thanks,
Richard
Richard Cochran (10):
  tlv: Encode and decode power profile TLVs.
  Introduce the power profile.
  Add a custom management message for power profile settings.
  tlv: Encode and decode alternate time offset indicator TLVs.
  Prepare clock based storage of up to four time zones.
  Add the ALTERNATE_TIME_OFFSET_PROPERTIES management message.
  Add the ALTERNATE_TIME_OFFSET_NAME management message.
  Implement the ALTERNATE_TIME_OFFSET_ENABLE management message.
  pmc: Convert internal helper function into global method.
  Introduce a time zone helper program.
 clock.c         | 184 +++++++++++++++++++++--
 clock.h         |   8 +
 config.c        |  15 +-
 makefile        |   7 +-
 pmc.c           |  45 ++++++
 pmc_common.c    | 135 ++++++++++++++++-
 pmc_common.h    |   2 +
 port.c          |  76 +++++++++-
 port_private.h  |   3 +
 power_profile.h |  31 ++++
 tlv.c           | 146 +++++++++++++++++++
 tlv.h           |  64 ++++++++
 tz.h            |  26 ++++
 tztool.c        | 377 ++++++++++++++++++++++++++++++++++++++++++++++++
 util.h          |   7 +
 15 files changed, 1107 insertions(+), 19 deletions(-)
 create mode 100644 power_profile.h
 create mode 100644 tz.h
 create mode 100644 tztool.c
Hi,
IEEE C37.238-2017 says that domainNumber value can be 254. Is it possible to expand maximum value of this parameter?
 | 
| 
      
      
      From: Носиков В. А. <v.n...@pr...> - 2021-11-10 12:11:32
       | 
| Hi,
IEEE C37.238-2017 says that domainNumber value can be 254. Is it possible to expand the maximum value of this parameter or somehow allow to use of this single value?
________________________________________
From: Richard Cochran <ric...@gm...>
Sent: Wednesday, November 10, 2021 1:11:48 AM
To: lin...@li...
Subject: [Linuxptp-devel] [PATCH RFC v3 00/10] Profile support for IEEE C37.238-2011 and IEEE C37.238-2017
ChangeLog:
v3 First public release
v2 never published
v1 never published
The Power Profiles need two TLVs:
1. IEEE_C37_238 TLV
   - New configuration option to enable the TLV.
   - New configuration options for grandmasterID and default
     totalTimeInaccuracy.
   - New custom management message for changing totalTimeInaccuracy at
     run time. This message allows switching the TLV from the 2017
     format into the 2011 format by specifying grandmasterTimeInaccuracy
     and networkTimeInaccuracy.
2. ALTERNATE_TIME_OFFSET_INDICATOR TLV
   - New custom management message for enabling the TLV and setting
     the TLV values.
   - New helper program that computes the values from the local time
     and the Linux time zone data base and then sends the values to
     ptp4l. This method assumes that the local Linux system time and
     the PHC clock are synchronized.
Comments and feedback are much appreciated!
Thanks,
Richard
Richard Cochran (10):
  tlv: Encode and decode power profile TLVs.
  Introduce the power profile.
  Add a custom management message for power profile settings.
  tlv: Encode and decode alternate time offset indicator TLVs.
  Prepare clock based storage of up to four time zones.
  Add the ALTERNATE_TIME_OFFSET_PROPERTIES management message.
  Add the ALTERNATE_TIME_OFFSET_NAME management message.
  Implement the ALTERNATE_TIME_OFFSET_ENABLE management message.
  pmc: Convert internal helper function into global method.
  Introduce a time zone helper program.
 clock.c         | 184 +++++++++++++++++++++--
 clock.h         |   8 +
 config.c        |  15 +-
 makefile        |   7 +-
 pmc.c           |  45 ++++++
 pmc_common.c    | 135 ++++++++++++++++-
 pmc_common.h    |   2 +
 port.c          |  76 +++++++++-
 port_private.h  |   3 +
 power_profile.h |  31 ++++
 tlv.c           | 146 +++++++++++++++++++
 tlv.h           |  64 ++++++++
 tz.h            |  26 ++++
 tztool.c        | 377 ++++++++++++++++++++++++++++++++++++++++++++++++
 util.h          |   7 +
 15 files changed, 1107 insertions(+), 19 deletions(-)
 create mode 100644 power_profile.h
 create mode 100644 tz.h
 create mode 100644 tztool.c
--
2.20.1
_______________________________________________
Linuxptp-devel mailing list
Lin...@li...
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel
 |