zephyr/subsys/net/lib/ptp/msg.c
Tomas Choat ecfc44469c net: lib: ptp: fix follow up msg bug
When sending follow up messages, the pre send function
does not insert the correct time stamp in the message.
Issue is fixed by calling the pre send timestamp function.

Signed-off-by: Tomas Choat <trc@ixys.no>
2024-12-09 15:10:28 +01:00

489 lines
12 KiB
C

/*
* Copyright (c) 2024 BayLibre SAS
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(ptp_msg, CONFIG_PTP_LOG_LEVEL);
#include <zephyr/kernel.h>
#include <zephyr/net/udp.h>
#include <zephyr/net/ptp.h>
#include "clock.h"
#include "msg.h"
#include "port.h"
#include "tlv.h"
#include "transport.h"
static struct k_mem_slab msg_slab;
K_MEM_SLAB_DEFINE_STATIC(msg_slab, sizeof(struct ptp_msg), CONFIG_PTP_MSG_POLL_SIZE, 8);
static const char *msg_type_str(struct ptp_msg *msg)
{
switch (ptp_msg_type(msg)) {
case PTP_MSG_SYNC:
return "Sync";
case PTP_MSG_DELAY_REQ:
return "Delay_Req";
case PTP_MSG_PDELAY_REQ:
return "Pdelay_Req";
case PTP_MSG_PDELAY_RESP:
return "Pdelay_Resp";
case PTP_MSG_FOLLOW_UP:
return "Follow_Up";
case PTP_MSG_DELAY_RESP:
return "Delay_Resp";
case PTP_MSG_PDELAY_RESP_FOLLOW_UP:
return "Pdelay_Resp_Follow_Up";
case PTP_MSG_ANNOUNCE:
return "Announce";
case PTP_MSG_SIGNALING:
return "Signaling";
case PTP_MSG_MANAGEMENT:
return "Management";
default:
return "Not recognized";
}
}
static void msg_timestamp_post_recv(struct ptp_msg *msg, struct ptp_timestamp *ts)
{
msg->timestamp.protocol._sec.high = ntohs(ts->seconds_high);
msg->timestamp.protocol._sec.low = ntohl(ts->seconds_low);
msg->timestamp.protocol.nanosecond = ntohl(ts->nanoseconds);
}
static void msg_timestamp_pre_send(struct ptp_timestamp *ts)
{
ts->seconds_high = htons(ts->seconds_high);
ts->seconds_low = htonl(ts->seconds_low);
ts->nanoseconds = htonl(ts->nanoseconds);
}
static void msg_port_id_post_recv(struct ptp_port_id *port_id)
{
port_id->port_number = ntohs(port_id->port_number);
}
static void msg_port_id_pre_send(struct ptp_port_id *port_id)
{
port_id->port_number = htons(port_id->port_number);
}
static int msg_header_post_recv(struct ptp_header *header)
{
if ((header->version & 0xF) != PTP_MAJOR_VERSION) {
/* Incompatible protocol version */
return -1;
}
header->msg_length = ntohs(header->msg_length);
header->correction = ntohll(header->correction);
header->sequence_id = ntohs(header->sequence_id);
msg_port_id_post_recv(&header->src_port_id);
return 0;
}
static void msg_header_pre_send(struct ptp_header *header)
{
header->msg_length = htons(header->msg_length);
header->correction = htonll(header->correction);
header->sequence_id = htons(header->sequence_id);
msg_port_id_pre_send(&header->src_port_id);
}
static uint8_t *msg_suffix(struct ptp_msg *msg)
{
uint8_t *suffix = NULL;
switch (ptp_msg_type(msg)) {
case PTP_MSG_SYNC:
suffix = msg->sync.suffix;
break;
case PTP_MSG_DELAY_REQ:
suffix = msg->delay_req.suffix;
break;
case PTP_MSG_PDELAY_REQ:
suffix = msg->pdelay_req.suffix;
break;
case PTP_MSG_PDELAY_RESP:
suffix = msg->pdelay_resp.suffix;
break;
case PTP_MSG_FOLLOW_UP:
suffix = msg->follow_up.suffix;
break;
case PTP_MSG_DELAY_RESP:
suffix = msg->delay_resp.suffix;
break;
case PTP_MSG_PDELAY_RESP_FOLLOW_UP:
suffix = msg->pdelay_resp_follow_up.suffix;
break;
case PTP_MSG_ANNOUNCE:
suffix = msg->announce.suffix;
break;
case PTP_MSG_SIGNALING:
suffix = msg->signaling.suffix;
break;
case PTP_MSG_MANAGEMENT:
suffix = msg->management.suffix;
break;
}
return suffix;
}
static int msg_tlv_post_recv(struct ptp_msg *msg, int length)
{
int suffix_len = 0, ret = 0;
struct ptp_tlv_container *tlv_container;
uint8_t *suffix = msg_suffix(msg);
if (!suffix) {
LOG_DBG("No TLV attached to the message");
return 0;
}
while (length >= sizeof(struct ptp_tlv)) {
tlv_container = ptp_tlv_alloc();
if (!tlv_container) {
return -ENOMEM;
}
tlv_container->tlv = (struct ptp_tlv *)suffix;
tlv_container->tlv->type = ntohs(tlv_container->tlv->type);
tlv_container->tlv->length = ntohs(tlv_container->tlv->length);
if (tlv_container->tlv->length % 2) {
/* IEEE 1588-2019 Section 5.3.8 - length is an even number */
LOG_ERR("Incorrect length of TLV");
ptp_tlv_free(tlv_container);
return -EBADMSG;
}
length -= sizeof(struct ptp_tlv);
suffix += sizeof(struct ptp_tlv);
suffix_len += sizeof(struct ptp_tlv);
if (tlv_container->tlv->length > length) {
LOG_ERR("Incorrect length of TLV");
ptp_tlv_free(tlv_container);
return -EBADMSG;
}
length -= tlv_container->tlv->length;
suffix += tlv_container->tlv->length;
suffix_len += tlv_container->tlv->length;
ret = ptp_tlv_post_recv(tlv_container->tlv);
if (ret) {
ptp_tlv_free(tlv_container);
return ret;
}
sys_slist_append(&msg->tlvs, &tlv_container->node);
}
return suffix_len;
}
static void msg_tlv_free(struct ptp_msg *msg)
{
struct ptp_tlv_container *tlv_container;
for (sys_snode_t *iter = sys_slist_get(&msg->tlvs);
iter;
iter = sys_slist_get(&msg->tlvs)) {
tlv_container = CONTAINER_OF(iter, struct ptp_tlv_container, node);
ptp_tlv_free(tlv_container);
}
}
static void msg_tlv_pre_send(struct ptp_msg *msg)
{
struct ptp_tlv_container *tlv_container;
SYS_SLIST_FOR_EACH_CONTAINER(&msg->tlvs, tlv_container, node) {
ptp_tlv_pre_send(tlv_container->tlv);
}
/* No need to track TLVs attached to the message. */
msg_tlv_free(msg);
}
struct ptp_msg *ptp_msg_alloc(void)
{
struct ptp_msg *msg = NULL;
int ret = k_mem_slab_alloc(&msg_slab, (void **)&msg, K_FOREVER);
if (ret) {
LOG_ERR("Couldn't allocate memory for the message");
return NULL;
}
memset(msg, 0, sizeof(*msg));
sys_slist_init(&msg->tlvs);
atomic_inc(&msg->ref);
return msg;
}
void ptp_msg_unref(struct ptp_msg *msg)
{
__ASSERT_NO_MSG(msg != NULL);
atomic_t val = atomic_dec(&msg->ref);
if (val > 1) {
return;
}
msg_tlv_free(msg);
k_mem_slab_free(&msg_slab, (void *)msg);
}
void ptp_msg_ref(struct ptp_msg *msg)
{
__ASSERT_NO_MSG(msg != NULL);
atomic_inc(&msg->ref);
}
enum ptp_msg_type ptp_msg_type(const struct ptp_msg *msg)
{
return (enum ptp_msg_type)(msg->header.type_major_sdo_id & 0xF);
}
struct ptp_msg *ptp_msg_from_pkt(struct net_pkt *pkt)
{
static const size_t eth_hdr_len = IS_ENABLED(CONFIG_NET_VLAN) ?
sizeof(struct net_eth_vlan_hdr) :
sizeof(struct net_eth_hdr);
struct net_udp_hdr *hdr;
struct ptp_msg *msg;
int port, payload;
if (pkt->buffer->len == eth_hdr_len) {
/* Packet contain Ethernet header at the beginning. */
struct net_buf *buf;
/* remove packet temporarily. */
buf = pkt->buffer;
pkt->buffer = buf->frags;
hdr = net_udp_get_hdr(pkt, NULL);
/* insert back temporarily femoved frag. */
net_pkt_frag_insert(pkt, buf);
} else {
hdr = net_udp_get_hdr(pkt, NULL);
}
if (!hdr) {
LOG_ERR("Couldn't retrieve UDP header from the net packet");
return NULL;
}
payload = ntohs(hdr->len) - NET_UDPH_LEN;
port = ntohs(hdr->dst_port);
if (port != PTP_SOCKET_PORT_EVENT && port != PTP_SOCKET_PORT_GENERAL) {
LOG_ERR("Couldn't retrieve PTP message from the net packet");
return NULL;
}
msg = (struct ptp_msg *)((uintptr_t)hdr + NET_UDPH_LEN);
if (payload == ntohs(msg->header.msg_length)) {
return msg;
}
return NULL;
}
void ptp_msg_pre_send(struct ptp_msg *msg)
{
int64_t current;
msg_header_pre_send(&msg->header);
switch (ptp_msg_type(msg)) {
case PTP_MSG_SYNC:
break;
case PTP_MSG_DELAY_REQ:
current = k_uptime_get();
msg->timestamp.host.second = (uint64_t)(current / MSEC_PER_SEC);
msg->timestamp.host.nanosecond = (current % MSEC_PER_SEC) * NSEC_PER_MSEC;
break;
case PTP_MSG_PDELAY_REQ:
break;
case PTP_MSG_PDELAY_RESP:
msg_timestamp_pre_send(&msg->pdelay_resp.req_receipt_timestamp);
msg_port_id_pre_send(&msg->pdelay_resp.req_port_id);
break;
case PTP_MSG_FOLLOW_UP:
msg_timestamp_pre_send(&msg->follow_up.precise_origin_timestamp);
break;
case PTP_MSG_DELAY_RESP:
msg_timestamp_pre_send(&msg->delay_resp.receive_timestamp);
msg_port_id_pre_send(&msg->delay_resp.req_port_id);
break;
case PTP_MSG_PDELAY_RESP_FOLLOW_UP:
msg_timestamp_pre_send(&msg->pdelay_resp_follow_up.resp_origin_timestamp);
msg_port_id_pre_send(&msg->pdelay_resp_follow_up.req_port_id);
break;
case PTP_MSG_ANNOUNCE:
msg->announce.current_utc_offset = htons(msg->announce.current_utc_offset);
msg->announce.gm_clk_quality.offset_scaled_log_variance =
htons(msg->announce.gm_clk_quality.offset_scaled_log_variance);
msg->announce.steps_rm = htons(msg->announce.steps_rm);
break;
case PTP_MSG_SIGNALING:
msg_port_id_pre_send(&msg->signaling.target_port_id);
break;
case PTP_MSG_MANAGEMENT:
msg_port_id_pre_send(&msg->management.target_port_id);
break;
}
msg_tlv_pre_send(msg);
}
int ptp_msg_post_recv(struct ptp_port *port, struct ptp_msg *msg, int cnt)
{
static const int msg_size[] = {
[PTP_MSG_SYNC] = sizeof(struct ptp_sync_msg),
[PTP_MSG_DELAY_REQ] = sizeof(struct ptp_delay_req_msg),
[PTP_MSG_PDELAY_REQ] = sizeof(struct ptp_pdelay_req_msg),
[PTP_MSG_PDELAY_RESP] = sizeof(struct ptp_pdelay_resp_msg),
[PTP_MSG_FOLLOW_UP] = sizeof(struct ptp_follow_up_msg),
[PTP_MSG_DELAY_RESP] = sizeof(struct ptp_delay_resp_msg),
[PTP_MSG_PDELAY_RESP_FOLLOW_UP] = sizeof(struct ptp_pdelay_resp_follow_up_msg),
[PTP_MSG_ANNOUNCE] = sizeof(struct ptp_announce_msg),
[PTP_MSG_SIGNALING] = sizeof(struct ptp_signaling_msg),
[PTP_MSG_MANAGEMENT] = sizeof(struct ptp_management_msg),
};
enum ptp_msg_type type = ptp_msg_type(msg);
int64_t current;
int tlv_len;
if (msg_size[type] > cnt) {
LOG_ERR("Received message with incorrect length");
return -EBADMSG;
}
if (msg_header_post_recv(&msg->header)) {
LOG_ERR("Received message incomplient with supported PTP version");
return -EBADMSG;
}
LOG_DBG("Port %d received %s message", port->port_ds.id.port_number, msg_type_str(msg));
switch (type) {
case PTP_MSG_SYNC:
msg_timestamp_post_recv(msg, &msg->sync.origin_timestamp);
break;
case PTP_MSG_DELAY_REQ:
break;
case PTP_MSG_PDELAY_REQ:
break;
case PTP_MSG_PDELAY_RESP:
msg_timestamp_post_recv(msg, &msg->pdelay_resp.req_receipt_timestamp);
msg_port_id_post_recv(&msg->pdelay_resp.req_port_id);
break;
case PTP_MSG_FOLLOW_UP:
msg_timestamp_post_recv(msg, &msg->follow_up.precise_origin_timestamp);
break;
case PTP_MSG_DELAY_RESP:
msg_timestamp_post_recv(msg, &msg->delay_resp.receive_timestamp);
msg_port_id_post_recv(&msg->delay_resp.req_port_id);
break;
case PTP_MSG_PDELAY_RESP_FOLLOW_UP:
msg_timestamp_post_recv(msg, &msg->pdelay_resp_follow_up.resp_origin_timestamp);
msg_port_id_post_recv(&msg->pdelay_resp_follow_up.req_port_id);
break;
case PTP_MSG_ANNOUNCE:
current = k_uptime_get();
msg->timestamp.host.second = (uint64_t)(current / MSEC_PER_SEC);
msg->timestamp.host.nanosecond = (current % MSEC_PER_SEC) * NSEC_PER_MSEC;
msg_timestamp_post_recv(msg, &msg->announce.origin_timestamp);
msg->announce.current_utc_offset = ntohs(msg->announce.current_utc_offset);
msg->announce.gm_clk_quality.offset_scaled_log_variance =
ntohs(msg->announce.gm_clk_quality.offset_scaled_log_variance);
msg->announce.steps_rm = ntohs(msg->announce.steps_rm);
break;
case PTP_MSG_SIGNALING:
msg_port_id_post_recv(&msg->signaling.target_port_id);
break;
case PTP_MSG_MANAGEMENT:
msg_port_id_post_recv(&msg->management.target_port_id);
break;
}
tlv_len = msg_tlv_post_recv(msg, cnt - msg_size[type]);
if (tlv_len < 0) {
LOG_ERR("Failed processing TLVs");
return -EBADMSG;
}
if (msg_size[type] + tlv_len != msg->header.msg_length) {
LOG_ERR("Length and TLVs don't correspond with specified in the message");
return -EMSGSIZE;
}
return 0;
}
struct ptp_tlv *ptp_msg_add_tlv(struct ptp_msg *msg, int length)
{
struct ptp_tlv_container *tlv_container;
uint8_t *suffix = msg_suffix(msg);
if (!suffix) {
return NULL;
}
tlv_container = (struct ptp_tlv_container *)sys_slist_peek_tail(&msg->tlvs);
if (tlv_container) {
suffix = (uint8_t *)tlv_container->tlv;
suffix += sizeof(*tlv_container->tlv);
suffix += tlv_container->tlv->length;
}
if ((intptr_t)(suffix + length) >= (intptr_t)&msg->ref) {
LOG_ERR("Not enough space for TLV of %d length", length);
return NULL;
}
tlv_container = ptp_tlv_alloc();
if (tlv_container) {
tlv_container->tlv = (struct ptp_tlv *)suffix;
msg->header.msg_length += length;
}
return tlv_container ? tlv_container->tlv : NULL;
}
int ptp_msg_announce_cmp(const struct ptp_announce_msg *m1, const struct ptp_announce_msg *m2)
{
int len = sizeof(m1->gm_priority1) + sizeof(m1->gm_clk_quality) +
sizeof(m1->gm_priority1) + sizeof(m1->gm_id) +
sizeof(m1->steps_rm);
return memcmp(&m1->gm_priority1, &m2->gm_priority1, len);
}
bool ptp_msg_current_parent(const struct ptp_msg *msg)
{
const struct ptp_parent_ds *pds = ptp_clock_parent_ds();
return ptp_port_id_eq(&pds->port_id, &msg->header.src_port_id);
}