When ptpd runs over IEEE 802.3 (-2) with hardware timestamping and
ETHTOOL_GET_TS_INFO does not report SOF_TIMESTAMPING_TX_HARDWARE,
SOF_TIMESTAMPING_RX_HARDWARE and SOF_TIMESTAMPING_RAW_HARDWARE for the
interface, refuse to start instead of logging a warning and running in
software - the same way linuxptp/ptp4l refuses to start when hardware
timestamping is configured but not reported as supported by ethtool,
rather than silently degrading. The error message names the missing
capability and points to -S, and the usage text documents the
requirement.
Hardware RX timestamps are required as well: without them the receive
timestamps come from the system clock while the transmit ones come from
the MAC, and the two cannot be combined into a meaningful delay.
The check is limited to the 802.3 transport, the only one on which ptpd
retrieves hardware TX timestamps. -H is the default with
CONFIG_NET_TIMESTAMP, so applying it to the UDP transports would make a
plain "ptpd" refuse to start on any interface whose driver does not
report hardware timestamping, although it never needs that capability.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
Query interface hardware timestamping capabilities with the SIOCETHTOOL
ETHTOOL_GET_TS_INFO ioctl during initialization, the same way
linuxptp/ptp4l does on Linux, instead of detecting support through
runtime trial and error. If the query itself fails, refuse to start.
Remove the consecutive failure counter (hwts_tx_failures,
PTP_HWTS_TX_MAX_FAILURES, hwts_tx_disabled). When hardware TX
timestamping is supported and requested, report genuine runtime timeouts
as errors (ptperr) instead of silently downgrading to software
timestamping. Invalidate clock_source_valid in ptpd status while a
hardware TX timestamp failure persists.
Assisted-by: Gemini:gemini-3.8-pro
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
The frame leaves the MAC later than the moment its hardware transmit
timestamp is latched, because of the clock domain crossing and the PHY.
This fixed delay is the egressLatency port parameter of IEEE 1588.
Add the configured latency to every hardware transmit timestamp
obtained through MSG_ERRQUEUE, the counterpart of the ingress
compensation.
- Add CONFIG_NETUTILS_PTPD_EGRESS_LATENCY_NS (default 0, which applies
no compensation).
- Add the -O option to override it at run time.
- Add egress_latency_ns to struct ptpd_config_s.
Software timestamps are not affected.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
Timestamp transmitted event messages with the hardware clock instead of
reading the clock after sendmsg() returns.
When hardware timestamping is selected on an AF_PACKET socket, request
SOF_TIMESTAMPING_TX_HARDWARE for each event message, wait for the
looped-back packet on the error queue with MSG_ERRQUEUE, and take the
timestamp from its SO_TIMESTAMPING control message, as on Linux. Sync,
Delay_Req and Pdelay_Req get their real departure time.
- Use a transmit socket of its own, separate from the event socket, so
the error queue is not shared with received packets.
- Handle POLLERR separately from POLLIN in the main loop and drain all
pending packets on each wakeup.
- If the timestamp does not arrive, fall back to a software timestamp
taken before the frame is sent. After three consecutive failures the
driver is assumed not to provide hardware transmit timestamps, a
warning is printed once and only software timestamps are used, so a
driver without support does not stall the daemon.
- Take the software timestamp before sending in every mode. It used to
be taken after sendmsg() returned, so a fast peer's reply could appear
to arrive before the request had left and give a negative delay.
- Accept a measured path delay down to -100 microseconds and clamp it to
zero, since hardware timestamps on both ends can make a short link
measure slightly negative.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
A single Sync sample whose receive timestamp was taken late, for
example because the task was scheduled late with software
timestamping, was fed straight into the phase correction and the
drift estimate, and could pull the clock away from the master.
- Add CONFIG_NETUTILS_PTPD_OUTLIER_THRESHOLD_NS (default 0, which
disables the check). A phase error that differs by more than this
many nanoseconds from the median of the last five accepted samples
is discarded, with a warning.
- Accept the sample after eight consecutive rejections and restart
the history from it, so that a real step of the master is still
followed while a short burst of disturbed samples is ridden out.
- Restart the history whenever the clock is stepped, since the old
samples no longer describe the new time base.
- With the default of 0 the behaviour is unchanged.
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
Assisted-by: Claude:claude-sonnet-5
PR #3789 replaced the in-memory sigqueue + shared memory IPC in ptpd_status()
with file-based IPC to support Protected and Kernel modes across address
spaces. However, on microcontrollers running CONFIG_BUILD_FLAT, a filesystem
or /tmp (TMPFS) is rarely mounted or available, causing ptpd_status() to fail
with -ETIMEDOUT (errno 110) because the status file cannot be created.
Retain the file-based IPC for !CONFIG_BUILD_FLAT (Protected and Kernel modes)
while restoring the zero-overhead in-memory sigqueue + semaphore IPC for
CONFIG_BUILD_FLAT. Both modes share the status serialization logic via
ptp_populate_status() and support all fields including P2P.
Assisted-by: Gemini:gemini-3.8-flash-medium
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
The MAC latches a hardware receive timestamp later than the frame
reaches the wire, because of the PHY and the clock domain crossing.
This fixed delay is the ingressLatency port parameter of IEEE 1588 and
shows up as a constant phase error between the local and the master
clock.
Subtract the configured latency from every hardware receive timestamp
in ptp_getrxtime(), the single place where they enter the daemon, so
Sync, Delay_Resp and the peer delay messages are all corrected.
- Add CONFIG_NETUTILS_PTPD_INGRESS_LATENCY_NS (default 0, which applies
no compensation).
- Add the -I option to override it at run time.
- Add ingress_latency_ns to struct ptpd_config_s.
Software timestamps are not affected.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
When state->clockid is configured to a hardware PTP clock device
(e.g., /dev/ptp0) instead of CLOCK_REALTIME, ptp_adjtime() previously
passed only the measured frequency drift (-ppb) to clock_adjtime(),
ignoring the residual phase offset (delta_ns / adjustment_ns).
As a result, while the hardware counter tracked frequency, its phase
was never pulled into alignment with the master clock.
Convert delta_ns (which combines frequency drift and current phase error
clamped to max_adjust_ns) to ppb over CONFIG_CLOCK_ADJTIME_PERIOD_MS,
acting as a proportional-integral (PI) phase servo. This drives the
hardware clock to phase lock with the master via POSIX clock_adjtime()
using ADJ_FREQUENCY without requiring proprietary ioctl calls.
Assisted-by: Claude:claude-sonnet-5
Assisted-by: Gemini:gemini-3.8-flash-medium
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
A single drift-rate sample computed between two consecutive sync
updates was clamped against CLOCK_ADJTIME_SLEWLIMIT_PPM - the
hardware's slew-rate safety limit, not a bound on how large a real
crystal-oscillator drift measurement can plausibly be. An abnormally
short or long measurement interval (e.g. right after a clock
source outage/reconnect, or a burst of closely spaced sync packets
following packet loss) could therefore produce a wildly implausible
sample that still passed the check and corrupted the long-term
drift_ppb average.
Add CONFIG_NETUTILS_PTPD_MAX_DRIFT_PPB (default 500000, well above
any real crystal's few-hundred-ppm drift) as a dedicated plausibility
bound, intentionally much tighter than CLOCK_ADJTIME_SLEWLIMIT_PPM.
A sample outside this bound is discarded and the previous averaged
drift_ppb is kept unchanged instead of being corrupted.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
Implements the Peer-to-Peer (P2P) transparent clock delay measurement
mechanism (IEEE 1588-2008 §11.4 / IEEE 802.1AS / IEC/IEEE 61850-9-3)
in apps/netutils/ptpd:
- Add PTP_MSGTYPE_PDELAY_REQ, PTP_MSGTYPE_PDELAY_RESP, and
PTP_MSGTYPE_PDELAY_RESP_FOLLOW_UP definitions and structs in ptpv2.h.
- Define IEEE 1588-2008 Annex F peer delay multicast MAC address
01:80:c2:00:00:0e and Annex D peer delay IP address 224.0.0.107.
- Replace bool delay_e2e with enum ptp_delay_mechanism_e (PTP_DELAY_NONE,
PTP_DELAY_E2E, PTP_DELAY_P2P) in include/netutils/ptpd.h.
- Add -P CLI option in system/ptpd/ptpd_main.c with mutual exclusion
check against -E, and display last_transmitted_pdelayreq in status.
- Implement responder logic in ptp_process_pdelay_req() sending
Pdelay_Resp (t2) and Pdelay_Resp_Follow_Up (t3) regardless of master
or slave state.
- Implement requester logic in ptp_send_pdelay_req() gated on the
physical link without requiring prior BMCA master selection.
- Implement ptp_process_pdelay_resp() and
ptp_process_pdelay_resp_followup() using canonical mean path delay
formula ((t4 - t1) - (t3 - t2)) / 2.
- Refactor path delay bounds checking and moving average filter into
ptp_record_path_delay() shared across E2E and P2P mechanisms.
- Set PTP version 2.0 and controlField 0x05 in Pdelay_Req, Pdelay_Resp
and Pdelay_Resp_Follow_Up, and in the own-identity header, so that
peers such as linuxptp accept the messages.
- Clear pdelay_waiting_followup when a new Pdelay_Req is sent, so an
orphaned Pdelay_Resp_Follow_Up from an abandoned cycle is not paired
with stale timestamps.
- Warn at startup when P2P is selected without CONFIG_SCHED_TICKLESS,
since a tick-driven clock cannot resolve the peer delay.
- Skip IP multicast join/leave handling for AF_PACKET.
Assisted-by: Claude:claude-sonnet-5
Assisted-by: Gemini:gemini-3.8-flash-medium
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
ptp_sendmsg() called a blocking recvmsg(state->tx_socket, ...) right
after sending a Delay_Req whenever hardware_ts was set, assuming a
Linux-style MSG_ERRQUEUE/loopback semantics NuttX does not have.
Since tx_socket and event_socket share the same underlying
connection, this call instead blocked on and consumed whatever PTP
packet arrived next on the wire — almost always the Delay_Resp,
which typically arrives within milliseconds of the request. Its
payload was read into a local buffer that went out of scope on
return, so the packet never reached ptp_process_rx_packet() and
path_delay_ns stayed at 0 in -H mode. t3 is now captured locally
via ptp_gettime(), the same way -S mode already did, until
hardware TX timestamping is supported.
Also replaces the path delay heuristic in ptp_process_delay_resp()
(which derived an approximation of (t2-t1) from path_delay_ns and
last_delta_ns, only valid once the clock had already converged) with
the canonical IEEE 1588-2008 §11.3 formula: store (t2-t1) directly
from Sync/Follow_Up as sync_diff_ns, then average it with (t4-t3)
from the Delay_Req/Delay_Resp exchange. Relaxes the path delay
ceiling to 10ms unconditionally, since Delay_Req's t3 is software-
timestamped in both modes until hardware TX timestamping is supported.
Assisted-by: Claude:claude-sonnet-5
Assisted-by: Gemini:gemini-3.8-flash-medium
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
Replace the sigqueue + shared-memory IPC mechanism in ptpd_status()
with a file-based approach:
- Daemon side: on SIGUSR1, write a binary ptpd_status_s struct to
a temp file and atomically rename it to the status path.
- Client side: send kill(SIGUSR1), poll for the file to appear,
then read the struct back.
This removes the CONFIG_BUILD_FLAT restriction (the old code returned
-ENOTSUP for Protected and Kernel builds) and avoids passing pointers
across address spaces via sigqueue. The status file path is
configurable via NETUTILS_PTPD_STATUSFILE (default /tmp/ptpd.status).
The atomic temp + rename pattern ensures readers never see a partial
write.
Signed-off-by: wenquan1 <wenquan1@xiaomi.com>
The IGMP multicast join (ipmsfilter) was previously called before
the interface address (interface_addr) was populated via SIOCGIFADDR.
This meant the IGMP join had to locate the network device without
a valid local address, which could fail or join on the wrong interface.
Move the multicast group subscription to after the interface address
is queried, and guard it with an AF_INET check since IGMP only applies
to IPv4. This ensures the IGMP join can always locate the correct
network device.
Signed-off-by: wenquan1 <wenquan1@xiaomi.com>
Add ptpinfo()/ptpwarn() calls, gated by the existing
CONFIG_DEBUG_PTP_INFO/_WARN symbols (zero cost when disabled), at
points that previously failed silently: an unrecognized L2 protocol,
a domain mismatch, and a Delay_Resp rejected by the source/requester
identity check. These were essential to diagnosing the drift and
Delay_Req bugs fixed in the two preceding commits on real hardware,
and are kept for future maintainers debugging this path.
Assisted-by: Claude:claude-sonnet-5
Assisted-by: Gemini:gemini-3.8-flash-medium
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
1. On AF_PACKET SOCK_RAW sockets in NuttX, msg_name must be NULL because
the destination MAC address is already contained in the Ethernet header.
Previously, passing sockaddr_in caused sendmsg() to fail immediately
with -EAFNOSUPPORT, completely blocking transmission of Delay_Req.
2. Correct PTP primary multicast MAC address to 01:1b:19:00:00:00
(IEEE 1588 Annex F) and ensure ether_type is in network byte order.
3. Initialize delayreq_interval to 1 second default and guard against 0.
4. Set logmessageinterval to 0x7f (IEEE 1588-2008 Table 23 sentinel for
Delay_Req) instead of inheriting 0 from the announce header template.
5. Update PTP version to 0x12 (2.1, minorVersionPTP=1) to match the
value used by mature implementations such as linuxptp.
Assisted-by: Claude:claude-sonnet-5
Assisted-by: Gemini:gemini-3.8-flash-medium
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
1. Post-jump drift bootstrap: on the first Sync packet following a step
jump, do not compute frequency drift against a synthesized zero delta,
which previously caused the entire residual phase offset (~ms) to be
mistaken for frequency drift (~million ppb) and immediately absorbed.
2. Drift rate formula: normalize the adjustment contribution by the actual
measurement interval instead of the adjtime slew period, and compute
natural delta rate as (delta - last_delta + last_adjtime) / interval.
3. Remove broken last_delta > delta comparison that prevented offsets from
converging and applied inverted corrections on negative overshoots.
4. Correct ptp_adjtime() invocation to always pass adjustment_ns for
CLOCK_REALTIME slewing rather than dropping drift compensation when
delta exceeds threshold. Clamp adjustment_ns to the hardware slew limit
so last_adjtime_ns accurately mirrors the true slew applied.
5. Enable Delay_Req in E2E mode once clock is tracking (not jumping) and
allow software timestamping latency in ptp_process_delay_resp().
6. Propagate initialization return code from ptpd_start() and avoid
unconditional failure print in do_ptpd_start().
Assisted-by: Claude:claude-sonnet-5
Assisted-by: Gemini:gemini-3.8-flash-medium
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
Now that time_t is unconditionally 64-bit (signed int64_t) and the
struct timespec fields tv_sec / tv_nsec are wide enough on their own,
the explicit (uint64_t)/(int64_t)/(int) casts that used to guard the
multiplications and subtractions in *_us / *_ms / *_ns helpers are no
longer needed. Drop them to keep the timekeeping math readable.
In the same spirit, this commit also normalises the printf-style format
specifiers and casts used to print tv_sec / tv_nsec / tv_usec values.
The prior code was a mix of "%d"/"%u"/"%ld"/"%lu"/"%lld" with matching
(int)/(unsigned long)/(long long) casts; some formats truncated time_t
on 32-bit hosts, others mismatched signedness or width. Replace all
such cases with the portable POSIX-recommended forms:
- tv_sec (time_t, signed, impl-defined width) -> %jd + (intmax_t)
- tv_nsec (long, signed) -> %ld (no cast)
- tv_usec (suseconds_t / long) -> %ld (no cast)
Also drop two stale `(FAR const time_t *)&ts.tv_sec` casts that are
unnecessary now that ts.tv_sec is plain time_t.
Arithmetic-cleanup files (existing scope):
- benchmarks/cyclictest/cyclictest.c: timediff_us()
- benchmarks/sd_bench/sd_bench_main.c: get_time_delta_us()
- examples/oneshot/oneshot_main.c: maxus computation
- examples/watchdog/watchdog_main.c: current_time_ms (x2)
- industry/nxmodbus/nxmb_internal.h: nxmb_util_clock_ms()
- netutils/ntpclient/ntpclient.c: timespec2ntp()
- netutils/ptpd/ptpd.c: ptp_adjtime()
- system/dd/dd_main.c: elapsed accounting
- testing/drivers/drivertest/drivertest_posix_timer.c:
get_timestamp()
- testing/drivers/sd_stress/sd_stress_main.c:get_time_delta()
- testing/sched/getprime/getprime_main.c: elapsed accounting
- testing/sched/pthread_mutex_perf/pthread_mutex_perf.c:
timespec_avg()
Printf-format-fix files (new in this revision):
- examples/adjtime/adjtime_main.c
- examples/charger/charger_main.c
- examples/netpkt/netpkt_ethercat.c
- fsutils/mkfatfs/mkfatfs.c
- graphics/tiff/tiff_initialize.c
- netutils/ptpd/ptpd.c
- nshlib/nsh_timcmds.c
- system/coredump/coredump.c
- system/ptpd/ptpd_main.c
- testing/drivers/drivertest/drivertest_oneshot.c
- testing/mm/kasantest/kasantest.c
- testing/ostest/semtimed.c
- testing/sched/pthread_mutex_perf/pthread_mutex_perf.c
- testing/sched/timerjitter/timerjitter.c
- testing/testsuites/kernel/time/cases/clock_test_clock01.c
- testing/testsuites/kernel/time/cases/clock_test_smoke.c
No behavioural change.
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
NuttX always uses a 64-bit system clock now (time_t/clock_t are
always 64-bit). Remove the obsolete CONFIG_SYSTEM_TIME64 #ifdef
branches and Kconfig dependency.
- examples/dronecan: drop SYSTEM_TIME64 dependency
- examples/netlink_route: always use PRIx64
- netutils/netinit: always use the 1-hour LONG_TIME_SEC
- netutils/ptpd: always pack the 48-bit seconds field and apply
the 64-bit overflow guard in timespec_delta_ns()
- testing/ostest/semtimed: always validate tv_sec >= 0
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
Replace app-side includes of <debug.h> with <nuttx/debug.h> to use the
header from the NuttX tree explicitly after the header move.
Signed-off-by: Piyush Patle <piyushpatle228@gmail.com>
In some scenarios, dynamic memory allocation is not allowed, so it is modified to a static allocation method.
Signed-off-by: gaohedong <gaohedong@xiaomi.com>
The current gPTP stack does not support path delay correction of the Switch.
This patch adds the path delay correction field in the Header.
Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
fix compile warning when only enable PTPD_CLIENT or PTPD_SERVER
ptpd.c:493:38: error: 'CONFIG_NETUTILS_PTPD_PRIORITY1' undeclared (first
use in this function); did you mean 'CONFIG_NETUTILS_PTPD_CLIENT'?
ptpd.c:494:39: error: 'CONFIG_NETUTILS_PTPD_CLASS' undeclared (first use
in this function); did you mean 'CONFIG_NETUTILS_PTPD_CLIENT'?
ptpd.c:495:39: error: 'CONFIG_NETUTILS_PTPD_ACCURACY' undeclared (first
use in this function); did you mean 'CONFIG_NETUTILS_PTPD_DEBUG'?
ptpd.c:498:38: error: 'CONFIG_NETUTILS_PTPD_PRIORITY2' undeclared (first
use in this function); did you mean 'CONFIG_NETUTILS_PTPD_CLIENT'?
ptpd.c:502:36: error: 'CONFIG_NETUTILS_PTPD_CLOCKSOURCE' undeclared
(first use in this function); did you mean
'CONFIG_NETUTILS_PTPD_STACKSIZE'?
Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
The original implementation only checked if each field was less than,
but didn't check for greater than before proceeding to the next field.
This caused incorrect clock selection behavior in the PTP Best Master
Clock Algorithm (BMCA).
The fix expands each comparison to explicitly check both < and >
conditions, returning the appropriate result immediately. This ensures
proper precedence evaluation according to IEEE 1588 specification:
- gm_priority1
- gm_quality (class, accuracy, variance)
- gm_priority2
- gm_identity
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
Fixed bug where negative offsets were not properly handled because
clock_timespec_subtract clamps values to zero.
Implement support for SO_TIMESTAMP to get accurate packet
reception timestamp.
Implemented delay requests for measuring packet transfer delay.
Implemented clock drift estimation to bring the clocks closer to
sync and to filter out measurement jitter.
If multicast PTP packets are not being received, rejoin the multicast group.
This automatically recovers from situations such as rebooting a network switch.
What works:
- Basic server & client operation
- Transmission and reception of announce, sync and follow-up
Still missing:
- SO_TIMINGS for getting more precise packet timestamps
- Implementation of delay_req and delay_resp packets
- Status and stop interfaces for the daemon