Current clock synchronization was always stepping clock. This was causing large offset, and discontiguous ptp hardware clock time. For TSN hardware, discontiguous ptp hardware clock time was not able to be used for other TSN protocols. This patch is to convert to frequency adjustment with a basic PI control algorithm. Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
684 lines
17 KiB
C
684 lines
17 KiB
C
/*
|
|
* Copyright (c) 2024 BayLibre SAS
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(ptp_clock, CONFIG_PTP_LOG_LEVEL);
|
|
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <zephyr/posix/sys/eventfd.h>
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/drivers/ptp_clock.h>
|
|
#include <zephyr/net/ethernet.h>
|
|
#include <zephyr/net/net_if.h>
|
|
#include <zephyr/net/socket.h>
|
|
#include <zephyr/sys/slist.h>
|
|
|
|
#include "btca.h"
|
|
#include "clock.h"
|
|
#include "ddt.h"
|
|
#include "msg.h"
|
|
#include "port.h"
|
|
#include "tlv.h"
|
|
#include "transport.h"
|
|
|
|
#define MIN_NSEC_TO_TIMEINTERVAL (0xFFFF800000000000ULL)
|
|
#define MAX_NSEC_TO_TIMEINTERVAL (0x00007FFFFFFFFFFFULL)
|
|
|
|
/**
|
|
* @brief PTP Clock structure.
|
|
*/
|
|
struct ptp_clock {
|
|
const struct device *phc;
|
|
struct ptp_default_ds default_ds;
|
|
struct ptp_current_ds current_ds;
|
|
struct ptp_parent_ds parent_ds;
|
|
struct ptp_time_prop_ds time_prop_ds;
|
|
struct ptp_dataset dataset;
|
|
struct ptp_foreign_tt_clock *best;
|
|
sys_slist_t ports_list;
|
|
struct zsock_pollfd pollfd[1 + 2 * CONFIG_PTP_NUM_PORTS];
|
|
bool pollfd_valid;
|
|
bool state_decision_event;
|
|
uint8_t time_src;
|
|
struct {
|
|
uint64_t t1;
|
|
uint64_t t2;
|
|
uint64_t t3;
|
|
uint64_t t4;
|
|
} timestamp; /* latest timestamps in nanoseconds */
|
|
double pi_drift;
|
|
};
|
|
|
|
__maybe_unused static struct ptp_clock ptp_clk = { 0 };
|
|
char str_clock_id[] = "FF:FF:FF:FF:FF:FF:FF:FF";
|
|
|
|
static int clock_generate_id(ptp_clk_id *clock_id, struct net_if *iface)
|
|
{
|
|
struct net_linkaddr *addr = net_if_get_link_addr(iface);
|
|
|
|
if (addr) {
|
|
clock_id->id[0] = addr->addr[0];
|
|
clock_id->id[1] = addr->addr[1];
|
|
clock_id->id[2] = addr->addr[2];
|
|
clock_id->id[3] = 0xFF;
|
|
clock_id->id[4] = 0xFE;
|
|
clock_id->id[5] = addr->addr[3];
|
|
clock_id->id[6] = addr->addr[4];
|
|
clock_id->id[7] = addr->addr[5];
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static const char *clock_id_str(ptp_clk_id *clock_id)
|
|
{
|
|
uint8_t *cid = clock_id->id;
|
|
|
|
snprintk(str_clock_id, sizeof(str_clock_id), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
|
|
cid[0],
|
|
cid[1],
|
|
cid[2],
|
|
cid[3],
|
|
cid[4],
|
|
cid[5],
|
|
cid[6],
|
|
cid[7]);
|
|
|
|
return str_clock_id;
|
|
}
|
|
|
|
static ptp_timeinterval clock_ns_to_timeinterval(int64_t val)
|
|
{
|
|
if (val < (int64_t)MIN_NSEC_TO_TIMEINTERVAL) {
|
|
val = MIN_NSEC_TO_TIMEINTERVAL;
|
|
} else if (val > (int64_t)MAX_NSEC_TO_TIMEINTERVAL) {
|
|
val = MAX_NSEC_TO_TIMEINTERVAL;
|
|
}
|
|
|
|
return (uint64_t)val << 16;
|
|
}
|
|
|
|
static int clock_forward_msg(struct ptp_port *ingress,
|
|
struct ptp_port *port,
|
|
struct ptp_msg *msg,
|
|
bool *network_byte_order)
|
|
{
|
|
if (ingress == port) {
|
|
return 0;
|
|
}
|
|
|
|
if (*network_byte_order == false) {
|
|
ptp_msg_pre_send(msg);
|
|
*network_byte_order = true;
|
|
}
|
|
|
|
return ptp_transport_send(port, msg, PTP_SOCKET_GENERAL);
|
|
}
|
|
|
|
static void clock_forward_management_msg(struct ptp_port *port, struct ptp_msg *msg)
|
|
{
|
|
int length;
|
|
struct ptp_port *iter;
|
|
bool net_byte_ord = false;
|
|
enum ptp_port_state state = ptp_port_state(port);
|
|
|
|
if (ptp_clock_type() != PTP_CLOCK_TYPE_BOUNDARY) {
|
|
/* Clocks other than Boundary Clock shouldn't retransmit messages. */
|
|
return;
|
|
}
|
|
|
|
if (msg->header.flags[0] & PTP_MSG_UNICAST_FLAG) {
|
|
return;
|
|
}
|
|
|
|
if (msg->management.boundary_hops &&
|
|
(state == PTP_PS_GRAND_MASTER ||
|
|
state == PTP_PS_TIME_TRANSMITTER ||
|
|
state == PTP_PS_PRE_TIME_TRANSMITTER ||
|
|
state == PTP_PS_TIME_RECEIVER ||
|
|
state == PTP_PS_UNCALIBRATED)) {
|
|
length = msg->header.msg_length;
|
|
msg->management.boundary_hops--;
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&ptp_clk.ports_list, iter, node) {
|
|
if (clock_forward_msg(port, iter, msg, &net_byte_ord)) {
|
|
LOG_ERR("Failed to forward message to %d Port",
|
|
iter->port_ds.id.port_number);
|
|
}
|
|
}
|
|
|
|
if (net_byte_ord) {
|
|
ptp_msg_post_recv(port, msg, length);
|
|
msg->management.boundary_hops++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int clock_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_PRIORITY1:
|
|
ptp_clk.default_ds.priority1 = *tlv->data;
|
|
send_resp = true;
|
|
break;
|
|
case PTP_MGMT_PRIORITY2:
|
|
ptp_clk.default_ds.priority2 = *tlv->data;
|
|
send_resp = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return send_resp ? ptp_port_management_resp(port, req, tlv) : 0;
|
|
}
|
|
|
|
static void clock_update_grandmaster(void)
|
|
{
|
|
memset(&ptp_clk.current_ds, 0, sizeof(struct ptp_current_ds));
|
|
|
|
memcpy(&ptp_clk.parent_ds.port_id.clk_id,
|
|
&ptp_clk.default_ds.clk_id,
|
|
sizeof(ptp_clk_id));
|
|
memcpy(&ptp_clk.parent_ds.gm_id,
|
|
&ptp_clk.default_ds.clk_id,
|
|
sizeof(ptp_clk_id));
|
|
ptp_clk.parent_ds.port_id.port_number = 0;
|
|
ptp_clk.parent_ds.gm_clk_quality = ptp_clk.default_ds.clk_quality;
|
|
ptp_clk.parent_ds.gm_priority1 = ptp_clk.default_ds.priority1;
|
|
ptp_clk.parent_ds.gm_priority2 = ptp_clk.default_ds.priority2;
|
|
|
|
ptp_clk.time_prop_ds.current_utc_offset = 37; /* IEEE 1588-2019 9.4 */
|
|
ptp_clk.time_prop_ds.time_src = ptp_clk.time_src;
|
|
ptp_clk.time_prop_ds.flags = 0;
|
|
}
|
|
|
|
static void clock_update_time_receiver(void)
|
|
{
|
|
struct ptp_msg *best_msg = (struct ptp_msg *)k_fifo_peek_tail(&ptp_clk.best->messages);
|
|
|
|
ptp_clk.current_ds.steps_rm = 1 + ptp_clk.best->dataset.steps_rm;
|
|
|
|
memcpy(&ptp_clk.parent_ds.gm_id,
|
|
&best_msg->announce.gm_id,
|
|
sizeof(best_msg->announce.gm_id));
|
|
memcpy(&ptp_clk.parent_ds.port_id,
|
|
&ptp_clk.best->dataset.sender,
|
|
sizeof(ptp_clk.best->dataset.sender));
|
|
ptp_clk.parent_ds.gm_clk_quality = best_msg->announce.gm_clk_quality;
|
|
ptp_clk.parent_ds.gm_priority1 = best_msg->announce.gm_priority1;
|
|
ptp_clk.parent_ds.gm_priority2 = best_msg->announce.gm_priority2;
|
|
|
|
ptp_clk.time_prop_ds.current_utc_offset = best_msg->announce.current_utc_offset;
|
|
ptp_clk.time_prop_ds.flags = best_msg->header.flags[1];
|
|
}
|
|
|
|
static void clock_check_pollfd(void)
|
|
{
|
|
struct ptp_port *port;
|
|
struct zsock_pollfd *fd = &ptp_clk.pollfd[1];
|
|
|
|
if (ptp_clk.pollfd_valid) {
|
|
return;
|
|
}
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&ptp_clk.ports_list, port, node) {
|
|
for (int i = 0; i < PTP_SOCKET_CNT; i++) {
|
|
fd->fd = port->socket[i];
|
|
fd->events = ZSOCK_POLLIN | ZSOCK_POLLPRI;
|
|
fd++;
|
|
}
|
|
}
|
|
|
|
ptp_clk.pollfd_valid = true;
|
|
}
|
|
|
|
const struct ptp_clock *ptp_clock_init(void)
|
|
{
|
|
struct ptp_default_ds *dds = &ptp_clk.default_ds;
|
|
struct ptp_parent_ds *pds = &ptp_clk.parent_ds;
|
|
struct net_if *iface = net_if_get_first_by_type(&NET_L2_GET_NAME(ETHERNET));
|
|
|
|
ptp_clk.time_src = (enum ptp_time_src)PTP_TIME_SRC_INTERNAL_OSC;
|
|
|
|
/* Initialize Default Dataset. */
|
|
int ret = clock_generate_id(&dds->clk_id, iface);
|
|
|
|
if (ret) {
|
|
LOG_ERR("Couldn't assign Clock Identity.");
|
|
return NULL;
|
|
}
|
|
|
|
dds->type = (enum ptp_clock_type)CONFIG_PTP_CLOCK_TYPE;
|
|
dds->n_ports = 0;
|
|
dds->time_receiver_only = IS_ENABLED(CONFIG_PTP_TIME_RECEIVER_ONLY) ? true : false;
|
|
|
|
dds->clk_quality.cls = dds->time_receiver_only ? 255 : 248;
|
|
dds->clk_quality.accuracy = CONFIG_PTP_CLOCK_ACCURACY;
|
|
/* 0xFFFF means that value has not been computed - IEEE 1588-2019 7.6.3.3 */
|
|
dds->clk_quality.offset_scaled_log_variance = 0xFFFF;
|
|
|
|
dds->max_steps_rm = 255;
|
|
|
|
dds->priority1 = CONFIG_PTP_PRIORITY1;
|
|
dds->priority2 = CONFIG_PTP_PRIORITY2;
|
|
|
|
/* Initialize Parent Dataset. */
|
|
clock_update_grandmaster();
|
|
pds->obsreved_parent_offset_scaled_log_variance = 0xFFFF;
|
|
pds->obsreved_parent_clk_phase_change_rate = 0x7FFFFFFF;
|
|
/* Parent statistics haven't been measured - IEEE 1588-2019 7.6.4.2 */
|
|
pds->stats = false;
|
|
|
|
ptp_clk.phc = net_eth_get_ptp_clock(iface);
|
|
if (!ptp_clk.phc) {
|
|
LOG_ERR("Couldn't get PTP HW Clock for the interface.");
|
|
return NULL;
|
|
}
|
|
|
|
ptp_clk.pollfd[0].fd = eventfd(0, EFD_NONBLOCK);
|
|
ptp_clk.pollfd[0].events = ZSOCK_POLLIN;
|
|
|
|
sys_slist_init(&ptp_clk.ports_list);
|
|
LOG_DBG("PTP Clock %s initialized", clock_id_str(&dds->clk_id));
|
|
return &ptp_clk;
|
|
}
|
|
|
|
struct zsock_pollfd *ptp_clock_poll_sockets(void)
|
|
{
|
|
int ret;
|
|
|
|
clock_check_pollfd();
|
|
ret = zsock_poll(ptp_clk.pollfd, PTP_SOCKET_CNT * ptp_clk.default_ds.n_ports + 1, -1);
|
|
if (ret > 0 && ptp_clk.pollfd[0].revents) {
|
|
eventfd_t value;
|
|
|
|
eventfd_read(ptp_clk.pollfd[0].fd, &value);
|
|
}
|
|
|
|
return &ptp_clk.pollfd[1];
|
|
}
|
|
|
|
void ptp_clock_handle_state_decision_evt(void)
|
|
{
|
|
struct ptp_foreign_tt_clock *best = NULL, *foreign;
|
|
struct ptp_port *port;
|
|
bool tt_changed = false;
|
|
|
|
if (!ptp_clk.state_decision_event) {
|
|
return;
|
|
}
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&ptp_clk.ports_list, port, node) {
|
|
foreign = ptp_port_best_foreign(port);
|
|
if (!foreign) {
|
|
continue;
|
|
}
|
|
if (!best || ptp_btca_ds_cmp(&foreign->dataset, &best->dataset)) {
|
|
best = foreign;
|
|
}
|
|
}
|
|
|
|
ptp_clk.best = best;
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&ptp_clk.ports_list, port, node) {
|
|
enum ptp_port_state state;
|
|
enum ptp_port_event event;
|
|
|
|
state = ptp_btca_state_decision(port);
|
|
|
|
switch (state) {
|
|
case PTP_PS_LISTENING:
|
|
event = PTP_EVT_NONE;
|
|
break;
|
|
case PTP_PS_GRAND_MASTER:
|
|
clock_update_grandmaster();
|
|
event = PTP_EVT_RS_GRAND_MASTER;
|
|
break;
|
|
case PTP_PS_TIME_TRANSMITTER:
|
|
event = PTP_EVT_RS_TIME_TRANSMITTER;
|
|
break;
|
|
case PTP_PS_TIME_RECEIVER:
|
|
clock_update_time_receiver();
|
|
event = PTP_EVT_RS_TIME_RECEIVER;
|
|
break;
|
|
case PTP_PS_PASSIVE:
|
|
event = PTP_EVT_RS_PASSIVE;
|
|
break;
|
|
default:
|
|
event = PTP_EVT_FAULT_DETECTED;
|
|
break;
|
|
}
|
|
|
|
ptp_port_event_handle(port, event, tt_changed);
|
|
}
|
|
|
|
ptp_clk.state_decision_event = false;
|
|
}
|
|
|
|
int ptp_clock_management_msg_process(struct ptp_port *port, struct ptp_msg *msg)
|
|
{
|
|
static const ptp_clk_id all_ones = {
|
|
.id = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
|
|
};
|
|
int ret;
|
|
bool state_decision_required = false;
|
|
enum ptp_mgmt_op action = ptp_mgmt_action(msg);
|
|
struct ptp_port_id *target_port = &msg->management.target_port_id;
|
|
const struct ptp_default_ds *dds = ptp_clock_default_ds();
|
|
struct ptp_tlv_mgmt *mgmt = (struct ptp_tlv_mgmt *)msg->management.suffix;
|
|
struct ptp_port *iter;
|
|
|
|
if (!ptp_clock_id_eq(&dds->clk_id, &target_port->clk_id) &&
|
|
!ptp_clock_id_eq(&target_port->clk_id, &all_ones)) {
|
|
return state_decision_required;
|
|
}
|
|
|
|
if (sys_slist_len(&msg->tlvs) != 1) {
|
|
/* IEEE 1588-2019 15.3.2 - PTP mgmt msg transports single mgmt TLV */
|
|
return state_decision_required;
|
|
}
|
|
|
|
clock_forward_management_msg(port, msg);
|
|
|
|
switch (action) {
|
|
case PTP_MGMT_SET:
|
|
ret = clock_management_set(port, msg, mgmt);
|
|
if (ret < 0) {
|
|
return state_decision_required;
|
|
}
|
|
state_decision_required = ret ? true : false;
|
|
break;
|
|
case PTP_MGMT_GET:
|
|
__fallthrough;
|
|
case PTP_MGMT_CMD:
|
|
break;
|
|
default:
|
|
return state_decision_required;
|
|
}
|
|
|
|
switch (mgmt->id) {
|
|
case PTP_MGMT_CLOCK_DESCRIPTION:
|
|
__fallthrough;
|
|
case PTP_MGMT_USER_DESCRIPTION:
|
|
__fallthrough;
|
|
case PTP_MGMT_SAVE_IN_NON_VOLATILE_STORAGE:
|
|
__fallthrough;
|
|
case PTP_MGMT_RESET_NON_VOLATILE_STORAGE:
|
|
__fallthrough;
|
|
case PTP_MGMT_INITIALIZE:
|
|
__fallthrough;
|
|
case PTP_MGMT_FAULT_LOG:
|
|
__fallthrough;
|
|
case PTP_MGMT_FAULT_LOG_RESET:
|
|
__fallthrough;
|
|
case PTP_MGMT_DOMAIN:
|
|
__fallthrough;
|
|
case PTP_MGMT_TIME_RECEIVER_ONLY:
|
|
__fallthrough;
|
|
case PTP_MGMT_ANNOUNCE_RECEIPT_TIMEOUT:
|
|
__fallthrough;
|
|
case PTP_MGMT_VERSION_NUMBER:
|
|
__fallthrough;
|
|
case PTP_MGMT_ENABLE_PORT:
|
|
__fallthrough;
|
|
case PTP_MGMT_DISABLE_PORT:
|
|
__fallthrough;
|
|
case PTP_MGMT_TIME:
|
|
__fallthrough;
|
|
case PTP_MGMT_CLOCK_ACCURACY:
|
|
__fallthrough;
|
|
case PTP_MGMT_UTC_PROPERTIES:
|
|
__fallthrough;
|
|
case PTP_MGMT_TRACEBILITY_PROPERTIES:
|
|
__fallthrough;
|
|
case PTP_MGMT_TIMESCALE_PROPERTIES:
|
|
__fallthrough;
|
|
case PTP_MGMT_UNICAST_NEGOTIATION_ENABLE:
|
|
__fallthrough;
|
|
case PTP_MGMT_PATH_TRACE_LIST:
|
|
__fallthrough;
|
|
case PTP_MGMT_PATH_TRACE_ENABLE:
|
|
__fallthrough;
|
|
case PTP_MGMT_GRANDMASTER_CLUSTER_TABLE:
|
|
__fallthrough;
|
|
case PTP_MGMT_UNICAST_TIME_TRANSMITTER_TABLE:
|
|
__fallthrough;
|
|
case PTP_MGMT_UNICAST_TIME_TRANSMITTER_MAX_TABLE_SIZE:
|
|
__fallthrough;
|
|
case PTP_MGMT_ACCEPTABLE_TIME_TRANSMITTER_TABLE:
|
|
__fallthrough;
|
|
case PTP_MGMT_ACCEPTABLE_TIME_TRANSMITTER_TABLE_ENABLED:
|
|
__fallthrough;
|
|
case PTP_MGMT_ACCEPTABLE_TIME_TRANSMITTER_MAX_TABLE_SIZE:
|
|
__fallthrough;
|
|
case PTP_MGMT_ALTERNATE_TIME_TRANSMITTER:
|
|
__fallthrough;
|
|
case PTP_MGMT_ALTERNATE_TIME_OFFSET_ENABLE:
|
|
__fallthrough;
|
|
case PTP_MGMT_ALTERNATE_TIME_OFFSET_NAME:
|
|
__fallthrough;
|
|
case PTP_MGMT_ALTERNATE_TIME_OFFSET_MAX_KEY:
|
|
__fallthrough;
|
|
case PTP_MGMT_ALTERNATE_TIME_OFFSET_PROPERTIES:
|
|
__fallthrough;
|
|
case PTP_MGMT_EXTERNAL_PORT_CONFIGURATION_ENABLED:
|
|
__fallthrough;
|
|
case PTP_MGMT_TIME_TRANSMITTER_ONLY:
|
|
__fallthrough;
|
|
case PTP_MGMT_HOLDOVER_UPGRADE_ENABLE:
|
|
__fallthrough;
|
|
case PTP_MGMT_EXT_PORT_CONFIG_PORT_DATA_SET:
|
|
__fallthrough;
|
|
case PTP_MGMT_TRANSPARENT_CLOCK_DEFAULT_DATA_SET:
|
|
__fallthrough;
|
|
case PTP_MGMT_TRANSPARENT_CLOCK_PORT_DATA_SET:
|
|
__fallthrough;
|
|
case PTP_MGMT_PRIMARY_DOMAIN:
|
|
__fallthrough;
|
|
case PTP_MGMT_DELAY_MECHANISM:
|
|
__fallthrough;
|
|
case PTP_MGMT_LOG_MIN_PDELAY_REQ_INTERVAL:
|
|
ptp_port_management_error(port, msg, PTP_MGMT_ERR_NOT_SUPPORTED);
|
|
break;
|
|
default:
|
|
if (target_port->port_number == port->port_ds.id.port_number) {
|
|
ptp_port_management_msg_process(port, port, msg, mgmt);
|
|
} else if (target_port->port_number == UINT16_MAX) {
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&ptp_clk.ports_list, iter, node) {
|
|
if (ptp_port_management_msg_process(iter, port, msg, mgmt)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return state_decision_required;
|
|
}
|
|
|
|
static double ptp_servo_pi(int64_t nanosecond_diff)
|
|
{
|
|
double kp = 0.7;
|
|
double ki = 0.3;
|
|
double ppb;
|
|
|
|
ptp_clk.pi_drift += ki * nanosecond_diff;
|
|
ppb = kp * nanosecond_diff + ptp_clk.pi_drift;
|
|
|
|
return ppb;
|
|
}
|
|
|
|
void ptp_clock_synchronize(uint64_t ingress, uint64_t egress)
|
|
{
|
|
double ppb;
|
|
int64_t offset;
|
|
int64_t delay = ptp_clk.current_ds.mean_delay >> 16;
|
|
|
|
ptp_clk.timestamp.t1 = egress;
|
|
ptp_clk.timestamp.t2 = ingress;
|
|
|
|
if (!ptp_clk.current_ds.mean_delay) {
|
|
return;
|
|
}
|
|
|
|
offset = (int64_t)(ptp_clk.timestamp.t2 - ptp_clk.timestamp.t1) - delay;
|
|
|
|
/* If diff is too big, ptp_clk needs to be set first. */
|
|
if ((offset > (int64_t)NSEC_PER_SEC) || (offset < -(int64_t)NSEC_PER_SEC)) {
|
|
struct net_ptp_time current;
|
|
int32_t dest_nsec;
|
|
|
|
LOG_WRN("Clock offset exceeds 1 second.");
|
|
|
|
ptp_clock_get(ptp_clk.phc, ¤t);
|
|
|
|
current.second = (uint64_t)(current.second - (offset / NSEC_PER_SEC));
|
|
dest_nsec = (int32_t)(current.nanosecond - (offset % NSEC_PER_SEC));
|
|
|
|
if (dest_nsec < 0) {
|
|
current.second--;
|
|
dest_nsec += NSEC_PER_SEC;
|
|
} else if (dest_nsec >= NSEC_PER_SEC) {
|
|
current.second++;
|
|
dest_nsec -= NSEC_PER_SEC;
|
|
}
|
|
|
|
current.nanosecond = (uint32_t)dest_nsec;
|
|
|
|
ptp_clock_set(ptp_clk.phc, ¤t);
|
|
LOG_WRN("Set clock time: %"PRIu64".%09u", current.second, current.nanosecond);
|
|
return;
|
|
}
|
|
|
|
LOG_DBG("Offset %lldns", offset);
|
|
ptp_clk.current_ds.offset_from_tt = clock_ns_to_timeinterval(offset);
|
|
|
|
ppb = ptp_servo_pi(-offset);
|
|
ptp_clock_rate_adjust(ptp_clk.phc, 1.0 + (ppb / 1000000000.0));
|
|
}
|
|
|
|
void ptp_clock_delay(uint64_t egress, uint64_t ingress)
|
|
{
|
|
int64_t delay;
|
|
|
|
if (ptp_clk.timestamp.t1 == 0 || ptp_clk.timestamp.t2 == 0) {
|
|
return;
|
|
}
|
|
|
|
ptp_clk.timestamp.t3 = egress;
|
|
ptp_clk.timestamp.t4 = ingress;
|
|
|
|
delay = ((int64_t)(ptp_clk.timestamp.t2 - ptp_clk.timestamp.t3) +
|
|
(int64_t)(ptp_clk.timestamp.t4 - ptp_clk.timestamp.t1)) /
|
|
2LL;
|
|
|
|
LOG_DBG("Delay %lldns", delay);
|
|
ptp_clk.current_ds.mean_delay = clock_ns_to_timeinterval(delay);
|
|
}
|
|
|
|
sys_slist_t *ptp_clock_ports_list(void)
|
|
{
|
|
return &ptp_clk.ports_list;
|
|
}
|
|
|
|
enum ptp_clock_type ptp_clock_type(void)
|
|
{
|
|
return (enum ptp_clock_type)ptp_clk.default_ds.type;
|
|
}
|
|
|
|
const struct ptp_default_ds *ptp_clock_default_ds(void)
|
|
{
|
|
return &ptp_clk.default_ds;
|
|
}
|
|
|
|
const struct ptp_parent_ds *ptp_clock_parent_ds(void)
|
|
{
|
|
return &ptp_clk.parent_ds;
|
|
}
|
|
|
|
const struct ptp_current_ds *ptp_clock_current_ds(void)
|
|
{
|
|
return &ptp_clk.current_ds;
|
|
}
|
|
|
|
const struct ptp_time_prop_ds *ptp_clock_time_prop_ds(void)
|
|
{
|
|
return &ptp_clk.time_prop_ds;
|
|
}
|
|
|
|
const struct ptp_dataset *ptp_clock_ds(void)
|
|
{
|
|
struct ptp_dataset *ds = &ptp_clk.dataset;
|
|
|
|
ds->priority1 = ptp_clk.default_ds.priority1;
|
|
ds->clk_quality = ptp_clk.default_ds.clk_quality;
|
|
ds->priority2 = ptp_clk.default_ds.priority2;
|
|
ds->steps_rm = 0;
|
|
ds->sender.port_number = 0;
|
|
ds->receiver.port_number = 0;
|
|
memcpy(&ds->clk_id, &ptp_clk.default_ds.clk_id, sizeof(ptp_clk_id));
|
|
memcpy(&ds->sender.clk_id, &ptp_clk.default_ds.clk_id, sizeof(ptp_clk_id));
|
|
memcpy(&ds->receiver.clk_id, &ptp_clk.default_ds.clk_id, sizeof(ptp_clk_id));
|
|
return ds;
|
|
}
|
|
|
|
const struct ptp_dataset *ptp_clock_best_foreign_ds(void)
|
|
{
|
|
return ptp_clk.best ? &ptp_clk.best->dataset : NULL;
|
|
}
|
|
|
|
struct ptp_port *ptp_clock_port_from_iface(struct net_if *iface)
|
|
{
|
|
struct ptp_port *port;
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&ptp_clk.ports_list, port, node) {
|
|
if (port->iface == iface) {
|
|
return port;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void ptp_clock_pollfd_invalidate(void)
|
|
{
|
|
ptp_clk.pollfd_valid = false;
|
|
}
|
|
|
|
void ptp_clock_signal_timeout(void)
|
|
{
|
|
eventfd_write(ptp_clk.pollfd[0].fd, 1);
|
|
}
|
|
|
|
void ptp_clock_state_decision_req(void)
|
|
{
|
|
ptp_clk.state_decision_event = true;
|
|
}
|
|
|
|
void ptp_clock_port_add(struct ptp_port *port)
|
|
{
|
|
ptp_clk.default_ds.n_ports++;
|
|
sys_slist_append(&ptp_clk.ports_list, &port->node);
|
|
}
|
|
|
|
const struct ptp_foreign_tt_clock *ptp_clock_best_time_transmitter(void)
|
|
{
|
|
return ptp_clk.best;
|
|
}
|
|
|
|
bool ptp_clock_id_eq(const ptp_clk_id *c1, const ptp_clk_id *c2)
|
|
{
|
|
return memcmp(c1, c2, sizeof(ptp_clk_id)) == 0;
|
|
}
|