From 27c18ab8c53510dd97d099a324a095ace14237be Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Sat, 22 Mar 2025 15:09:48 +0800 Subject: [PATCH 01/11] drivers/timers/ptp: support ptp clock driver model This patch introduces the foundational PTP (Precision Time Protocol) clock driver framework for NuttX, enabling precise time synchronization support based on IEEE 1588 standard. Key changes include: 1. New PTP clock driver infrastructure: - Added drivers/timers/ptp_clock.c implementing upper-half driver - Created include/nuttx/timers/ptp_clock.h with PTP clock interfaces - Implemented upper/lower half driver architecture for hardware abstraction 2. Core functionality: - Clock time get/set operations (gettime, settime) - Frequency adjustment support (adjtime, adjfine) - Phase adjustment capabilities - System-device cross-timestamping for precise synchronization 3. IOCTL commands: - PTP_CLOCK_SETTIME/GETTIME for time manipulation - PTP_CLOCK_GETRES for resolution queries - PTP_CLOCK_ADJTIME for time adjustment - PTP_CLOCK_GETCAPS for capability queries - PTP_SYS_OFFSET* for system offset measurements 4. Supporting structures: - struct ptp_lowerhalf_s: lower-half driver interface - struct ptp_clock_caps: clock capabilities descriptor - struct ptp_sys_offset: system time offset measurement - Added timex structures in include/sys/timex.h for ADJ_* operations 5. Build system integration: - Added CONFIG_PTP_CLOCK Kconfig option - Updated CMakeLists.txt and Make.defs - Added PTPCLK debug macros in include/debug.h This framework provides the base for PTP clock implementations, allowing hardware-specific drivers to register and provide precise time services through a standardized interface. Signed-off-by: dongjiuzhu1 --- Kconfig | 35 +++ drivers/timers/CMakeLists.txt | 4 + drivers/timers/Kconfig | 16 ++ drivers/timers/Make.defs | 4 + drivers/timers/ptp_clock.c | 476 +++++++++++++++++++++++++++++++ include/debug.h | 18 ++ include/nuttx/clock.h | 6 +- include/nuttx/fs/ioctl.h | 8 + include/nuttx/timers/ptp_clock.h | 355 +++++++++++++++++++++++ include/sys/timex.h | 98 +++++++ 10 files changed, 1019 insertions(+), 1 deletion(-) create mode 100644 drivers/timers/ptp_clock.c create mode 100644 include/nuttx/timers/ptp_clock.h create mode 100644 include/sys/timex.h diff --git a/Kconfig b/Kconfig index 962a5255844f8..921cfc33e2fcc 100644 --- a/Kconfig +++ b/Kconfig @@ -2366,6 +2366,41 @@ config DEBUG_PCI_INFO endif # DEBUG_PCI +config DEBUG_PTP + bool "PTP Debug Features" + default n + depends on PTP_CLOCK + ---help--- + Enable PTP driver debug features. + + Support for this debug option is architecture-specific and may not + be available for some MCUs. + +if DEBUG_PTP + +config DEBUG_PTP_ERROR + bool "PTP Error Output" + default n + depends on DEBUG_ERROR + ---help--- + Enable PTP driver error output to SYSLOG. + +config DEBUG_PTP_WARN + bool "PTP Warnings Output" + default n + depends on DEBUG_WARN + ---help--- + Enable PTP driver warning output to SYSLOG. + +config DEBUG_PTP_INFO + bool "PTP Informational Output" + default n + depends on DEBUG_INFO + ---help--- + Enable PTP driver informational output to SYSLOG. + +endif # DEBUG_PTP + config DEBUG_RPMSG bool "RPMSG Debug Features" default n diff --git a/drivers/timers/CMakeLists.txt b/drivers/timers/CMakeLists.txt index 60fdda858e900..0b56569c697d2 100644 --- a/drivers/timers/CMakeLists.txt +++ b/drivers/timers/CMakeLists.txt @@ -90,4 +90,8 @@ if(CONFIG_GOLDFISH_TIMER) list(APPEND SRCS goldfish_timer.c) endif() +if(CONFIG_PTP_CLOCK) + list(APPEND SRCS ptp_clock.c) +endif() + target_sources(drivers PRIVATE ${SRCS}) diff --git a/drivers/timers/Kconfig b/drivers/timers/Kconfig index 8916a22594396..eb47a0a7532e2 100644 --- a/drivers/timers/Kconfig +++ b/drivers/timers/Kconfig @@ -551,4 +551,20 @@ config GOLDFISH_TIMER emulator. It is used to provide a virtual timer to the guest OS. See: https://android.googlesource.com/platform/external/qemu/+/master/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT +config PTP_CLOCK + bool "PTP clock driver" + default n + ---help--- + The IEEE 1588 standard defines a method to precisely + synchronize distributed clocks over Ethernet networks. The + standard defines a Precision Time Protocol (PTP), which can + be used to achieve synchronization within a few dozen + microseconds. In addition, with the help of special hardware + time stamping units, it can be possible to achieve + synchronization to within a few hundred nanoseconds. + + This driver adds support for PTP clocks as character + devices. If you want to use a PTP clock, then you should + also enable at least one clock driver as well. + endmenu # Timer Driver Support diff --git a/drivers/timers/Make.defs b/drivers/timers/Make.defs index ddf2620ffaca3..c8b6d21cdf418 100644 --- a/drivers/timers/Make.defs +++ b/drivers/timers/Make.defs @@ -124,6 +124,10 @@ ifeq ($(CONFIG_GOLDFISH_TIMER),y) TMRVPATH = :timers endif +ifeq ($(CONFIG_PTP_CLOCK),y) + CSRCS += ptp_clock.c +endif + # Include timer build support (if any were selected) DEPPATH += $(TMRDEPPATH) diff --git a/drivers/timers/ptp_clock.c b/drivers/timers/ptp_clock.c new file mode 100644 index 0000000000000..8ad6b3ca42dea --- /dev/null +++ b/drivers/timers/ptp_clock.c @@ -0,0 +1,476 @@ +/**************************************************************************** + * drivers/timers/ptp_clock.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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure describes the state of the upper half driver */ + +struct ptp_upperhalf_s +{ + FAR struct ptp_lowerhalf_s *lower; /* The handle of lower half driver */ + mutex_t lock; /* Manages exclusive access to file operations */ + long max_adj; /* The maximum frequency adjustment */ + long adj_freq; /* remembers the frequency adjustment */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static ssize_t ptp_clock_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static int ptp_clock_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_ptp_clock_file_ops = +{ + NULL, /* open */ + NULL, /* close */ + ptp_clock_read, /* read */ + NULL, /* write */ + NULL, /* seek */ + ptp_clock_ioctl, /* ioctl */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static ssize_t ptp_clock_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + return 0; +} + +static inline long ptp_clock_scaled_ppm_to_ppb(long ppm) +{ + /* The 'freq' field in the 'struct timex' is in parts per + * million, but with a 16 bit binary fractional field. + * + * We want to calculate + * + * ppb = scaled_ppm * 1000 / 2^16 + * + * which simplifies to + * + * ppb = scaled_ppm * 125 / 2^13 + */ + + int64_t ppb = 1 + ppm; + + ppb *= 125; + ppb >>= 13; + return (long)ppb; +} + +static int ptp_clock_adjtime(FAR struct ptp_lowerhalf_s *lower, + FAR struct timex *tx) +{ + FAR struct ptp_upperhalf_s *upper = lower->upper; + int ret = -ENOTSUP; + + if (tx->modes & ADJ_SETOFFSET) + { + struct timespec ts; + int64_t delta; + + ts.tv_sec = tx->time.tv_sec; + ts.tv_nsec = tx->time.tv_usec; + if (!(tx->modes & ADJ_NANO)) + { + ts.tv_nsec *= 1000; + } + + if ((unsigned long)ts.tv_nsec >= NSEC_PER_SEC) + { + return -EINVAL; + } + + delta = ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; + ret = lower->ops->adjtime(lower, delta); + } + else if ((tx->modes & ADJ_FREQUENCY) && lower->ops->adjfine != NULL) + { + long ppb = ptp_clock_scaled_ppm_to_ppb(tx->freq); + + if (ppb > upper->max_adj || ppb < -upper->max_adj) + { + return -ERANGE; + } + + ret = lower->ops->adjfine(lower, tx->freq); + upper->adj_freq = tx->freq; + } + else if ((tx->modes & ADJ_OFFSET) && + lower->ops->adjphase != NULL) + { + int32_t offset = tx->offset; + + if (!(tx->modes & ADJ_NANO)) + { + offset *= NSEC_PER_USEC; + } + + ret = lower->ops->adjphase(lower, offset); + } + else if (tx->modes == 0) + { + tx->freq = upper->adj_freq; + ret = 0; + } + + return ret; +} + +static int ptp_clock_ioctl(FAR struct file *filep, int cmd, + unsigned long arg) +{ + FAR struct ptp_upperhalf_s *upper = filep->f_inode->i_private; + FAR struct ptp_lowerhalf_s *lower = upper->lower; + int ret = -ENOTTY; + int i; + + nxmutex_lock(&upper->lock); + switch (cmd) + { + case PTP_CLOCK_SETTIME: + { + ret = -ENOTSUP; + if (lower->ops->settime) + { + ret = lower->ops->settime(lower, + (FAR const struct timespec *)(uintptr_t)arg); + } + } + break; + + case PTP_CLOCK_GETTIME: + { + ret = -ENOTSUP; + if (lower->ops->gettime) + { + ret = lower->ops->gettime(lower, + (FAR struct timespec *)(uintptr_t)arg, NULL); + } + } + break; + + case PTP_CLOCK_GETRES: + { + ret = -ENOTSUP; + if (lower->ops->getres) + { + ret = lower->ops->getres(lower, + (FAR struct timespec *)(uintptr_t)arg); + } + } + break; + + case PTP_CLOCK_ADJTIME: + { + ret = ptp_clock_adjtime(lower, (FAR struct timex *)(uintptr_t)arg); + } + break; + + case PTP_CLOCK_GETCAPS: + case PTP_CLOCK_GETCAPS2: + { + FAR struct ptp_clock_caps *caps = (FAR struct ptp_clock_caps *) + (uintptr_t)arg; + + memset(caps, 0, sizeof(*caps)); + caps->max_adj = upper->max_adj; + caps->cross_timestamping = lower->ops->getcrosststamp != NULL; + caps->adjust_phase = lower->ops->adjphase != NULL; + ret = OK; + } + break; + + case PTP_SYS_OFFSET_PRECISE: + case PTP_SYS_OFFSET_PRECISE2: + { + FAR struct ptp_sys_offset_precise *preoff = + (FAR struct ptp_sys_offset_precise *)(uintptr_t)arg; + struct system_device_crosststamp xtstamp; + + if (!lower->ops->getcrosststamp) + { + ret = -ENOTSUP; + break; + } + + ret = lower->ops->getcrosststamp(lower, &xtstamp); + if (ret != 0) + { + break; + } + + memset(preoff, 0, sizeof(*preoff)); + preoff->device.sec = xtstamp.device.tv_sec; + preoff->device.nsec = xtstamp.device.tv_nsec; + preoff->sys_realtime.sec = xtstamp.realtime.tv_sec; + preoff->sys_realtime.nsec = xtstamp.realtime.tv_nsec; + preoff->sys_monoraw.sec = xtstamp.monoraw.tv_sec; + preoff->sys_monoraw.nsec = xtstamp.monoraw.tv_nsec; + } + break; + + case PTP_SYS_OFFSET_EXTENDED: + case PTP_SYS_OFFSET_EXTENDED2: + { + FAR struct ptp_sys_offset_extended *extoff = + (FAR struct ptp_sys_offset_extended *)(uintptr_t)arg; + struct ptp_system_timestamp sts; + struct timespec ts; + + if (!lower->ops->gettime) + { + ret = -ENOTSUP; + break; + } + + if (extoff->n_samples > PTP_MAX_SAMPLES || + extoff->rsv[0] || extoff->rsv[1] || extoff->rsv[2]) + { + ret = -EINVAL; + break; + } + + for (i = 0; i < extoff->n_samples; i++) + { + ret = lower->ops->gettime(lower, &ts, &sts); + if (ret < 0) + { + break; + } + + extoff->ts[i][0].sec = sts.pre_ts.tv_sec; + extoff->ts[i][0].nsec = sts.pre_ts.tv_nsec; + extoff->ts[i][1].sec = ts.tv_sec; + extoff->ts[i][1].nsec = ts.tv_nsec; + extoff->ts[i][2].sec = sts.post_ts.tv_sec; + extoff->ts[i][2].nsec = sts.post_ts.tv_nsec; + } + } + break; + + case PTP_SYS_OFFSET: + case PTP_SYS_OFFSET2: + { + FAR struct ptp_sys_offset *sysoff = + (FAR struct ptp_sys_offset *)(uintptr_t)arg; + FAR struct ptp_clock_time *pct; + struct timespec ts; + + if (lower->ops->gettime == NULL) + { + ret = -ENOTSUP; + break; + } + + if (sysoff->n_samples > PTP_MAX_SAMPLES) + { + ret = -EINVAL; + break; + } + + pct = &sysoff->ts[0]; + for (i = 0; i < sysoff->n_samples; i++) + { + nxclock_gettime(CLOCK_REALTIME, &ts); + pct->sec = ts.tv_sec; + pct->nsec = ts.tv_nsec; + pct++; + ret = lower->ops->gettime(lower, &ts, NULL); + if (ret < 0) + { + break; + } + + pct->sec = ts.tv_sec; + pct->nsec = ts.tv_nsec; + pct++; + } + + nxclock_gettime(CLOCK_REALTIME, &ts); + pct->sec = ts.tv_sec; + pct->nsec = ts.tv_nsec; + } + break; + + default: + break; + } + + nxmutex_unlock(&upper->lock); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ptp_clockid_to_filep + * + * Description: + * Convert clockid to struct filep. + * + ****************************************************************************/ + +int ptp_clockid_to_filep(clockid_t clock_id, FAR struct file **filep) +{ + FAR const struct file_operations *ops; + int ret; + + if ((clock_id & CLOCK_MASK) != CLOCK_FD) + { + return -EINVAL; + } + + ret = clock_id >> CLOCK_SHIFT; + if (ret >= 0) + { + ret = fs_getfilep(ret, filep); + } + + if (ret < 0) + { + return ret; + } + + ops = (*filep)->f_inode->u.i_ops; + if (ops != &g_ptp_clock_file_ops) + { + fs_putfilep(*filep); + return -EINVAL; + } + + return 0; +} + +/**************************************************************************** + * Name: ptp_clock_register + * + * Description: + * This function binds an instance of a "lower half" ptp driver with the + * "upper half" ptp device and registers that device so that can be used + * by application code. + * + * Input Parameters: + * lower - A pointer to an instance of lower half ptp driver. This + * instance is bound to the ptp driver and must persists as long + * as the driver persists. + * mxa_adj - The maximum frequency adjustment in parts per billion. + * devno - The user specifies number of device. ex: /dev/ptpX. + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int ptp_clock_register(FAR struct ptp_lowerhalf_s *lower, int32_t max_adj, + int devno) +{ + FAR struct ptp_upperhalf_s *upper; + char path[16]; + int ret; + + DEBUGASSERT(lower != NULL); + + /* Allocate the upper-half data structure */ + + upper = kmm_zalloc(sizeof(struct ptp_upperhalf_s)); + if (!upper) + { + ptperr("ERROR: Allocation failed\n"); + return -ENOMEM; + } + + upper->lower = lower; + upper->max_adj = max_adj; + lower->upper = upper; + nxmutex_init(&upper->lock); + + snprintf(path, sizeof(path), "/dev/ptp%d", devno); + ptpinfo("Registering %s\n", path); + ret = register_driver(path, &g_ptp_clock_file_ops, 0666, upper); + if (ret < 0) + { + nxmutex_destroy(&upper->lock); + kmm_free(upper); + } + + return ret; +} + +/**************************************************************************** + * Name: ptp_clock_unregister + * + * Description: + * This function unregister character node and release all resource about + * upper half driver. + * + * Input Parameters: + * lower - A pointer to an instance of lower half ptp driver. This + * instance is bound to the ptp driver and must persists as long + * as the driver persists. + * devno - The user specifies which device of this type, from 0. + ****************************************************************************/ + +void ptp_clock_unregister(FAR struct ptp_lowerhalf_s *lower, int devno) +{ + FAR struct ptp_upperhalf_s *upper = lower->upper; + + if (upper != NULL) + { + char path[16]; + + snprintf(path, sizeof(path), "/dev/ptp%d", devno); + unregister_driver(path); + nxmutex_destroy(&upper->lock); + kmm_free(upper); + } +} diff --git a/include/debug.h b/include/debug.h index cc406d0a987d9..7c96eaf599453 100644 --- a/include/debug.h +++ b/include/debug.h @@ -1031,6 +1031,24 @@ # define csinfo _none #endif +#ifdef CONFIG_DEBUG_PTP_ERROR +# define ptperr _err +#else +# define ptperr _none +#endif + +#ifdef CONFIG_DEBUG_PTP_WARN +# define ptpwarn _warn +#else +# define ptpwarn _none +#endif + +#ifdef CONFIG_DEBUG_PTP_INFO +# define ptpinfo _info +#else +# define ptpinfo _none +#endif + /* Buffer dumping macros do not depend on varargs */ #ifdef CONFIG_DEBUG_ERROR diff --git a/include/nuttx/clock.h b/include/nuttx/clock.h index 140f3c39f08a5..60d2941e07189 100644 --- a/include/nuttx/clock.h +++ b/include/nuttx/clock.h @@ -91,11 +91,15 @@ * CLOCK_PROCESS_CPUTIME_ID - 2 * CLOCK_THREAD_CPUTIME_ID - 3 * CLOCK_BOOTTIME - 4 - * bit 3~32: the pid or tid value + * CLOCK_FD - 5 + * + * if the clockid value exceeds CLOCK_MASK, it indicates a dynamic clockid. + * bit 3~32: the fd, pid or tid value * * The CLOCK_MASK are using to extract the clock_type from the clockid_t */ +#define CLOCK_FD 5 #define CLOCK_MASK 7 #define CLOCK_SHIFT 3 diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h index 8ca85dc779b80..a231b827dd071 100644 --- a/include/nuttx/fs/ioctl.h +++ b/include/nuttx/fs/ioctl.h @@ -112,6 +112,7 @@ #define _I2SOCBASE (0x4400) /* I2S driver ioctl commands */ #define _1WIREBASE (0x4500) /* 1WIRE ioctl commands */ #define _EEPIOCBASE (0x4600) /* EEPROM driver ioctl commands */ +#define _PTPBASE (0x4700) /* PTP ioctl commands */ #define _WLIOCBASE (0x8b00) /* Wireless modules ioctl network commands */ /* boardctl() commands share the same number space */ @@ -799,6 +800,13 @@ #define _EEPIOCVALID(c) (_IOC_TYPE(c)==_EEPIOCBASE) #define _EEPIOC(nr) _IOC(_EEPIOCBASE,nr) +/* PTP driver ioctl definitions *********************************************/ + +/* see nuttx/include/ptp_clock.h */ + +#define _PTPIOCVALID(c) (_IOC_TYPE(c)==_PTPBASE) +#define _PTPIOC(nr) _IOC(_PTPBASE,nr) + /**************************************************************************** * Public Type Definitions ****************************************************************************/ diff --git a/include/nuttx/timers/ptp_clock.h b/include/nuttx/timers/ptp_clock.h new file mode 100644 index 0000000000000..3733abc6ec471 --- /dev/null +++ b/include/nuttx/timers/ptp_clock.h @@ -0,0 +1,355 @@ +/**************************************************************************** + * include/nuttx/timers/ptp_clock.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_TIMERS_PTP_CLOCK_H +#define __INCLUDE_NUTTX_TIMERS_PTP_CLOCK_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PTP_CLOCK_SETTIME _PTPIOC(0x1) +#define PTP_CLOCK_GETTIME _PTPIOC(0x2) +#define PTP_CLOCK_GETRES _PTPIOC(0x3) +#define PTP_CLOCK_ADJTIME _PTPIOC(0x4) + +#define PTP_CLOCK_GETCAPS _PTPIOC(0x5) +#define PTP_SYS_OFFSET _PTPIOC(0x6) +#define PTP_SYS_OFFSET_PRECISE _PTPIOC(0x7) +#define PTP_SYS_OFFSET_EXTENDED _PTPIOC(0x8) + +#define PTP_CLOCK_GETCAPS2 _PTPIOC(0x9) +#define PTP_SYS_OFFSET2 _PTPIOC(0xa) +#define PTP_SYS_OFFSET_PRECISE2 _PTPIOC(0xb) +#define PTP_SYS_OFFSET_EXTENDED2 _PTPIOC(0xc) + +/* Maximum allowed offset measurement samples. */ + +#define PTP_MAX_SAMPLES 25 + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* struct system_device_crosststamp - system/device cross-timestamp + * (synchronized capture) + */ + +struct system_device_crosststamp +{ + struct timespec device; /* Device time */ + struct timespec realtime; /* Realtime simultaneous with device time */ + struct timespec monoraw; /* Monotonic raw simultaneous with device time */ +}; + +/* struct ptp_clock_time - represents a time value + * + * The sign of the seconds field applies to the whole value. The + * nanoseconds field is always unsigned. The reserved field is + * included for sub-nanosecond resolution, should the demand for + * this ever appear. + */ + +struct ptp_clock_time +{ + int64_t sec; + uint32_t nsec; + uint32_t reserved; +}; + +struct ptp_clock_caps +{ + int max_adj; /* Maximum frequency adjustment in parts per billion. */ + int cross_timestamping; /* Whether the clock supports precise system-device cross timestamps */ + int adjust_phase; /* Whether the clock supports phase adjustment */ +}; + +struct ptp_sys_offset +{ + unsigned int n_samples; /* Desired number of measurements. */ + unsigned int rsv[3]; /* Reserved for future use. */ + + /* Array of interleaved system/phc time stamps. The kernel + * will provide 2*n_samples + 1 time stamps, with the last + * one as a system time stamp. + */ + + struct ptp_clock_time ts[2 * PTP_MAX_SAMPLES + 1]; +}; + +struct ptp_sys_offset_extended +{ + unsigned int n_samples; /* Desired number of measurements. */ + unsigned int rsv[3]; /* Reserved for future use. */ + + /* Array of [system, phc, system] time stamps. The kernel will provide + * 3*n_samples time stamps. + */ + + struct ptp_clock_time ts[PTP_MAX_SAMPLES][3]; +}; + +struct ptp_sys_offset_precise +{ + struct ptp_clock_time device; + struct ptp_clock_time sys_realtime; + struct ptp_clock_time sys_monoraw; + unsigned int rsv[4]; +}; + +/* struct ptp_system_timestamp - system time corresponding to a + * PHC timestamp. + */ + +struct ptp_system_timestamp +{ + struct timespec pre_ts; + struct timespec post_ts; +}; + +struct ptp_lowerhalf_s; +struct ptp_ops_s +{ + /************************************************************************** + * Name: adjfine + * + * Description: + * Adjusts the frequency of the hardware clock. + * + * Input Parameters: + * lower - The instance of lower half ptp driver + * scaled_ppm - Desired frequency offset from nominal frequency in parts + * per million, but with a 16 bit binary fractional field. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*adjfine)(FAR struct ptp_lowerhalf_s *lower, long scaled_ppm); + + /************************************************************************** + * Name: adjphase + * + * Description: + * Adjusts the phase offset of the hardware clock. + * + * Input Parameters: + * lower - The instance of lower half ptp driver. + * phase - Desired change in nanoseconds. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*adjphase)(FAR struct ptp_lowerhalf_s *lower, int32_t phase); + + /************************************************************************** + * Name: adjtime + * + * Description: + * Shifts the time of the hardware clock. + * + * Input Parameters: + * lower - The instance of lower half ptp driver. + * delta - Desired change in nanoseconds. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*adjtime)(FAR struct ptp_lowerhalf_s *lower, int64_t delta); + + /************************************************************************** + * Name: gettime + * + * Description: + * Reads the current time from the hardware clock and optionally also + * also the system clock. The first reading is made right before reading + * the lowest bits of the PHC timestamp and the second reading + * immediately follows that. + * + * Input Parameters: + * lower - The instance of lower half ptp driver. + * ts - Holds the PHC timestamp. + * sts - If not NULL, it holds a pair of timestamps from the + * system clock. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*gettime)(FAR struct ptp_lowerhalf_s *lower, + FAR struct timespec *ts, + FAR struct ptp_system_timestamp *sts); + + /************************************************************************** + * Name: getcrosststamp + * + * Description: + * Reads the current time from the hardware clock and system clock + * simultaneously. + * + * Input Parameters: + * lower - The instance of lower half ptp driver. + * cts - Contains timestamp (device,system) pair, where system time is + * realtime and monotonic. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*getcrosststamp)(FAR struct ptp_lowerhalf_s *lower, + FAR struct system_device_crosststamp *cts); + + /************************************************************************** + * Name: settime + * + * Description: + * Set the current time on the hardware clock. + * + * Input Parameters: + * lower - The instance of lower half ptp driver + * ts - Time value to set. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*settime)(FAR struct ptp_lowerhalf_s *lower, + FAR const struct timespec *ts); + + /************************************************************************** + * Name: getres + * + * Description: + * Finds the resolution (precision) of the specified clock, and, if res + * is non-NULL, stores it in the struct timespec pointed to by res. + * + * Input Parameters: + * pc - The instance of lower half ptp driver. + * res - Holds the resolution. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*getres)(FAR struct ptp_lowerhalf_s *lower, + FAR struct timespec *res); +}; + +/* The ptp lower half driver interface, describes a PTP hardware + * clock driver. + */ + +struct ptp_lowerhalf_s +{ + FAR const struct ptp_ops_s *ops; /* Lower half driver operations. */ + FAR void *upper; /* The upper handle */ +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: ptp_clockid_to_filep + * + * Description: + * Convert clockid to struct filep. + * + ****************************************************************************/ + +int ptp_clockid_to_filep(clockid_t clock_id, FAR struct file **filep); + +/**************************************************************************** + * Name: ptp_clock_register + * + * Description: + * This function binds an instance of a "lower half" ptp driver with the + * "upper half" ptp device and registers that device so that can be used + * by application code. + * + * Input Parameters: + * lower - A pointer to an instance of lower half ptp driver. This + * instance is bound to the ptp driver and must persists as long + * as the driver persists. + * mxa_adj - The maximum frequency adjustment in parts per billion. + * devno - The user specifies number of device. ex: /dev/ptpX. + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int ptp_clock_register(FAR struct ptp_lowerhalf_s *lower, int32_t max_adj, + int devno); + +/**************************************************************************** + * Name: ptp_clock_unregister + * + * Description: + * This function unregisters character node and releases all resource from + * upper half driver. This API corresponds to the ptp_register. + * + * Input Parameters: + * dev - A pointer to an instance of lower half ptp driver. This + * instance is bound to the ptp driver and must persists as long + * as the driver persists. + * devno - The user specifies which device of this type, from 0. + ****************************************************************************/ + +void ptp_clock_unregister(FAR struct ptp_lowerhalf_s *dev, int devno); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif +#endif /* __INCLUDE_NUTTX_TIMERS_PTP_CLOCK_H */ diff --git a/include/sys/timex.h b/include/sys/timex.h new file mode 100644 index 0000000000000..19418da7f9447 --- /dev/null +++ b/include/sys/timex.h @@ -0,0 +1,98 @@ +/**************************************************************************** + * include/sys/timex.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_SYS_TIMEX_H +#define __INCLUDE_SYS_TIMEX_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Mode codes (timex.mode) */ + +#define ADJ_OFFSET 0x0001 /* time offset */ +#define ADJ_FREQUENCY 0x0002 /* frequency offset */ +#define ADJ_MAXERROR 0x0004 /* maximum time error */ +#define ADJ_ESTERROR 0x0008 /* estimated time error */ +#define ADJ_STATUS 0x0010 /* clock status */ +#define ADJ_TIMECONST 0x0020 /* pll time constant */ +#define ADJ_TAI 0x0080 /* set TAI offset */ +#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */ +#define ADJ_MICRO 0x1000 /* select microsecond resolution */ +#define ADJ_NANO 0x2000 /* select nanosecond resolution */ +#define ADJ_TICK 0x4000 /* tick value */ +#define ADJ_OFFSET_SINGLESHOT 0x8001 /* old-fashioned adjtime */ +#define ADJ_OFFSET_SS_READ 0xa001 /* read-only adjtime */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct timex +{ + unsigned int modes; /* mode selector */ + long offset; /* time offset (usec) */ + long freq; /* frequency offset (scaled ppm) */ + long maxerror; /* maximum error (usec) */ + long esterror; /* estimated error (usec) */ + int status; /* clock command/status */ + long constant; /* pll time constant */ + long precision; /* clock precision (usec) (read only) */ + long tolerance; /* clock frequency tolerance (ppm) (read only) */ + struct timeval time; /* (read only, except for ADJ_SETOFFSET) */ + long tick; /* (modified) usecs between clock ticks */ + long ppsfreq; /* pps frequency (scaled ppm) (ro) */ + long jitter; /* pps jitter (us) (ro) */ + int shift; /* interval duration (s) (shift) (ro) */ + long stabil; /* pps stability (scaled ppm) (ro) */ + long jitcnt; /* jitter limit exceeded (ro) */ + long calcnt; /* calibration intervals (ro) */ + long errcnt; /* calibration errors (ro) */ + long stbcnt; /* stability limit exceeded (ro) */ + int tai; /* TAI offset (ro) */ +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __INCLUDE_SYS_TIMEX_H */ From b8072e6cd0fbd88e926d27f9aa05eaf825246e9a Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Sat, 22 Mar 2025 15:12:28 +0800 Subject: [PATCH 02/11] drivers/timers/ptp: support ptp clock dummy driver This patch adds a dummy PTP clock driver implementation that provides a software-based PTP clock for testing and development purposes. Key changes include: 1. Dummy PTP clock driver implementation: - Added drivers/timers/ptp_clock_dummy.c - Provides software-based PTP clock using system monotonic clock - Implements all required lower-half driver operations 2. Lower-half driver operations: - adjfine: Frequency adjustment (stores adjustment value) - adjtime: Time offset adjustment (applies delta to base time) - gettime: Returns current PTP clock time - settime: Sets PTP clock time - getcaps: Reports clock capabilities (1 billion PPB max adjustment) - getcrosststamp: Provides system/device cross-timestamp 3. Driver initialization: - Added ptp_clock_dummy_init() in drivers/drivers_initialize.c - Auto-registration under CONFIG_PTP_CLOCK_DUMMY configuration - Creates /dev/ptp0 character device node 4. Build system integration: - Added CONFIG_PTP_CLOCK_DUMMY Kconfig option (depends on PTP_CLOCK) - Updated CMakeLists.txt and Make.defs - Added include/nuttx/timers/ptp_clock_dummy.h header 5. Implementation details: - Uses clock_gettime(CLOCK_MONOTONIC) as time base - Tracks time offset and frequency adjustment internally - Suitable for testing PTP clock applications without hardware This dummy driver enables development and testing of PTP clock applications on platforms without dedicated PTP hardware support. Signed-off-by: dongjiuzhu1 --- drivers/drivers_initialize.c | 5 + drivers/timers/CMakeLists.txt | 4 + drivers/timers/Kconfig | 7 + drivers/timers/Make.defs | 4 + drivers/timers/ptp_clock_dummy.c | 203 +++++++++++++++++++++++++ include/nuttx/timers/ptp_clock_dummy.h | 74 +++++++++ 6 files changed, 297 insertions(+) create mode 100644 drivers/timers/ptp_clock_dummy.c create mode 100644 include/nuttx/timers/ptp_clock_dummy.h diff --git a/drivers/drivers_initialize.c b/drivers/drivers_initialize.c index 43b85aa42084b..fec2a9857580c 100644 --- a/drivers/drivers_initialize.c +++ b/drivers/drivers_initialize.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -293,5 +294,9 @@ void drivers_initialize(void) thermal_init(); #endif +#ifdef CONFIG_PTP_CLOCK_DUMMY + ptp_clock_dummy_initialize(0); +#endif + drivers_trace_end(); } diff --git a/drivers/timers/CMakeLists.txt b/drivers/timers/CMakeLists.txt index 0b56569c697d2..0f1370b71bedd 100644 --- a/drivers/timers/CMakeLists.txt +++ b/drivers/timers/CMakeLists.txt @@ -94,4 +94,8 @@ if(CONFIG_PTP_CLOCK) list(APPEND SRCS ptp_clock.c) endif() +if(CONFIG_PTP_CLOCK_DUMMY) + list(APPEND SRCS ptp_clock_dummy.c) +endif() + target_sources(drivers PRIVATE ${SRCS}) diff --git a/drivers/timers/Kconfig b/drivers/timers/Kconfig index eb47a0a7532e2..6e276e3299db6 100644 --- a/drivers/timers/Kconfig +++ b/drivers/timers/Kconfig @@ -567,4 +567,11 @@ config PTP_CLOCK devices. If you want to use a PTP clock, then you should also enable at least one clock driver as well. +config PTP_CLOCK_DUMMY + bool "the dummy test driver for ptp clock" + default n + select PTP_CLOCK + ---help--- + The dummy test driver. + endmenu # Timer Driver Support diff --git a/drivers/timers/Make.defs b/drivers/timers/Make.defs index c8b6d21cdf418..b9ab14e0fbdaf 100644 --- a/drivers/timers/Make.defs +++ b/drivers/timers/Make.defs @@ -128,6 +128,10 @@ ifeq ($(CONFIG_PTP_CLOCK),y) CSRCS += ptp_clock.c endif +ifeq ($(CONFIG_PTP_CLOCK_DUMMY),y) + CSRCS += ptp_clock_dummy.c +endif + # Include timer build support (if any were selected) DEPPATH += $(TMRDEPPATH) diff --git a/drivers/timers/ptp_clock_dummy.c b/drivers/timers/ptp_clock_dummy.c new file mode 100644 index 0000000000000..bc16559344f25 --- /dev/null +++ b/drivers/timers/ptp_clock_dummy.c @@ -0,0 +1,203 @@ +/**************************************************************************** + * drivers/timers/ptp_clock_dummy.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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct ptp_clock_dummy_s +{ + struct ptp_lowerhalf_s lower; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int ptp_clock_dummy_adjfine(FAR struct ptp_lowerhalf_s *lower, + long scaled_ppm); +static int ptp_clock_dummy_adjphase(FAR struct ptp_lowerhalf_s *lower, + int32_t phase); +static int ptp_clock_dummy_adjtime(FAR struct ptp_lowerhalf_s *lower, + int64_t delta); +static int ptp_clock_dummy_gettime(FAR struct ptp_lowerhalf_s *lower, + FAR struct timespec *ts, + FAR struct ptp_system_timestamp *sts); +static int ptp_clock_dummy_getcrosststamp(FAR struct ptp_lowerhalf_s *lower, + FAR struct system_device_crosststamp *cts); +static int ptp_clock_dummy_settime(FAR struct ptp_lowerhalf_s *lower, + FAR const struct timespec *ts); +static int ptp_clock_dummy_getres(FAR struct ptp_lowerhalf_s *lower, + FAR struct timespec *res); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct ptp_ops_s g_ptp_clock_dummy_ops = +{ + ptp_clock_dummy_adjfine, /* adjfine */ + ptp_clock_dummy_adjphase, /* adjphase */ + ptp_clock_dummy_adjtime, /* adjtime */ + ptp_clock_dummy_gettime, /* gettime */ + ptp_clock_dummy_getcrosststamp, /* getcroasststamp */ + ptp_clock_dummy_settime, /* settime */ + ptp_clock_dummy_getres, /* getres */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int ptp_clock_dummy_adjfine(FAR struct ptp_lowerhalf_s *lower, + long scaled_ppm) +{ + ptpinfo("ptp_clock_dummy_adjfine ppm:%ld\n", scaled_ppm); + return 0; +} + +static int ptp_clock_dummy_adjphase(FAR struct ptp_lowerhalf_s *lower, + int32_t phase) +{ + ptpinfo("ptp_clock_dummy_adjphase phase:%"PRIi32"\n", phase); + return 0; +} + +static int ptp_clock_dummy_adjtime(FAR struct ptp_lowerhalf_s *lower, + int64_t delta) +{ + ptpinfo("ptp_clock_dummy_adjtime delta:%"PRIi64"\n", delta); + return 0; +} + +static int ptp_clock_dummy_gettime(FAR struct ptp_lowerhalf_s *lower, + FAR struct timespec *ts, + FAR struct ptp_system_timestamp *sts) +{ + clock_gettime(CLOCK_REALTIME, ts); + + if (sts != NULL) + { + sts->pre_ts.tv_sec = ts->tv_sec; + sts->pre_ts.tv_nsec = ts->tv_nsec; + sts->post_ts.tv_sec = ts->tv_sec; + sts->post_ts.tv_nsec = ts->tv_nsec; + } + + ptpinfo("ptp_clock_dummy_gettime sec:%ld, nsec:%ld\n", ts->tv_sec, + ts->tv_nsec); + return 0; +} + +static int +ptp_clock_dummy_getcrosststamp(FAR struct ptp_lowerhalf_s *lower, + FAR struct system_device_crosststamp *cts) +{ + struct timespec ts; + + if (cts == NULL) + { + return -EINVAL; + } + + clock_systime_timespec(&ts); + cts->device.tv_sec = ts.tv_sec; + cts->device.tv_nsec = ts.tv_nsec; + + clock_gettime(CLOCK_REALTIME, &ts); + cts->realtime.tv_sec = ts.tv_sec; + cts->realtime.tv_nsec = ts.tv_nsec; + + clock_gettime(CLOCK_MONOTONIC, &ts); + cts->monoraw.tv_sec = ts.tv_sec; + cts->monoraw.tv_nsec = ts.tv_sec; + + ptpinfo("ptp_clock_dummy_getcrosststamp sec:%ld, nsec:%ld\n", + (long)ts.tv_sec, ts.tv_nsec); + return 0; +} + +static int ptp_clock_dummy_settime(FAR struct ptp_lowerhalf_s *lower, + FAR const struct timespec *ts) +{ + ptpinfo("ptp_clock_dummy_settime sec:%ld, nsec:%ld\n", (long)ts->tv_sec, + ts->tv_nsec); + return 0; +} + +static int ptp_clock_dummy_getres(FAR struct ptp_lowerhalf_s *lower, + FAR struct timespec *res) +{ + res->tv_sec = 0; + res->tv_nsec = 1; + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ptp_clock_dummy_initialize + * + * Description: + * This function will initialize dummy ptp device driver for test. + * + * Input Parameters: + * devno - The user specifies number of device. ex: /dev/ptpX. + * + * Returned Value: + * OK if the driver was successfully initialize; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int ptp_clock_dummy_initialize(int devno) +{ + FAR struct ptp_clock_dummy_s *priv; + + /* Allocate the upper-half data structure */ + + priv = kmm_zalloc(sizeof(struct ptp_clock_dummy_s)); + if (!priv) + { + ptperr("ERROR: Allocation failed\n"); + return -ENOMEM; + } + + priv->lower.ops = &g_ptp_clock_dummy_ops; + + return ptp_clock_register(&priv->lower, 1000000, devno); +} diff --git a/include/nuttx/timers/ptp_clock_dummy.h b/include/nuttx/timers/ptp_clock_dummy.h new file mode 100644 index 0000000000000..8562d74ff63a2 --- /dev/null +++ b/include/nuttx/timers/ptp_clock_dummy.h @@ -0,0 +1,74 @@ +/**************************************************************************** + * include/nuttx/timers/ptp_clock_dummy.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_TIMERS_PTP_CLOCK_DUMMY_H +#define __INCLUDE_NUTTX_TIMERS_PTP_CLOCK_DUMMY_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: ptp_clock_dummy_initialize + * + * Description: + * This function will initialize dummy ptp device driver for test. + * + * Input Parameters: + * devno - The user specifies number of device. ex: /dev/ptpX. + * + * Returned Value: + * OK if the driver was successfully initialize; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int ptp_clock_dummy_initialize(int devno); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif +#endif /* __INCLUDE_NUTTX_TIMERS_PTP_CLOCK_DUMMY_H */ From 89fbac97de1ef1b7d9889b29d454c65c06ee1143 Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Thu, 20 Mar 2025 18:20:26 +0800 Subject: [PATCH 03/11] sched/clock: support using CLOCKFD to call clock_gettime/settime This patch implements POSIX-compliant CLOCKFD support, enabling user applications to access dynamic PTP clocks through file descriptors combined with clock_gettime() and clock_settime() system calls. Key changes include: 1. CLOCKFD macro support: - Added CLOCKFD() macro in include/nuttx/clock.h - Allows encoding file descriptor into clockid_t: CLOCKFD(fd) - Enables accessing PTP clocks via: clock_gettime(CLOCKFD(fd), &ts) 2. clock_gettime() enhancements: - Modified sched/clock/clock_gettime.c to detect CLOCKFD clockids - Extracts file descriptor from clockid using CLOCKFD_TO_FD() - Validates file descriptor and calls file_ioctl() with PTP_CLOCK_GETTIME - Falls back to standard clock handling for non-CLOCKFD clockids 3. clock_settime() enhancements: - Modified sched/clock/clock_settime.c to support CLOCKFD clockids - Extracts file descriptor and validates accessibility - Issues PTP_CLOCK_SETTIME ioctl to underlying PTP clock device - Maintains backward compatibility with standard POSIX clocks 4. File descriptor validation: - Checks file descriptor validity using fs_getfilep() - Ensures proper reference counting with fs_putfilep() - Returns appropriate error codes (EINVAL, EBADF) on failures 5. Integration with PTP clock framework: - Seamlessly integrates with PTP clock driver's ioctl interface - Enables standard POSIX clock API usage for dynamic PTP clocks - No changes required to existing applications using standard clocks Usage example: int fd = open("/dev/ptp0", O_RDONLY); struct timespec ts; clock_gettime(CLOCKFD(fd), &ts); /* Get PTP clock time */ clock_settime(CLOCKFD(fd), &ts); /* Set PTP clock time */ close(fd); This implementation follows the Linux kernel's PTP clock model, providing a familiar interface for developers working with PTP hardware. Signed-off-by: dongjiuzhu1 --- include/nuttx/clock.h | 4 +- sched/clock/clock_gettime.c | 46 +++++++++++++++++++--- sched/clock/clock_settime.c | 76 ++++++++++++++++++++++++++++--------- 3 files changed, 101 insertions(+), 25 deletions(-) diff --git a/include/nuttx/clock.h b/include/nuttx/clock.h index 60d2941e07189..05b568af6a462 100644 --- a/include/nuttx/clock.h +++ b/include/nuttx/clock.h @@ -827,7 +827,7 @@ unsigned long perf_getfreq(void); * ****************************************************************************/ -void nxclock_settime(clockid_t clock_id, FAR const struct timespec *tp); +int nxclock_settime(clockid_t clock_id, FAR const struct timespec *tp); /**************************************************************************** * Name: nxclock_gettime @@ -837,7 +837,7 @@ void nxclock_settime(clockid_t clock_id, FAR const struct timespec *tp); * ****************************************************************************/ -void nxclock_gettime(clockid_t clock_id, FAR struct timespec *tp); +int nxclock_gettime(clockid_t clock_id, FAR struct timespec *tp); #undef EXTERN #ifdef __cplusplus diff --git a/sched/clock/clock_gettime.c b/sched/clock/clock_gettime.c index 89ebde1766529..3dcf862e1c36c 100644 --- a/sched/clock/clock_gettime.c +++ b/sched/clock/clock_gettime.c @@ -32,10 +32,12 @@ #include #include +#include #include #include #include #include +#include #include "clock/clock.h" #include "sched/sched.h" @@ -86,9 +88,16 @@ static clock_t clock_process_runtime(FAR struct tcb_s *tcb) * ****************************************************************************/ -void nxclock_gettime(clockid_t clock_id, FAR struct timespec *tp) +int nxclock_gettime(clockid_t clock_id, FAR struct timespec *tp) { - if (clock_id == CLOCK_MONOTONIC) + int ret = 0; + + if (tp == NULL) + { + return -EINVAL; + } + + if (clock_id == CLOCK_MONOTONIC || clock_id == CLOCK_BOOTTIME) { /* The the time elapsed since the timer was initialized at power on * reset, excluding the time that the system is suspended. @@ -124,6 +133,21 @@ void nxclock_gettime(clockid_t clock_id, FAR struct timespec *tp) clock_timekeeping_get_wall_time(tp); #endif } +#ifdef CONFIG_PTP_CLOCK + else if ((clock_id & CLOCK_MASK) == CLOCK_FD) + { + FAR struct file *filep; + + ret = ptp_clockid_to_filep(clock_id, &filep); + if (ret < 0) + { + return ret; + } + + ret = file_ioctl(filep, PTP_CLOCK_GETTIME, tp); + fs_putfilep(filep); + } +#endif else { #if CONFIG_SCHED_CRITMONITOR_MAXTIME_THREAD >= 0 @@ -150,9 +174,19 @@ void nxclock_gettime(clockid_t clock_id, FAR struct timespec *tp) { up_perf_convert(tcb->run_time, tp); } + else + { + ret = -EINVAL; + } + } + else + { + return -EINVAL; } #endif } + + return ret; } /**************************************************************************** @@ -181,12 +215,14 @@ void nxclock_gettime(clockid_t clock_id, FAR struct timespec *tp) int clock_gettime(clockid_t clock_id, FAR struct timespec *tp) { - if (tp == NULL || clock_id < 0 || clock_id > CLOCK_BOOTTIME) + int ret; + + ret = nxclock_gettime(clock_id, tp); + if (ret < 0) { - set_errno(EINVAL); + set_errno(-ret); return ERROR; } - nxclock_gettime(clock_id, tp); return OK; } diff --git a/sched/clock/clock_settime.c b/sched/clock/clock_settime.c index 41017e69647e2..a448e809ea146 100644 --- a/sched/clock/clock_settime.c +++ b/sched/clock/clock_settime.c @@ -30,11 +30,13 @@ #include #include #include +#include #include +#include #include #include -#include +#include #include "clock/clock.h" #ifdef CONFIG_CLOCK_TIMEKEEPING @@ -42,21 +44,10 @@ #endif /**************************************************************************** - * Public Functions + * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: nxclock_settime - * - * Description: - * Clock Functions based on POSIX APIs - * - * CLOCK_REALTIME - POSIX demands this to be present. This is the wall - * time clock. - * - ****************************************************************************/ - -void nxclock_settime(clockid_t clock_id, FAR const struct timespec *tp) +static void nxclock_set_realtime(FAR const struct timespec *tp) { #ifndef CONFIG_CLOCK_TIMEKEEPING struct timespec bias; @@ -103,6 +94,54 @@ void nxclock_settime(clockid_t clock_id, FAR const struct timespec *tp) #endif } +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxclock_settime + * + * Description: + * Clock Functions based on POSIX APIs + * + * CLOCK_REALTIME - POSIX demands this to be present. This is the wall + * time clock. + * + ****************************************************************************/ + +int nxclock_settime(clockid_t clock_id, FAR const struct timespec *tp) +{ + int ret = -EINVAL; + + if (tp == NULL || tp->tv_nsec < 0 || tp->tv_nsec >= 1000000000) + { + return ret; + } + + if (clock_id == CLOCK_REALTIME) + { + nxclock_set_realtime(tp); + return 0; + } +#ifdef CONFIG_PTP_CLOCK + else if ((clock_id & CLOCK_MASK) == CLOCK_FD) + { + FAR struct file *filep; + + ret = ptp_clockid_to_filep(clock_id, &filep); + if (ret < 0) + { + return ret; + } + + ret = file_ioctl(filep, PTP_CLOCK_SETTIME, tp); + fs_putfilep(filep); + } +#endif + + return ret; +} + /**************************************************************************** * Name: clock_settime * @@ -116,13 +155,14 @@ void nxclock_settime(clockid_t clock_id, FAR const struct timespec *tp) int clock_settime(clockid_t clock_id, FAR const struct timespec *tp) { - if (clock_id != CLOCK_REALTIME || tp == NULL || - tp->tv_nsec < 0 || tp->tv_nsec >= 1000000000) + int ret; + + ret = nxclock_settime(clock_id, tp); + if (ret < 0) { - set_errno(EINVAL); + set_errno(-ret); return ERROR; } - nxclock_settime(clock_id, tp); return OK; } From 36b68c6a41717542d7cacafc9f3f1f7a411334d9 Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Thu, 20 Mar 2025 22:46:44 +0800 Subject: [PATCH 04/11] sched/clock: support using CLOCKFD to call clock_getres This patch extends CLOCKFD support to clock_getres(), allowing applications to query the resolution of PTP clocks through file descriptors using the standard POSIX clock API. Key changes include: 1. Move clock_getres() from libc to kernel: - Relocated from libs/libc/sched/clock_getres.c to sched/clock/ - Enables direct kernel-level file descriptor handling - Maintains POSIX compliance while adding CLOCKFD support 2. CLOCKFD support in clock_getres(): - Detects CLOCKFD-encoded clockids using CLOCKFD_TO_FD() check - Extracts file descriptor from clockid parameter - Validates file descriptor through fs_getfilep() - Issues PTP_CLOCK_GETRES ioctl to query clock resolution 3. PTP clock resolution query: - Retrieves resolution from PTP clock device via ioctl - Returns nanosecond-precision timing resolution - Enables applications to determine clock accuracy capabilities - Falls back to standard clock resolution for non-CLOCKFD clockids 4. Error handling: - Returns EINVAL for invalid CLOCKFD file descriptors - Properly manages file reference counting with fs_putfilep() - Maintains backward compatibility with existing clock types 5. Build system updates: - Updated libs/libc/sched/CMakeLists.txt and Make.defs - Updated sched/clock/CMakeLists.txt to include clock_getres.c - Ensures proper compilation in kernel context Usage example: int fd = open("/dev/ptp0", O_RDONLY); struct timespec res; clock_getres(CLOCKFD(fd), &res); /* Query PTP clock resolution */ printf("Resolution: %ld.%09ld sec\n", res.tv_sec, res.tv_nsec); close(fd); This completes the basic POSIX clock API support for dynamic PTP clocks, providing applications with comprehensive clock information access. Signed-off-by: dongjiuzhu1 --- libs/libc/sched/CMakeLists.txt | 1 - libs/libc/sched/Make.defs | 2 +- sched/clock/CMakeLists.txt | 1 + sched/clock/Make.defs | 2 +- .../libc/sched => sched/clock}/clock_getres.c | 23 ++++++++++++++++--- 5 files changed, 23 insertions(+), 6 deletions(-) rename {libs/libc/sched => sched/clock}/clock_getres.c (85%) diff --git a/libs/libc/sched/CMakeLists.txt b/libs/libc/sched/CMakeLists.txt index c447e575f20b8..8297eebce7798 100644 --- a/libs/libc/sched/CMakeLists.txt +++ b/libs/libc/sched/CMakeLists.txt @@ -24,7 +24,6 @@ set(SRCS sched_getprioritymax.c sched_getprioritymin.c clock_getcpuclockid.c - clock_getres.c task_cancelpt.c task_setcancelstate.c task_setcanceltype.c diff --git a/libs/libc/sched/Make.defs b/libs/libc/sched/Make.defs index aa164356ddfc8..23aa8c0b2d5f6 100644 --- a/libs/libc/sched/Make.defs +++ b/libs/libc/sched/Make.defs @@ -23,9 +23,9 @@ # Add the sched C files to the build CSRCS += sched_getprioritymax.c sched_getprioritymin.c -CSRCS += clock_getcpuclockid.c clock_getres.c CSRCS += task_cancelpt.c task_setcancelstate.c task_setcanceltype.c CSRCS += task_testcancel.c task_gettid.c +CSRCS += clock_getcpuclockid.c ifeq ($(CONFIG_SMP),y) CSRCS += sched_cpucount.c diff --git a/sched/clock/CMakeLists.txt b/sched/clock/CMakeLists.txt index 62c4346bf0d37..d7740ca4777fa 100644 --- a/sched/clock/CMakeLists.txt +++ b/sched/clock/CMakeLists.txt @@ -23,6 +23,7 @@ set(SRCS clock.c clock_initialize.c + clock_getres.c clock_settime.c clock_gettime.c clock_realtime2absticks.c diff --git a/sched/clock/Make.defs b/sched/clock/Make.defs index e61da4dbd9ece..659abd6ea185d 100644 --- a/sched/clock/Make.defs +++ b/sched/clock/Make.defs @@ -22,7 +22,7 @@ CSRCS += clock.c clock_initialize.c clock_settime.c clock_gettime.c CSRCS += clock_systime_ticks.c clock_systime_timespec.c clock_sched_ticks.c -CSRCS += clock_perf.c clock_realtime2absticks.c +CSRCS += clock_perf.c clock_realtime2absticks.c clock_getres.c # Unless a driver with a more accurate version of up_*delay is enabled, always # include the base delay definition (busy-loop) as a minimum-viable product. We diff --git a/libs/libc/sched/clock_getres.c b/sched/clock/clock_getres.c similarity index 85% rename from libs/libc/sched/clock_getres.c rename to sched/clock/clock_getres.c index 6d373175c6418..b457edc27516a 100644 --- a/libs/libc/sched/clock_getres.c +++ b/sched/clock/clock_getres.c @@ -1,5 +1,5 @@ /**************************************************************************** - * libs/libc/sched/clock_getres.c + * sched/clock/clock_getres.c * * SPDX-License-Identifier: Apache-2.0 * @@ -24,14 +24,14 @@ * Included Files ****************************************************************************/ -#include - #include #include #include #include #include +#include +#include /**************************************************************************** * Public Functions @@ -74,6 +74,23 @@ int clock_getres(clockid_t clock_id, struct timespec *res) sinfo("Returning res=(%d,%d)\n", (int)res->tv_sec, (int)res->tv_nsec); break; + +#ifdef CONFIG_PTP_CLOCK + case CLOCK_FD: + { + FAR struct file *filep; + + ret = ptp_clockid_to_filep(clock_id, &filep); + if (ret < 0) + { + return ret; + } + + ret = file_ioctl(filep, PTP_CLOCK_GETRES, res); + fs_putfilep(filep); + } + break; +#endif } return ret; From 4d7ac1b403f84dee721cdeaa37fad79f8a6ad344 Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Thu, 20 Mar 2025 23:08:37 +0800 Subject: [PATCH 05/11] sched/clock: support using CLOCKFD to call clock_adjtime This patch implements clock_adjtime() with CLOCKFD support, enabling precise time and frequency adjustments for PTP clocks through the standard POSIX clock API. Key changes include: 1. New clock_adjtime() system call: - Added sched/clock/clock_adjtime.c implementation - Provides POSIX-compliant time adjustment interface - Supports both standard clocks and CLOCKFD-based dynamic clocks - Added CONFIG_CLOCK_ADJTIME Kconfig option 2. CLOCKFD support in clock_adjtime(): - Detects CLOCKFD-encoded clockids via CLOCKFD_TO_FD() check - Extracts file descriptor and validates through fs_getfilep() - Issues PTP_CLOCK_ADJTIME ioctl to underlying PTP clock device - Returns clock status and adjustment results via struct timex 3. Enhanced struct timex support: - Extended include/sys/timex.h with additional ADJ_* flags - Added ADJ_OFFSET, ADJ_FREQUENCY, ADJ_MAXERROR, ADJ_ESTERROR - Added ADJ_STATUS, ADJ_TIMECONST, ADJ_TAI, ADJ_SETOFFSET - Added ADJ_MICRO, ADJ_NANO for time unit selection - Added STA_* status flags for clock state reporting 4. Clock operations supported: - ADJ_SETOFFSET: Apply time offset adjustment - ADJ_FREQUENCY: Adjust clock frequency (PPM) - ADJ_MAXERROR: Set maximum error estimate - ADJ_ESTERROR: Set estimated error - ADJ_STATUS: Modify clock status bits - ADJ_NANO: Use nanosecond resolution - ADJ_SETOFFSET: Set absolute time offset 5. API declarations: - Added clock_adjtime() prototype in include/nuttx/clock.h - Enabled by CONFIG_CLOCK_ADJTIME configuration - Compatible with Linux clock_adjtime() interface Usage example: int fd = open("/dev/ptp0", O_RDWR); struct timex tx = {0}; tx.modes = ADJ_FREQUENCY; tx.freq = 10000000; /* Adjust frequency by 10 PPM */ clock_adjtime(CLOCKFD(fd), &tx); close(fd); This completes the PTP clock framework's POSIX clock API integration, providing comprehensive time control capabilities for precision timing applications including PTP synchronization daemons (ptp4l, timemaster). Signed-off-by: dongjiuzhu1 --- include/nuttx/clock.h | 13 +++++ include/sys/timex.h | 31 ++++++++++++ sched/Kconfig | 5 +- sched/clock/clock_adjtime.c | 94 +++++++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 1 deletion(-) diff --git a/include/nuttx/clock.h b/include/nuttx/clock.h index 05b568af6a462..71718f450642e 100644 --- a/include/nuttx/clock.h +++ b/include/nuttx/clock.h @@ -29,6 +29,7 @@ #include +#include #include #include #include @@ -839,6 +840,18 @@ int nxclock_settime(clockid_t clock_id, FAR const struct timespec *tp); int nxclock_gettime(clockid_t clock_id, FAR struct timespec *tp); +/**************************************************************************** + * Name: nxclock_adjtime + * + * Description: + * Adjust the frequency and/or phase of a clock. + * + ****************************************************************************/ + +#ifdef CONFIG_CLOCK_ADJTIME +int nxclock_adjtime(clockid_t clock_id, FAR struct timex *buf); +#endif + #undef EXTERN #ifdef __cplusplus } diff --git a/include/sys/timex.h b/include/sys/timex.h index 19418da7f9447..bbd8b16a9032e 100644 --- a/include/sys/timex.h +++ b/include/sys/timex.h @@ -90,6 +90,37 @@ extern "C" #define EXTERN extern #endif +/**************************************************************************** + * Name: clock_adjtime + * + * Description: + * Adjust the frequency and/or phase of a clock. + * This function allows the adjustment of the frequency and/or phase of a + * specified clock. It can be used to synchronize the clock with an + * external time source or to apply a frequency offset. + * + * Input Parameters: + * clk_id - The identifier of the clock to be adjusted. This is typically + * one of the predefined clock IDs such as CLOCK_REALTIME, + * CLOCK_MONOTONIC, or CLOCK_BOOTTIME. + * + * buf - A pointer to a `timex` structure that specifies the adjustment + * parameters. This structure includes fields for the frequency + * adjustment (`freq`), the maximum frequency error (`maxerror`), + * the estimated error (`esterror`), the phase offset (`offset`), + * and flags to indicate the type of adjustment (`status`). + * + * Returned Value: + * Return On success, the function returns 0. On error, it returns + * -1 and sets 'errno` to indicate the specific error that + * occurred. + * + ****************************************************************************/ + +#ifdef CONFIG_CLOCK_ADJTIME +int clock_adjtime(clockid_t clk_id, FAR struct timex *buf); +#endif + #undef EXTERN #if defined(__cplusplus) } diff --git a/sched/Kconfig b/sched/Kconfig index 4e17db70868fc..36755124f0724 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -208,7 +208,7 @@ config ARCH_HAVE_ADJTIME config CLOCK_ADJTIME bool "Support adjtime function" default n - depends on ARCH_HAVE_ADJTIME || RTC_ADJTIME + depends on ARCH_HAVE_ADJTIME || RTC_ADJTIME || PTP_CLOCK ---help--- Enables usage of adjtime() interface used to correct the system time clock. This requires specific architecture support. @@ -216,6 +216,9 @@ config CLOCK_ADJTIME Adjustment can affect system timer period and/or high-resolution RTC. These are implemented by interfaces up_adjtime() and up_rtc_adjtime(). + Enables usage of clock_adjtime() interface used to correct the system + and other ptp time clock. This requires ptp clock support. + This is not a POSIX interface but derives from 4.3BSD, System V. It is also supported for Linux compatibility. diff --git a/sched/clock/clock_adjtime.c b/sched/clock/clock_adjtime.c index a46924b70333e..4091cef2c771e 100644 --- a/sched/clock/clock_adjtime.c +++ b/sched/clock/clock_adjtime.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include "clock/clock.h" @@ -215,4 +217,96 @@ int adjtime(FAR const struct timeval *delta, FAR struct timeval *olddelta) } } +/**************************************************************************** + * Name: nxclock_adjtime + * + * Description: + * Adjust the frequency and/or phase of a clock. + * This function allows the adjustment of the frequency and/or phase of a + * specified clock. It can be used to synchronize the clock with an + * external time source or to apply a frequency offset. + * + * Input Parameters: + * clk_id - The identifier of the clock to be adjusted. This is typically + * one of the predefined clock IDs such as CLOCK_REALTIME, + * CLOCK_MONOTONIC, or CLOCK_BOOTTIME. + * + * buf - A pointer to a `timex` structure that specifies the adjustment + * parameters. This structure includes fields for the frequency + * adjustment (`freq`), the maximum frequency error (`maxerror`), + * the estimated error (`esterror`), the phase offset (`offset`), + * and flags to indicate the type of adjustment (`status`). + * + * Returned Value: + * Return On success, the function returns 0. On error, it returns + * -1 and sets 'errno` to indicate the specific error that + * occurred. + * + ****************************************************************************/ + +int nxclock_adjtime(clockid_t clock_id, FAR struct timex *buf) +{ + int ret = -EINVAL; + +#ifdef CONFIG_PTP_CLOCK + if ((clock_id & CLOCK_MASK) == CLOCK_FD) + { + FAR struct file *filep; + + ret = ptp_clockid_to_filep(clock_id, &filep); + if (ret < 0) + { + return ret; + } + + ret = file_ioctl(filep, PTP_CLOCK_ADJTIME, + (unsigned long)(uintptr_t)buf); + fs_putfilep(filep); + } +#endif + + return ret; +} + +/**************************************************************************** + * Name: clock_adjtime + * + * Description: + * Adjust the frequency and/or phase of a clock. + * This function allows the adjustment of the frequency and/or phase of a + * specified clock. It can be used to synchronize the clock with an + * external time source or to apply a frequency offset. + * + * Input Parameters: + * clk_id - The identifier of the clock to be adjusted. This is typically + * one of the predefined clock IDs such as CLOCK_REALTIME, + * CLOCK_MONOTONIC, or CLOCK_BOOTTIME. + * + * buf - A pointer to a `timex` structure that specifies the adjustment + * parameters. This structure includes fields for the frequency + * adjustment (`freq`), the maximum frequency error (`maxerror`), + * the estimated error (`esterror`), the phase offset (`offset`), + * and flags to indicate the type of adjustment (`status`). + * + * Returned Value: + * Return On success, the function returns 0. On error, it returns + * -1 and sets 'errno` to indicate the specific error that + * occurred. + * + ****************************************************************************/ + +int clock_adjtime(clockid_t clk_id, FAR struct timex *buf) +{ + int ret; + + ret = nxclock_adjtime(clk_id, buf); + if (ret < 0) + { + set_errno(-ret); + return ERROR; + } + + return ret; +} + #endif /* CONFIG_CLOCK_ADJTIME */ From 4742a80224186f80b9d59158ef23bac91824c936 Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Thu, 11 Dec 2025 22:58:59 +0800 Subject: [PATCH 06/11] Documentation: Add PTP clock driver framework documentation This patch adds comprehensive documentation for the PTP (Precision Time Protocol) clock driver framework in NuttX. The documentation covers: 1. Overview and Architecture: - IEEE 1588 PTP clock framework introduction - Upper-half and lower-half driver architecture - Integration with POSIX clock APIs 2. Configuration Options: - CONFIG_PTP_CLOCK: Main framework configuration - CONFIG_PTP_CLOCK_DUMMY: Dummy driver for testing - CONFIG_CLOCK_ADJTIME: clock_adjtime() system call support 3. Device Interface: - Character device interface (/dev/ptpN) - IOCTL commands: PTP_CLOCK_GETTIME, PTP_CLOCK_SETTIME, PTP_CLOCK_GETRES, PTP_CLOCK_ADJTIME, PTP_CLOCK_GETCAPS, PTP_SYS_OFFSET, PTP_SYS_OFFSET_PRECISE 4. POSIX Clock API (CLOCKFD): - Using CLOCKFD() macro to access PTP clocks - clock_gettime(), clock_settime(), clock_getres() examples - clock_adjtime() with various adjustment modes - ADJ_OFFSET, ADJ_FREQUENCY, ADJ_SETOFFSET support 5. Dummy PTP Clock Driver: - Software-based implementation for testing - Features and initialization details 6. Example Usage: - Basic time operations - Frequency adjustment examples - Time offset adjustment examples 7. Implementing Lower-Half Drivers: - Step-by-step guide for hardware driver implementation - Required operations and structures - Registration process 8. Integration with PTP Daemons: - ptp4l, timemaster, ptpd compatibility - Standard POSIX clock API usage 9. Performance Considerations: - Hardware timestamping requirements - Cross-timestamping support - Frequency adjustment resolution 10. Debugging: - Debug configuration options - Debug output examples The documentation is added to: - Documentation/components/drivers/special/ptp.rst (new file) - Documentation/components/drivers/special/index.rst (updated) This provides developers with complete reference material for using and implementing PTP clock drivers in NuttX. Signed-off-by: dongjiuzhu1 --- .../components/drivers/special/index.rst | 1 + .../components/drivers/special/ptp.rst | 497 ++++++++++++++++++ 2 files changed, 498 insertions(+) create mode 100644 Documentation/components/drivers/special/ptp.rst diff --git a/Documentation/components/drivers/special/index.rst b/Documentation/components/drivers/special/index.rst index f2df595c82693..0d0e1829417e5 100644 --- a/Documentation/components/drivers/special/index.rst +++ b/Documentation/components/drivers/special/index.rst @@ -53,6 +53,7 @@ following section. pinctrl.rst pipes.rst power/index.rst + ptp.rst virtio.rst video.rst wireless.rst diff --git a/Documentation/components/drivers/special/ptp.rst b/Documentation/components/drivers/special/ptp.rst new file mode 100644 index 0000000000000..2fa0182de3764 --- /dev/null +++ b/Documentation/components/drivers/special/ptp.rst @@ -0,0 +1,497 @@ +=========================== +PTP Clock Driver Framework +=========================== + +Overview +======== + +The PTP (Precision Time Protocol) Clock driver framework provides support for +IEEE 1588 compliant hardware clocks in NuttX. This framework enables precise +time synchronization across networked systems, achieving accuracy within +microseconds or even nanoseconds with hardware timestamping support. + +The PTP clock framework follows a layered architecture with upper-half driver +logic in the kernel and lower-half hardware-specific implementations, similar +to other NuttX device drivers. + +Architecture +============ + +The PTP clock framework consists of the following components: + +Upper-Half Driver +----------------- + +The upper-half driver (``drivers/timers/ptp_clock.c``) provides: + +- Character device interface (``/dev/ptpN``) +- Common ioctl command handling +- Frequency and time adjustment logic +- Cross-timestamp support +- Interface to POSIX clock APIs via CLOCKFD mechanism + +Lower-Half Driver +----------------- + +Hardware-specific drivers implement the ``struct ptp_lowerhalf_s`` interface +with the following operations: + +.. code-block:: c + + struct ptp_clock_ops_s + { + CODE int (*adjfine)(FAR struct ptp_lowerhalf_s *lower, long scaled_ppm); + CODE int (*adjtime)(FAR struct ptp_lowerhalf_s *lower, int64_t delta); + CODE int (*gettime)(FAR struct ptp_lowerhalf_s *lower, + FAR struct timespec *ts); + CODE int (*settime)(FAR struct ptp_lowerhalf_s *lower, + FAR const struct timespec *ts); + CODE int (*getcaps)(FAR struct ptp_lowerhalf_s *lower, + FAR struct ptp_clock_caps *caps); + CODE int (*getcrosststamp)(FAR struct ptp_lowerhalf_s *lower, + FAR struct system_device_crosststamp *xts); + }; + +Configuration Options +===================== + +The PTP clock framework can be enabled with the following Kconfig options: + +``CONFIG_PTP_CLOCK`` + Enable PTP clock driver framework support. This provides the upper-half + driver infrastructure and POSIX clock API integration. + +``CONFIG_PTP_CLOCK_DUMMY`` + Enable a software-based dummy PTP clock driver for testing and development. + This driver provides a PTP clock implementation without hardware support, + using the system monotonic clock as the time base. + +``CONFIG_CLOCK_ADJTIME`` + Enable the ``clock_adjtime()`` system call, required for frequency and + phase adjustments of PTP clocks. + +Device Interface +================ + +Character Device +---------------- + +PTP clocks are exposed as character devices with names like ``/dev/ptp0``, +``/dev/ptp1``, etc. Applications can open these devices and perform operations +using ioctl commands. + +IOCTL Commands +-------------- + +The following ioctl commands are supported: + +``PTP_CLOCK_GETTIME`` + Get the current time from the PTP clock. + + .. code-block:: c + + struct ptp_clock_time time; + ioctl(fd, PTP_CLOCK_GETTIME, &time); + +``PTP_CLOCK_SETTIME`` + Set the time of the PTP clock. + + .. code-block:: c + + struct ptp_clock_time time; + time.sec = 1234567890; + time.nsec = 123456789; + ioctl(fd, PTP_CLOCK_SETTIME, &time); + +``PTP_CLOCK_GETRES`` + Get the resolution of the PTP clock. + + .. code-block:: c + + struct timespec res; + ioctl(fd, PTP_CLOCK_GETRES, &res); + +``PTP_CLOCK_ADJTIME`` + Adjust the time or frequency of the PTP clock. + + .. code-block:: c + + struct timex tx; + memset(&tx, 0, sizeof(tx)); + tx.modes = ADJ_FREQUENCY; + tx.freq = 10000000; /* +10 PPM */ + ioctl(fd, PTP_CLOCK_ADJTIME, &tx); + +``PTP_CLOCK_GETCAPS`` + Get the capabilities of the PTP clock. + + .. code-block:: c + + struct ptp_clock_caps caps; + ioctl(fd, PTP_CLOCK_GETCAPS, &caps); + printf("Max adjustment: %d PPB\n", caps.max_adj); + +``PTP_SYS_OFFSET`` + Measure the offset between the PTP clock and system time. + + .. code-block:: c + + struct ptp_sys_offset offset; + offset.n_samples = 10; + ioctl(fd, PTP_SYS_OFFSET, &offset); + +``PTP_SYS_OFFSET_PRECISE`` + Get precise system-device cross-timestamp. + + .. code-block:: c + + struct ptp_sys_offset_precise precise; + ioctl(fd, PTP_SYS_OFFSET_PRECISE, &precise); + +POSIX Clock API (CLOCKFD) +========================== + +NuttX implements the CLOCKFD mechanism, allowing PTP clocks to be accessed +through standard POSIX clock APIs. This provides a more familiar interface +for applications already using ``clock_gettime()``, ``clock_settime()``, +``clock_getres()``, and ``clock_adjtime()``. + +The CLOCKFD mechanism works by encoding a file descriptor into a clockid_t +value using the ``CLOCKFD()`` macro: + +.. code-block:: c + + #include + #include + #include + + int fd = open("/dev/ptp0", O_RDONLY); + struct timespec ts; + + /* Get PTP clock time using POSIX API */ + clock_gettime(CLOCKFD(fd), &ts); + + /* Set PTP clock time */ + clock_settime(CLOCKFD(fd), &ts); + + /* Get PTP clock resolution */ + struct timespec res; + clock_getres(CLOCKFD(fd), &res); + + /* Adjust PTP clock frequency */ + struct timex tx = {0}; + tx.modes = ADJ_FREQUENCY; + tx.freq = -5000000; /* -5 PPM */ + clock_adjtime(CLOCKFD(fd), &tx); + + close(fd); + +Supported Adjustment Modes +--------------------------- + +The ``clock_adjtime()`` function supports the following adjustment modes +via ``struct timex``: + +- ``ADJ_OFFSET``: Apply time offset adjustment +- ``ADJ_FREQUENCY``: Adjust clock frequency in scaled PPM +- ``ADJ_MAXERROR``: Set maximum time error estimate +- ``ADJ_ESTERROR``: Set estimated time error +- ``ADJ_STATUS``: Modify clock status bits +- ``ADJ_TIMECONST``: Set PLL time constant +- ``ADJ_SETOFFSET``: Set absolute time offset (with ``ADJ_NANO`` flag) +- ``ADJ_MICRO``: Interpret time values as microseconds +- ``ADJ_NANO``: Interpret time values as nanoseconds + +Dummy PTP Clock Driver +======================= + +NuttX provides a software-based dummy PTP clock driver for testing and +development purposes. This driver can be used on platforms without hardware +PTP support. + +Features +-------- + +- Software-based PTP clock using system monotonic clock +- Supports all standard PTP clock operations +- Frequency adjustment simulation +- Time offset adjustment +- Suitable for testing PTP applications without hardware + +Initialization +-------------- + +The dummy driver is automatically initialized when ``CONFIG_PTP_CLOCK_DUMMY`` +is enabled. It creates a ``/dev/ptp0`` device node on system startup. + +Example Usage +============= + +Basic Time Operations +--------------------- + +.. code-block:: c + + #include + #include + #include + #include + + int main(void) + { + int fd; + struct timespec ts; + struct timespec res; + + /* Open PTP clock device */ + fd = open("/dev/ptp0", O_RDWR); + if (fd < 0) + { + perror("Failed to open PTP clock"); + return -1; + } + + /* Get current PTP clock time */ + if (clock_gettime(CLOCKFD(fd), &ts) == 0) + { + printf("PTP time: %ld.%09ld\n", ts.tv_sec, ts.tv_nsec); + } + + /* Get PTP clock resolution */ + if (clock_getres(CLOCKFD(fd), &res) == 0) + { + printf("PTP resolution: %ld.%09ld\n", res.tv_sec, res.tv_nsec); + } + + close(fd); + return 0; + } + +Frequency Adjustment +-------------------- + +.. code-block:: c + + #include + #include + #include + #include + #include + + int main(void) + { + int fd; + struct timex tx; + + fd = open("/dev/ptp0", O_RDWR); + if (fd < 0) + { + perror("Failed to open PTP clock"); + return -1; + } + + /* Adjust frequency by +10 PPM */ + memset(&tx, 0, sizeof(tx)); + tx.modes = ADJ_FREQUENCY; + tx.freq = 10000000; /* 10 PPM in scaled PPM (65536 * PPM) */ + + if (clock_adjtime(CLOCKFD(fd), &tx) == 0) + { + printf("Frequency adjusted successfully\n"); + } + else + { + perror("Failed to adjust frequency"); + } + + close(fd); + return 0; + } + +Time Offset Adjustment +---------------------- + +.. code-block:: c + + #include + #include + #include + #include + #include + + int main(void) + { + int fd; + struct timex tx; + + fd = open("/dev/ptp0", O_RDWR); + if (fd < 0) + { + perror("Failed to open PTP clock"); + return -1; + } + + /* Apply time offset: +1 second */ + memset(&tx, 0, sizeof(tx)); + tx.modes = ADJ_SETOFFSET | ADJ_NANO; + tx.time.tv_sec = 1; + tx.time.tv_usec = 0; /* tv_usec holds nanoseconds when ADJ_NANO is set */ + + if (clock_adjtime(CLOCKFD(fd), &tx) == 0) + { + printf("Time offset applied successfully\n"); + } + else + { + perror("Failed to apply time offset"); + } + + close(fd); + return 0; + } + +Implementing a Lower-Half Driver +================================= + +To implement a hardware-specific PTP clock driver, create a lower-half driver +that implements the ``struct ptp_lowerhalf_s`` interface: + +.. code-block:: c + + #include + + /* Hardware-specific state */ + struct my_ptp_lowerhalf_s + { + struct ptp_lowerhalf_s base; /* Must be first */ + /* Hardware-specific fields */ + uint32_t hw_base_addr; + /* ... */ + }; + + /* Implement required operations */ + static int my_ptp_adjfine(FAR struct ptp_lowerhalf_s *lower, + long scaled_ppm) + { + FAR struct my_ptp_lowerhalf_s *priv = + (FAR struct my_ptp_lowerhalf_s *)lower; + + /* Adjust hardware clock frequency */ + /* ... hardware-specific code ... */ + + return OK; + } + + static int my_ptp_gettime(FAR struct ptp_lowerhalf_s *lower, + FAR struct timespec *ts) + { + FAR struct my_ptp_lowerhalf_s *priv = + (FAR struct my_ptp_lowerhalf_s *)lower; + + /* Read time from hardware */ + /* ... hardware-specific code ... */ + + return OK; + } + + /* Define operations structure */ + static const struct ptp_clock_ops_s g_my_ptp_ops = + { + .adjfine = my_ptp_adjfine, + .adjtime = my_ptp_adjtime, + .gettime = my_ptp_gettime, + .settime = my_ptp_settime, + .getcaps = my_ptp_getcaps, + .getcrosststamp = my_ptp_getcrosststamp, + }; + + /* Registration function */ + int my_ptp_register(void) + { + FAR struct my_ptp_lowerhalf_s *priv; + + priv = kmm_zalloc(sizeof(struct my_ptp_lowerhalf_s)); + if (priv == NULL) + { + return -ENOMEM; + } + + priv->base.ops = &g_my_ptp_ops; + + /* Initialize hardware */ + /* ... */ + + return ptp_clock_register(&priv->base, 0); /* Register as /dev/ptp0 */ + } + +Integration with PTP Daemon +=========================== + +The PTP clock framework is designed to work with standard PTP synchronization +daemons such as: + +- **ptp4l**: IEEE 1588 PTP daemon from the linuxptp project +- **timemaster**: Synchronization manager combining PTP and NTP +- **ptpd**: PTP daemon (IEEE 1588-2008 implementation) + +These daemons can use the PTP clock devices through the standard POSIX clock +APIs via the CLOCKFD mechanism, making porting straightforward. + +Performance Considerations +========================== + +Hardware Timestamping +--------------------- + +For best synchronization accuracy (sub-microsecond), PTP clocks should support +hardware timestamping of network packets. This requires coordination between +the PTP clock driver and network interface driver. + +Cross-Timestamping +------------------ + +The ``getcrosststamp()`` operation provides synchronized capture of both the +PTP clock and system time, which is essential for: + +- Accurate offset measurements +- System time synchronization from PTP clock +- Minimizing measurement errors + +Frequency Adjustment Resolution +-------------------------------- + +The frequency adjustment resolution depends on hardware capabilities. Most +hardware supports adjustments in the range of: + +- Maximum: ±500 to ±1000 parts per million (PPM) +- Resolution: Better than 1 part per billion (PPB) + +Debugging +========= + +Debug output can be enabled using the ``CONFIG_DEBUG_PTPCLK_*`` configuration +options: + +- ``CONFIG_DEBUG_PTPCLK_ERROR``: Error messages +- ``CONFIG_DEBUG_PTPCLK_WARN``: Warning messages +- ``CONFIG_DEBUG_PTPCLK_INFO``: Informational messages + +Example debug output: + +.. code-block:: text + + ptpclk: PTP clock registered as /dev/ptp0 + ptpclk: adjfine: scaled_ppm=656360 (10 PPM) + ptpclk: gettime: ts=1234567890.123456789 + +References +========== + +- IEEE 1588-2008: IEEE Standard for a Precision Clock Synchronization Protocol + for Networked Measurement and Control Systems + +- `Linux PTP Project `_ + +- ``include/nuttx/timers/ptp_clock.h`` - PTP clock header file +- ``drivers/timers/ptp_clock.c`` - Upper-half driver implementation +- ``drivers/timers/ptp_clock_dummy.c`` - Dummy driver implementation From 53e709cde37cb28a29d875ee8451c83ead97f3af Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Wed, 2 Apr 2025 23:18:56 +0800 Subject: [PATCH 07/11] drivers/ptp_clock: pass ppb to driver to adjust frequency using Desired frequency offset from nominal frequency in parts per billion(ppb) to adjust frequency Signed-off-by: dongjiuzhu1 --- drivers/timers/ptp_clock.c | 2 +- include/nuttx/timers/ptp_clock.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/timers/ptp_clock.c b/drivers/timers/ptp_clock.c index 8ad6b3ca42dea..02711a9fc6de2 100644 --- a/drivers/timers/ptp_clock.c +++ b/drivers/timers/ptp_clock.c @@ -139,7 +139,7 @@ static int ptp_clock_adjtime(FAR struct ptp_lowerhalf_s *lower, return -ERANGE; } - ret = lower->ops->adjfine(lower, tx->freq); + ret = lower->ops->adjfine(lower, ppb); upper->adj_freq = tx->freq; } else if ((tx->modes & ADJ_OFFSET) && diff --git a/include/nuttx/timers/ptp_clock.h b/include/nuttx/timers/ptp_clock.h index 3733abc6ec471..e486d2a20e59e 100644 --- a/include/nuttx/timers/ptp_clock.h +++ b/include/nuttx/timers/ptp_clock.h @@ -148,16 +148,16 @@ struct ptp_ops_s * Adjusts the frequency of the hardware clock. * * Input Parameters: - * lower - The instance of lower half ptp driver - * scaled_ppm - Desired frequency offset from nominal frequency in parts - * per million, but with a 16 bit binary fractional field. + * lower - The instance of lower half ptp driver + * ppb - Desired frequency offset from nominal frequency in parts + * per billion. * * Returned Value: * Zero (OK) or positive on success; a negated errno value on failure. * **************************************************************************/ - CODE int (*adjfine)(FAR struct ptp_lowerhalf_s *lower, long scaled_ppm); + CODE int (*adjfine)(FAR struct ptp_lowerhalf_s *lower, long ppb); /************************************************************************** * Name: adjphase From a6fb176efeb17dac7922ea9e6dd928397c6523f6 Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Wed, 12 Nov 2025 17:35:22 +0800 Subject: [PATCH 08/11] drivers/ptp: add set/get statistics interface for driver using ptp_statistics_s structure to record statistics info about ptp daemon Signed-off-by: dongjiuzhu1 --- drivers/timers/ptp_clock.c | 6 +++++ include/nuttx/timers/ptp_clock.h | 44 ++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/drivers/timers/ptp_clock.c b/drivers/timers/ptp_clock.c index 02711a9fc6de2..90555363b766f 100644 --- a/drivers/timers/ptp_clock.c +++ b/drivers/timers/ptp_clock.c @@ -340,6 +340,12 @@ static int ptp_clock_ioctl(FAR struct file *filep, int cmd, break; default: + { + if (lower->ops->control) + { + ret = lower->ops->control(lower, cmd, arg); + } + } break; } diff --git a/include/nuttx/timers/ptp_clock.h b/include/nuttx/timers/ptp_clock.h index e486d2a20e59e..f3277b070d3d6 100644 --- a/include/nuttx/timers/ptp_clock.h +++ b/include/nuttx/timers/ptp_clock.h @@ -50,10 +50,15 @@ #define PTP_SYS_OFFSET_PRECISE2 _PTPIOC(0xb) #define PTP_SYS_OFFSET_EXTENDED2 _PTPIOC(0xc) +#define PTP_CLOCK_SETSTATS _PTPIOC(0xd) +#define PTP_CLOCK_GETSTATS _PTPIOC(0xe) + /* Maximum allowed offset measurement samples. */ #define PTP_MAX_SAMPLES 25 +#define PTP_STATS_NUM 10 + /**************************************************************************** * Inline Functions ****************************************************************************/ @@ -62,6 +67,25 @@ * Public Types ****************************************************************************/ +/* PTP statistics info */ + +struct ptp_statistics_s +{ + struct timespec origin_time[PTP_STATS_NUM]; + struct timespec correction_time[PTP_STATS_NUM]; + int64_t delta_ns[PTP_STATS_NUM]; + int64_t adjustment_ns[PTP_STATS_NUM]; + int32_t drift_ppb[PTP_STATS_NUM]; + int32_t follow_up_error; + int32_t sync_error; + int32_t announce; + int32_t sync; + int32_t follow_up; + int32_t delay_resp; + int32_t delay_req; + int32_t unknown; +}; + /* struct system_device_crosststamp - system/device cross-timestamp * (synchronized capture) */ @@ -273,6 +297,26 @@ struct ptp_ops_s CODE int (*getres)(FAR struct ptp_lowerhalf_s *lower, FAR struct timespec *res); + + /************************************************************************** + * Name: control + * + * Description: + * Performs platform-specific operations. + * + * Input Parameters: + * lower - The instance of lower half ptp driver. + * cmd - The command to perform. + * arg - The argument of the command. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * -ENOTTY - The cmd don't support. + * + **************************************************************************/ + + CODE int (*control)(FAR struct ptp_lowerhalf_s *lower, + int cmd, unsigned long arg); }; /* The ptp lower half driver interface, describes a PTP hardware From 8d296fa8245cf1344a14343fadc0b7a961ee838e Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Tue, 30 Dec 2025 10:46:59 +0800 Subject: [PATCH 09/11] Documentaion/ptp: add documentaion about system ptpd add introduction about ptpd Signed-off-by: dongjiuzhu1 --- .../applications/system/ptpd/index.rst | 606 ++++++++++++++++++ 1 file changed, 606 insertions(+) diff --git a/Documentation/applications/system/ptpd/index.rst b/Documentation/applications/system/ptpd/index.rst index e431980ef6fec..88a1710fe8661 100644 --- a/Documentation/applications/system/ptpd/index.rst +++ b/Documentation/applications/system/ptpd/index.rst @@ -1,3 +1,609 @@ ============================ ``ptpd`` PTP daemon commands ============================ +Overview +======== + +The ``ptpd`` application provides a complete IEEE 1588-2008 Precision Time +Protocol (PTPv2) implementation for NuttX. This daemon enables sub-microsecond +time synchronization across networked systems, supporting both client (slave) +and server (master) modes. + +The PTP daemon can synchronize the system clock to a remote PTP master with +accuracy better than 1 microsecond when using software timestamps, or better +than 500 nanoseconds with hardware timestamping support. + +Features +-------- + +- **IEEE 1588-2008 PTPv2 compliant** implementation +- **Dual role support**: operates as both PTP master and slave +- **Multiple transport options**: + + - UDP over IPv4 (default) + - UDP over IPv6 + - IEEE 802.3 Ethernet (Layer 2) + +- **Hardware timestamping** support for enhanced accuracy +- **PTP clock device integration** via ``/dev/ptp*`` devices +- **BMCA (Best Master Clock Algorithm)** for automatic master selection +- **Two delay mechanisms**: + + - End-to-End (E2E) delay measurement + - Peer-to-Peer (P2P) delay measurement + +- **gPTP support** with switch path delay correction +- **Clock drift compensation** with automatic frequency adjustment +- **Client-only mode** for dedicated slave operation + +Architecture +============ + +The ptpd implementation consists of two main components: + +Upper Layer (``netutils/ptpd/``) +--------------------------------- + +Provides the core PTP protocol implementation: + +- PTP packet encoding/decoding (Announce, Sync, Follow-Up, Delay_Req, Delay_Resp) +- BMCA (Best Master Clock Algorithm) for master selection +- Clock synchronization and adjustment algorithms +- Path delay measurement and compensation +- Network socket management (multicast, unicast) +- Hardware and software timestamping support + +Lower Layer (``system/ptpd/``) +------------------------------- + +Provides the command-line interface and daemon management: + +- Command-line argument parsing +- Configuration management +- Daemon lifecycle control (start, stop, status) +- Status reporting and monitoring + +Configuration Options +===================== + +The PTP daemon can be configured through Kconfig options in +``apps/netutils/ptpd/Kconfig``. Key configuration parameters include: + +Domain and Priority +------------------- + +``CONFIG_NETUTILS_PTPD_DOMAIN`` (default: 0) + PTP domain number (0-127). Isolates different PTP domains on the same network. + +``CONFIG_NETUTILS_PTPD_PRIORITY1`` (default: 128) + Primary priority field (0-255). Lower value = higher priority in BMCA. + +``CONFIG_NETUTILS_PTPD_PRIORITY2`` (default: 128) + Secondary priority field (0-255). Used when priority1 values are equal. + +Clock Quality +------------- + +``CONFIG_NETUTILS_PTPD_CLASS`` (default: 248) + Clock class value (0-255): + + - 6: Primary reference (e.g., GPS) + - 13: Application-specific time source + - 52: Degraded mode + - 248: Default (unknown) + +``CONFIG_NETUTILS_PTPD_ACCURACY`` (default: 254) + Clock accuracy on logarithmic scale: + + - 32: ±25 ns + - 35: ±1 μs + - 39: ±100 μs + - 41: ±1 ms + - 47: ±1 s + - 254: Unknown + +``CONFIG_NETUTILS_PTPD_CLOCKSOURCE`` (default: 160) + Time source type: + + - 32: GPS + - 64: PTP + - 80: NTP + - 160: Internal oscillator + +Timing Parameters +----------------- + +``CONFIG_NETUTILS_PTPD_SYNC_INTERVAL_MSEC`` (default: 1000) + Interval between Sync messages when acting as master (milliseconds). + +``CONFIG_NETUTILS_PTPD_ANNOUNCE_INTERVAL_MSEC`` (default: 10000) + Interval between Announce messages when acting as master (milliseconds). + +``CONFIG_NETUTILS_PTPD_TIMEOUT_MS`` (default: 60000) + Timeout for switching to alternate clock source (milliseconds). + +Adjustment Thresholds +--------------------- + +``CONFIG_NETUTILS_PTPD_SETTIME_THRESHOLD_MS`` (default: 1000) + Clock offset threshold for using ``settimeofday()`` instead of ``adjtime()``. + If offset exceeds this value, time is stepped rather than slewed. + +``CONFIG_NETUTILS_PTPD_ADJTIME_THRESHOLD_NS`` (default: 500) + Threshold for using current PPB instead of accumulated PPB to accelerate + adjustment (nanoseconds). + +``CONFIG_NETUTILS_PTPD_DRIFT_AVERAGE_S`` (default: 600) + Time period for averaging clock drift rate (seconds, 10-86400). + +Path Delay +---------- + +``CONFIG_NETUTILS_PTPD_MAX_PATH_DELAY_NS`` (default: 100000) + Maximum acceptable path delay (nanoseconds). Longer delays are ignored. + +``CONFIG_NETUTILS_PTPD_DELAYREQ_AVGCOUNT`` (default: 100) + Number of samples for path delay averaging. + +Command Line Interface +====================== + +Usage +----- + +.. code-block:: console + + ptpd [options] + +The daemon must be run in background mode using the ``&`` operator. + +Options +------- + +**Mode Selection** + +``-s`` + Enable client-only mode (slave only, no master capability) + +**Network Transport** + +``-2`` + Use IEEE 802.3 Ethernet transport (Layer 2, raw sockets) + +``-4`` + Use UDP over IPv4 (default) + +``-6`` + Use UDP over IPv6 + +**Time Stamping** + +``-H`` + Use hardware timestamping (default if ``CONFIG_NET_TIMESTAMP`` is enabled) + +``-S`` + Use software timestamping (fallback mode) + +**Protocol Options** + +``-B`` + Enable Best Master Clock Algorithm messages + +``-E`` + Use End-to-End (E2E) delay mechanism (supports Delay_Req/Delay_Resp) + +``-r`` + Synchronize system realtime clock (instead of PTP clock device) + +**Device Configuration** + +``-i [device]`` + Network interface to use (e.g., ``eth0``, ``eth1``) + +``-p [device]`` + PTP clock device to use (e.g., ``ptp0``). If not specified, uses ``realtime``. + +**Daemon Control** + +``-t [pid]`` + Query and display status of running PTP daemon + +``-d [pid]`` + Stop PTP daemon with given PID + +Examples +======== + +Start as PTP Client (Slave) +---------------------------- + +Basic client synchronization using IPv4 UDP: + +.. code-block:: console + + nsh> ptpd -i eth0 & + [PTP] Starting ptpd on interface eth0 + [PTP] Operating in client+server mode + [PTP] Using software timestamps + +Client-only mode with hardware timestamping: + +.. code-block:: console + + nsh> ptpd -s -H -i eth0 & + [PTP] Starting ptpd on interface eth0 + [PTP] Client-only mode + [PTP] Using hardware timestamps + +Start as PTP Master (Server) +----------------------------- + +Act as PTP master using BMCA: + +.. code-block:: console + + nsh> ptpd -B -i eth0 & + [PTP] Starting ptpd on interface eth0 + [PTP] BMCA enabled + +Layer 2 Ethernet Transport +--------------------------- + +Use IEEE 802.3 Ethernet transport for gPTP: + +.. code-block:: console + + nsh> ptpd -2 -i eth0 & + [PTP] Starting ptpd on interface eth0 + [PTP] Using Ethernet transport (raw socket) + +PTP Clock Device Integration +----------------------------- + +Synchronize PTP hardware clock instead of system clock: + +.. code-block:: console + + nsh> ptpd -i eth0 -p ptp0 & + [PTP] Starting ptpd on interface eth0 + [PTP] Using PTP clock device /dev/ptp0 + +Query Daemon Status +------------------- + +Check synchronization status of running daemon: + +.. code-block:: console + + nsh> ptpd -t 42 + PTPD (PID 42) status: + - clock_source_valid: 1 + |- id: 00 1a 2b 3c 4d 5e 6f 70 + |- utcoffset: 37 + |- priority1: 128 + |- class: 6 + |- accuracy: 32 + |- variance: 4321 + |- priority2: 128 + |- gm_id: 00 1a 2b 3c 4d 5e 6f 70 + |- stepsremoved: 0 + '- timesource: 32 + - last_clock_update: 2025-12-15T10:30:45.123456789 + - last_delta_ns: 234 + - last_adjtime_ns: -123 + - drift_ppb: 1234 + - path_delay_ns: 5678 + - last_received_multicast: 0 s ago + - last_received_announce: 2 s ago + - last_received_sync: 0 s ago + +Stop Daemon +----------- + +Stop running PTP daemon: + +.. code-block:: console + + nsh> ptpd -d 42 + Stopped ptpd + +Status Information +================== + +When querying status with ``-t [pid]``, the following information is displayed: + +Clock Source Information +------------------------ + +- **clock_source_valid**: Whether a valid PTP master has been selected +- **id**: PTP clock identity (EUI-64 format) +- **utcoffset**: Offset between TAI and UTC (seconds) +- **priority1/priority2**: BMCA priority fields +- **class**: Clock class (6=GPS, 13=application, 248=default) +- **accuracy**: Accuracy specification (32=±25ns, 254=unknown) +- **variance**: Clock variance estimate +- **gm_id**: Grandmaster clock identity +- **stepsremoved**: Hops from grandmaster +- **timesource**: Source type (32=GPS, 64=PTP, 80=NTP, 160=internal) + +Synchronization Status +----------------------- + +- **last_clock_update**: Timestamp of last clock adjustment +- **last_delta_ns**: Latest measured clock offset (nanoseconds) +- **last_adjtime_ns**: Previously applied adjustment offset +- **drift_ppb**: Averaged clock drift rate (parts per billion) +- **path_delay_ns**: Network path delay estimate (nanoseconds) + +Activity Timestamps +------------------- + +Time since last received/transmitted PTP message: + +- **last_received_multicast**: Any PTP multicast packet +- **last_received_announce**: Announce message from any master +- **last_received_sync**: Sync message from selected master +- **last_transmitted_sync**: Sync message (when acting as master) +- **last_transmitted_announce**: Announce message (when acting as master) +- **last_transmitted_delayresp**: Delay response (when acting as master) +- **last_transmitted_delayreq**: Delay request (when acting as slave) + +API Functions +============= + +The ptpd library provides C API functions for programmatic control: + +ptpd_start() +------------ + +.. code-block:: c + + int ptpd_start(FAR const struct ptpd_config_s *config); + +Start PTP daemon with specified configuration. + +**Parameters:** + +- ``config``: Pointer to configuration structure + +**Returns:** + +- Does not return on success (runs as daemon) +- Negative errno value on error + +**Configuration Structure:** + +.. code-block:: c + + struct ptpd_config_s + { + FAR const char *interface; /* Network interface (e.g., "eth0") */ + FAR const char *clock; /* Clock device (e.g., "ptp0", "realtime") */ + bool client_only; /* Client-only mode flag */ + bool hardware_ts; /* Hardware timestamping flag */ + bool delay_e2e; /* E2E delay mechanism flag */ + bool bmca; /* BMCA enable flag */ + sa_family_t af; /* Address family (AF_INET, AF_INET6, AF_PACKET) */ + }; + +ptpd_status() +------------- + +.. code-block:: c + + int ptpd_status(pid_t pid, FAR struct ptpd_status_s *status); + +Query status of running PTP daemon. + +**Parameters:** + +- ``pid``: Process ID of ptpd daemon +- ``status``: Pointer to status structure to fill + +**Returns:** + +- ``OK`` on success +- Negative errno value on error + +ptpd_stop() +----------- + +.. code-block:: c + + int ptpd_stop(pid_t pid); + +Stop running PTP daemon. + +**Parameters:** + +- ``pid``: Process ID of ptpd daemon to stop + +**Returns:** + +- ``OK`` on success +- Negative errno value on error + +Implementation Details +====================== + +Synchronization Algorithm +-------------------------- + +The PTP daemon uses a multi-stage synchronization approach: + +1. **Clock Selection (BMCA)** + + - Evaluates Announce messages from all masters + - Selects best clock source based on priority, class, accuracy + - Switches sources if current master times out + +2. **Offset Measurement** + + - Measures clock offset using Sync and Follow-Up messages + - Compensates for network path delay using Delay_Req/Delay_Resp + - Averages measurements to reduce jitter + +3. **Clock Adjustment** + + - Small offsets (<1ms): uses ``adjtime()`` for smooth slewing + - Large offsets (>1ms): uses ``settimeofday()`` for immediate step + - Tracks clock drift rate and applies frequency compensation + +4. **Path Delay Measurement** + + - Continuously measures round-trip network delay + - Averages over configurable sample count + - Detects and rejects outliers (>MAX_PATH_DELAY) + +Hardware Timestamping +--------------------- + +When hardware timestamping is enabled (``-H`` option): + +- TX timestamps captured at MAC layer on packet transmission +- RX timestamps captured at MAC layer on packet reception +- Eliminates kernel and network stack processing delays +- Achieves sub-microsecond synchronization accuracy + +Requires network driver support for: + +- ``SO_TIMESTAMPNS`` socket option +- ``MSG_TRUNC`` flag in ``recvmsg()`` +- ``SCM_TIMESTAMPNS`` control message + +PTP Clock Device Support +------------------------- + +When using PTP clock devices (``/dev/ptp*``): + +- Synchronizes hardware PTP clock instead of system clock +- Uses ``clock_adjtime()`` via CLOCKFD mechanism +- Supports frequency and phase adjustments +- Maintains separation between system and PTP time domains + +Requires kernel support for: + +- ``CONFIG_PTP_CLOCK`` in NuttX kernel +- PTP clock driver implementation +- ``CONFIG_CLOCK_ADJTIME`` system call + +Transport Modes +--------------- + +**UDP IPv4 (default)** + +- Multicast address: 224.0.1.129 +- Event port: 319, General port: 320 +- Firewall-friendly, router-compatible + +**UDP IPv6** + +- Multicast address: FF0E::181 +- Same port numbers as IPv4 +- IPv6-only network support + +**IEEE 802.3 Ethernet** + +- Multicast MAC: 01:1B:19:00:00:00 (PTP) or 01:80:C2:00:00:0E (gPTP) +- EtherType: 0x88F7 +- No IP/UDP overhead +- Required for gPTP compliance + +Performance Considerations +========================== + +Synchronization Accuracy +------------------------ + +Typical accuracy achievable: + +- **Software timestamps**: 10-100 μs +- **Hardware timestamps**: 100-500 ns +- **With PTP clock device**: 50-200 ns + +Factors affecting accuracy: + +- Network jitter and asymmetry +- Interrupt latency +- Clock crystal quality +- Temperature variations + +CPU and Memory Usage +-------------------- + +- **CPU overhead**: ~1-3% on ARM Cortex-M4 @ 168MHz +- **Memory footprint**: ~45KB code, ~8KB RAM +- **Network bandwidth**: ~10-20 packets/second (typical) + +Network Requirements +-------------------- + +- **Multicast support**: Required for PTP operation +- **IGMP**: Must be enabled for IPv4 multicast +- **Switch compatibility**: PTP-aware switches recommended for best results +- **Bandwidth**: Minimal (<100 Kbps) + +Troubleshooting +=============== + +Common Issues +------------- + +**No synchronization occurring** + +- Check network interface is up and has IP address +- Verify multicast routing is enabled +- Ensure firewall allows UDP ports 319/320 +- Check PTP master is on same network/VLAN + +**Large clock offsets** + +- Verify network path delay is reasonable (<100μs typical) +- Check for asymmetric network delays +- Enable hardware timestamping if available +- Review BMCA priority settings + +**Frequent master switches** + +- Increase ``CONFIG_NETUTILS_PTPD_TIMEOUT_MS`` +- Check network stability and packet loss +- Verify all masters have unique clock identities + +**High CPU usage** + +- Increase sync interval (``CONFIG_NETUTILS_PTPD_SYNC_INTERVAL_MSEC``) +- Use hardware timestamping to reduce processing +- Check for excessive packet errors/retries + +Debug Output +------------ + +Enable PTP debug messages in kernel configuration: + +- ``CONFIG_DEBUG_PTP_ERROR``: Error messages +- ``CONFIG_DEBUG_PTP_WARN``: Warning messages +- ``CONFIG_DEBUG_PTP_INFO``: Informational messages + +Monitor debug output: + +.. code-block:: console + + nsh> dmesg | grep PTP + +Related Documentation +===================== + +- :doc:`/components/drivers/special/ptp` - PTP Clock Driver Framework +- :doc:`/applications/netutils/index` - Network Utilities Overview +- IEEE 1588-2008 Standard - Precision Time Protocol specification +- IEEE 802.1AS Standard - Timing and Synchronization for Time-Sensitive Applications (gPTP) + +References +========== + +- IEEE 1588-2008: "IEEE Standard for a Precision Clock Synchronization Protocol for Networked Measurement and Control Systems" +- IEEE 802.1AS-2020: "IEEE Standard for Local and Metropolitan Area Networks - Timing and Synchronization for Time-Sensitive Applications" +- Linux PTP Project: https://linuxptp.sourceforge.net/ +- ``apps/netutils/ptpd/`` - PTP protocol implementation +- ``apps/system/ptpd/`` - PTP daemon command interface +- ``include/netutils/ptpd.h`` - PTP API header From c985d52f6a8847bc55b25ddabfd9a7c046b66923 Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Wed, 23 Apr 2025 13:49:43 +0800 Subject: [PATCH 10/11] syscall/adjtime: fix minior issue about adjtime and add clock_adjtime to syscall add new syscall clock_adjtime Signed-off-by: dongjiuzhu1 --- include/sys/syscall_lookup.h | 3 ++- include/sys/time.h | 2 +- syscall/syscall.csv | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/sys/syscall_lookup.h b/include/sys/syscall_lookup.h index 63bfb57b1cd87..dd42e41b795f5 100644 --- a/include/sys/syscall_lookup.h +++ b/include/sys/syscall_lookup.h @@ -170,7 +170,8 @@ SYSCALL_LOOKUP(clock_nanosleep, 4) SYSCALL_LOOKUP(clock, 0) SYSCALL_LOOKUP(clock_gettime, 2) SYSCALL_LOOKUP(clock_settime, 2) -#ifdef CONFIG_CLOCK_TIMEKEEPING +#ifdef CONFIG_CLOCK_ADJTIME + SYSCALL_LOOKUP(clock_adjtime, 2) SYSCALL_LOOKUP(adjtime, 2) #endif diff --git a/include/sys/time.h b/include/sys/time.h index 794293dc43c3d..4c09536072ce3 100644 --- a/include/sys/time.h +++ b/include/sys/time.h @@ -253,7 +253,7 @@ int settimeofday(FAR const struct timeval *tv, * ****************************************************************************/ -#if defined(CONFIG_CLOCK_TIMEKEEPING) || defined(CONFIG_CLOCK_ADJTIME) +#ifdef CONFIG_CLOCK_ADJTIME int adjtime(FAR const struct timeval *delta, FAR struct timeval *olddelta); #endif diff --git a/syscall/syscall.csv b/syscall/syscall.csv index 2e3444c1322fa..3fe9675905d31 100644 --- a/syscall/syscall.csv +++ b/syscall/syscall.csv @@ -1,7 +1,7 @@ "_assert","assert.h","","void","FAR const char *","int","FAR const char *","FAR void *" "_exit","unistd.h","","noreturn","int" "accept4","sys/socket.h","defined(CONFIG_NET)","int","int","FAR struct sockaddr *","FAR socklen_t *","int" -"adjtime","sys/time.h","defined(CONFIG_CLOCK_TIMEKEEPING)","int","FAR const struct timeval *","FAR struct timeval *" +"adjtime","sys/time.h","defined(CONFIG_CLOCK_ADJTIME)","int","FAR const struct timeval *","FAR struct timeval *" "aio_cancel","aio.h","defined(CONFIG_FS_AIO)","int","int","FAR struct aiocb *" "aio_fsync","aio.h","defined(CONFIG_FS_AIO)","int","int","FAR struct aiocb *" "aio_read","aio.h","defined(CONFIG_FS_AIO)","int","FAR struct aiocb *" @@ -12,6 +12,7 @@ "chown","unistd.h","","int","FAR const char *","uid_t","gid_t" "clearenv","stdlib.h","!defined(CONFIG_DISABLE_ENVIRON)","int" "clock","time.h","","clock_t" +"clock_adjtime","sys/timex.h","defined(CONFIG_CLOCK_ADJTIME)","int","clockid_t","struct timex *" "clock_gettime","time.h","","int","clockid_t","FAR struct timespec *" "clock_nanosleep","time.h","","int","clockid_t","int","FAR const struct timespec *", "FAR struct timespec *" "clock_settime","time.h","","int","clockid_t","const struct timespec*" From 714166975ce3d3dac18f8e0d9460c8a29f850b16 Mon Sep 17 00:00:00 2001 From: guohao15 Date: Sun, 27 Apr 2025 15:26:49 +0800 Subject: [PATCH 11/11] sched/clock: [bugfix] should return -EINVAL when clock_id is invalid bug fix about clock_gettime Signed-off-by: guohao15 --- sched/clock/clock_gettime.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sched/clock/clock_gettime.c b/sched/clock/clock_gettime.c index 3dcf862e1c36c..198b88c59baab 100644 --- a/sched/clock/clock_gettime.c +++ b/sched/clock/clock_gettime.c @@ -183,6 +183,8 @@ int nxclock_gettime(clockid_t clock_id, FAR struct timespec *tp) { return -EINVAL; } +#else + ret = -EINVAL; #endif }