blob: dac0617b0c04a85d965b234095898812f52db67c [file] [log] [blame]
/*
* 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:
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);
}