You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1528 lines
42 KiB
1528 lines
42 KiB
/* |
|
* Copyright (c) 2024 BayLibre SAS |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(ptp_port, CONFIG_PTP_LOG_LEVEL); |
|
|
|
#include <zephyr/kernel.h> |
|
#include <zephyr/net/net_if.h> |
|
#include <zephyr/net/ptp.h> |
|
#include <zephyr/net/ptp_time.h> |
|
#include <zephyr/random/random.h> |
|
|
|
#include "btca.h" |
|
#include "clock.h" |
|
#include "port.h" |
|
#include "msg.h" |
|
#include "tlv.h" |
|
#include "transport.h" |
|
|
|
#define DEFAULT_LOG_MSG_INTERVAL (0x7F) |
|
|
|
#define PORT_DELAY_REQ_CLEARE_TO (3 * NSEC_PER_SEC) |
|
|
|
#define PORT_LINK_UP BIT(0) |
|
#define PORT_LINK_DOWN BIT(1) |
|
#define PORT_LINK_CHANGED BIT(2) |
|
#define PORT_LINK_EVENT_MASK (NET_EVENT_IF_DOWN | NET_EVENT_IF_UP) |
|
|
|
static struct ptp_port ports[CONFIG_PTP_NUM_PORTS]; |
|
static struct k_mem_slab foreign_tts_slab; |
|
#if CONFIG_PTP_FOREIGN_TIME_TRANSMITTER_FEATURE |
|
BUILD_ASSERT(CONFIG_PTP_FOREIGN_TIME_TRANSMITTER_RECORD_SIZE >= 5 * CONFIG_PTP_NUM_PORTS, |
|
"PTP_FOREIGN_TIME_TRANSMITTER_RECORD_SIZE is smaller than expected!"); |
|
|
|
K_MEM_SLAB_DEFINE_STATIC(foreign_tts_slab, |
|
sizeof(struct ptp_foreign_tt_clock), |
|
CONFIG_PTP_FOREIGN_TIME_TRANSMITTER_RECORD_SIZE, |
|
8); |
|
#endif |
|
|
|
char str_port_id[] = "FF:FF:FF:FF:FF:FF:FF:FF-FFFF"; |
|
|
|
const char *port_id_str(struct ptp_port_id *port_id) |
|
{ |
|
uint8_t *pid = port_id->clk_id.id; |
|
|
|
snprintk(str_port_id, sizeof(str_port_id), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X-%04X", |
|
pid[0], |
|
pid[1], |
|
pid[2], |
|
pid[3], |
|
pid[4], |
|
pid[5], |
|
pid[6], |
|
pid[7], |
|
port_id->port_number); |
|
|
|
return str_port_id; |
|
} |
|
|
|
static const char *port_state_str(enum ptp_port_state state) |
|
{ |
|
const static char * const states[] = { |
|
[PTP_PS_INITIALIZING] = "INITIALIZING", |
|
[PTP_PS_FAULTY] = "FAULTY", |
|
[PTP_PS_DISABLED] = "DISABLED", |
|
[PTP_PS_LISTENING] = "LISTENING", |
|
[PTP_PS_PRE_TIME_TRANSMITTER] = "PRE TIME TRANSMITTER", |
|
[PTP_PS_TIME_TRANSMITTER] = "TIME TRANSMITTER", |
|
[PTP_PS_GRAND_MASTER] = "GRAND MASTER", |
|
[PTP_PS_PASSIVE] = "PASSIVE", |
|
[PTP_PS_UNCALIBRATED] = "UNCALIBRATED", |
|
[PTP_PS_TIME_RECEIVER] = "TIME RECEIVER", |
|
}; |
|
|
|
return states[state]; |
|
} |
|
|
|
static int port_msg_send(struct ptp_port *port, struct ptp_msg *msg, enum ptp_socket idx) |
|
{ |
|
ptp_msg_pre_send(msg); |
|
|
|
return ptp_transport_send(port, msg, idx); |
|
} |
|
|
|
static void port_timer_set_timeout(struct k_timer *timer, uint8_t factor, int8_t log_seconds) |
|
{ |
|
uint64_t timeout = log_seconds < 0 ? |
|
((uint64_t)NSEC_PER_SEC * factor) >> -log_seconds : |
|
((uint64_t)NSEC_PER_SEC * factor) << log_seconds; |
|
|
|
k_timer_start(timer, K_NSEC(timeout), K_NO_WAIT); |
|
} |
|
|
|
static void port_timer_set_timeout_random(struct k_timer *timer, |
|
int min_factor, |
|
int span, |
|
int log_seconds) |
|
{ |
|
uint64_t timeout, random_ns; |
|
|
|
if (log_seconds < 0) { |
|
timeout = ((uint64_t)NSEC_PER_SEC * min_factor) >> -log_seconds; |
|
random_ns = (uint64_t)NSEC_PER_SEC >> -log_seconds; |
|
} else { |
|
timeout = ((uint64_t)NSEC_PER_SEC * min_factor) << log_seconds; |
|
random_ns = ((uint64_t)span * NSEC_PER_SEC) << log_seconds; |
|
} |
|
|
|
timeout = (uint64_t)(timeout + (random_ns * (sys_rand32_get() % (1 << 15) + 1) >> 15)); |
|
k_timer_start(timer, K_NSEC(timeout), K_NO_WAIT); |
|
} |
|
|
|
static void port_synchronize(struct ptp_port *port, |
|
struct net_ptp_time ingress_ts, |
|
struct net_ptp_time origin_ts, |
|
ptp_timeinterval correction1, |
|
ptp_timeinterval correction2) |
|
{ |
|
uint64_t t1, t2, t1c; |
|
|
|
t1 = origin_ts.second * NSEC_PER_SEC + origin_ts.nanosecond; |
|
t2 = ingress_ts.second * NSEC_PER_SEC + ingress_ts.nanosecond; |
|
t1c = t1 + (correction1 >> 16) + (correction2 >> 16); |
|
|
|
ptp_clock_synchronize(t2, t1c); |
|
|
|
port_timer_set_timeout(&port->timers.sync, |
|
port->port_ds.announce_receipt_timeout, |
|
port->port_ds.log_sync_interval); |
|
} |
|
|
|
static void port_ds_init(struct ptp_port *port) |
|
{ |
|
struct ptp_port_ds *ds = &port->port_ds; |
|
const struct ptp_default_ds *dds = ptp_clock_default_ds(); |
|
|
|
memcpy(&ds->id.clk_id, &dds->clk_id, sizeof(ptp_clk_id)); |
|
ds->id.port_number = dds->n_ports + 1; |
|
|
|
ds->state = PTP_PS_INITIALIZING; |
|
ds->log_min_delay_req_interval = CONFIG_PTP_MIN_DELAY_REQ_LOG_INTERVAL; |
|
ds->log_announce_interval = CONFIG_PTP_ANNOUNCE_LOG_INTERVAL; |
|
ds->announce_receipt_timeout = CONFIG_PTP_ANNOUNCE_RECV_TIMEOUT; |
|
ds->log_sync_interval = CONFIG_PTP_SYNC_LOG_INTERVAL; |
|
ds->delay_mechanism = PTP_DM_E2E; |
|
ds->log_min_pdelay_req_interval = CONFIG_PTP_MIN_PDELAY_REQ_LOG_INTERVAL; |
|
ds->version = PTP_VERSION; |
|
ds->delay_asymmetry = 0; |
|
} |
|
|
|
static void port_delay_req_timestamp_cb(struct net_pkt *pkt) |
|
{ |
|
struct ptp_port *port = ptp_clock_port_from_iface(pkt->iface); |
|
struct ptp_msg *req, *msg = ptp_msg_from_pkt(pkt); |
|
sys_snode_t *iter, *last = NULL; |
|
|
|
if (!port || !msg) { |
|
return; |
|
} |
|
|
|
msg->header.src_port_id.port_number = ntohs(msg->header.src_port_id.port_number); |
|
|
|
if (!ptp_port_id_eq(&port->port_ds.id, &msg->header.src_port_id) || |
|
ptp_msg_type(msg) != PTP_MSG_DELAY_REQ) { |
|
return; |
|
} |
|
|
|
for (iter = sys_slist_peek_head(&port->delay_req_list); |
|
iter; |
|
iter = sys_slist_peek_next(iter), last = iter) { |
|
|
|
req = CONTAINER_OF(iter, struct ptp_msg, node); |
|
|
|
if (req->header.sequence_id != msg->header.sequence_id) { |
|
continue; |
|
} |
|
|
|
if (pkt->timestamp.second == UINT64_MAX || |
|
(pkt->timestamp.second == 0 && pkt->timestamp.nanosecond == 0)) { |
|
net_if_unregister_timestamp_cb(&port->delay_req_ts_cb); |
|
sys_slist_remove(&port->delay_req_list, last, iter); |
|
ptp_msg_unref(req); |
|
return; |
|
} |
|
|
|
req->timestamp.host._sec.high = pkt->timestamp._sec.high; |
|
req->timestamp.host._sec.low = pkt->timestamp._sec.low; |
|
req->timestamp.host.nanosecond = pkt->timestamp.nanosecond; |
|
|
|
LOG_DBG("Port %d registered timestamp for %d Delay_Req", |
|
port->port_ds.id.port_number, |
|
ntohs(msg->header.sequence_id)); |
|
|
|
if (iter == sys_slist_peek_tail(&port->delay_req_list)) { |
|
net_if_unregister_timestamp_cb(&port->delay_req_ts_cb); |
|
} |
|
} |
|
} |
|
|
|
static void port_sync_timestamp_cb(struct net_pkt *pkt) |
|
{ |
|
struct ptp_port *port = ptp_clock_port_from_iface(pkt->iface); |
|
struct ptp_msg *msg = ptp_msg_from_pkt(pkt); |
|
|
|
if (!port || !msg) { |
|
return; |
|
} |
|
|
|
msg->header.src_port_id.port_number = ntohs(msg->header.src_port_id.port_number); |
|
|
|
if (ptp_port_id_eq(&port->port_ds.id, &msg->header.src_port_id) && |
|
ptp_msg_type(msg) == PTP_MSG_SYNC) { |
|
|
|
const struct ptp_default_ds *dds = ptp_clock_default_ds(); |
|
const struct ptp_time_prop_ds *tpds = ptp_clock_time_prop_ds(); |
|
struct ptp_msg *resp = ptp_msg_alloc(); |
|
|
|
if (!resp) { |
|
return; |
|
} |
|
|
|
resp->header.type_major_sdo_id = PTP_MSG_FOLLOW_UP; |
|
resp->header.version = PTP_VERSION; |
|
resp->header.msg_length = sizeof(struct ptp_follow_up_msg); |
|
resp->header.domain_number = dds->domain; |
|
resp->header.flags[1] = tpds->flags; |
|
resp->header.src_port_id = port->port_ds.id; |
|
resp->header.sequence_id = port->seq_id.sync++; |
|
resp->header.log_msg_interval = port->port_ds.log_sync_interval; |
|
|
|
resp->follow_up.precise_origin_timestamp.seconds_high = pkt->timestamp._sec.high; |
|
resp->follow_up.precise_origin_timestamp.seconds_low = pkt->timestamp._sec.low; |
|
resp->follow_up.precise_origin_timestamp.nanoseconds = pkt->timestamp.nanosecond; |
|
|
|
net_if_unregister_timestamp_cb(&port->sync_ts_cb); |
|
|
|
port_msg_send(port, resp, PTP_SOCKET_GENERAL); |
|
ptp_msg_unref(resp); |
|
|
|
LOG_DBG("Port %d sends Follow_Up message", port->port_ds.id.port_number); |
|
} |
|
} |
|
|
|
static int port_announce_msg_transmit(struct ptp_port *port) |
|
{ |
|
const struct ptp_parent_ds *pds = ptp_clock_parent_ds(); |
|
const struct ptp_current_ds *cds = ptp_clock_current_ds(); |
|
const struct ptp_default_ds *dds = ptp_clock_default_ds(); |
|
const struct ptp_time_prop_ds *tpds = ptp_clock_time_prop_ds(); |
|
struct ptp_msg *msg = ptp_msg_alloc(); |
|
int ret; |
|
|
|
if (!msg) { |
|
return -ENOMEM; |
|
} |
|
|
|
msg->header.type_major_sdo_id = PTP_MSG_ANNOUNCE; |
|
msg->header.version = PTP_VERSION; |
|
msg->header.msg_length = sizeof(struct ptp_announce_msg); |
|
msg->header.domain_number = dds->domain; |
|
msg->header.flags[1] = tpds->flags; |
|
msg->header.src_port_id = port->port_ds.id; |
|
msg->header.sequence_id = port->seq_id.announce++; |
|
msg->header.log_msg_interval = port->port_ds.log_sync_interval; |
|
|
|
msg->announce.current_utc_offset = tpds->current_utc_offset; |
|
msg->announce.gm_priority1 = pds->gm_priority1; |
|
msg->announce.gm_clk_quality = pds->gm_clk_quality; |
|
msg->announce.gm_priority2 = pds->gm_priority2; |
|
msg->announce.gm_id = pds->gm_id; |
|
msg->announce.steps_rm = cds->steps_rm; |
|
msg->announce.time_src = tpds->time_src; |
|
|
|
ret = port_msg_send(port, msg, PTP_SOCKET_GENERAL); |
|
ptp_msg_unref(msg); |
|
|
|
if (ret < 0) { |
|
return -EFAULT; |
|
} |
|
|
|
LOG_DBG("Port %d sends Announce message", port->port_ds.id.port_number); |
|
return 0; |
|
} |
|
|
|
static int port_delay_req_msg_transmit(struct ptp_port *port) |
|
{ |
|
const struct ptp_default_ds *dds = ptp_clock_default_ds(); |
|
struct ptp_msg *msg = ptp_msg_alloc(); |
|
int ret; |
|
|
|
if (!msg) { |
|
return -ENOMEM; |
|
} |
|
|
|
msg->header.type_major_sdo_id = PTP_MSG_DELAY_REQ; |
|
msg->header.version = PTP_VERSION; |
|
msg->header.msg_length = sizeof(struct ptp_delay_req_msg); |
|
msg->header.domain_number = dds->domain; |
|
msg->header.src_port_id = port->port_ds.id; |
|
msg->header.sequence_id = port->seq_id.delay++; |
|
msg->header.log_msg_interval = DEFAULT_LOG_MSG_INTERVAL; |
|
|
|
net_if_register_timestamp_cb(&port->delay_req_ts_cb, |
|
NULL, |
|
port->iface, |
|
port_delay_req_timestamp_cb); |
|
|
|
sys_slist_append(&port->delay_req_list, &msg->node); |
|
ret = port_msg_send(port, msg, PTP_SOCKET_EVENT); |
|
if (ret < 0) { |
|
sys_slist_find_and_remove(&port->delay_req_list, &msg->node); |
|
ptp_msg_unref(msg); |
|
return -EFAULT; |
|
} |
|
|
|
LOG_DBG("Port %d sends Delay_Req message", port->port_ds.id.port_number); |
|
return 0; |
|
} |
|
|
|
static int port_sync_msg_transmit(struct ptp_port *port) |
|
{ |
|
const struct ptp_default_ds *dds = ptp_clock_default_ds(); |
|
const struct ptp_time_prop_ds *tpds = ptp_clock_time_prop_ds(); |
|
struct ptp_msg *msg = ptp_msg_alloc(); |
|
int ret; |
|
|
|
if (!msg) { |
|
return -ENOMEM; |
|
} |
|
|
|
msg->header.type_major_sdo_id = PTP_MSG_SYNC; |
|
msg->header.version = PTP_VERSION; |
|
msg->header.msg_length = sizeof(struct ptp_sync_msg); |
|
msg->header.domain_number = dds->domain; |
|
msg->header.flags[0] = PTP_MSG_TWO_STEP_FLAG; |
|
msg->header.flags[1] = tpds->flags; |
|
msg->header.src_port_id = port->port_ds.id; |
|
msg->header.sequence_id = port->seq_id.sync; |
|
msg->header.log_msg_interval = port->port_ds.log_sync_interval; |
|
|
|
net_if_register_timestamp_cb(&port->sync_ts_cb, |
|
NULL, |
|
port->iface, |
|
port_sync_timestamp_cb); |
|
|
|
ret = port_msg_send(port, msg, PTP_SOCKET_EVENT); |
|
ptp_msg_unref(msg); |
|
|
|
if (ret < 0) { |
|
return -EFAULT; |
|
} |
|
LOG_DBG("Port %d sends Sync message", port->port_ds.id.port_number); |
|
return 0; |
|
} |
|
|
|
static void port_timer_init(struct k_timer *timer, k_timer_expiry_t timeout_fn, void *user_data) |
|
{ |
|
k_timer_init(timer, timeout_fn, NULL); |
|
k_timer_user_data_set(timer, user_data); |
|
} |
|
|
|
static void port_timer_to_handler(struct k_timer *timer) |
|
{ |
|
struct ptp_port *port = (struct ptp_port *)k_timer_user_data_get(timer); |
|
|
|
if (timer == &port->timers.announce) { |
|
atomic_set_bit(&port->timeouts, PTP_PORT_TIMER_ANNOUNCE_TO); |
|
} else if (timer == &port->timers.sync) { |
|
atomic_set_bit(&port->timeouts, PTP_PORT_TIMER_SYNC_TO); |
|
} else if (timer == &port->timers.delay) { |
|
atomic_set_bit(&port->timeouts, PTP_PORT_TIMER_DELAY_TO); |
|
} else if (timer == &port->timers.qualification) { |
|
atomic_set_bit(&port->timeouts, PTP_PORT_TIMER_QUALIFICATION_TO); |
|
} |
|
|
|
ptp_clock_signal_timeout(); |
|
} |
|
|
|
static void foreign_clock_cleanup(struct ptp_foreign_tt_clock *foreign) |
|
{ |
|
struct ptp_msg *msg; |
|
int64_t timestamp, timeout, current = k_uptime_get() * NSEC_PER_MSEC; |
|
|
|
while (foreign->messages_count > FOREIGN_TIME_TRANSMITTER_THRESHOLD) { |
|
msg = (struct ptp_msg *)k_fifo_get(&foreign->messages, K_NO_WAIT); |
|
ptp_msg_unref(msg); |
|
foreign->messages_count--; |
|
} |
|
|
|
/* Remove messages that don't arrived at |
|
* FOREIGN_TIME_TRANSMITTER_TIME_WINDOW (4 * announce interval) - IEEE 1588-2019 9.3.2.4.5 |
|
*/ |
|
while (!k_fifo_is_empty(&foreign->messages)) { |
|
msg = (struct ptp_msg *)k_fifo_peek_head(&foreign->messages); |
|
|
|
if (msg->header.log_msg_interval <= -31) { |
|
timeout = 0; |
|
} else if (msg->header.log_msg_interval >= 31) { |
|
timeout = INT64_MAX; |
|
} else if (msg->header.log_msg_interval > 0) { |
|
timeout = FOREIGN_TIME_TRANSMITTER_TIME_WINDOW_MUL * |
|
(1 << msg->header.log_msg_interval) * NSEC_PER_SEC; |
|
} else { |
|
timeout = FOREIGN_TIME_TRANSMITTER_TIME_WINDOW_MUL * NSEC_PER_SEC / |
|
(1 << (-msg->header.log_msg_interval)); |
|
} |
|
|
|
timestamp = msg->timestamp.host.second * NSEC_PER_SEC + |
|
msg->timestamp.host.nanosecond; |
|
|
|
if (current - timestamp < timeout) { |
|
/* Remaining messages are within time window */ |
|
break; |
|
} |
|
|
|
msg = (struct ptp_msg *)k_fifo_get(&foreign->messages, K_NO_WAIT); |
|
ptp_msg_unref(msg); |
|
foreign->messages_count--; |
|
} |
|
} |
|
|
|
static void port_clear_foreign_clock_records(struct ptp_foreign_tt_clock *foreign) |
|
{ |
|
struct ptp_msg *msg; |
|
|
|
while (!k_fifo_is_empty(&foreign->messages)) { |
|
msg = (struct ptp_msg *)k_fifo_get(&foreign->messages, K_NO_WAIT); |
|
ptp_msg_unref(msg); |
|
foreign->messages_count--; |
|
} |
|
} |
|
|
|
static void port_delay_req_cleanup(struct ptp_port *port) |
|
{ |
|
sys_snode_t *prev = NULL; |
|
struct ptp_msg *msg; |
|
int64_t timestamp, current = k_uptime_get() * NSEC_PER_MSEC; |
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&port->delay_req_list, msg, node) { |
|
timestamp = msg->timestamp.host.second * NSEC_PER_SEC + |
|
msg->timestamp.host.nanosecond; |
|
|
|
if (current - timestamp < PORT_DELAY_REQ_CLEARE_TO) { |
|
break; |
|
} |
|
|
|
ptp_msg_unref(msg); |
|
sys_slist_remove(&port->delay_req_list, prev, &msg->node); |
|
prev = &msg->node; |
|
} |
|
} |
|
|
|
static void port_clear_delay_req(struct ptp_port *port) |
|
{ |
|
sys_snode_t *prev = NULL; |
|
struct ptp_msg *msg; |
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&port->delay_req_list, msg, node) { |
|
ptp_msg_unref(msg); |
|
sys_slist_remove(&port->delay_req_list, prev, &msg->node); |
|
prev = &msg->node; |
|
} |
|
} |
|
|
|
static void port_sync_fup_ooo_handle(struct ptp_port *port, struct ptp_msg *msg) |
|
{ |
|
struct ptp_msg *last = port->last_sync_fup; |
|
|
|
if (ptp_msg_type(msg) != PTP_MSG_FOLLOW_UP && |
|
ptp_msg_type(msg) != PTP_MSG_SYNC) { |
|
return; |
|
} |
|
|
|
if (!last) { |
|
port->last_sync_fup = msg; |
|
ptp_msg_ref(msg); |
|
return; |
|
} |
|
|
|
if (ptp_msg_type(last) == PTP_MSG_SYNC && |
|
ptp_msg_type(msg) == PTP_MSG_FOLLOW_UP && |
|
msg->header.sequence_id == last->header.sequence_id) { |
|
|
|
port_synchronize(port, |
|
last->timestamp.host, |
|
msg->timestamp.protocol, |
|
last->header.correction, |
|
msg->header.correction); |
|
|
|
ptp_msg_unref(port->last_sync_fup); |
|
port->last_sync_fup = NULL; |
|
} else if (ptp_msg_type(last) == PTP_MSG_FOLLOW_UP && |
|
ptp_msg_type(msg) == PTP_MSG_SYNC && |
|
msg->header.sequence_id == last->header.sequence_id) { |
|
|
|
port_synchronize(port, |
|
msg->timestamp.host, |
|
last->timestamp.protocol, |
|
msg->header.correction, |
|
last->header.correction); |
|
|
|
ptp_msg_unref(port->last_sync_fup); |
|
port->last_sync_fup = NULL; |
|
} else { |
|
ptp_msg_unref(port->last_sync_fup); |
|
port->last_sync_fup = msg; |
|
ptp_msg_ref(msg); |
|
} |
|
} |
|
|
|
static int port_announce_msg_process(struct ptp_port *port, struct ptp_msg *msg) |
|
{ |
|
int ret = 0; |
|
const struct ptp_default_ds *dds = ptp_clock_default_ds(); |
|
|
|
if (msg->announce.steps_rm >= dds->max_steps_rm) { |
|
return ret; |
|
} |
|
|
|
switch (ptp_port_state(port)) { |
|
case PTP_PS_INITIALIZING: |
|
__fallthrough; |
|
case PTP_PS_DISABLED: |
|
__fallthrough; |
|
case PTP_PS_FAULTY: |
|
break; |
|
case PTP_PS_LISTENING: |
|
__fallthrough; |
|
case PTP_PS_PRE_TIME_TRANSMITTER: |
|
__fallthrough; |
|
case PTP_PS_TIME_TRANSMITTER: |
|
__fallthrough; |
|
case PTP_PS_GRAND_MASTER: |
|
#if CONFIG_PTP_FOREIGN_TIME_TRANSMITTER_FEATURE |
|
ret = ptp_port_add_foreign_tt(port, msg); |
|
break; |
|
#else |
|
__fallthrough; |
|
#endif |
|
case PTP_PS_TIME_RECEIVER: |
|
__fallthrough; |
|
case PTP_PS_UNCALIBRATED: |
|
__fallthrough; |
|
case PTP_PS_PASSIVE: |
|
ret = ptp_port_update_current_time_transmitter(port, msg); |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
static void port_sync_msg_process(struct ptp_port *port, struct ptp_msg *msg) |
|
{ |
|
enum ptp_port_state state = ptp_port_state(port); |
|
|
|
if (state != PTP_PS_TIME_RECEIVER && state != PTP_PS_UNCALIBRATED) { |
|
return; |
|
} |
|
|
|
if (!ptp_msg_current_parent(msg)) { |
|
return; |
|
} |
|
|
|
if (port->port_ds.log_sync_interval != msg->header.log_msg_interval) { |
|
port->port_ds.log_sync_interval = msg->header.log_msg_interval; |
|
} |
|
|
|
msg->header.correction += port->port_ds.delay_asymmetry; |
|
|
|
if (!(msg->header.flags[0] & PTP_MSG_TWO_STEP_FLAG)) { |
|
port_synchronize(port, |
|
msg->timestamp.host, |
|
msg->timestamp.protocol, |
|
msg->header.correction, |
|
0); |
|
|
|
if (port->last_sync_fup) { |
|
ptp_msg_unref(port->last_sync_fup); |
|
port->last_sync_fup = NULL; |
|
} |
|
|
|
return; |
|
} |
|
|
|
port_sync_fup_ooo_handle(port, msg); |
|
} |
|
|
|
static void port_follow_up_msg_process(struct ptp_port *port, struct ptp_msg *msg) |
|
{ |
|
enum ptp_port_state state = ptp_port_state(port); |
|
|
|
if (state != PTP_PS_TIME_RECEIVER && state != PTP_PS_UNCALIBRATED) { |
|
return; |
|
} |
|
|
|
if (!ptp_msg_current_parent(msg)) { |
|
return; |
|
} |
|
|
|
port_sync_fup_ooo_handle(port, msg); |
|
} |
|
|
|
static int port_delay_req_msg_process(struct ptp_port *port, struct ptp_msg *msg) |
|
{ |
|
int ret; |
|
struct ptp_msg *resp; |
|
enum ptp_port_state state = ptp_port_state(port); |
|
const struct ptp_default_ds *dds = ptp_clock_default_ds(); |
|
|
|
if (state != PTP_PS_TIME_TRANSMITTER && state != PTP_PS_GRAND_MASTER) { |
|
return 0; |
|
} |
|
|
|
resp = ptp_msg_alloc(); |
|
if (!resp) { |
|
return -ENOMEM; |
|
} |
|
|
|
resp->header.type_major_sdo_id = PTP_MSG_DELAY_RESP; |
|
resp->header.version = PTP_VERSION; |
|
resp->header.msg_length = sizeof(struct ptp_delay_resp_msg); |
|
resp->header.domain_number = dds->domain; |
|
resp->header.correction = msg->header.correction; |
|
resp->header.src_port_id = port->port_ds.id; |
|
resp->header.sequence_id = msg->header.sequence_id; |
|
resp->header.log_msg_interval = port->port_ds.log_min_delay_req_interval; |
|
|
|
resp->delay_resp.receive_timestamp.seconds_high = msg->timestamp.host._sec.high; |
|
resp->delay_resp.receive_timestamp.seconds_low = msg->timestamp.host._sec.low; |
|
resp->delay_resp.receive_timestamp.nanoseconds = msg->timestamp.host.nanosecond; |
|
resp->delay_resp.req_port_id = msg->header.src_port_id; |
|
|
|
if (msg->header.flags[0] & PTP_MSG_UNICAST_FLAG) { |
|
/* TODO handle unicast messages */ |
|
resp->header.flags[0] |= PTP_MSG_UNICAST_FLAG; |
|
} |
|
|
|
ret = port_msg_send(port, resp, PTP_SOCKET_EVENT); |
|
ptp_msg_unref(resp); |
|
|
|
if (ret < 0) { |
|
return -EFAULT; |
|
} |
|
|
|
LOG_DBG("Port %d responds to Delay_Req message", port->port_ds.id.port_number); |
|
return 0; |
|
} |
|
|
|
static void port_delay_resp_msg_process(struct ptp_port *port, struct ptp_msg *msg) |
|
{ |
|
uint64_t t3, t4, t4c; |
|
sys_snode_t *prev = NULL; |
|
struct ptp_msg *req; |
|
enum ptp_port_state state = ptp_port_state(port); |
|
|
|
if (state != PTP_PS_TIME_RECEIVER && state != PTP_PS_UNCALIBRATED) { |
|
return; |
|
} |
|
|
|
if (!ptp_port_id_eq(&msg->delay_resp.req_port_id, &port->port_ds.id)) { |
|
/* Message is not meant for this PTP Port */ |
|
return; |
|
} |
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&port->delay_req_list, req, node) { |
|
if (msg->header.sequence_id == ntohs(req->header.sequence_id)) { |
|
break; |
|
} |
|
prev = &req->node; |
|
} |
|
|
|
if (!req) { |
|
return; |
|
} |
|
|
|
t3 = req->timestamp.host.second * NSEC_PER_SEC + req->timestamp.host.nanosecond; |
|
t4 = msg->timestamp.protocol.second * NSEC_PER_SEC + msg->timestamp.protocol.nanosecond; |
|
t4c = t4 - (msg->header.correction >> 16); |
|
|
|
ptp_clock_delay(t3, t4c); |
|
|
|
sys_slist_remove(&port->delay_req_list, prev, &req->node); |
|
ptp_msg_unref(req); |
|
|
|
port->port_ds.log_min_delay_req_interval = msg->header.log_msg_interval; |
|
} |
|
|
|
static struct ptp_msg *port_management_resp_prepare(struct ptp_port *port, struct ptp_msg *req) |
|
{ |
|
struct ptp_msg *resp = ptp_msg_alloc(); |
|
const struct ptp_default_ds *dds = ptp_clock_default_ds(); |
|
|
|
if (!resp) { |
|
return NULL; |
|
} |
|
|
|
resp->header.type_major_sdo_id = PTP_MSG_MANAGEMENT; |
|
resp->header.version = PTP_VERSION; |
|
resp->header.msg_length = sizeof(struct ptp_management_msg); |
|
resp->header.domain_number = dds->domain; |
|
resp->header.src_port_id = port->port_ds.id; |
|
resp->header.sequence_id = req->header.sequence_id; |
|
resp->header.log_msg_interval = port->port_ds.log_min_delay_req_interval; |
|
|
|
if (req->management.action == PTP_MGMT_GET || |
|
req->management.action == PTP_MGMT_SET) { |
|
resp->management.action = PTP_MGMT_RESP; |
|
} else if (req->management.action == PTP_MGMT_CMD) { |
|
resp->management.action = PTP_MGMT_ACK; |
|
} |
|
|
|
memcpy(&resp->management.target_port_id, |
|
&req->header.src_port_id, |
|
sizeof(struct ptp_port_id)); |
|
|
|
resp->management.starting_boundary_hops = req->management.starting_boundary_hops - |
|
req->management.boundary_hops; |
|
resp->management.boundary_hops = resp->management.starting_boundary_hops; |
|
|
|
return resp; |
|
} |
|
|
|
static int port_management_resp_tlv_fill(struct ptp_port *port, |
|
struct ptp_msg *req, |
|
struct ptp_msg *resp, |
|
struct ptp_tlv_mgmt *req_mgmt) |
|
{ |
|
int length = 0; |
|
struct ptp_tlv_mgmt *mgmt; |
|
struct ptp_tlv_default_ds *tlv_dds; |
|
struct ptp_tlv_parent_ds *tlv_pds; |
|
const struct ptp_parent_ds *pds = ptp_clock_parent_ds(); |
|
const struct ptp_current_ds *cds = ptp_clock_current_ds(); |
|
const struct ptp_default_ds *dds = ptp_clock_default_ds(); |
|
const struct ptp_time_prop_ds *tpds = ptp_clock_time_prop_ds(); |
|
struct ptp_tlv_container *container = ptp_tlv_alloc(); |
|
|
|
if (!container) { |
|
return -ENOMEM; |
|
} |
|
|
|
container->tlv = (struct ptp_tlv *)resp->management.suffix; |
|
mgmt = (struct ptp_tlv_mgmt *)container->tlv; |
|
mgmt->type = PTP_TLV_TYPE_MANAGEMENT; |
|
mgmt->id = req_mgmt->id; |
|
|
|
switch (mgmt->id) { |
|
case PTP_MGMT_DEFAULT_DATA_SET: |
|
tlv_dds = (struct ptp_tlv_default_ds *)mgmt->data; |
|
|
|
length = sizeof(struct ptp_tlv_default_ds); |
|
tlv_dds->flags = 0x1 | (dds->time_receiver_only << 1); |
|
tlv_dds->n_ports = dds->n_ports; |
|
tlv_dds->priority1 = dds->priority1; |
|
tlv_dds->priority2 = dds->priority2; |
|
tlv_dds->domain = dds->domain; |
|
memcpy(&tlv_dds->clk_id, &dds->clk_id, sizeof(tlv_dds->clk_id)); |
|
memcpy(&tlv_dds->clk_quality, &dds->clk_quality, sizeof(tlv_dds->clk_quality)); |
|
break; |
|
case PTP_MGMT_CURRENT_DATA_SET: |
|
length = sizeof(struct ptp_tlv_current_ds); |
|
memcpy(mgmt->data, cds, sizeof(struct ptp_tlv_current_ds)); |
|
break; |
|
case PTP_MGMT_PARENT_DATA_SET: |
|
tlv_pds = (struct ptp_tlv_parent_ds *)mgmt->data; |
|
|
|
length = sizeof(struct ptp_tlv_parent_ds); |
|
|
|
tlv_pds->obsreved_parent_offset_scaled_log_variance = |
|
pds->obsreved_parent_offset_scaled_log_variance; |
|
tlv_pds->obsreved_parent_clk_phase_change_rate = |
|
pds->obsreved_parent_clk_phase_change_rate; |
|
tlv_pds->gm_priority1 = pds->gm_priority1; |
|
tlv_pds->gm_priority2 = pds->gm_priority2; |
|
memcpy(&tlv_pds->port_id, &pds->port_id, sizeof(tlv_pds->port_id)); |
|
memcpy(&tlv_pds->gm_id, &pds->gm_id, sizeof(tlv_pds->gm_id)); |
|
memcpy(&tlv_pds->gm_clk_quality, |
|
&pds->gm_clk_quality, |
|
sizeof(tlv_pds->gm_clk_quality)); |
|
|
|
break; |
|
case PTP_MGMT_TIME_PROPERTIES_DATA_SET: |
|
length = sizeof(struct ptp_tlv_time_prop_ds); |
|
memcpy(mgmt->data, tpds, sizeof(struct ptp_tlv_time_prop_ds)); |
|
break; |
|
case PTP_MGMT_PORT_DATA_SET: |
|
length = sizeof(struct ptp_tlv_port_ds); |
|
memcpy(mgmt->data, &port->port_ds, sizeof(struct ptp_tlv_port_ds)); |
|
break; |
|
case PTP_MGMT_PRIORITY1: |
|
length = sizeof(dds->priority1); |
|
*mgmt->data = dds->priority1; |
|
break; |
|
case PTP_MGMT_PRIORITY2: |
|
length = sizeof(dds->priority2); |
|
*mgmt->data = dds->priority2; |
|
break; |
|
case PTP_MGMT_DOMAIN: |
|
length = sizeof(dds->domain); |
|
*mgmt->data = dds->domain; |
|
break; |
|
case PTP_MGMT_TIME_RECEIVER_ONLY: |
|
length = sizeof(dds->time_receiver_only); |
|
*mgmt->data = dds->time_receiver_only; |
|
break; |
|
case PTP_MGMT_LOG_ANNOUNCE_INTERVAL: |
|
length = sizeof(port->port_ds.log_announce_interval); |
|
*mgmt->data = port->port_ds.log_announce_interval; |
|
break; |
|
case PTP_MGMT_LOG_SYNC_INTERVAL: |
|
length = sizeof(port->port_ds.log_sync_interval); |
|
*mgmt->data = port->port_ds.log_sync_interval; |
|
break; |
|
case PTP_MGMT_VERSION_NUMBER: |
|
length = sizeof(port->port_ds.version); |
|
*mgmt->data = port->port_ds.version; |
|
break; |
|
case PTP_MGMT_CLOCK_ACCURACY: |
|
length = sizeof(dds->clk_quality.accuracy); |
|
*mgmt->data = dds->clk_quality.accuracy; |
|
break; |
|
case PTP_MGMT_DELAY_MECHANISM: |
|
length = sizeof(port->port_ds.delay_mechanism); |
|
*(uint16_t *)mgmt->data = port->port_ds.delay_mechanism; |
|
break; |
|
default: |
|
ptp_tlv_free(container); |
|
return -EINVAL; |
|
} |
|
|
|
/* Management TLV length shall be an even number */ |
|
if (length % 2) { |
|
mgmt->data[length] = 0; |
|
length++; |
|
} |
|
|
|
container->tlv->length = sizeof(mgmt->id) + length; |
|
resp->header.msg_length += sizeof(*container->tlv) + container->tlv->length; |
|
sys_slist_append(&resp->tlvs, &container->node); |
|
|
|
return 0; |
|
} |
|
|
|
static int port_management_set(struct ptp_port *port, |
|
struct ptp_msg *req, |
|
struct ptp_tlv_mgmt *tlv) |
|
{ |
|
bool send_resp = false; |
|
|
|
switch (tlv->id) { |
|
case PTP_MGMT_LOG_ANNOUNCE_INTERVAL: |
|
port->port_ds.log_announce_interval = *tlv->data; |
|
send_resp = true; |
|
break; |
|
case PTP_MGMT_LOG_SYNC_INTERVAL: |
|
port->port_ds.log_sync_interval = *tlv->data; |
|
send_resp = true; |
|
break; |
|
case PTP_MGMT_UNICAST_NEGOTIATION_ENABLE: |
|
/* TODO unicast */ |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
return send_resp ? ptp_port_management_resp(port, req, tlv) : 0; |
|
} |
|
|
|
static int port_enable(struct ptp_port *port) |
|
{ |
|
while (!net_if_is_up(port->iface)) { |
|
return -1; |
|
} |
|
|
|
port->link_status = PORT_LINK_UP; |
|
|
|
if (ptp_transport_open(port)) { |
|
LOG_ERR("Couldn't open socket on Port %d.", port->port_ds.id.port_number); |
|
return -1; |
|
} |
|
|
|
port->port_ds.enable = true; |
|
|
|
ptp_clock_pollfd_invalidate(); |
|
LOG_DBG("Port %d opened", port->port_ds.id.port_number); |
|
return 0; |
|
} |
|
|
|
static bool port_is_enabled(struct ptp_port *port) |
|
{ |
|
enum ptp_port_state state = ptp_port_state(port); |
|
|
|
if (state == PTP_PS_FAULTY || |
|
state == PTP_PS_DISABLED || |
|
state == PTP_PS_INITIALIZING) { |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
static void port_disable(struct ptp_port *port) |
|
{ |
|
k_timer_stop(&port->timers.announce); |
|
k_timer_stop(&port->timers.delay); |
|
k_timer_stop(&port->timers.sync); |
|
k_timer_stop(&port->timers.qualification); |
|
|
|
atomic_clear(&port->timeouts); |
|
|
|
ptp_transport_close(port); |
|
ptp_port_free_foreign_tts(port); |
|
port->best = NULL; |
|
|
|
net_if_unregister_timestamp_cb(&port->sync_ts_cb); |
|
net_if_unregister_timestamp_cb(&port->delay_req_ts_cb); |
|
|
|
ptp_clock_pollfd_invalidate(); |
|
port->port_ds.enable = false; |
|
LOG_DBG("Port %d disabled", port->port_ds.id.port_number); |
|
} |
|
|
|
int port_state_update(struct ptp_port *port, enum ptp_port_event event, bool tt_diff) |
|
{ |
|
enum ptp_port_state next_state = port->state_machine(ptp_port_state(port), |
|
event, |
|
tt_diff); |
|
|
|
if (next_state == PTP_PS_FAULTY) { |
|
/* clear fault if interface is UP */ |
|
if (net_if_oper_state(port->iface) == NET_IF_OPER_UP) { |
|
next_state = port->state_machine(next_state, PTP_EVT_FAULT_CLEARED, false); |
|
} |
|
} |
|
|
|
if (next_state == PTP_PS_INITIALIZING) { |
|
if (port_is_enabled(port)) { |
|
port_disable(port); |
|
} |
|
if (port_enable(port)) { |
|
event = PTP_EVT_FAULT_DETECTED; |
|
} else { |
|
event = PTP_EVT_INIT_COMPLETE; |
|
} |
|
next_state = port->state_machine(next_state, event, false); |
|
} |
|
|
|
if (next_state != ptp_port_state(port)) { |
|
LOG_DBG("Port %d changed state from %s to %s", |
|
port->port_ds.id.port_number, |
|
port_state_str(ptp_port_state(port)), |
|
port_state_str(next_state)); |
|
|
|
port->port_ds.state = next_state; |
|
return 1; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static void port_link_monitor(struct net_mgmt_event_callback *cb, |
|
uint64_t mgmt_event, |
|
struct net_if *iface) |
|
{ |
|
ARG_UNUSED(cb); |
|
|
|
enum ptp_port_event event = PTP_EVT_NONE; |
|
struct ptp_port *port = ptp_clock_port_from_iface(iface); |
|
uint8_t iface_state = mgmt_event == NET_EVENT_IF_UP ? PORT_LINK_UP : PORT_LINK_DOWN; |
|
|
|
if (!port) { |
|
return; |
|
} |
|
|
|
if (iface_state & port->link_status) { |
|
port->link_status = iface_state; |
|
} else { |
|
port->link_status = iface_state | PORT_LINK_CHANGED; |
|
LOG_DBG("Port %d link %s", |
|
port->port_ds.id.port_number, |
|
port->link_status & PORT_LINK_UP ? "up" : "down"); |
|
} |
|
|
|
if (port->link_status & PORT_LINK_CHANGED) { |
|
event = iface_state == PORT_LINK_UP ? |
|
PTP_EVT_FAULT_CLEARED : PTP_EVT_FAULT_DETECTED; |
|
port->link_status ^= PORT_LINK_CHANGED; |
|
} |
|
|
|
if (port->link_status & PORT_LINK_DOWN) { |
|
ptp_clock_state_decision_req(); |
|
} |
|
|
|
ptp_port_event_handle(port, event, false); |
|
} |
|
|
|
void ptp_port_init(struct net_if *iface, void *user_data) |
|
{ |
|
struct ptp_port *port; |
|
const struct ptp_default_ds *dds = ptp_clock_default_ds(); |
|
|
|
ARG_UNUSED(user_data); |
|
|
|
if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) { |
|
return; |
|
} |
|
|
|
if (dds->n_ports >= CONFIG_PTP_NUM_PORTS) { |
|
LOG_WRN("Exceeded number of PTP Ports."); |
|
return; |
|
} |
|
|
|
port = &ports[dds->n_ports]; |
|
|
|
port->iface = iface; |
|
port->best = NULL; |
|
port->socket[PTP_SOCKET_EVENT] = -1; |
|
port->socket[PTP_SOCKET_GENERAL] = -1; |
|
|
|
port->state_machine = dds->time_receiver_only ? ptp_tr_state_machine : ptp_state_machine; |
|
port->last_sync_fup = NULL; |
|
|
|
port_ds_init(port); |
|
sys_slist_init(&port->foreign_list); |
|
sys_slist_init(&port->delay_req_list); |
|
|
|
port_timer_init(&port->timers.delay, port_timer_to_handler, port); |
|
port_timer_init(&port->timers.announce, port_timer_to_handler, port); |
|
port_timer_init(&port->timers.sync, port_timer_to_handler, port); |
|
port_timer_init(&port->timers.qualification, port_timer_to_handler, port); |
|
|
|
ptp_clock_pollfd_invalidate(); |
|
ptp_clock_port_add(port); |
|
|
|
net_mgmt_init_event_callback(&port->link_cb, port_link_monitor, PORT_LINK_EVENT_MASK); |
|
net_mgmt_add_event_callback(&port->link_cb); |
|
|
|
LOG_DBG("Port %d initialized", port->port_ds.id.port_number); |
|
} |
|
|
|
enum ptp_port_event ptp_port_event_gen(struct ptp_port *port, int idx) |
|
{ |
|
enum ptp_port_event event = PTP_EVT_NONE; |
|
struct ptp_msg *msg; |
|
int ret, cnt; |
|
|
|
if (idx < 0) { |
|
return event; |
|
} |
|
|
|
msg = ptp_msg_alloc(); |
|
if (!msg) { |
|
return PTP_EVT_FAULT_DETECTED; |
|
} |
|
|
|
cnt = ptp_transport_recv(port, msg, idx); |
|
if (cnt <= 0) { |
|
LOG_ERR("Error during message reception"); |
|
ptp_msg_unref(msg); |
|
return PTP_EVT_FAULT_DETECTED; |
|
} |
|
|
|
ret = ptp_msg_post_recv(port, msg, cnt); |
|
if (ret) { |
|
ptp_msg_unref(msg); |
|
return PTP_EVT_FAULT_DETECTED; |
|
} |
|
|
|
if (ptp_port_id_eq(&msg->header.src_port_id, &port->port_ds.id)) { |
|
ptp_msg_unref(msg); |
|
return PTP_EVT_NONE; |
|
} |
|
|
|
switch (ptp_msg_type(msg)) { |
|
case PTP_MSG_SYNC: |
|
port_sync_msg_process(port, msg); |
|
break; |
|
case PTP_MSG_DELAY_REQ: |
|
if (port_delay_req_msg_process(port, msg)) { |
|
event = PTP_EVT_FAULT_DETECTED; |
|
} |
|
break; |
|
case PTP_MSG_PDELAY_REQ: |
|
__fallthrough; |
|
case PTP_MSG_PDELAY_RESP: |
|
__fallthrough; |
|
case PTP_MSG_PDELAY_RESP_FOLLOW_UP: |
|
/* P2P delay mechanism not supported */ |
|
break; |
|
case PTP_MSG_FOLLOW_UP: |
|
port_follow_up_msg_process(port, msg); |
|
break; |
|
case PTP_MSG_DELAY_RESP: |
|
port_delay_resp_msg_process(port, msg); |
|
break; |
|
case PTP_MSG_ANNOUNCE: |
|
if (port_announce_msg_process(port, msg)) { |
|
event = PTP_EVT_STATE_DECISION; |
|
} |
|
break; |
|
case PTP_MSG_SIGNALING: |
|
/* Signalling messages not supported */ |
|
break; |
|
case PTP_MSG_MANAGEMENT: |
|
if (ptp_clock_management_msg_process(port, msg)) { |
|
event = PTP_EVT_STATE_DECISION; |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
ptp_msg_unref(msg); |
|
return event; |
|
} |
|
|
|
void ptp_port_event_handle(struct ptp_port *port, enum ptp_port_event event, bool tt_diff) |
|
{ |
|
const struct ptp_current_ds *cds = ptp_clock_current_ds(); |
|
|
|
if (event == PTP_EVT_NONE) { |
|
return; |
|
} |
|
|
|
if (!port_state_update(port, event, tt_diff)) { |
|
/* No PTP Port state change */ |
|
return; |
|
} |
|
|
|
k_timer_stop(&port->timers.announce); |
|
k_timer_stop(&port->timers.delay); |
|
k_timer_stop(&port->timers.sync); |
|
k_timer_stop(&port->timers.qualification); |
|
|
|
switch (port->port_ds.state) { |
|
case PTP_PS_INITIALIZING: |
|
break; |
|
case PTP_PS_FAULTY: |
|
__fallthrough; |
|
case PTP_PS_DISABLED: |
|
port_disable(port); |
|
break; |
|
case PTP_PS_LISTENING: |
|
port_timer_set_timeout_random(&port->timers.announce, |
|
port->port_ds.announce_receipt_timeout, |
|
1, |
|
port->port_ds.log_announce_interval); |
|
break; |
|
case PTP_PS_PRE_TIME_TRANSMITTER: |
|
port_timer_set_timeout(&port->timers.qualification, |
|
1 + cds->steps_rm, |
|
port->port_ds.log_announce_interval); |
|
break; |
|
case PTP_PS_GRAND_MASTER: |
|
__fallthrough; |
|
case PTP_PS_TIME_TRANSMITTER: |
|
port_timer_set_timeout(&port->timers.announce, |
|
1, |
|
port->port_ds.log_announce_interval); |
|
port_timer_set_timeout(&port->timers.sync, 1, port->port_ds.log_sync_interval); |
|
break; |
|
case PTP_PS_PASSIVE: |
|
port_timer_set_timeout_random(&port->timers.announce, |
|
port->port_ds.announce_receipt_timeout, |
|
1, |
|
port->port_ds.log_announce_interval); |
|
break; |
|
case PTP_PS_UNCALIBRATED: |
|
if (port->last_sync_fup) { |
|
ptp_msg_unref(port->last_sync_fup); |
|
port->last_sync_fup = NULL; |
|
} |
|
port_clear_delay_req(port); |
|
__fallthrough; |
|
case PTP_PS_TIME_RECEIVER: |
|
port_timer_set_timeout_random(&port->timers.announce, |
|
port->port_ds.announce_receipt_timeout, |
|
1, |
|
port->port_ds.log_announce_interval); |
|
port_timer_set_timeout_random(&port->timers.delay, |
|
0, |
|
2, |
|
port->port_ds.log_min_delay_req_interval); |
|
break; |
|
}; |
|
} |
|
|
|
enum ptp_port_state ptp_port_state(struct ptp_port *port) |
|
{ |
|
return (enum ptp_port_state)port->port_ds.state; |
|
} |
|
|
|
enum ptp_port_event ptp_port_timer_event_gen(struct ptp_port *port, struct k_timer *timer) |
|
{ |
|
enum ptp_port_event event = PTP_EVT_NONE; |
|
enum ptp_port_state state = ptp_port_state(port); |
|
|
|
switch (state) { |
|
case PTP_PS_PRE_TIME_TRANSMITTER: |
|
if (timer == &port->timers.qualification && |
|
atomic_test_bit(&port->timeouts, PTP_PORT_TIMER_QUALIFICATION_TO)) { |
|
LOG_DBG("Port %d Qualification timeout", port->port_ds.id.port_number); |
|
atomic_clear_bit(&port->timeouts, PTP_PORT_TIMER_QUALIFICATION_TO); |
|
|
|
return PTP_EVT_QUALIFICATION_TIMEOUT_EXPIRES; |
|
} |
|
break; |
|
case PTP_PS_GRAND_MASTER: |
|
__fallthrough; |
|
case PTP_PS_TIME_TRANSMITTER: |
|
if (timer == &port->timers.sync && |
|
atomic_test_bit(&port->timeouts, PTP_PORT_TIMER_SYNC_TO)) { |
|
LOG_DBG("Port %d TX Sync timeout", port->port_ds.id.port_number); |
|
port_timer_set_timeout(&port->timers.sync, |
|
1, |
|
port->port_ds.log_sync_interval); |
|
atomic_clear_bit(&port->timeouts, PTP_PORT_TIMER_SYNC_TO); |
|
|
|
return port_sync_msg_transmit(port) == 0 ? PTP_EVT_NONE : |
|
PTP_EVT_FAULT_DETECTED; |
|
} |
|
|
|
if (timer == &port->timers.announce && |
|
atomic_test_bit(&port->timeouts, PTP_PORT_TIMER_ANNOUNCE_TO)) { |
|
LOG_DBG("Port %d TimeTransmitter Announce timeout", |
|
port->port_ds.id.port_number); |
|
port_timer_set_timeout(&port->timers.announce, |
|
1, |
|
port->port_ds.log_announce_interval); |
|
atomic_clear_bit(&port->timeouts, PTP_PORT_TIMER_ANNOUNCE_TO); |
|
|
|
return port_announce_msg_transmit(port) == 0 ? PTP_EVT_NONE : |
|
PTP_EVT_FAULT_DETECTED; |
|
} |
|
break; |
|
case PTP_PS_TIME_RECEIVER: |
|
if (timer == &port->timers.delay && |
|
atomic_test_bit(&port->timeouts, PTP_PORT_TIMER_DELAY_TO)) { |
|
|
|
atomic_clear_bit(&port->timeouts, PTP_PORT_TIMER_DELAY_TO); |
|
port_delay_req_cleanup(port); |
|
port_timer_set_timeout(&port->timers.delay, |
|
1, |
|
port->port_ds.log_announce_interval); |
|
|
|
if (port_delay_req_msg_transmit(port) < 0) { |
|
return PTP_EVT_FAULT_DETECTED; |
|
} |
|
} |
|
__fallthrough; |
|
case PTP_PS_PASSIVE: |
|
__fallthrough; |
|
case PTP_PS_UNCALIBRATED: |
|
__fallthrough; |
|
case PTP_PS_LISTENING: |
|
if ((timer == &port->timers.announce || timer == &port->timers.sync) && |
|
(atomic_test_bit(&port->timeouts, PTP_PORT_TIMER_ANNOUNCE_TO) || |
|
atomic_test_bit(&port->timeouts, PTP_PORT_TIMER_SYNC_TO))) { |
|
|
|
LOG_DBG("Port %d %s timeout", |
|
port->port_ds.id.port_number, |
|
timer == &port->timers.announce ? "Announce" : "RX Sync"); |
|
|
|
atomic_clear_bit(&port->timeouts, PTP_PORT_TIMER_ANNOUNCE_TO); |
|
atomic_clear_bit(&port->timeouts, PTP_PORT_TIMER_SYNC_TO); |
|
|
|
if (port->best) { |
|
port_clear_foreign_clock_records(port->best); |
|
} |
|
|
|
port_delay_req_cleanup(port); |
|
port_timer_set_timeout_random(&port->timers.announce, |
|
port->port_ds.announce_receipt_timeout, |
|
1, |
|
port->port_ds.log_announce_interval); |
|
|
|
return PTP_EVT_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES; |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
return event; |
|
} |
|
|
|
bool ptp_port_id_eq(const struct ptp_port_id *p1, const struct ptp_port_id *p2) |
|
{ |
|
return memcmp(p1, p2, sizeof(struct ptp_port_id)) == 0; |
|
} |
|
|
|
struct ptp_dataset *ptp_port_best_foreign_ds(struct ptp_port *port) |
|
{ |
|
return port->best ? &port->best->dataset : NULL; |
|
} |
|
|
|
struct ptp_foreign_tt_clock *ptp_port_best_foreign(struct ptp_port *port) |
|
{ |
|
struct ptp_foreign_tt_clock *foreign; |
|
struct ptp_announce_msg *last; |
|
|
|
port->best = NULL; |
|
|
|
if (port->port_ds.time_transmitter_only) { |
|
return NULL; |
|
} |
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&port->foreign_list, foreign, node) { |
|
if (!foreign->messages_count) { |
|
continue; |
|
} |
|
|
|
foreign_clock_cleanup(foreign); |
|
|
|
if (foreign->messages_count < FOREIGN_TIME_TRANSMITTER_THRESHOLD) { |
|
continue; |
|
} |
|
|
|
last = (struct ptp_announce_msg *)k_fifo_peek_head(&foreign->messages); |
|
|
|
foreign->dataset.priority1 = last->gm_priority1; |
|
foreign->dataset.priority2 = last->gm_priority2; |
|
foreign->dataset.steps_rm = last->steps_rm; |
|
|
|
memcpy(&foreign->dataset.clk_quality, |
|
&last->gm_clk_quality, |
|
sizeof(last->gm_clk_quality)); |
|
memcpy(&foreign->dataset.clk_id, &last->gm_id, sizeof(last->gm_id)); |
|
memcpy(&foreign->dataset.receiver, &port->port_ds.id, sizeof(port->port_ds.id)); |
|
|
|
if (!port->best) { |
|
port->best = foreign; |
|
} else if (ptp_btca_ds_cmp(&foreign->dataset, &port->best->dataset)) { |
|
port->best = foreign; |
|
} else { |
|
port_clear_foreign_clock_records(foreign); |
|
} |
|
} |
|
return port->best; |
|
} |
|
|
|
int ptp_port_add_foreign_tt(struct ptp_port *port, struct ptp_msg *msg) |
|
{ |
|
struct ptp_foreign_tt_clock *foreign; |
|
struct ptp_msg *last; |
|
int diff = 0; |
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&port->foreign_list, foreign, node) { |
|
if (ptp_port_id_eq(&msg->header.src_port_id, &foreign->dataset.sender)) { |
|
break; |
|
} |
|
} |
|
|
|
if (!foreign) { |
|
LOG_DBG("Port %d has a new foreign timeTransmitter %s", |
|
port->port_ds.id.port_number, |
|
port_id_str(&msg->header.src_port_id)); |
|
|
|
int ret = k_mem_slab_alloc(&foreign_tts_slab, (void **)&foreign, K_NO_WAIT); |
|
|
|
if (ret) { |
|
LOG_ERR("Couldn't allocate memory for new foreign timeTransmitter"); |
|
return 0; |
|
} |
|
|
|
memset(foreign, 0, sizeof(*foreign)); |
|
memcpy(&foreign->dataset.sender, |
|
&msg->header.src_port_id, |
|
sizeof(foreign->dataset.sender)); |
|
k_fifo_init(&foreign->messages); |
|
foreign->port = port; |
|
|
|
sys_slist_append(&port->foreign_list, &foreign->node); |
|
|
|
/* First message is not added to records. */ |
|
return 0; |
|
} |
|
|
|
foreign_clock_cleanup(foreign); |
|
ptp_msg_ref(msg); |
|
|
|
foreign->messages_count++; |
|
k_fifo_put(&foreign->messages, (void *)msg); |
|
|
|
if (foreign->messages_count > 1) { |
|
last = (struct ptp_msg *)k_fifo_peek_tail(&foreign->messages); |
|
diff = ptp_msg_announce_cmp(&msg->announce, &last->announce); |
|
} |
|
|
|
return (foreign->messages_count == FOREIGN_TIME_TRANSMITTER_THRESHOLD ? 1 : 0) || diff; |
|
} |
|
|
|
void ptp_port_free_foreign_tts(struct ptp_port *port) |
|
{ |
|
sys_snode_t *iter; |
|
struct ptp_foreign_tt_clock *foreign; |
|
|
|
while (!sys_slist_is_empty(&port->foreign_list)) { |
|
iter = sys_slist_get(&port->foreign_list); |
|
foreign = CONTAINER_OF(iter, struct ptp_foreign_tt_clock, node); |
|
|
|
while (foreign->messages_count > FOREIGN_TIME_TRANSMITTER_THRESHOLD) { |
|
struct ptp_msg *msg = (struct ptp_msg *)k_fifo_get(&foreign->messages, |
|
K_NO_WAIT); |
|
foreign->messages_count--; |
|
ptp_msg_unref(msg); |
|
} |
|
|
|
k_mem_slab_free(&foreign_tts_slab, (void *)foreign); |
|
} |
|
} |
|
|
|
int ptp_port_update_current_time_transmitter(struct ptp_port *port, struct ptp_msg *msg) |
|
{ |
|
struct ptp_foreign_tt_clock *foreign = port->best; |
|
|
|
if (!foreign || |
|
!ptp_port_id_eq(&msg->header.src_port_id, &foreign->dataset.sender)) { |
|
return ptp_port_add_foreign_tt(port, msg); |
|
} |
|
|
|
foreign_clock_cleanup(foreign); |
|
ptp_msg_ref(msg); |
|
|
|
k_fifo_put(&foreign->messages, (void *)msg); |
|
foreign->messages_count++; |
|
|
|
port_timer_set_timeout_random(&port->timers.announce, |
|
port->port_ds.announce_receipt_timeout, |
|
1, |
|
port->port_ds.log_announce_interval); |
|
|
|
if (foreign->messages_count > 1) { |
|
struct ptp_msg *last = (struct ptp_msg *)k_fifo_peek_tail(&foreign->messages); |
|
|
|
return ptp_msg_announce_cmp(&msg->announce, &last->announce); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
int ptp_port_management_msg_process(struct ptp_port *port, |
|
struct ptp_port *ingress, |
|
struct ptp_msg *msg, |
|
struct ptp_tlv_mgmt *tlv) |
|
{ |
|
int ret = 0; |
|
uint16_t target_port = msg->management.target_port_id.port_number; |
|
|
|
if (target_port != port->port_ds.id.port_number && target_port != 0xFFFF) { |
|
return ret; |
|
} |
|
|
|
if (ptp_mgmt_action(msg) == PTP_MGMT_SET) { |
|
ret = port_management_set(port, msg, tlv); |
|
} else { |
|
ret = ptp_port_management_resp(port, msg, tlv); |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
int ptp_port_management_error(struct ptp_port *port, struct ptp_msg *msg, enum ptp_mgmt_err err) |
|
{ |
|
int ret; |
|
struct ptp_tlv *tlv; |
|
struct ptp_tlv_mgmt_err *mgmt_err; |
|
struct ptp_tlv_mgmt *mgmt = (struct ptp_tlv_mgmt *)msg->management.suffix; |
|
|
|
struct ptp_msg *resp = port_management_resp_prepare(port, msg); |
|
|
|
if (!resp) { |
|
return -ENOMEM; |
|
} |
|
|
|
tlv = ptp_msg_add_tlv(resp, sizeof(struct ptp_tlv_mgmt_err)); |
|
if (!tlv) { |
|
ptp_msg_unref(resp); |
|
return -ENOMEM; |
|
} |
|
|
|
mgmt_err = (struct ptp_tlv_mgmt_err *)tlv; |
|
|
|
mgmt_err->type = PTP_TLV_TYPE_MANAGEMENT_ERROR_STATUS; |
|
mgmt_err->length = 8; |
|
mgmt_err->err_id = err; |
|
mgmt_err->id = mgmt->id; |
|
|
|
ret = port_msg_send(port, resp, PTP_SOCKET_GENERAL); |
|
ptp_msg_unref(resp); |
|
|
|
if (ret) { |
|
return -EFAULT; |
|
} |
|
|
|
LOG_DBG("Port %d sends Menagement Error message", port->port_ds.id.port_number); |
|
return 0; |
|
} |
|
|
|
int ptp_port_management_resp(struct ptp_port *port, struct ptp_msg *req, struct ptp_tlv_mgmt *tlv) |
|
{ |
|
int ret; |
|
struct ptp_msg *resp = port_management_resp_prepare(port, req); |
|
|
|
if (!resp) { |
|
return -ENOMEM; |
|
} |
|
|
|
ret = port_management_resp_tlv_fill(port, req, resp, tlv); |
|
if (ret) { |
|
return ret; |
|
} |
|
|
|
ret = port_msg_send(port, resp, PTP_SOCKET_GENERAL); |
|
ptp_msg_unref(resp); |
|
|
|
if (ret < 0) { |
|
return -EFAULT; |
|
} |
|
|
|
LOG_DBG("Port %d sends Menagement message response", port->port_ds.id.port_number); |
|
return 0; |
|
}
|
|
|