zephyr/subsys/net/lib/shell/gptp.c
Jukka Rissanen 477a4a5d34 net: shell: Rename the common.h to be more unique
As the common.h is only meant to be used by the network
shell files, rename it to be more descriptive in order to
avoid possible conflicts with any other common.h file.

Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
2023-12-13 20:13:39 +01:00

652 lines
21 KiB
C

/*
* Copyright (c) 2016 Intel Corporation
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(net_shell);
#include <stdlib.h>
#if defined(CONFIG_NET_GPTP)
#include <zephyr/net/gptp.h>
#include "ethernet/gptp/gptp_messages.h"
#include "ethernet/gptp/gptp_md.h"
#include "ethernet/gptp/gptp_state.h"
#include "ethernet/gptp/gptp_data_set.h"
#include "ethernet/gptp/gptp_private.h"
#endif
#include "net_shell_private.h"
#if defined(CONFIG_NET_GPTP)
static const char *selected_role_str(int port);
static void gptp_port_cb(int port, struct net_if *iface, void *user_data)
{
struct net_shell_user_data *data = user_data;
const struct shell *sh = data->sh;
int *count = data->user_data;
if (*count == 0) {
PR("Port Interface \tRole\n");
}
(*count)++;
PR("%2d %p [%d] \t%s\n", port, iface, net_if_get_by_iface(iface),
selected_role_str(port));
}
static const char *pdelay_req2str(enum gptp_pdelay_req_states state)
{
switch (state) {
case GPTP_PDELAY_REQ_NOT_ENABLED:
return "REQ_NOT_ENABLED";
case GPTP_PDELAY_REQ_INITIAL_SEND_REQ:
return "INITIAL_SEND_REQ";
case GPTP_PDELAY_REQ_RESET:
return "REQ_RESET";
case GPTP_PDELAY_REQ_SEND_REQ:
return "SEND_REQ";
case GPTP_PDELAY_REQ_WAIT_RESP:
return "WAIT_RESP";
case GPTP_PDELAY_REQ_WAIT_FOLLOW_UP:
return "WAIT_FOLLOW_UP";
case GPTP_PDELAY_REQ_WAIT_ITV_TIMER:
return "WAIT_ITV_TIMER";
}
return "<unknown>";
};
static const char *pdelay_resp2str(enum gptp_pdelay_resp_states state)
{
switch (state) {
case GPTP_PDELAY_RESP_NOT_ENABLED:
return "RESP_NOT_ENABLED";
case GPTP_PDELAY_RESP_INITIAL_WAIT_REQ:
return "INITIAL_WAIT_REQ";
case GPTP_PDELAY_RESP_WAIT_REQ:
return "WAIT_REQ";
case GPTP_PDELAY_RESP_WAIT_TSTAMP:
return "WAIT_TSTAMP";
}
return "<unknown>";
}
static const char *sync_rcv2str(enum gptp_sync_rcv_states state)
{
switch (state) {
case GPTP_SYNC_RCV_DISCARD:
return "DISCARD";
case GPTP_SYNC_RCV_WAIT_SYNC:
return "WAIT_SYNC";
case GPTP_SYNC_RCV_WAIT_FOLLOW_UP:
return "WAIT_FOLLOW_UP";
}
return "<unknown>";
}
static const char *sync_send2str(enum gptp_sync_send_states state)
{
switch (state) {
case GPTP_SYNC_SEND_INITIALIZING:
return "INITIALIZING";
case GPTP_SYNC_SEND_SEND_SYNC:
return "SEND_SYNC";
case GPTP_SYNC_SEND_SEND_FUP:
return "SEND_FUP";
}
return "<unknown>";
}
static const char *pss_rcv2str(enum gptp_pss_rcv_states state)
{
switch (state) {
case GPTP_PSS_RCV_DISCARD:
return "DISCARD";
case GPTP_PSS_RCV_RECEIVED_SYNC:
return "RECEIVED_SYNC";
}
return "<unknown>";
}
static const char *pss_send2str(enum gptp_pss_send_states state)
{
switch (state) {
case GPTP_PSS_SEND_TRANSMIT_INIT:
return "TRANSMIT_INIT";
case GPTP_PSS_SEND_SYNC_RECEIPT_TIMEOUT:
return "SYNC_RECEIPT_TIMEOUT";
case GPTP_PSS_SEND_SEND_MD_SYNC:
return "SEND_MD_SYNC";
case GPTP_PSS_SEND_SET_SYNC_RECEIPT_TIMEOUT:
return "SET_SYNC_RECEIPT_TIMEOUT";
}
return "<unknown>";
}
static const char *pa_rcv2str(enum gptp_pa_rcv_states state)
{
switch (state) {
case GPTP_PA_RCV_DISCARD:
return "DISCARD";
case GPTP_PA_RCV_RECEIVE:
return "RECEIVE";
}
return "<unknown>";
};
static const char *pa_info2str(enum gptp_pa_info_states state)
{
switch (state) {
case GPTP_PA_INFO_DISABLED:
return "DISABLED";
case GPTP_PA_INFO_POST_DISABLED:
return "POST_DISABLED";
case GPTP_PA_INFO_AGED:
return "AGED";
case GPTP_PA_INFO_UPDATE:
return "UPDATE";
case GPTP_PA_INFO_CURRENT:
return "CURRENT";
case GPTP_PA_INFO_RECEIVE:
return "RECEIVE";
case GPTP_PA_INFO_SUPERIOR_MASTER_PORT:
return "SUPERIOR_MASTER_PORT";
case GPTP_PA_INFO_REPEATED_MASTER_PORT:
return "REPEATED_MASTER_PORT";
case GPTP_PA_INFO_INFERIOR_MASTER_OR_OTHER_PORT:
return "INFERIOR_MASTER_OR_OTHER_PORT";
}
return "<unknown>";
};
static const char *pa_transmit2str(enum gptp_pa_transmit_states state)
{
switch (state) {
case GPTP_PA_TRANSMIT_INIT:
return "INIT";
case GPTP_PA_TRANSMIT_PERIODIC:
return "PERIODIC";
case GPTP_PA_TRANSMIT_IDLE:
return "IDLE";
case GPTP_PA_TRANSMIT_POST_IDLE:
return "POST_IDLE";
}
return "<unknown>";
};
static const char *site_sync2str(enum gptp_site_sync_sync_states state)
{
switch (state) {
case GPTP_SSS_INITIALIZING:
return "INITIALIZING";
case GPTP_SSS_RECEIVING_SYNC:
return "RECEIVING_SYNC";
}
return "<unknown>";
}
static const char *clk_slave2str(enum gptp_clk_slave_sync_states state)
{
switch (state) {
case GPTP_CLK_SLAVE_SYNC_INITIALIZING:
return "INITIALIZING";
case GPTP_CLK_SLAVE_SYNC_SEND_SYNC_IND:
return "SEND_SYNC_IND";
}
return "<unknown>";
};
static const char *pr_selection2str(enum gptp_pr_selection_states state)
{
switch (state) {
case GPTP_PR_SELECTION_INIT_BRIDGE:
return "INIT_BRIDGE";
case GPTP_PR_SELECTION_ROLE_SELECTION:
return "ROLE_SELECTION";
}
return "<unknown>";
};
static const char *cms_rcv2str(enum gptp_cms_rcv_states state)
{
switch (state) {
case GPTP_CMS_RCV_INITIALIZING:
return "INITIALIZING";
case GPTP_CMS_RCV_WAITING:
return "WAITING";
case GPTP_CMS_RCV_SOURCE_TIME:
return "SOURCE_TIME";
}
return "<unknown>";
};
#if !defined(USCALED_NS_TO_NS)
#define USCALED_NS_TO_NS(val) (val >> 16)
#endif
static const char *selected_role_str(int port)
{
switch (GPTP_GLOBAL_DS()->selected_role[port]) {
case GPTP_PORT_INITIALIZING:
return "INITIALIZING";
case GPTP_PORT_FAULTY:
return "FAULTY";
case GPTP_PORT_DISABLED:
return "DISABLED";
case GPTP_PORT_LISTENING:
return "LISTENING";
case GPTP_PORT_PRE_MASTER:
return "PRE-MASTER";
case GPTP_PORT_MASTER:
return "MASTER";
case GPTP_PORT_PASSIVE:
return "PASSIVE";
case GPTP_PORT_UNCALIBRATED:
return "UNCALIBRATED";
case GPTP_PORT_SLAVE:
return "SLAVE";
}
return "<unknown>";
}
static void gptp_print_port_info(const struct shell *sh, int port)
{
struct gptp_port_bmca_data *port_bmca_data;
struct gptp_port_param_ds *port_param_ds;
struct gptp_port_states *port_state;
struct gptp_domain *domain;
struct gptp_port_ds *port_ds;
struct net_if *iface;
int ret, i;
domain = gptp_get_domain();
ret = gptp_get_port_data(domain,
port,
&port_ds,
&port_param_ds,
&port_state,
&port_bmca_data,
&iface);
if (ret < 0) {
PR_WARNING("Cannot get gPTP information for port %d (%d)\n",
port, ret);
return;
}
NET_ASSERT(port == port_ds->port_id.port_number,
"Port number mismatch! (%d vs %d)", port,
port_ds->port_id.port_number);
PR("Port id : %d (%s)\n", port_ds->port_id.port_number,
selected_role_str(port_ds->port_id.port_number));
PR("Interface : %p [%d]\n", iface, net_if_get_by_iface(iface));
PR("Clock id : ");
for (i = 0; i < sizeof(port_ds->port_id.clk_id); i++) {
PR("%02x", port_ds->port_id.clk_id[i]);
if (i != (sizeof(port_ds->port_id.clk_id) - 1)) {
PR(":");
}
}
PR("\n");
PR("Version : %d\n", port_ds->version);
PR("AS capable : %s\n", port_ds->as_capable ? "yes" : "no");
PR("\nConfiguration:\n");
PR("Time synchronization and Best Master Selection enabled "
": %s\n", port_ds->ptt_port_enabled ? "yes" : "no");
PR("The port is measuring the path delay "
": %s\n", port_ds->is_measuring_delay ? "yes" : "no");
PR("One way propagation time on %s : %u ns\n",
"the link attached to this port",
(uint32_t)port_ds->neighbor_prop_delay);
PR("Propagation time threshold for %s : %u ns\n",
"the link attached to this port",
(uint32_t)port_ds->neighbor_prop_delay_thresh);
PR("Estimate of the ratio of the frequency with the peer "
": %u\n", (uint32_t)port_ds->neighbor_rate_ratio);
PR("Asymmetry on the link relative to the grand master time base "
": %" PRId64 "\n", port_ds->delay_asymmetry);
PR("Maximum interval between sync %s "
": %" PRIu64 "\n", "messages",
port_ds->sync_receipt_timeout_time_itv);
PR("Maximum number of Path Delay Requests without a response "
": %d\n", port_ds->allowed_lost_responses);
PR("Current Sync %s : %d\n",
"sequence id for this port", port_ds->sync_seq_id);
PR("Current Path Delay Request %s : %d\n",
"sequence id for this port", port_ds->pdelay_req_seq_id);
PR("Current Announce %s : %d\n",
"sequence id for this port", port_ds->announce_seq_id);
PR("Current Signaling %s : %d\n",
"sequence id for this port", port_ds->signaling_seq_id);
PR("Whether neighborRateRatio %s : %s\n",
"needs to be computed for this port",
port_ds->compute_neighbor_rate_ratio ? "yes" : "no");
PR("Whether neighborPropDelay %s : %s\n",
"needs to be computed for this port",
port_ds->compute_neighbor_prop_delay ? "yes" : "no");
PR("Initial Announce Interval %s : %d\n",
"as a Logarithm to base 2", port_ds->ini_log_announce_itv);
PR("Current Announce Interval %s : %d\n",
"as a Logarithm to base 2", port_ds->cur_log_announce_itv);
PR("Initial Sync Interval %s : %d\n",
"as a Logarithm to base 2", port_ds->ini_log_half_sync_itv);
PR("Current Sync Interval %s : %d\n",
"as a Logarithm to base 2", port_ds->cur_log_half_sync_itv);
PR("Initial Path Delay Request Interval %s : %d\n",
"as a Logarithm to base 2", port_ds->ini_log_pdelay_req_itv);
PR("Current Path Delay Request Interval %s : %d\n",
"as a Logarithm to base 2", port_ds->cur_log_pdelay_req_itv);
PR("Time without receiving announce %s %s : %d ms (%d)\n",
"messages", "before running BMCA",
gptp_uscaled_ns_to_timer_ms(
&port_bmca_data->ann_rcpt_timeout_time_interval),
port_ds->announce_receipt_timeout);
PR("Time without receiving sync %s %s : %" PRIu64 " ms (%d)\n",
"messages", "before running BMCA",
(port_ds->sync_receipt_timeout_time_itv >> 16) /
(NSEC_PER_SEC / MSEC_PER_SEC),
port_ds->sync_receipt_timeout);
PR("Sync event %s : %" PRIu64 " ms\n",
"transmission interval for the port",
USCALED_NS_TO_NS(port_ds->half_sync_itv.low) /
(NSEC_PER_USEC * USEC_PER_MSEC));
PR("Path Delay Request %s : %" PRIu64 " ms\n",
"transmission interval for the port",
USCALED_NS_TO_NS(port_ds->pdelay_req_itv.low) /
(NSEC_PER_USEC * USEC_PER_MSEC));
PR("BMCA %s %s%d%s: %d\n", "default", "priority", 1,
" ",
domain->default_ds.priority1);
PR("BMCA %s %s%d%s: %d\n", "default", "priority", 2,
" ",
domain->default_ds.priority2);
PR("\nRuntime status:\n");
PR("Current global port state "
" : %s\n", selected_role_str(port));
PR("Path Delay Request state machine variables:\n");
PR("\tCurrent state "
": %s\n", pdelay_req2str(port_state->pdelay_req.state));
PR("\tInitial Path Delay Response Peer Timestamp "
": %" PRIu64 "\n", port_state->pdelay_req.ini_resp_evt_tstamp);
PR("\tInitial Path Delay Response Ingress Timestamp "
": %" PRIu64 "\n", port_state->pdelay_req.ini_resp_ingress_tstamp);
PR("\tPath Delay Response %s %s : %u\n",
"messages", "received",
port_state->pdelay_req.rcvd_pdelay_resp);
PR("\tPath Delay Follow Up %s %s : %u\n",
"messages", "received",
port_state->pdelay_req.rcvd_pdelay_follow_up);
PR("\tNumber of lost Path Delay Responses "
": %u\n", port_state->pdelay_req.lost_responses);
PR("\tTimer expired send a new Path Delay Request "
": %u\n", port_state->pdelay_req.pdelay_timer_expired);
PR("\tNeighborRateRatio has been computed successfully "
": %u\n", port_state->pdelay_req.neighbor_rate_ratio_valid);
PR("\tPath Delay has already been computed after init "
": %u\n", port_state->pdelay_req.init_pdelay_compute);
PR("\tCount consecutive reqs with multiple responses "
": %u\n", port_state->pdelay_req.multiple_resp_count);
PR("Path Delay Response state machine variables:\n");
PR("\tCurrent state "
": %s\n", pdelay_resp2str(port_state->pdelay_resp.state));
PR("SyncReceive state machine variables:\n");
PR("\tCurrent state "
": %s\n", sync_rcv2str(port_state->sync_rcv.state));
PR("\tA Sync %s %s : %s\n",
"Message", "has been received",
port_state->sync_rcv.rcvd_sync ? "yes" : "no");
PR("\tA Follow Up %s %s : %s\n",
"Message", "has been received",
port_state->sync_rcv.rcvd_follow_up ? "yes" : "no");
PR("\tA Follow Up %s %s : %s\n",
"Message", "timeout",
port_state->sync_rcv.follow_up_timeout_expired ? "yes" : "no");
PR("\tTime at which a Sync %s without Follow Up\n"
"\t will be discarded "
": %" PRIu64 "\n", "Message",
port_state->sync_rcv.follow_up_receipt_timeout);
PR("SyncSend state machine variables:\n");
PR("\tCurrent state "
": %s\n", sync_send2str(port_state->sync_send.state));
PR("\tA MDSyncSend structure %s : %s\n",
"has been received",
port_state->sync_send.rcvd_md_sync ? "yes" : "no");
PR("\tThe timestamp for the sync msg %s : %s\n",
"has been received",
port_state->sync_send.md_sync_timestamp_avail ? "yes" : "no");
PR("PortSyncSyncReceive state machine variables:\n");
PR("\tCurrent state "
": %s\n", pss_rcv2str(port_state->pss_rcv.state));
PR("\tGrand Master / Local Clock frequency ratio "
": %f\n", port_state->pss_rcv.rate_ratio);
PR("\tA MDSyncReceive struct is ready to be processed "
": %s\n", port_state->pss_rcv.rcvd_md_sync ? "yes" : "no");
PR("\tExpiry of SyncReceiptTimeoutTimer : %s\n",
port_state->pss_rcv.rcv_sync_receipt_timeout_timer_expired ?
"yes" : "no");
PR("PortSyncSyncSend state machine variables:\n");
PR("\tCurrent state "
": %s\n", pss_send2str(port_state->pss_send.state));
PR("\tFollow Up Correction Field of last recv PSS "
": %" PRId64 "\n",
port_state->pss_send.last_follow_up_correction_field);
PR("\tUpstream Tx Time of the last recv PortSyncSync "
": %" PRIu64 "\n", port_state->pss_send.last_upstream_tx_time);
PR("\tRate Ratio of the last received PortSyncSync "
": %f\n",
port_state->pss_send.last_rate_ratio);
PR("\tGM Freq Change of the last received PortSyncSync "
": %f\n", port_state->pss_send.last_gm_freq_change);
PR("\tGM Time Base Indicator of last recv PortSyncSync "
": %d\n", port_state->pss_send.last_gm_time_base_indicator);
PR("\tReceived Port Number of last recv PortSyncSync "
": %d\n",
port_state->pss_send.last_rcvd_port_num);
PR("\tPortSyncSync structure is ready to be processed "
": %s\n", port_state->pss_send.rcvd_pss_sync ? "yes" : "no");
PR("\tFlag when the %s has expired : %s\n",
"half_sync_itv_timer",
port_state->pss_send.half_sync_itv_timer_expired ? "yes" : "no");
PR("\tHas %s expired twice : %s\n",
"half_sync_itv_timer",
port_state->pss_send.sync_itv_timer_expired ? "yes" : "no");
PR("\tHas syncReceiptTimeoutTime expired "
": %s\n",
port_state->pss_send.send_sync_receipt_timeout_timer_expired ?
"yes" : "no");
PR("PortAnnounceReceive state machine variables:\n");
PR("\tCurrent state "
": %s\n", pa_rcv2str(port_state->pa_rcv.state));
PR("\tAn announce message is ready to be processed "
": %s\n",
port_state->pa_rcv.rcvd_announce ? "yes" : "no");
PR("PortAnnounceInformation state machine variables:\n");
PR("\tCurrent state "
": %s\n", pa_info2str(port_state->pa_info.state));
PR("\tExpired announce information "
": %s\n", port_state->pa_info.ann_expired ? "yes" : "no");
PR("PortAnnounceTransmit state machine variables:\n");
PR("\tCurrent state "
": %s\n", pa_transmit2str(port_state->pa_transmit.state));
PR("\tTrigger announce information "
": %s\n", port_state->pa_transmit.ann_trigger ? "yes" : "no");
#if defined(CONFIG_NET_GPTP_STATISTICS)
PR("\nStatistics:\n");
PR("Sync %s %s : %u\n",
"messages", "received", port_param_ds->rx_sync_count);
PR("Follow Up %s %s : %u\n",
"messages", "received", port_param_ds->rx_fup_count);
PR("Path Delay Request %s %s : %u\n",
"messages", "received", port_param_ds->rx_pdelay_req_count);
PR("Path Delay Response %s %s : %u\n",
"messages", "received", port_param_ds->rx_pdelay_resp_count);
PR("Path Delay %s threshold %s : %u\n",
"messages", "exceeded",
port_param_ds->neighbor_prop_delay_exceeded);
PR("Path Delay Follow Up %s %s : %u\n",
"messages", "received", port_param_ds->rx_pdelay_resp_fup_count);
PR("Announce %s %s : %u\n",
"messages", "received", port_param_ds->rx_announce_count);
PR("ptp %s discarded : %u\n",
"messages", port_param_ds->rx_ptp_packet_discard_count);
PR("Sync %s %s : %u\n",
"reception", "timeout",
port_param_ds->sync_receipt_timeout_count);
PR("Announce %s %s : %u\n",
"reception", "timeout",
port_param_ds->announce_receipt_timeout_count);
PR("Path Delay Requests without a response "
": %u\n",
port_param_ds->pdelay_allowed_lost_resp_exceed_count);
PR("Sync %s %s : %u\n",
"messages", "sent", port_param_ds->tx_sync_count);
PR("Follow Up %s %s : %u\n",
"messages", "sent", port_param_ds->tx_fup_count);
PR("Path Delay Request %s %s : %u\n",
"messages", "sent", port_param_ds->tx_pdelay_req_count);
PR("Path Delay Response %s %s : %u\n",
"messages", "sent", port_param_ds->tx_pdelay_resp_count);
PR("Path Delay Response FUP %s %s : %u\n",
"messages", "sent", port_param_ds->tx_pdelay_resp_fup_count);
PR("Announce %s %s : %u\n",
"messages", "sent", port_param_ds->tx_announce_count);
#endif /* CONFIG_NET_GPTP_STATISTICS */
}
#endif /* CONFIG_NET_GPTP */
static int cmd_net_gptp_port(const struct shell *sh, size_t argc, char *argv[])
{
#if defined(CONFIG_NET_GPTP)
int arg = 1;
char *endptr;
int port;
#endif
#if defined(CONFIG_NET_GPTP)
if (!argv[arg]) {
PR_WARNING("Port number must be given.\n");
return -ENOEXEC;
}
port = strtol(argv[arg], &endptr, 10);
if (*endptr == '\0') {
gptp_print_port_info(sh, port);
} else {
PR_WARNING("Not a valid gPTP port number: %s\n", argv[arg]);
}
#else
ARG_UNUSED(argc);
ARG_UNUSED(argv);
PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_GPTP", "gPTP");
#endif
return 0;
}
static int cmd_net_gptp(const struct shell *sh, size_t argc, char *argv[])
{
#if defined(CONFIG_NET_GPTP)
/* gPTP status */
struct gptp_domain *domain = gptp_get_domain();
int count = 0;
int arg = 1;
#endif
#if defined(CONFIG_NET_GPTP)
if (argv[arg]) {
cmd_net_gptp_port(sh, argc, argv);
} else {
struct net_shell_user_data user_data;
user_data.sh = sh;
user_data.user_data = &count;
gptp_foreach_port(gptp_port_cb, &user_data);
PR("\n");
PR("SiteSyncSync state machine variables:\n");
PR("\tCurrent state : %s\n",
site_sync2str(domain->state.site_ss.state));
PR("\tA PortSyncSync struct is ready : %s\n",
domain->state.site_ss.rcvd_pss ? "yes" : "no");
PR("ClockSlaveSync state machine variables:\n");
PR("\tCurrent state : %s\n",
clk_slave2str(domain->state.clk_slave_sync.state));
PR("\tA PortSyncSync struct is ready : %s\n",
domain->state.clk_slave_sync.rcvd_pss ? "yes" : "no");
PR("\tThe local clock has expired : %s\n",
domain->state.clk_slave_sync.rcvd_local_clk_tick ?
"yes" : "no");
PR("PortRoleSelection state machine variables:\n");
PR("\tCurrent state : %s\n",
pr_selection2str(domain->state.pr_sel.state));
PR("ClockMasterSyncReceive state machine variables:\n");
PR("\tCurrent state : %s\n",
cms_rcv2str(domain->state.clk_master_sync_receive.state));
PR("\tA ClockSourceTime : %s\n",
domain->state.clk_master_sync_receive.rcvd_clock_source_req
? "yes" : "no");
PR("\tThe local clock has expired : %s\n",
domain->state.clk_master_sync_receive.rcvd_local_clock_tick
? "yes" : "no");
}
#else
ARG_UNUSED(argc);
ARG_UNUSED(argv);
PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_GPTP", "gPTP");
#endif
return 0;
}
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_gptp,
SHELL_CMD(port, NULL,
"'net gptp [<port>]' prints detailed information about "
"gPTP port.",
cmd_net_gptp_port),
SHELL_SUBCMD_SET_END
);
SHELL_SUBCMD_ADD((net), gptp, &net_cmd_gptp,
"Print information about gPTP support.",
cmd_net_gptp, 1, 1);