| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437 |
- /*
- * SPDX-FileCopyrightText: 2020-2024 The Apache Software Foundation
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * SPDX-FileContributor: 2024-2025 Espressif Systems (Shanghai) CO LTD
- */
- /****************************************************************************
- * apps/netutils/ptpd/ptpd.c
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership. The
- * ASF licenses this file to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the
- * License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- ****************************************************************************/
- #define ESP_PTP 1
- /****************************************************************************
- * Included Files
- ****************************************************************************/
- #ifndef ESP_PTP
- #include <nuttx/config.h>
- #endif
- #include <stdbool.h>
- #include <stdint.h>
- #include <sys/socket.h>
- #include <sys/time.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <sched.h>
- #include <assert.h>
- #include <errno.h>
- #ifndef ESP_PTP
- #include <debug.h>
- #endif
- #include <unistd.h>
- #include <fcntl.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #ifndef ESP_PTP
- #include <netutils/ipmsfilter.h>
- #endif
- #include <net/if.h>
- #include <sys/ioctl.h>
- #include <sys/poll.h>
- #ifndef ESP_PTP
- #include <nuttx/net/netconfig.h>
- #include <netutils/ptpd.h>
- #endif
- #include "ptpv2.h"
- #ifdef ESP_PTP
- #include "ptpd.h"
- #include "esp_netif.h"
- #include "esp_eth_mac_esp.h"
- #include "esp_eth_driver.h"
- #include "esp_vfs_l2tap.h"
- #include "semaphore.h"
- #include "esp_log.h"
- #include "esp_err.h"
- #include "lwip/prot/ethernet.h" // Ethernet headers
- #include "esp_eth_time.h"
- #define ETH_TYPE_PTP 0x88F7
- #define SET_MAC_ADDR(addr, a, b, c, d, e, f) do { \
- addr[0] = a; addr[1] = b; addr[2] = c; \
- addr[3] = d; addr[4] = e; addr[5] = f; \
- } while(0)
- #define ERROR ESP_FAIL
- #define OK ESP_OK
- #define UNUSED (void)
- #define MSEC_PER_SEC 1000
- #define NSEC_PER_USEC 1000
- #define NSEC_PER_MSEC 1000000ll
- #define NSEC_PER_SEC 1000000000ll
- // To able to set either only server or only client
- #ifndef CONFIG_NETUTILS_PTPD_TIMEOUT_MS
- #define CONFIG_NETUTILS_PTPD_TIMEOUT_MS 0
- #endif
- #ifndef CONFIG_NETUTILS_PTPD_SETTIME_THRESHOLD_MS
- #define CONFIG_NETUTILS_PTPD_SETTIME_THRESHOLD_MS 0
- #endif
- #ifndef CONFIG_NETUTILS_PTPD_MAX_PATH_DELAY_NS
- #define CONFIG_NETUTILS_PTPD_MAX_PATH_DELAY_NS 0
- #endif
- #ifndef CONFIG_NETUTILS_PTPD_DELAYREQ_AVGCOUNT
- #define CONFIG_NETUTILS_PTPD_DELAYREQ_AVGCOUNT 0
- #endif
- #ifndef CONFIG_NETUTILS_PTPD_DELAYRESP_INTERVAL
- #define CONFIG_NETUTILS_PTPD_DELAYRESP_INTERVAL 0
- #endif
- #ifndef CONFIG_NETUTILS_PTPD_PATH_DELAY_STABILITY_NS
- #define CONFIG_NETUTILS_PTPD_PATH_DELAY_STABILITY_NS 0
- #endif
- #define clock_timespec_subtract(ts1, ts2, ts3) timespecsub(ts1, ts2, ts3)
- #define clock_timespec_add(ts1, ts2, ts3) timespecadd(ts1, ts2, ts3)
- #endif // ESP_PTP
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- #ifdef ESP_PTP
- #define ADJ_FREQ_MAX 512000 // TODO tuneup
- typedef struct
- {
- int32_t kp;
- int32_t ki;
- int32_t drift_acc;
- } pi_cntrl_t;
- #endif // ESP_PTP
- /* Carrier structure for querying PTPD status */
- struct ptpd_statusreq_s
- {
- FAR sem_t *done;
- FAR struct ptpd_status_s *dest;
- };
- /* Main PTPD state storage */
- struct ptp_state_s
- {
- /* Request for PTPD task to stop or report status */
- bool stop;
- struct ptpd_statusreq_s status_req;
- #ifdef ESP_PTP
- uint8_t intf_hw_addr[ETH_ADDR_LEN];
- int ptp_socket;
- esp_eth_handle_t cached_eth_handle; /* Cached for freq adj calls */
- int64_t remote_time_ns_prev;
- int64_t local_time_ns_prev;
- int64_t last_offset_ns;
- pi_cntrl_t offset_pi;
- #endif // ESP_PTP
- /* Address of network interface we are operating on */
- struct sockaddr_in interface_addr;
- /* Socket bound to interface for transmission (UDP) */
- int tx_socket;
- /* Sockets for PTP event and information ports (UDP) */
- int event_socket;
- int info_socket;
- /* Our own identity as a clock source */
- struct ptp_announce_s own_identity;
- /* Sequence number counters per message type */
- uint16_t announce_seq;
- uint16_t sync_seq;
- uint16_t delay_req_seq;
- /* Previous measurement and estimated clock drift rate */
- struct timespec last_delta_timestamp;
- int64_t last_delta_ns;
- int64_t last_adjtime_ns;
- long drift_avg_total_ms;
- long drift_ppb;
- /* Identity of currently selected clock source,
- * from the latest announcement message.
- *
- * The timestamps are used for timeout when a source disappears.
- * They are from the local CLOCK_MONOTONIC.
- */
- bool selected_source_valid; /* True if operating as client */
- struct ptp_announce_s selected_source; /* Currently selected server */
- struct timespec last_received_multicast; /* Any multicast packet */
- struct timespec last_received_announce; /* Announce from any server */
- struct timespec last_received_sync; /* Sync from selected source */
- /* Last transmitted packet timestamps (CLOCK_MONOTONIC)
- * Used to set transmission interval.
- */
- struct timespec last_transmitted_sync;
- struct timespec last_transmitted_announce;
- struct timespec last_transmitted_delayresp;
- struct timespec last_transmitted_delayreq;
- /* Timestamps related to path delay calculation (CLOCK_REALTIME) */
- bool can_send_delayreq;
- struct timespec delayreq_time;
- int path_delay_avgcount;
- long path_delay_ns;
- long delayreq_interval;
- /* Latest received packet and its timestamp (CLOCK_REALTIME) */
- struct timespec rxtime;
- union
- {
- struct ptp_header_s header;
- struct ptp_announce_s announce;
- struct ptp_sync_s sync;
- struct ptp_follow_up_s follow_up;
- struct ptp_delay_req_s delay_req;
- struct ptp_delay_resp_s delay_resp;
- uint8_t raw[128];
- } rxbuf;
- #ifndef ESP_PTP
- uint8_t rxcmsg[CMSG_LEN(sizeof(struct timeval))];
- #endif // ESP_PTP
- /* Buffered sync packet for two-step clock setting where server sends
- * the accurate timestamp in a separate follow-up message.
- */
- struct ptp_sync_s twostep_packet;
- struct timespec twostep_rxtime;
- };
- #ifdef CONFIG_NETUTILS_PTPD_SERVER
- # define PTPD_POLL_INTERVAL CONFIG_NETUTILS_PTPD_SYNC_INTERVAL_MSEC
- #else
- # define PTPD_POLL_INTERVAL CONFIG_NETUTILS_PTPD_TIMEOUT_MS
- #endif
- /* PTP debug messages are enabled by either CONFIG_DEBUG_NET_INFO
- * or separately by CONFIG_NETUTILS_PTPD_DEBUG. This simplifies
- * debugging without having excessive amount of logging from net.
- */
- #ifdef ESP_PTP
- static const char *TAG = "ptpd";
- #define ptpinfo(format, ...) ESP_LOGI(TAG, format, ##__VA_ARGS__)
- #define ptpwarn(format, ...) ESP_LOGW(TAG, format, ##__VA_ARGS__)
- #define ptperr(format, ...) ESP_LOGE(TAG, format, ##__VA_ARGS__)
- #else
- #ifdef CONFIG_NETUTILS_PTPD_DEBUG
- # define ptpinfo _info
- # define ptpwarn _warn
- # define ptperr _err
- #else
- # define ptpinfo ninfo
- # define ptpwarn nwarn
- # define ptperr nerr
- #endif
- #endif // ESP_PTP
- #ifdef ESP_PTP
- static struct ptp_state_s *s_state;
- #endif
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- #ifdef ESP_PTP
- static void ptp_create_eth_frame(struct ptp_state_s *state, uint8_t *eth_frame, void *ptp_msg, uint16_t ptp_msg_len)
- {
- struct eth_hdr eth_hdr = {
- //.dest.addr = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x0E}, // TODO only for Pdelay_Req, Pdelay_Resp and Pdelay_Resp_Follow_Up
- .dest.addr = {0x01, 0x1B, 0x19, 0x00, 0x00, 0x00}, // All except peer delay messages, ptp4l sends everything at this addr
- .type = htons(ETH_TYPE_PTP)
- };
- memcpy(ð_hdr.src.addr, state->intf_hw_addr, ETH_ADDR_LEN);
- memcpy(eth_frame, ð_hdr, sizeof(eth_hdr));
- memcpy(eth_frame + sizeof(eth_hdr), ptp_msg, ptp_msg_len);
- }
- static int ptp_net_send(FAR struct ptp_state_s *state, void *ptp_msg, uint16_t ptp_msg_len, struct timespec *ts)
- {
- uint8_t eth_frame[ptp_msg_len + ETH_HEADER_LEN];
- ptp_create_eth_frame(state, eth_frame, ptp_msg, ptp_msg_len);
- // wrap "Info Records Buffer" into union to ensure proper alignment of data (this is typically needed when
- // accessing double word variables or structs containing double word variables)
- union {
- uint8_t info_recs_buff[L2TAP_IREC_SPACE(sizeof(struct timespec))];
- l2tap_irec_hdr_t align;
- } u;
- l2tap_extended_buff_t ptp_msg_ext_buff;
- ptp_msg_ext_buff.info_recs_len = sizeof(u.info_recs_buff);
- ptp_msg_ext_buff.info_recs_buff = u.info_recs_buff;
- ptp_msg_ext_buff.buff = eth_frame;
- ptp_msg_ext_buff.buff_len = sizeof(eth_frame);
- l2tap_irec_hdr_t *ts_info = L2TAP_IREC_FIRST(&ptp_msg_ext_buff);
- ts_info->len = L2TAP_IREC_LEN(sizeof(struct timespec));
- ts_info->type = L2TAP_IREC_TIME_STAMP;
- int ret = write(state->ptp_socket, &ptp_msg_ext_buff, 0);
- // check if write was successful, ts exists and ts_info is valid
- if (ret > 0 && ts && ts_info->type == L2TAP_IREC_TIME_STAMP)
- {
- *ts = *(struct timespec *)ts_info->data;
- }
- return ret;
- }
- static int ptp_net_recv(FAR struct ptp_state_s *state, void *ptp_msg, uint16_t ptp_msg_len, struct timespec *ts)
- {
- uint8_t eth_frame[ptp_msg_len + ETH_HEADER_LEN];
- // wrap "Info Records Buffer" into union to ensure proper alignment of data (this is typically needed when
- // accessing double word variables or structs containing double word variables)
- union {
- uint8_t info_recs_buff[L2TAP_IREC_SPACE(sizeof(struct timespec))];
- l2tap_irec_hdr_t align;
- } u;
- l2tap_extended_buff_t ptp_msg_ext_buff;
- ptp_msg_ext_buff.info_recs_len = sizeof(u.info_recs_buff);
- ptp_msg_ext_buff.info_recs_buff = u.info_recs_buff;
- ptp_msg_ext_buff.buff = eth_frame;
- ptp_msg_ext_buff.buff_len = sizeof(eth_frame);
- l2tap_irec_hdr_t *ts_info = L2TAP_IREC_FIRST(&ptp_msg_ext_buff);
- ts_info->len = L2TAP_IREC_LEN(sizeof(struct timespec));
- ts_info->type = L2TAP_IREC_TIME_STAMP;
- int ret = read(state->ptp_socket, &ptp_msg_ext_buff, 0);
- // check if read was successful, ts exists and ts_info is valid
- if (ret > 0 && ts && ts_info->type == L2TAP_IREC_TIME_STAMP)
- {
- *ts = *(struct timespec *)ts_info->data;
- }
- memcpy(ptp_msg, ð_frame[ETH_HEADER_LEN], ret);
- return ret;
- }
- static int64_t timespec_to_ns(FAR const struct timespec *ts)
- {
- return ts->tv_sec * NSEC_PER_SEC + (ts->tv_nsec);
- }
- #endif // ESP_PTP
- /* Convert from timespec to PTP format */
- static void timespec_to_ptp_format(FAR struct timespec *ts,
- FAR uint8_t *timestamp)
- {
- /* IEEE 1588 uses 48 bits for seconds and 32 bits for nanoseconds,
- * both fields big-endian.
- */
- #ifdef CONFIG_SYSTEM_TIME64
- timestamp[0] = (uint8_t)(ts->tv_sec >> 40);
- timestamp[1] = (uint8_t)(ts->tv_sec >> 32);
- #else
- timestamp[0] = 0;
- timestamp[1] = 0;
- #endif
- timestamp[2] = (uint8_t)(ts->tv_sec >> 24);
- timestamp[3] = (uint8_t)(ts->tv_sec >> 16);
- timestamp[4] = (uint8_t)(ts->tv_sec >> 8);
- timestamp[5] = (uint8_t)(ts->tv_sec >> 0);
- timestamp[6] = (uint8_t)(ts->tv_nsec >> 24);
- timestamp[7] = (uint8_t)(ts->tv_nsec >> 16);
- timestamp[8] = (uint8_t)(ts->tv_nsec >> 8);
- timestamp[9] = (uint8_t)(ts->tv_nsec >> 0);
- }
- /* Convert from PTP format to timespec */
- static void ptp_format_to_timespec(FAR const uint8_t *timestamp,
- FAR struct timespec *ts)
- {
- ts->tv_sec =
- (((int64_t)timestamp[0]) << 40)
- | (((int64_t)timestamp[1]) << 32)
- | (((int64_t)timestamp[2]) << 24)
- | (((int64_t)timestamp[3]) << 16)
- | (((int64_t)timestamp[4]) << 8)
- | (((int64_t)timestamp[5]) << 0);
- ts->tv_nsec =
- (((long)timestamp[6]) << 24)
- | (((long)timestamp[7]) << 16)
- | (((long)timestamp[8]) << 8)
- | (((long)timestamp[9]) << 0);
- }
- /* Returns true if A is a better clock source than B.
- * Implements Best Master Clock algorithm from IEEE-1588.
- */
- static bool is_better_clock(FAR const struct ptp_announce_s *a,
- FAR const struct ptp_announce_s *b)
- {
- if (a->gm_priority1 < b->gm_priority1 /* Main priority field */
- || a->gm_quality[0] < b->gm_quality[0] /* Clock class */
- || a->gm_quality[1] < b->gm_quality[1] /* Clock accuracy */
- || a->gm_quality[2] < b->gm_quality[2] /* Clock variance high byte */
- || a->gm_quality[3] < b->gm_quality[3] /* Clock variance low byte */
- || a->gm_priority2 < b->gm_priority2 /* Sub priority field */
- || memcmp(a->gm_identity, b->gm_identity, sizeof(a->gm_identity)) < 0)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- static int64_t timespec_to_ms(FAR const struct timespec *ts)
- {
- return ts->tv_sec * MSEC_PER_SEC + (ts->tv_nsec / NSEC_PER_MSEC);
- }
- /* Get positive or negative delta between two timespec values.
- * If value would exceed int64 limit (292 years), return INT64_MAX/MIN.
- */
- static int64_t timespec_delta_ns(FAR const struct timespec *ts1,
- FAR const struct timespec *ts2)
- {
- int64_t delta_s;
- delta_s = ts1->tv_sec - ts2->tv_sec;
- #ifdef CONFIG_SYSTEM_TIME64
- /* Conversion to nanoseconds could overflow if the system time is 64-bit */
- if (delta_s >= INT64_MAX / NSEC_PER_SEC)
- {
- return INT64_MAX;
- }
- else if (delta_s <= INT64_MIN / NSEC_PER_SEC)
- {
- return INT64_MIN;
- }
- #endif
- return delta_s * NSEC_PER_SEC + (ts1->tv_nsec - ts2->tv_nsec);
- }
- /* Check if the currently selected source is still valid */
- static bool is_selected_source_valid(FAR struct ptp_state_s *state)
- {
- struct timespec time_now;
- struct timespec delta;
- if ((state->selected_source.header.messagetype & PTP_MSGTYPE_MASK)
- != PTP_MSGTYPE_ANNOUNCE)
- {
- return false; /* Uninitialized value */
- }
- /* Note: this uses monotonic clock to track the timeout even when
- * system clock is adjusted.
- */
- clock_gettime(CLOCK_MONOTONIC, &time_now);
- clock_timespec_subtract(&time_now, &state->last_received_sync, &delta);
- if (timespec_to_ms(&delta) > CONFIG_NETUTILS_PTPD_TIMEOUT_MS)
- {
- #ifdef ESP_PTP
- ESP_LOGD(TAG, "Too long time since received packet\n");
- #endif // ESP_PTP
- return false; /* Too long time since received packet */
- }
- return true;
- }
- /* Increment sequence number for packet type, and copy to header */
- static void ptp_increment_sequence(FAR uint16_t *sequence_num,
- FAR struct ptp_header_s *hdr)
- {
- *sequence_num += 1;
- hdr->sequenceid[0] = (uint8_t)(*sequence_num >> 8);
- hdr->sequenceid[1] = (uint8_t)(*sequence_num);
- }
- /* Get sequence number from received packet */
- static uint16_t ptp_get_sequence(FAR const struct ptp_header_s *hdr)
- {
- return ((uint16_t)hdr->sequenceid[0] << 8) | hdr->sequenceid[1];
- }
- /* Get current system timestamp as a timespec
- * TODO: Possibly add support for selecting different clock or using
- * architecture-specific interface for clock access.
- */
- static int ptp_gettime(FAR struct ptp_state_s *state,
- FAR struct timespec *ts)
- {
- UNUSED(state);
- #ifdef ESP_PTP
- return esp_eth_clock_gettime(CLOCK_PTP_SYSTEM, ts);
- #else
- return clock_gettime(CLOCK_REALTIME, ts);
- #endif // ESP_PTP
- }
- /* Change current system timestamp by jumping */
- static int ptp_settime(FAR struct ptp_state_s *state,
- FAR struct timespec *ts)
- {
- UNUSED(state);
- #ifdef ESP_PTP
- return esp_eth_clock_settime(CLOCK_PTP_SYSTEM, ts);
- #else
- return clock_settime(CLOCK_REALTIME, ts);
- #endif // ESP_PTP
- }
- #ifndef ESP_PTP
- /* Smoothly adjust timestamp. */
- static int ptp_adjtime(FAR struct ptp_state_s *state, int64_t delta_ns)
- {
- struct timeval delta;
- delta.tv_sec = delta_ns / NSEC_PER_SEC;
- delta_ns -= (int64_t)delta.tv_sec * NSEC_PER_SEC;
- delta.tv_usec = delta_ns / NSEC_PER_USEC;
- return adjtime(&delta, NULL);
- }
- #endif // !ESP_PTP
- #ifndef ESP_PTP
- /* Get timestamp of latest received packet */
- static int ptp_getrxtime(FAR struct ptp_state_s *state,
- FAR struct msghdr *rxhdr,
- FAR struct timespec *ts)
- {
- /* Get hardware or kernel timestamp if available */
- #ifdef CONFIG_NET_TIMESTAMP
- struct cmsghdr *cmsg;
- for_each_cmsghdr(cmsg, rxhdr)
- {
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SO_TIMESTAMP &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
- {
- TIMEVAL_TO_TIMESPEC((FAR struct timeval *)CMSG_DATA(cmsg), ts);
- /* Sanity-check the value */
- if (ts->tv_sec > 0 || ts->tv_nsec > 0)
- {
- return OK;
- }
- }
- }
- ptpwarn("CONFIG_NET_TIMESTAMP enabled but did not get packet timestamp\n");
- #endif
- /* Fall back to current timestamp */
- return ptp_gettime(state, ts);
- }
- #endif // !ESP_PTP
- /* Initialize PTP client/server state and create sockets */
- #ifdef ESP_PTP
- static int ptp_initialize_state(FAR struct ptp_state_s *state,
- FAR const char *interface)
- {
- state->ptp_socket = open("/dev/net/tap", 0);
- if (state->ptp_socket < 0)
- {
- ptperr("Failed to create tx socket: %d\n", errno);
- return ERROR;
- }
- // Set Ethernet interface on which to get raw frames
- if (ioctl(state->ptp_socket, L2TAP_S_INTF_DEVICE, interface) < 0)
- {
- ptperr("failed to set network interface at socket: %d\n", errno);
- return ERROR;
- }
- // Set the Ethertype filter (frames with this type will be available through the state->tx_socket)
- uint16_t eth_type_filter = ETH_TYPE_PTP;
- if (ioctl(state->ptp_socket, L2TAP_S_RCV_FILTER, ð_type_filter) < 0)
- {
- ptperr("failed to set Ethertype filter: %d\n", errno);
- return ERROR;
- }
- // Enable time stamping in driver
- esp_eth_handle_t eth_handle;
- if (ioctl(state->ptp_socket, L2TAP_G_DEVICE_DRV_HNDL, ð_handle) < 0)
- {
- ptperr("failed to get socket eth_handle %d\n", errno);
- return ERROR;
- }
- state->cached_eth_handle = eth_handle;
- esp_eth_clock_cfg_t clk_cfg = {
- .eth_hndl = eth_handle,
- };
- esp_eth_clock_init(CLOCK_PTP_SYSTEM, &clk_cfg);
- // Enable time stamping in L2TAP
- if(ioctl(state->ptp_socket, L2TAP_S_TIMESTAMP_EN) < 0)
- {
- ptperr("failed to enable time stamping in l2 socket: %d\n", errno);
- return ERROR;
- }
- // get HW address
- esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, &state->intf_hw_addr);
- // Add well-known PTP multicast destination MAC addresses to the filter
- uint8_t dest_addr[ETH_ADDR_LEN];
- SET_MAC_ADDR(dest_addr, 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00);
- esp_eth_ioctl(eth_handle, ETH_CMD_ADD_MAC_FILTER, dest_addr);
- SET_MAC_ADDR(dest_addr, 0x01, 0x80, 0xC2, 0x00, 0x00, 0x0E);
- esp_eth_ioctl(eth_handle, ETH_CMD_ADD_MAC_FILTER, dest_addr);
- state->remote_time_ns_prev = 0;
- state->local_time_ns_prev = 0;
- /* PI controller gains (multiplicative, fixed-point * 1000).
- * These are the default gains used when offset is small (<1ms).
- * When offset is large, adaptive scaling increases them. */
- state->offset_pi.kp = 150; /* 0.15 as fixed-point *1000 */
- state->offset_pi.ki = 10; /* 0.01 as fixed-point *1000 */
- state->offset_pi.drift_acc = 0;
- state->own_identity.header.version = 2;
- state->own_identity.header.domain = CONFIG_NETUTILS_PTPD_DOMAIN;
- state->own_identity.header.sourceidentity[0] = state->intf_hw_addr[0];
- state->own_identity.header.sourceidentity[1] = state->intf_hw_addr[1];
- state->own_identity.header.sourceidentity[2] = state->intf_hw_addr[2];
- state->own_identity.header.sourceidentity[3] = 0xff;
- state->own_identity.header.sourceidentity[4] = 0xfe;
- state->own_identity.header.sourceidentity[5] = state->intf_hw_addr[3];
- state->own_identity.header.sourceidentity[6] = state->intf_hw_addr[4];
- state->own_identity.header.sourceidentity[7] = state->intf_hw_addr[5];
- state->own_identity.header.sourceportindex[0] = 0;
- state->own_identity.header.sourceportindex[1] = 1;
- #ifdef CONFIG_NETUTILS_PTPD_SERVER
- state->own_identity.gm_priority1 = CONFIG_NETUTILS_PTPD_PRIORITY1;
- state->own_identity.gm_quality[0] = CONFIG_NETUTILS_PTPD_CLASS;
- state->own_identity.gm_quality[1] = CONFIG_NETUTILS_PTPD_ACCURACY;
- state->own_identity.gm_quality[2] = 0xff; /* No variance estimate */
- state->own_identity.gm_quality[3] = 0xff;
- state->own_identity.gm_priority2 = CONFIG_NETUTILS_PTPD_PRIORITY2;
- memcpy(state->own_identity.gm_identity,
- state->own_identity.header.sourceidentity,
- sizeof(state->own_identity.gm_identity));
- state->own_identity.timesource = CONFIG_NETUTILS_PTPD_CLOCKSOURCE;
- #else
- state->own_identity.gm_priority1 = 255; // When daemon is statically configured as slave, set the worst
- #endif
- /* --- UDP socket setup for AES67 interoperability ---
- * In addition to L2 TAP, set up standard UDP sockets on the PTP
- * multicast group (224.0.1.129) so we can communicate with devices
- * that use PTP over UDP/IPv4 (which is most AES67 gear). */
- /* Get interface IP for binding and IGMP */
- esp_netif_t *netif = esp_netif_get_handle_from_ifkey(interface);
- if (netif) {
- esp_netif_ip_info_t ip_info;
- if (esp_netif_get_ip_info(netif, &ip_info) == ESP_OK) {
- state->interface_addr.sin_family = AF_INET;
- state->interface_addr.sin_addr.s_addr = ip_info.ip.addr;
- }
- }
- /* Create UDP sockets */
- state->tx_socket = socket(AF_INET, SOCK_DGRAM, 0);
- state->event_socket = socket(AF_INET, SOCK_DGRAM, 0);
- state->info_socket = socket(AF_INET, SOCK_DGRAM, 0);
- if (state->tx_socket < 0 || state->event_socket < 0 || state->info_socket < 0) {
- ptperr("Failed to create UDP sockets for PTP: %d\n", errno);
- /* Non-fatal: L2 transport still works */
- } else {
- struct sockaddr_in bind_addr;
- memset(&bind_addr, 0, sizeof(bind_addr));
- bind_addr.sin_family = AF_INET;
- bind_addr.sin_addr.s_addr = htonl(PTP_MULTICAST_ADDR);
- /* Join PTP multicast group via IGMP */
- struct ip_mreq mreq;
- mreq.imr_multiaddr.s_addr = htonl(PTP_MULTICAST_ADDR);
- mreq.imr_interface.s_addr = state->interface_addr.sin_addr.s_addr;
- if (setsockopt(state->event_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- &mreq, sizeof(mreq)) < 0) {
- ptperr("Failed to join PTP multicast on event socket: %d\n", errno);
- }
- if (setsockopt(state->info_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- &mreq, sizeof(mreq)) < 0) {
- ptperr("Failed to join PTP multicast on info socket: %d\n", errno);
- }
- /* Allow address reuse so multiple sockets can bind to the same port */
- int reuse = 1;
- setsockopt(state->event_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
- setsockopt(state->info_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
- /* Bind event socket to port 319 */
- bind_addr.sin_port = htons(PTP_UDP_PORT_EVENT);
- if (bind(state->event_socket, (struct sockaddr *)&bind_addr,
- sizeof(bind_addr)) < 0) {
- ptperr("Failed to bind event socket to port %d: %d\n",
- PTP_UDP_PORT_EVENT, errno);
- }
- /* Bind info socket to port 320 */
- bind_addr.sin_port = htons(PTP_UDP_PORT_INFO);
- if (bind(state->info_socket, (struct sockaddr *)&bind_addr,
- sizeof(bind_addr)) < 0) {
- ptperr("Failed to bind info socket to port %d: %d\n",
- PTP_UDP_PORT_INFO, errno);
- }
- /* Bind TX socket to local interface address */
- bind_addr.sin_addr = state->interface_addr.sin_addr;
- bind_addr.sin_port = 0; /* Any port */
- bind(state->tx_socket, (struct sockaddr *)&bind_addr, sizeof(bind_addr));
- /* Set multicast TTL */
- int ttl = 64;
- setsockopt(state->tx_socket, IPPROTO_IP, IP_MULTICAST_TTL,
- &ttl, sizeof(ttl));
- ptpinfo("UDP PTP sockets ready (event:%d, info:%d, tx:%d)\n",
- state->event_socket, state->info_socket, state->tx_socket);
- }
- s_state = state;
- return OK;
- }
- #else
- static int ptp_initialize_state(FAR struct ptp_state_s *state,
- FAR const char *interface)
- {
- int ret;
- struct ifreq req;
- struct sockaddr_in bind_addr;
- #ifdef CONFIG_NET_TIMESTAMP
- int arg;
- #endif
- /* Create sockets */
- state->tx_socket = socket(AF_INET, SOCK_DGRAM, 0);
- if (state->tx_socket < 0)
- {
- ptperr("Failed to create tx socket: %d\n", errno);
- return ERROR;
- }
- state->event_socket = socket(AF_INET, SOCK_DGRAM, 0);
- if (state->event_socket < 0)
- {
- ptperr("Failed to create event socket: %d\n", errno);
- return ERROR;
- }
- state->info_socket = socket(AF_INET, SOCK_DGRAM, 0);
- if (state->info_socket < 0)
- {
- ptperr("Failed to create info socket: %d\n", errno);
- return ERROR;
- }
- /* Get address information of the specified interface for binding socket
- * Only supports IPv4 currently.
- */
- memset(&req, 0, sizeof(req));
- strncpy(req.ifr_name, interface, sizeof(req.ifr_name));
- if (ioctl(state->event_socket, SIOCGIFADDR, (unsigned long)&req) < 0)
- {
- ptperr("Failed to get IP address information for interface %s\n",
- interface);
- return ERROR;
- }
- state->interface_addr = *(struct sockaddr_in *)&req.ifr_ifru.ifru_addr;
- /* Get hardware address to initialize the identity field in header.
- * Clock identity is EUI-64, which we make from EUI-48.
- */
- if (ioctl(state->event_socket, SIOCGIFHWADDR, (unsigned long)&req) < 0)
- {
- ptperr("Failed to get HW address information for interface %s\n",
- interface);
- return ERROR;
- }
- state->own_identity.header.version = 2;
- state->own_identity.header.domain = CONFIG_NETUTILS_PTPD_DOMAIN;
- state->own_identity.header.sourceidentity[0] = req.ifr_hwaddr.sa_data[0];
- state->own_identity.header.sourceidentity[1] = req.ifr_hwaddr.sa_data[1];
- state->own_identity.header.sourceidentity[2] = req.ifr_hwaddr.sa_data[2];
- state->own_identity.header.sourceidentity[3] = 0xff;
- state->own_identity.header.sourceidentity[4] = 0xfe;
- state->own_identity.header.sourceidentity[5] = req.ifr_hwaddr.sa_data[3];
- state->own_identity.header.sourceidentity[6] = req.ifr_hwaddr.sa_data[4];
- state->own_identity.header.sourceidentity[7] = req.ifr_hwaddr.sa_data[5];
- state->own_identity.header.sourceportindex[0] = 0;
- state->own_identity.header.sourceportindex[1] = 1;
- state->own_identity.gm_priority1 = CONFIG_NETUTILS_PTPD_PRIORITY1;
- state->own_identity.gm_quality[0] = CONFIG_NETUTILS_PTPD_CLASS;
- state->own_identity.gm_quality[1] = CONFIG_NETUTILS_PTPD_ACCURACY;
- state->own_identity.gm_quality[2] = 0xff; /* No variance estimate */
- state->own_identity.gm_quality[3] = 0xff;
- state->own_identity.gm_priority2 = CONFIG_NETUTILS_PTPD_PRIORITY2;
- memcpy(state->own_identity.gm_identity,
- state->own_identity.header.sourceidentity,
- sizeof(state->own_identity.gm_identity));
- state->own_identity.timesource = CONFIG_NETUTILS_PTPD_CLOCKSOURCE;
- /* Subscribe to PTP multicast address */
- bind_addr.sin_family = AF_INET;
- bind_addr.sin_addr.s_addr = htonl(PTP_MULTICAST_ADDR);
- clock_gettime(CLOCK_MONOTONIC, &state->last_received_multicast);
- ret = ipmsfilter(&state->interface_addr.sin_addr,
- &bind_addr.sin_addr,
- MCAST_INCLUDE);
- if (ret < 0)
- {
- ptperr("Failed to bind multicast address: %d\n", errno);
- return ERROR;
- }
- /* Bind socket for events */
- bind_addr.sin_port = htons(PTP_UDP_PORT_EVENT);
- ret = bind(state->event_socket, (struct sockaddr *)&bind_addr,
- sizeof(bind_addr));
- if (ret < 0)
- {
- ptperr("Failed to bind to udp port %d\n", bind_addr.sin_port);
- return ERROR;
- }
- #ifdef CONFIG_NET_TIMESTAMP
- arg = 1;
- ret = setsockopt(state->event_socket, SOL_SOCKET, SO_TIMESTAMP,
- &arg, sizeof(arg));
- if (ret < 0)
- {
- ptperr("Failed to enable SO_TIMESTAMP: %s\n", strerror(errno));
- /* PTPD can operate without, but with worse accuracy */
- }
- #endif
- /* Bind socket for announcements */
- bind_addr.sin_port = htons(PTP_UDP_PORT_INFO);
- ret = bind(state->info_socket, (struct sockaddr *)&bind_addr,
- sizeof(bind_addr));
- if (ret < 0)
- {
- ptperr("Failed to bind to udp port %d\n", bind_addr.sin_port);
- return ERROR;
- }
- /* Bind TX socket to interface address (local addr cannot be multicast) */
- bind_addr.sin_addr = state->interface_addr.sin_addr;
- ret = bind(state->tx_socket, (struct sockaddr *)&bind_addr,
- sizeof(bind_addr));
- if (ret < 0)
- {
- ptperr("Failed to bind tx to port %d\n", bind_addr.sin_port);
- return ERROR;
- }
- return OK;
- }
- #endif // ESP_PTP
- /* Unsubscribe multicast and destroy sockets */
- static int ptp_destroy_state(FAR struct ptp_state_s *state)
- {
- #ifdef ESP_PTP
- // Remove well-known PTP multicast destination MAC addresses from the filter
- esp_eth_handle_t eth_handle;
- if (ioctl(state->ptp_socket, L2TAP_G_DEVICE_DRV_HNDL, ð_handle) < 0)
- {
- ptperr("failed to get socket eth_handle %d\n", errno);
- return ERROR;
- }
- uint8_t dest_addr[ETH_ADDR_LEN];
- SET_MAC_ADDR(dest_addr, 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00);
- esp_eth_ioctl(eth_handle, ETH_CMD_DEL_MAC_FILTER, dest_addr);
- SET_MAC_ADDR(dest_addr, 0x01, 0x80, 0xC2, 0x00, 0x00, 0x0E);
- esp_eth_ioctl(eth_handle, ETH_CMD_DEL_MAC_FILTER, dest_addr);
- if (state->ptp_socket > 0)
- {
- close(state->ptp_socket);
- state->ptp_socket = -1;
- }
- #else
- struct in_addr mcast_addr;
- mcast_addr.s_addr = htonl(PTP_MULTICAST_ADDR);
- ipmsfilter(&state->interface_addr.sin_addr,
- &mcast_addr,
- MCAST_EXCLUDE);
- if (state->tx_socket > 0)
- {
- close(state->tx_socket);
- state->tx_socket = -1;
- }
- if (state->event_socket > 0)
- {
- close(state->event_socket);
- state->event_socket = -1;
- }
- if (state->info_socket > 0)
- {
- close(state->info_socket);
- state->info_socket = -1;
- }
- #endif // ESP_PTP
- return OK;
- }
- #ifndef ESP_PTP
- /* Re-subscribe multicast address.
- * This can become necessary if Ethernet interface gets reset or if external
- * IGMP-compliant Ethernet switch gets plugged in.
- */
- static int ptp_check_multicast_status(FAR struct ptp_state_s *state)
- {
- #if CONFIG_NETUTILS_PTPD_MULTICAST_TIMEOUT_MS > 0
- struct in_addr mcast_addr;
- struct timespec time_now;
- struct timespec delta;
- clock_gettime(CLOCK_MONOTONIC, &time_now);
- clock_timespec_subtract(&time_now, &state->last_received_multicast,
- &delta);
- if (timespec_to_ms(&delta) > CONFIG_NETUTILS_PTPD_MULTICAST_TIMEOUT_MS)
- {
- /* Remove and re-add the multicast group */
- state->last_received_multicast = time_now;
- mcast_addr.s_addr = htonl(PTP_MULTICAST_ADDR);
- ipmsfilter(&state->interface_addr.sin_addr,
- &mcast_addr,
- MCAST_EXCLUDE);
- return ipmsfilter(&state->interface_addr.sin_addr,
- &mcast_addr,
- MCAST_INCLUDE);
- }
- #else
- UNUSED(state);
- #endif /* CONFIG_NETUTILS_PTPD_MULTICAST_TIMEOUT_MS */
- return OK;
- }
- #endif // !ESP_PTP
- /* Send PTP server announcement packet */
- static int ptp_send_announce(FAR struct ptp_state_s *state)
- {
- struct ptp_announce_s msg;
- #ifndef ESP_PTP
- struct sockaddr_in addr;
- #endif // !ESP_PTP
- struct timespec ts;
- int ret;
- #ifndef ESP_PTP
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = htonl(PTP_MULTICAST_ADDR);
- addr.sin_port = htons(PTP_UDP_PORT_INFO);
- #endif // !ESP_PTP
- memset(&msg, 0, sizeof(msg));
- msg = state->own_identity;
- msg.header.messagetype = PTP_MSGTYPE_ANNOUNCE;
- msg.header.messagelength[1] = sizeof(msg);
- ptp_increment_sequence(&state->announce_seq, &msg.header);
- ptp_gettime(state, &ts);
- timespec_to_ptp_format(&ts, msg.origintimestamp);
- #ifdef ESP_PTP
- /* Send via L2 TAP for hardware timestamped transport */
- ret = ptp_net_send(state, &msg, sizeof(msg), NULL);
- /* Also send via UDP for interoperability with standard PTP/AES67 devices */
- {
- struct sockaddr_in udp_addr;
- udp_addr.sin_family = AF_INET;
- udp_addr.sin_addr.s_addr = htonl(PTP_MULTICAST_ADDR);
- udp_addr.sin_port = htons(PTP_UDP_PORT_INFO);
- sendto(state->tx_socket, &msg, sizeof(msg), 0,
- (struct sockaddr *)&udp_addr, sizeof(udp_addr));
- }
- #else
- ret = sendto(state->tx_socket, &msg, sizeof(msg), 0,
- (struct sockaddr *)&addr, sizeof(addr));
- #endif // ESP_PTP
- if (ret < 0)
- {
- ptperr("sendto failed: %d", errno);
- }
- else
- {
- ESP_LOGD(TAG, "Sent announce, seq %ld",
- (long)ptp_get_sequence(&msg.header));
- }
- return ret;
- }
- /* Send PTP server synchronization packet */
- static int ptp_send_sync(FAR struct ptp_state_s *state)
- {
- #ifndef ESP_PTP
- struct msghdr txhdr;
- struct iovec txiov;
- #endif // !ESP_PTP
- struct ptp_sync_s msg;
- #ifndef ESP_PTP
- struct sockaddr_in addr;
- #endif // !ESP_PTP
- struct timespec ts;
- #ifndef ESP_PTP
- uint8_t controlbuf[64];
- #endif // !ESP_PTP
- int ret;
- #ifndef ESP_PTP
- memset(&txhdr, 0, sizeof(txhdr));
- memset(&txiov, 0, sizeof(txiov));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = htonl(PTP_MULTICAST_ADDR);
- addr.sin_port = htons(PTP_UDP_PORT_EVENT);
- #endif // !ESP_PTP
- memset(&msg, 0, sizeof(msg));
- msg.header = state->own_identity.header;
- msg.header.messagetype = PTP_MSGTYPE_SYNC;
- msg.header.messagelength[1] = sizeof(msg);
- #ifdef CONFIG_NETUTILS_PTPD_TWOSTEP_SYNC
- msg.header.flags[0] = PTP_FLAGS0_TWOSTEP;
- #endif
- #ifndef ESP_PTP
- txhdr.msg_name = &addr;
- txhdr.msg_namelen = sizeof(addr);
- txhdr.msg_iov = &txiov;
- txhdr.msg_iovlen = 1;
- txhdr.msg_control = controlbuf;
- txhdr.msg_controllen = sizeof(controlbuf);
- txiov.iov_base = &msg;
- txiov.iov_len = sizeof(msg);
- #endif //!ESP_PTP
- /* Timestamp and send the sync message */
- ptp_increment_sequence(&state->sync_seq, &msg.header);
- ptp_gettime(state, &ts);
- timespec_to_ptp_format(&ts, msg.origintimestamp);
- #ifdef ESP_PTP
- ret = ptp_net_send(state, &msg, sizeof(msg), &ts);
- /* Also send sync via UDP for AES67 interoperability */
- {
- struct sockaddr_in udp_addr;
- udp_addr.sin_family = AF_INET;
- udp_addr.sin_addr.s_addr = htonl(PTP_MULTICAST_ADDR);
- udp_addr.sin_port = htons(PTP_UDP_PORT_EVENT);
- sendto(state->tx_socket, &msg, sizeof(msg), 0,
- (struct sockaddr *)&udp_addr, sizeof(udp_addr));
- }
- #else
- ret = sendmsg(state->tx_socket, &txhdr, 0);
- #endif // ESP_PTP
- if (ret < 0)
- {
- ptperr("sendmsg for sync message failed: %d\n", errno);
- return ret;
- }
- #ifdef CONFIG_NETUTILS_PTPD_TWOSTEP_SYNC
- #ifndef ESP_PTP
- /* Get timestamp after send completes and send follow-up message
- *
- * TODO: Implement SO_TIMESTAMPING and use the actual tx timestamp here.
- */
- ptp_gettime(state, &ts);
- #endif // !ESP_PTP
- timespec_to_ptp_format(&ts, msg.origintimestamp);
- msg.header.messagetype = PTP_MSGTYPE_FOLLOW_UP;
- msg.header.flags[0] = 0;
- #ifdef ESP_PTP
- ret = ptp_net_send(state, &msg, sizeof(msg), NULL);
- /* Also send follow-up via UDP */
- {
- struct sockaddr_in udp_addr;
- udp_addr.sin_family = AF_INET;
- udp_addr.sin_addr.s_addr = htonl(PTP_MULTICAST_ADDR);
- udp_addr.sin_port = htons(PTP_UDP_PORT_INFO);
- sendto(state->tx_socket, &msg, sizeof(msg), 0,
- (struct sockaddr *)&udp_addr, sizeof(udp_addr));
- }
- #else
- addr.sin_port = htons(PTP_UDP_PORT_INFO);
- ret = sendto(state->tx_socket, &msg, sizeof(msg), 0,
- (struct sockaddr *)&addr, sizeof(addr));
- #endif // ESP_PTP
- if (ret < 0)
- {
- ptperr("sendto for follow-up message failed: %d\n", errno);
- return ret;
- }
- ESP_LOGD(TAG, "Sent sync + follow-up, seq %ld",
- (long)ptp_get_sequence(&msg.header));
- #else
- ESP_LOGD(TAG, "Sent sync, seq %ld",
- (long)ptp_get_sequence(&msg.header));
- #endif /* CONFIG_NETUTILS_PTPD_TWOSTEP_SYNC */
- return OK;
- }
- /* Send delay request packet to selected source */
- static int ptp_send_delay_req(FAR struct ptp_state_s *state)
- {
- struct ptp_delay_req_s req;
- #ifndef ESP_PTP
- struct sockaddr_in addr;
- #endif // !ESP_PTP
- int ret;
- #ifndef ESP_PTP
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = htonl(PTP_MULTICAST_ADDR);
- addr.sin_port = htons(PTP_UDP_PORT_EVENT);
- #endif // !ESP_PTP
- memset(&req, 0, sizeof(req));
- req.header = state->own_identity.header;
- req.header.messagetype = PTP_MSGTYPE_DELAY_REQ;
- req.header.messagelength[1] = sizeof(req);
- ptp_increment_sequence(&state->delay_req_seq, &req.header);
- ptp_gettime(state, &state->delayreq_time);
- timespec_to_ptp_format(&state->delayreq_time, req.origintimestamp);
- #ifdef ESP_PTP
- ret = ptp_net_send(state, &req, sizeof(req), &state->delayreq_time);
- /* Also send delay_req via UDP */
- {
- struct sockaddr_in udp_addr;
- udp_addr.sin_family = AF_INET;
- udp_addr.sin_addr.s_addr = htonl(PTP_MULTICAST_ADDR);
- udp_addr.sin_port = htons(PTP_UDP_PORT_EVENT);
- sendto(state->tx_socket, &req, sizeof(req), 0,
- (struct sockaddr *)&udp_addr, sizeof(udp_addr));
- }
- #else
- ret = sendto(state->tx_socket, &req, sizeof(req), 0,
- (FAR struct sockaddr *)&addr, sizeof(addr));
- /* Get timestamp after send completes.
- * TODO: Implement SO_TIMESTAMPING and use the actual tx timestamp here.
- */
- ptp_gettime(state, &state->delayreq_time);
- #endif // ESP_PTP
- if (ret < 0)
- {
- ptperr("sendto failed: %d", errno);
- }
- else
- {
- clock_gettime(CLOCK_MONOTONIC, &state->last_transmitted_delayreq);
- ESP_LOGD(TAG, "Sent delay req, seq %ld",
- (long)ptp_get_sequence(&req.header));
- }
- return ret;
- }
- /* Check if we need to send packets */
- static int ptp_periodic_send(FAR struct ptp_state_s *state)
- {
- #ifdef CONFIG_NETUTILS_PTPD_SERVER
- /* If there is no better master clock on the network,
- * act as the reference source and send server packets.
- */
- if (!state->selected_source_valid)
- {
- struct timespec time_now;
- struct timespec delta;
- clock_gettime(CLOCK_MONOTONIC, &time_now);
- clock_timespec_subtract(&time_now,
- &state->last_transmitted_announce, &delta);
- if (timespec_to_ms(&delta)
- > CONFIG_NETUTILS_PTPD_ANNOUNCE_INTERVAL_MSEC)
- {
- state->last_transmitted_announce = time_now;
- ptp_send_announce(state);
- }
- clock_timespec_subtract(&time_now,
- &state->last_transmitted_sync, &delta);
- if (timespec_to_ms(&delta) > CONFIG_NETUTILS_PTPD_SYNC_INTERVAL_MSEC)
- {
- state->last_transmitted_sync = time_now;
- ptp_send_sync(state);
- }
- }
- #endif /* CONFIG_NETUTILS_PTPD_SERVER */
- #ifdef CONFIG_NETUTILS_PTPD_SEND_DELAYREQ
- if (state->selected_source_valid && state->can_send_delayreq)
- {
- struct timespec time_now;
- struct timespec delta;
- clock_gettime(CLOCK_MONOTONIC, &time_now);
- clock_timespec_subtract(&time_now,
- &state->last_transmitted_delayreq, &delta);
- if (timespec_to_ms(&delta) > state->delayreq_interval * MSEC_PER_SEC)
- {
- ptp_send_delay_req(state);
- }
- }
- #endif
- return OK;
- }
- /* Process received PTP announcement */
- static int ptp_process_announce(FAR struct ptp_state_s *state,
- FAR struct ptp_announce_s *msg)
- {
- clock_gettime(CLOCK_MONOTONIC, &state->last_received_announce);
- /* Log announce only when it's from a new/different GM than our current source,
- * or if it's better than us and we haven't selected it yet. */
- bool better = is_better_clock(msg, &state->own_identity);
- bool is_new_gm = !state->selected_source_valid ||
- memcmp(msg->gm_identity,
- state->selected_source.gm_identity,
- sizeof(msg->gm_identity)) != 0;
- if (is_new_gm || !state->selected_source_valid) {
- ESP_LOGI(TAG, "Announce from GM %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X "
- "(p1=%u, class=%u, acc=%u, p2=%u) %s",
- msg->gm_identity[0], msg->gm_identity[1],
- msg->gm_identity[2], msg->gm_identity[3],
- msg->gm_identity[4], msg->gm_identity[5],
- msg->gm_identity[6], msg->gm_identity[7],
- msg->gm_priority1,
- msg->gm_quality[0], /* clock class */
- msg->gm_quality[1], /* clock accuracy */
- msg->gm_priority2,
- better ? "<- BETTER than us" : "(not better)");
- }
- if (better)
- {
- if (!state->selected_source_valid ||
- is_better_clock(msg, &state->selected_source))
- {
- ESP_LOGI(TAG, "Selecting GM %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X as clock source",
- msg->gm_identity[0], msg->gm_identity[1],
- msg->gm_identity[2], msg->gm_identity[3],
- msg->gm_identity[4], msg->gm_identity[5],
- msg->gm_identity[6], msg->gm_identity[7]);
- state->selected_source = *msg;
- state->last_received_sync = state->last_received_announce;
- state->path_delay_avgcount = 0;
- state->path_delay_ns = 0;
- state->delayreq_time.tv_sec = 0;
- }
- }
- return OK;
- }
- #ifdef ESP_PTP
- static void ptp_lock_local_clock_freq(FAR struct ptp_state_s *state,
- FAR struct timespec *remote_timestamp,
- FAR struct timespec *local_timestamp)
- {
- /* Compute clock offset against master */
- int64_t offset_ns = timespec_delta_ns(remote_timestamp, local_timestamp);
- offset_ns += state->path_delay_ns;
- /* Outlier filter: reject measurements where the offset changes by
- * more than 2ms from the previous sample. These are caused by network
- * jitter (delayed packets), not real clock error. Skip adjustment
- * but still update timestamps for the next cycle. */
- if (state->last_offset_ns != 0) {
- int64_t delta = offset_ns - state->last_offset_ns;
- if (delta > 500000LL || delta < -500000LL) {
- ESP_LOGD(TAG, "Outlier filtered: offset %+lld ns (prev %+lld, delta %+lld)",
- offset_ns, state->last_offset_ns, delta);
- /* Update prev timestamps but skip PI adjustment */
- state->remote_time_ns_prev = timespec_to_ns(remote_timestamp);
- state->local_time_ns_prev = timespec_to_ns(local_timestamp);
- state->last_offset_ns = offset_ns;
- state->last_delta_ns = offset_ns;
- return;
- }
- }
- state->last_offset_ns = offset_ns;
- /* Convert offset from nanoseconds to ppb using the actual sync interval.
- * ppb = offset_ns * 1e9 / interval_ns */
- int64_t local_time_ns_now = timespec_to_ns(local_timestamp);
- int64_t interval_ns = 1000000000LL;
- if (state->local_time_ns_prev > 0) {
- int64_t measured = local_time_ns_now - state->local_time_ns_prev;
- if (measured > 100000000LL && measured < 5000000000LL) {
- interval_ns = measured;
- }
- }
- int64_t offset_ppb = (offset_ns * 1000000000LL) / interval_ns;
- /* Adaptive gain scaling: use stronger gains when offset is large
- * to converge quickly, then ease off for stable fine tracking.
- * > 1ms offset: gains * 3 (aggressive pull-in)
- * > 100us offset: gains * 2 (moderate convergence)
- * < 100us offset: base gains (fine tracking) */
- int32_t kp = state->offset_pi.kp;
- int32_t ki = state->offset_pi.ki;
- int64_t abs_offset = (offset_ns < 0) ? -offset_ns : offset_ns;
- if (abs_offset > 1000000) { /* > 1ms */
- kp *= 2;
- ki *= 2;
- }
- /* No extra scaling for 100us-1ms range -- let the base gains handle it */
- /* PI controller with multiplicative gains.
- * P term = offset_ppb * kp / 1000
- * I term: drift_acc += offset_ppb * ki / 1000 */
- int32_t p_term = (int32_t)((offset_ppb * kp) / 1000);
- state->offset_pi.drift_acc += (int32_t)((offset_ppb * ki) / 1000);
- /* Clamp I term to +/- 50 ppm to prevent overshoot */
- if (state->offset_pi.drift_acc > 50000) {
- state->offset_pi.drift_acc = 50000;
- } else if (state->offset_pi.drift_acc < -50000) {
- state->offset_pi.drift_acc = -50000;
- }
- int32_t adj_ppb = p_term + state->offset_pi.drift_acc;
- /* Clamp total output to +/- 200 ppm */
- if (adj_ppb > 200000) adj_ppb = 200000;
- if (adj_ppb < -200000) adj_ppb = -200000;
- #ifndef AES67_DISABLE_PTP_PI
- /* Use the ppb-based API (ETH_MAC_ESP_CMD_ADJ_PTP_TIME) which calls
- * emac_hal_ptp_adj_inc. This is idempotent: it always computes the
- * new addend relative to the stored base addend, avoiding the
- * compounding problem of the freq_scale API. */
- esp_err_t adj_err = esp_eth_ioctl(state->cached_eth_handle,
- ETH_MAC_ESP_CMD_ADJ_PTP_TIME, &adj_ppb);
- if (adj_err != ESP_OK) {
- ESP_LOGW(TAG, "PTP freq adj failed: %s", esp_err_to_name(adj_err));
- }
- #else
- (void)adj_ppb;
- /* PI disabled: PTP clock runs free, no frequency discipline */
- #endif
- /* Track timestamps for drift measurement */
- int64_t remote_time_ns = timespec_to_ns(remote_timestamp);
- int64_t local_time_ns = timespec_to_ns(local_timestamp);
- if (state->remote_time_ns_prev != 0 && state->local_time_ns_prev != 0) {
- int64_t local_delta_ns = local_time_ns - state->local_time_ns_prev;
- int64_t remote_delta_ns = remote_time_ns - state->remote_time_ns_prev;
- if (local_delta_ns > 0 && local_delta_ns < 5000000000LL) {
- int64_t tick_diff = remote_delta_ns - local_delta_ns;
- state->drift_ppb = (long)((tick_diff * 1000000000LL) / local_delta_ns);
- }
- }
- state->remote_time_ns_prev = remote_time_ns;
- state->local_time_ns_prev = local_time_ns;
- state->last_delta_ns = offset_ns;
- ESP_LOGD(TAG, "offset %+lld ns, p=%+ld, i=%+ld, adj=%+ld ppb",
- offset_ns, (long)p_term, (long)state->offset_pi.drift_acc, (long)adj_ppb);
- // Get the path delay only when clock is stable enough. If we were in process of adjustion (speeding/slowing slave),
- // we would get incorrect delay
- int64_t diff = llabs(offset_ns) - llabs(state->last_offset_ns);
- static int cnt = 0;
- if (llabs(diff) < CONFIG_NETUTILS_PTPD_PATH_DELAY_STABILITY_NS) {
- if (cnt <= 3)
- cnt++;
- } else {
- cnt = 0;
- }
- if (cnt > 3)
- {
- state->can_send_delayreq = true;
- }
- state->last_offset_ns = offset_ns;
- }
- void ptp_clean_after_step(FAR struct ptp_state_s *state)
- {
- state->remote_time_ns_prev = 0;
- state->local_time_ns_prev = 0;
- state->offset_pi.drift_acc = 0;
- state->last_offset_ns = 0;
- }
- #endif // ESP_PTP
- /* Update local clock either by smooth adjustment or by jumping.
- * Remote time was remote_timestamp at local_timestamp.
- */
- static int ptp_update_local_clock(FAR struct ptp_state_s *state,
- FAR struct timespec *remote_timestamp,
- FAR struct timespec *local_timestamp)
- {
- int ret = OK;
- int64_t delta_ns;
- int64_t absdelta_ns;
- const int64_t adj_limit_ns = CONFIG_NETUTILS_PTPD_SETTIME_THRESHOLD_MS
- * (int64_t)NSEC_PER_MSEC;
- ESP_LOGD(TAG, "Local time: %lld.%09ld, remote time %lld.%09ld",
- (long long)local_timestamp->tv_sec,
- (long)local_timestamp->tv_nsec,
- (long long)remote_timestamp->tv_sec,
- (long)remote_timestamp->tv_nsec);
- delta_ns = timespec_delta_ns(remote_timestamp, local_timestamp);
- delta_ns += state->path_delay_ns;
- absdelta_ns = (delta_ns < 0) ? -delta_ns : delta_ns;
- if (absdelta_ns > adj_limit_ns)
- {
- /* Large difference, move by jumping.
- * Account for delay since packet was received.
- */
- struct timespec new_time;
- ptp_gettime(state, &new_time);
- clock_timespec_subtract(&new_time, local_timestamp, &new_time);
- clock_timespec_add(&new_time, remote_timestamp, &new_time);
- ret = ptp_settime(state, &new_time);
- /* Reinitialize drift adjustment parameters */
- state->last_delta_timestamp = new_time;
- state->last_delta_ns = 0;
- state->last_adjtime_ns = 0;
- state->drift_avg_total_ms = 0;
- state->drift_ppb = 0;
- #ifdef ESP_PTP
- ptp_clean_after_step(state);
- #endif // ESP_PTP
- if (ret == OK)
- {
- ESP_LOGI(TAG, "Jumped to timestamp %lld.%09ld s",
- (long long)new_time.tv_sec, (long)new_time.tv_nsec);
- }
- else
- {
- ptperr("ptp_settime() failed: %d\n", errno);
- }
- }
- else
- {
- #ifdef ESP_PTP
- ptp_lock_local_clock_freq(state, remote_timestamp, local_timestamp);
- #else
- /* Track drift rate based on two consecutive measurements and
- * the adjustment that was made previously.
- */
- int64_t drift_ppb;
- struct timespec interval;
- int interval_ms;
- int max_avg_period_ms;
- int64_t adjustment_ns;
- clock_timespec_subtract(local_timestamp,
- &state->last_delta_timestamp,
- &interval);
- interval_ms = timespec_to_ms(&interval);
- if (interval_ms > 0 && interval_ms < CONFIG_NETUTILS_PTPD_TIMEOUT_MS)
- {
- drift_ppb = (delta_ns - state->last_delta_ns) * MSEC_PER_SEC
- / interval_ms;
- }
- else
- {
- ptpwarn("Measurement interval out of range: %d ms\n", interval_ms);
- drift_ppb = 0;
- interval_ms = 1;
- }
- /* Account for the adjustment previously made */
- drift_ppb += state->last_adjtime_ns * MSEC_PER_SEC
- / CONFIG_CLOCK_ADJTIME_PERIOD_MS;
- if (drift_ppb > CONFIG_CLOCK_ADJTIME_SLEWLIMIT_PPM * 1000 ||
- drift_ppb < -CONFIG_CLOCK_ADJTIME_SLEWLIMIT_PPM * 1000)
- {
- ptpwarn("Drift estimate out of range: %lld\n",
- (long long)drift_ppb);
- drift_ppb = state->drift_ppb;
- }
- /* Take direct average of drift estimate for first measurements,
- * after that update the exponential sliding average.
- * Measurements are weighted according to the interval, because
- * drift estimate is more accurate over longer timespan.
- */
- state->drift_avg_total_ms += interval_ms;
- max_avg_period_ms = CONFIG_NETUTILS_PTPD_DRIFT_AVERAGE_S
- * MSEC_PER_SEC;
- if (state->drift_avg_total_ms > max_avg_period_ms)
- {
- state->drift_avg_total_ms = max_avg_period_ms;
- }
- state->drift_ppb += (drift_ppb - state->drift_ppb) * interval_ms
- / state->drift_avg_total_ms;
- /* Compute the value we need to give to adjtime() to match the
- * drift rate.
- */
- adjustment_ns = state->drift_ppb * CONFIG_CLOCK_ADJTIME_PERIOD_MS
- / MSEC_PER_SEC;
- /* Drift estimation ensures local clock runs at same rate as remote.
- *
- * Adding the current clock offset to adjustment brings the clocks
- * to match. To avoid individual outliers from causing jitter, we
- * take the larger signed value of two previous deltas. This is based
- * on the logic that packets can get delayed in transit, but do not
- * travel backwards in time.
- *
- * Clock offset is applied over ADJTIME_PERIOD. If there is significant
- * noise in measurements, increasing ADJTIME_PERIOD will reduce its
- * effect on the local clock run rate.
- */
- if (state->last_delta_ns > delta_ns)
- {
- adjustment_ns += state->last_delta_ns;
- }
- else
- {
- adjustment_ns += delta_ns;
- }
- /* Apply adjustment and store information for next time */
- state->last_delta_ns = delta_ns;
- state->last_delta_timestamp = *local_timestamp;
- state->last_adjtime_ns = adjustment_ns;
- ESP_LOGD(TAG, "Delta: %+lld ns, adjustment %+lld ns, drift rate %+lld ppb",
- (long long)delta_ns,
- (long long)state->last_adjtime_ns,
- (long long)state->drift_ppb);
- ret = ptp_adjtime(state, adjustment_ns);
- if (ret != OK)
- {
- ptperr("ptp_adjtime() failed: %d\n", errno);
- }
- /* Check if clock is stable enough for sending delay requests */
- if (absdelta_ns < CONFIG_NETUTILS_PTPD_MAX_PATH_DELAY_NS)
- {
- state->can_send_delayreq = true;
- }
- #endif // ESP_PTP
- }
- return ret;
- }
- /* Process received PTP sync packet */
- static int ptp_process_sync(FAR struct ptp_state_s *state,
- FAR struct ptp_sync_s *msg)
- {
- struct timespec remote_time;
- if (memcmp(msg->header.sourceidentity,
- state->selected_source.header.sourceidentity,
- sizeof(msg->header.sourceidentity)) != 0)
- {
- /* This packet wasn't from the currently selected source */
- #ifdef ESP_PTP
- ESP_LOGD(TAG, "This packet wasn't from the currently selected source");
- #endif // ESP_PTP
- return OK;
- }
- /* Update timeout tracking */
- clock_gettime(CLOCK_MONOTONIC, &state->last_received_sync);
- if (msg->header.flags[0] & PTP_FLAGS0_TWOSTEP)
- {
- /* We need to wait for a follow-up packet before setting the clock. */
- state->twostep_rxtime = state->rxtime;
- state->twostep_packet = *msg;
- ESP_LOGD(TAG, "Waiting for follow-up");
- return OK;
- }
- /* Update local clock */
- ptp_format_to_timespec(msg->origintimestamp, &remote_time);
- return ptp_update_local_clock(state, &remote_time, &state->rxtime);
- }
- static int ptp_process_followup(FAR struct ptp_state_s *state,
- FAR struct ptp_follow_up_s *msg)
- {
- struct timespec remote_time;
- if (memcmp(msg->header.sourceidentity,
- state->twostep_packet.header.sourceidentity,
- sizeof(msg->header.sourceidentity)) != 0)
- {
- return OK; /* This packet wasn't from the currently selected source */
- }
- if (ptp_get_sequence(&msg->header)
- != ptp_get_sequence(&state->twostep_packet.header))
- {
- ptpwarn("PTP follow-up packet sequence %ld does not match initial "
- "sync packet sequence %ld, ignoring\n",
- (long)ptp_get_sequence(&msg->header),
- (long)ptp_get_sequence(&state->twostep_packet.header));
- return OK;
- }
- /* Update local clock based on the remote timestamp we received now
- * and the local timestamp of when the sync packet was received.
- */
- ptp_format_to_timespec(msg->origintimestamp, &remote_time);
- return ptp_update_local_clock(state, &remote_time, &state->twostep_rxtime);
- }
- static int ptp_process_delay_req(FAR struct ptp_state_s *state,
- FAR struct ptp_delay_req_s *msg)
- {
- struct ptp_delay_resp_s resp;
- #ifndef ESP_PTP
- struct sockaddr_in addr;
- #endif // !ESP_PTP
- int ret;
- if (state->selected_source_valid)
- {
- /* We are operating as a client, ignore delay requests */
- return OK;
- }
- #ifndef ESP_PTP
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = htonl(PTP_MULTICAST_ADDR);
- addr.sin_port = htons(PTP_UDP_PORT_INFO);
- #endif // !ESP_PTP
- memset(&resp, 0, sizeof(resp));
- resp.header = state->own_identity.header;
- resp.header.messagetype = PTP_MSGTYPE_DELAY_RESP;
- resp.header.messagelength[1] = sizeof(resp);
- timespec_to_ptp_format(&state->rxtime, resp.receivetimestamp);
- memcpy(resp.reqidentity, msg->header.sourceidentity,
- sizeof(resp.reqidentity));
- memcpy(resp.reqportindex, msg->header.sourceportindex,
- sizeof(resp.reqportindex));
- memcpy(resp.header.sequenceid, msg->header.sequenceid,
- sizeof(resp.header.sequenceid));
- resp.header.logmessageinterval = CONFIG_NETUTILS_PTPD_DELAYRESP_INTERVAL;
- #ifdef ESP_PTP
- ret = ptp_net_send(state, &resp, sizeof(resp), NULL);
- /* Also send delay_resp via UDP */
- {
- struct sockaddr_in udp_addr;
- udp_addr.sin_family = AF_INET;
- udp_addr.sin_addr.s_addr = htonl(PTP_MULTICAST_ADDR);
- udp_addr.sin_port = htons(PTP_UDP_PORT_INFO);
- sendto(state->tx_socket, &resp, sizeof(resp), 0,
- (struct sockaddr *)&udp_addr, sizeof(udp_addr));
- }
- #else
- ret = sendto(state->tx_socket, &resp, sizeof(resp), 0,
- (FAR struct sockaddr *)&addr, sizeof(addr));
- #endif // ESP_PTP
- if (ret < 0)
- {
- ptperr("sendto failed: %d", errno);
- }
- else
- {
- clock_gettime(CLOCK_MONOTONIC, &state->last_transmitted_delayresp);
- ESP_LOGD(TAG, "Sent delay resp, seq %ld",
- (long)ptp_get_sequence(&msg->header));
- }
- return ret;
- }
- static int ptp_process_delay_resp(FAR struct ptp_state_s *state,
- FAR struct ptp_delay_resp_s *msg)
- {
- int64_t path_delay;
- int64_t sync_delay;
- struct timespec remote_rxtime;
- uint16_t sequence;
- int interval;
- if (!state->selected_source_valid ||
- memcmp(msg->header.sourceidentity,
- state->selected_source.header.sourceidentity,
- sizeof(msg->header.sourceidentity)) != 0 ||
- memcmp(msg->reqidentity,
- state->own_identity.header.sourceidentity,
- sizeof(msg->reqidentity)) != 0)
- {
- return OK; /* This packet wasn't for us */
- }
- sequence = ptp_get_sequence(&msg->header);
- if (sequence != state->delay_req_seq)
- {
- ptpwarn("Ignoring out-of-sequence delay resp (%d vs. expected %d)\n",
- (int)sequence, (int)state->delay_req_seq);
- return OK;
- }
- /* Path delay is calculated as the average between delta for sync
- * message and delta for delay req message.
- * (IEEE-1588 section 11.3: Delay request-response mechanism)
- */
- ptp_format_to_timespec(msg->receivetimestamp, &remote_rxtime);
- path_delay = timespec_delta_ns(&remote_rxtime, &state->delayreq_time);
- sync_delay = state->path_delay_ns - state->last_delta_ns;
- path_delay = (path_delay + sync_delay) / 2;
- if (path_delay >= 0 && path_delay < CONFIG_NETUTILS_PTPD_MAX_PATH_DELAY_NS)
- {
- if (state->path_delay_avgcount <
- CONFIG_NETUTILS_PTPD_DELAYREQ_AVGCOUNT)
- {
- state->path_delay_avgcount++;
- }
- state->path_delay_ns += (path_delay - state->path_delay_ns)
- / state->path_delay_avgcount;
- ESP_LOGD(TAG, "Path delay: %ld ns (avg: %ld ns)",
- (long)path_delay, (long)state->path_delay_ns);
- }
- else
- {
- ESP_LOGD(TAG, "Path delay out of range: %lld ns",
- (long long)path_delay);
- }
- /* Calculate interval until next packet */
- if (msg->header.logmessageinterval <= 12)
- {
- interval = (1 << msg->header.logmessageinterval);
- }
- else
- {
- interval = 4096; /* Refuse to obey excessively long intervals */
- }
- /* Randomize up to 2x nominal delay) */
- state->delayreq_interval = interval + (random() % interval);
- return OK;
- }
- /* Determine received packet type and process it */
- static int ptp_process_rx_packet(FAR struct ptp_state_s *state,
- ssize_t length)
- {
- if (length < sizeof(struct ptp_header_s))
- {
- ptpwarn("Ignoring invalid PTP packet, length only %d bytes\n",
- (int)length);
- return OK;
- }
- if (state->rxbuf.header.domain != CONFIG_NETUTILS_PTPD_DOMAIN)
- {
- /* Part of different clock domain, ignore */
- return OK;
- }
- clock_gettime(CLOCK_MONOTONIC, &state->last_received_multicast);
- switch (state->rxbuf.header.messagetype & PTP_MSGTYPE_MASK)
- {
- #ifdef CONFIG_NETUTILS_PTPD_CLIENT
- case PTP_MSGTYPE_ANNOUNCE:
- ESP_LOGD(TAG, "Got announce packet, seq %ld",
- (long)ptp_get_sequence(&state->rxbuf.header));
- return ptp_process_announce(state, &state->rxbuf.announce);
- case PTP_MSGTYPE_SYNC:
- ESP_LOGD(TAG, "Got sync packet, seq %ld",
- (long)ptp_get_sequence(&state->rxbuf.header));
- return ptp_process_sync(state, &state->rxbuf.sync);
- case PTP_MSGTYPE_FOLLOW_UP:
- ESP_LOGD(TAG, "Got follow-up packet, seq %ld",
- (long)ptp_get_sequence(&state->rxbuf.header));
- return ptp_process_followup(state, &state->rxbuf.follow_up);
- case PTP_MSGTYPE_DELAY_RESP:
- ESP_LOGD(TAG, "Got delay-resp, seq %ld",
- (long)ptp_get_sequence(&state->rxbuf.header));
- return ptp_process_delay_resp(state, &state->rxbuf.delay_resp);
- #endif
- #ifdef CONFIG_NETUTILS_PTPD_SERVER
- case PTP_MSGTYPE_DELAY_REQ:
- ESP_LOGD(TAG, "Got delay req, seq %ld",
- (long)ptp_get_sequence(&state->rxbuf.header));
- return ptp_process_delay_req(state, &state->rxbuf.delay_req);
- #endif
- default:
- ptpinfo("Ignoring unknown PTP packet type: 0x%02x\n",
- state->rxbuf.header.messagetype);
- return OK;
- }
- }
- /* Signal handler for status / stop requests */
- #ifndef ESP_PTP
- static void ptp_signal_handler(int signo, FAR siginfo_t *siginfo,
- FAR void *context)
- {
- FAR struct ptp_state_s *state = (FAR struct ptp_state_s *)siginfo->si_user;
- if (signo == SIGHUP)
- {
- state->stop = true;
- }
- else if (signo == SIGUSR1 && siginfo->si_value.sival_ptr)
- {
- state->status_req =
- *(FAR struct ptpd_statusreq_s *)siginfo->si_value.sival_ptr;
- }
- }
- static void ptp_setup_sighandlers(FAR struct ptp_state_s *state)
- {
- struct sigaction act;
- act.sa_sigaction = ptp_signal_handler;
- sigfillset(&act.sa_mask);
- act.sa_flags = SA_SIGINFO;
- act.sa_user = state;
- sigaction(SIGHUP, &act, NULL);
- sigaction(SIGUSR1, &act, NULL);
- }
- #endif // !ESP_PTP
- /* Process status information request */
- static void ptp_process_statusreq(FAR struct ptp_state_s *state)
- {
- FAR struct ptpd_status_s *status;
- if (!state->status_req.dest)
- {
- return; /* No active request */
- }
- status = state->status_req.dest;
- status->clock_source_valid = state->selected_source_valid;
- if (status->clock_source_valid)
- {
- /* Copy relevant parts of announce info to status struct */
- FAR struct ptp_announce_s *s = &state->selected_source;
- memcpy(status->clock_source_info.id,
- s->header.sourceidentity,
- sizeof(status->clock_source_info.id));
- status->clock_source_info.utcoffset =
- (int16_t)(((uint16_t)s->utcoffset[0] << 8) | s->utcoffset[1]);
- status->clock_source_info.priority1 = s->gm_priority1;
- status->clock_source_info.clockclass = s->gm_quality[0];
- status->clock_source_info.accuracy = s->gm_quality[1];
- status->clock_source_info.priority2 = s->gm_priority2;
- status->clock_source_info.variance =
- ((uint16_t)s->gm_quality[2] << 8) | s->gm_quality[3];
- memcpy(status->clock_source_info.gm_id,
- s->gm_identity,
- sizeof(status->clock_source_info.gm_id));
- status->clock_source_info.stepsremoved =
- ((uint16_t)s->stepsremoved[0] << 8) | s->stepsremoved[1];
- status->clock_source_info.timesource = s->timesource;
- }
- /* Copy latest adjustment info */
- status->last_clock_update = state->last_delta_timestamp;
- status->last_delta_ns = state->last_delta_ns;
- status->last_adjtime_ns = state->last_adjtime_ns;
- status->drift_ppb = state->drift_ppb;
- status->path_delay_ns = state->path_delay_ns;
- /* Copy timestamps */
- status->last_received_multicast = state->last_received_multicast;
- status->last_received_announce = state->last_received_announce;
- status->last_received_sync = state->last_received_sync;
- status->last_transmitted_sync = state->last_transmitted_sync;
- status->last_transmitted_announce = state->last_transmitted_announce;
- status->last_transmitted_delayresp = state->last_transmitted_delayresp;
- status->last_transmitted_delayreq = state->last_transmitted_delayreq;
- /* Post semaphore to inform that we are done */
- if (state->status_req.done)
- {
- sem_post(state->status_req.done);
- }
- state->status_req.done = NULL;
- state->status_req.dest = NULL;
- }
- /* Main PTPD task */
- #ifdef ESP_PTP
- static void ptp_daemon(void *task_param)
- #else
- static int ptp_daemon(int argc, FAR char** argv)
- #endif // ESP_PTP
- {
- FAR const char *interface = "eth0";
- FAR struct ptp_state_s *state;
- #ifdef ESP_PTP
- struct pollfd pollfds[3]; /* L2 TAP + UDP event (319) + UDP info (320) */
- #else
- struct pollfd pollfds[2];
- struct msghdr rxhdr;
- struct iovec rxiov;
- #endif // ESP_PTP
- int ret;
- #ifndef ESP_PTP
- memset(&rxhdr, 0, sizeof(rxhdr));
- memset(&rxiov, 0, sizeof(rxiov));
- #endif // !ESP_PTP
- state = calloc(1, sizeof(struct ptp_state_s));
- #ifdef ESP_PTP
- if (task_param != NULL)
- {
- interface = task_param;
- }
- #else
- if (argc > 1)
- {
- interface = argv[1];
- }
- #endif // ESP_PTP
- if (ptp_initialize_state(state, interface) != OK)
- {
- ptperr("Failed to initialize PTP state, exiting\n");
- ptp_destroy_state(state);
- free(state);
- #ifdef ESP_PTP
- goto err;
- #else
- return ERROR;
- #endif // ESP_PTP
- }
- #ifndef ESP_PTP
- ptp_setup_sighandlers(state);
- #endif // !ESP_PTP
- pollfds[0].events = POLLIN;
- #ifdef ESP_PTP
- /* Poll L2 TAP (for HW timestamps) and UDP event socket (for
- * interoperability with devices that send PTP over UDP/IPv4).
- * The info socket (port 320) is also polled for announce and
- * follow-up messages. */
- pollfds[0].fd = state->ptp_socket;
- pollfds[1].events = POLLIN;
- pollfds[1].fd = state->event_socket;
- pollfds[2].events = POLLIN;
- pollfds[2].fd = state->info_socket;
- #else
- pollfds[0].fd = state->event_socket;
- pollfds[1].events = POLLIN;
- pollfds[1].fd = state->info_socket;
- #endif // ESP_PTP
- while (!state->stop)
- {
- state->can_send_delayreq = false;
- #ifndef ESP_PTP
- rxhdr.msg_name = NULL;
- rxhdr.msg_namelen = 0;
- rxhdr.msg_iov = &rxiov;
- rxhdr.msg_iovlen = 1;
- rxhdr.msg_control = &state->rxcmsg;
- rxhdr.msg_controllen = sizeof(state->rxcmsg);
- rxhdr.msg_flags = 0;
- rxiov.iov_base = &state->rxbuf;
- rxiov.iov_len = sizeof(state->rxbuf);
- #endif // !ESP_PTP
- pollfds[0].revents = 0;
- pollfds[1].revents = 0;
- #ifdef ESP_PTP
- pollfds[2].revents = 0;
- ret = poll(pollfds, 3, PTPD_POLL_INTERVAL);
- #else
- ret = poll(pollfds, 2, PTPD_POLL_INTERVAL);
- #endif
- if (pollfds[0].revents)
- {
- /* Receive time-critical packet, potentially with cmsg
- * indicating the timestamp.
- */
- #ifdef ESP_PTP
- ret = ptp_net_recv(state, &state->rxbuf, sizeof(state->rxbuf), &state->rxtime);
- #else
- ret = recvmsg(state->event_socket, &rxhdr, MSG_DONTWAIT);
- #endif // ESP_PTP
- if (ret > 0)
- {
- #ifndef ESP_PTP
- ptp_getrxtime(state, &rxhdr, &state->rxtime);
- #endif
- ptp_process_rx_packet(state, ret);
- }
- }
- #ifdef ESP_PTP
- if (pollfds[1].revents)
- {
- /* Receive PTP packet from UDP event socket (port 319:
- * sync, delay_req - time-critical). */
- ret = recv(state->event_socket, &state->rxbuf, sizeof(state->rxbuf),
- MSG_DONTWAIT);
- if (ret > 0)
- {
- ptp_gettime(state, &state->rxtime);
- ptp_process_rx_packet(state, ret);
- }
- }
- if (pollfds[2].revents)
- {
- /* Receive PTP packet from UDP info socket (port 320:
- * announce, follow_up, delay_resp - non-time-critical). */
- ret = recv(state->info_socket, &state->rxbuf, sizeof(state->rxbuf),
- MSG_DONTWAIT);
- if (ret > 0)
- {
- ptp_gettime(state, &state->rxtime);
- ptp_process_rx_packet(state, ret);
- }
- }
- #else
- if (pollfds[1].revents)
- {
- /* Receive non-time-critical packet. */
- ret = recv(state->info_socket, &state->rxbuf, sizeof(state->rxbuf),
- MSG_DONTWAIT);
- if (ret > 0)
- {
- ptp_process_rx_packet(state, ret);
- }
- }
- if (pollfds[0].revents == 0 && pollfds[1].revents == 0)
- {
- /* No packets received, check for multicast timeout */
- ptp_check_multicast_status(state);
- }
- #endif // !ESP_PTP
- ptp_periodic_send(state);
- state->selected_source_valid = is_selected_source_valid(state);
- ptp_process_statusreq(state);
- #ifdef ESP_PTP
- /* Yield to lower-priority tasks (IDLE watchdog) after each iteration.
- * poll() may return immediately if the L2 TAP socket has pending events,
- * which would otherwise starve the IDLE task and trigger the watchdog. */
- vTaskDelay(1);
- #endif
- }
- ptp_destroy_state(state);
- free(state);
- #ifdef ESP_PTP
- err:
- s_state = NULL;
- vTaskDelete(NULL);
- #else
- return 0;
- #endif // ESP_PTP
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: ptpd_start
- *
- * Description:
- * Start the PTP daemon and bind it to specified interface.
- *
- * Input Parameters:
- * interface - Name of the network interface to bind to, e.g. "eth0"
- *
- * Returned Value:
- * On success, the non-negative task ID of the PTP daemon is returned;
- * On failure, a negated errno value is returned.
- *
- ****************************************************************************/
- int ptpd_start(FAR const char *interface)
- {
- #ifdef ESP_PTP
- if (s_state == NULL) {
- xTaskCreate(ptp_daemon, "PTPD", CONFIG_NETUTILS_PTPD_STACKSIZE,
- (void *)interface, tskIDLE_PRIORITY + 2, NULL);
- return 1;
- }
- ESP_LOGE(TAG, "Other instance of PTP is already running");
- return -1;
- #else
- int pid;
- FAR char *task_argv[] = {
- (FAR char *)interface,
- NULL
- };
- pid = task_create("PTPD", CONFIG_NETUTILS_PTPD_SERVERPRIO,
- CONFIG_NETUTILS_PTPD_STACKSIZE, ptp_daemon, task_argv);
- /* Use kill with signal 0 to check if the process is still alive
- * after initialization.
- */
- usleep(USEC_PER_TICK);
- if (kill(pid, 0) != OK)
- {
- return ERROR;
- }
- else
- {
- return pid;
- }
- #endif // ESP_PTP
- }
- /****************************************************************************
- * Name: ptpd_status
- *
- * Description:
- * Query status from a running PTP daemon.
- *
- * Input Parameters:
- * pid - Process ID previously returned by ptpd_start()
- * status - Pointer to storage for status information.
- *
- * Returned Value:
- * On success, returns OK.
- * On failure, a negated errno value is returned.
- *
- * Assumptions/Limitations:
- * Multiple threads with priority less than CONFIG_NETUTILS_PTPD_SERVERPRIO
- * can request status simultaneously. If higher priority threads request
- * status simultaneously, some of the requests may timeout.
- *
- ****************************************************************************/
- int ptpd_status(int pid, FAR struct ptpd_status_s *status)
- {
- #ifdef ESP_PTP
- int ret = 0;
- sem_t donesem;
- struct ptpd_statusreq_s req;
- struct timespec timeout;
- /* Fill in the status request */
- memset(status, 0, sizeof(struct ptpd_status_s));
- sem_init(&donesem, 0, 0);
- req.done = &donesem;
- req.dest = status;
- s_state->status_req = req;
- /* Wait for status request to be handled */
- clock_gettime(CLOCK_REALTIME, &timeout); // sem_timedwait uses CLOCK_REALTIME
- timeout.tv_sec += 1;
- if (sem_timedwait(&donesem, &timeout) != 0)
- {
- req.done = NULL;
- req.dest = NULL;
- s_state->status_req = req;
- ret = -errno;
- }
- sem_destroy(&donesem);
- return ret;
- #endif
- #ifndef CONFIG_BUILD_FLAT
- /* TODO: Use SHM memory to pass the status information if processes
- * do not share the same memory space.
- */
- return -ENOTSUP;
- #else
- int ret = OK;
- sem_t donesem;
- struct ptpd_statusreq_s req;
- union sigval val;
- struct timespec timeout;
- /* Fill in the status request */
- memset(status, 0, sizeof(struct ptpd_status_s));
- sem_init(&donesem, 0, 0);
- req.done = &donesem;
- req.dest = status;
- val.sival_ptr = &req;
- if (sigqueue(pid, SIGUSR1, val) != OK)
- {
- return -errno;
- }
- /* Wait for status request to be handled */
- clock_gettime(CLOCK_MONOTONIC, &timeout);
- timeout.tv_sec += 1;
- if (sem_clockwait(&donesem, CLOCK_MONOTONIC, &timeout) != 0)
- {
- ret = -errno;
- }
- return ret;
- #endif /* CONFIG_BUILD_FLAT */
- }
- /****************************************************************************
- * Name: ptpd_stop
- *
- * Description:
- * Stop PTP daemon
- *
- * Input Parameters:
- * pid - Process ID previously returned by ptpd_start()
- *
- * Returned Value:
- * On success, returns OK.
- * On failure, a negated errno value is returned.
- *
- ****************************************************************************/
- int ptpd_stop(int pid)
- {
- #ifdef ESP_PTP
- s_state->stop = true;
- return OK;
- #else
- if (kill(pid, SIGHUP) == OK)
- {
- return OK;
- }
- else
- {
- return -errno;
- }
- #endif
- }
|