This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 964c68b1090 Documentation/ptp: correct the PTP clock driver page.
964c68b1090 is described below

commit 964c68b1090a3c9268f6752a1a3368275bf8b988
Author: Daniel P. Carvalho <[email protected]>
AuthorDate: Sat Sep 19 18:35:40 2026 -0300

    Documentation/ptp: correct the PTP clock driver page.
    
    The page did not match the code in several places.
    
    - Describe struct ptp_ops_s as it is: adjfine takes parts per billion,
      adjphase, getres and control exist, there is no getcaps, and gettime
      has a system timestamp argument. State which operations are optional
      and give the real ptp_clock_register() arguments.
    - Replace the CLOCKFD() macro, which does not exist, with the clock
      identifier built from CLOCK_SHIFT and CLOCK_FD, in all examples.
    - Fix the frequency values: struct timex freq is in parts per million
      with 16 fractional bits, so +10 ppm is 655360.
    - List the modes clock_adjtime() handles for a PTP clock
      (ADJ_SETOFFSET, ADJ_FREQUENCY, ADJ_OFFSET for the phase) and say that
      the others return -ENOTSUP.
    - PTP_CLOCK_GETTIME and PTP_CLOCK_SETTIME take a struct timespec. Add
      the ioctls that were missing.
    - The dummy driver uses CLOCK_REALTIME, not the monotonic clock.
    - Point to ptpd instead of ptp4l and timemaster, which are not part of
      NuttX, and use the real CONFIG_DEBUG_PTP options.
    
    Signed-off-by: Daniel P. Carvalho <[email protected]>
    Assisted-by: Claude:claude-sonnet-5
---
 Documentation/components/drivers/special/ptp.rst | 285 ++++++++++++++---------
 1 file changed, 175 insertions(+), 110 deletions(-)

diff --git a/Documentation/components/drivers/special/ptp.rst 
b/Documentation/components/drivers/special/ptp.rst
index 2fa0182de37..0bacee1f2e4 100644
--- a/Documentation/components/drivers/special/ptp.rst
+++ b/Documentation/components/drivers/special/ptp.rst
@@ -28,30 +28,59 @@ The upper-half driver (``drivers/timers/ptp_clock.c``) 
provides:
 - Common ioctl command handling
 - Frequency and time adjustment logic
 - Cross-timestamp support
-- Interface to POSIX clock APIs via CLOCKFD mechanism
+- Interface to POSIX clock APIs through the ``CLOCK_FD`` clock type
 
 Lower-Half Driver
 -----------------
 
-Hardware-specific drivers implement the ``struct ptp_lowerhalf_s`` interface
-with the following operations:
+Hardware-specific drivers embed a ``struct ptp_lowerhalf_s`` and point its
+``ops`` field to a ``struct ptp_ops_s`` with the following operations:
 
 .. code-block:: c
 
-   struct ptp_clock_ops_s
+   struct ptp_lowerhalf_s
    {
-     CODE int (*adjfine)(FAR struct ptp_lowerhalf_s *lower, long scaled_ppm);
+     FAR const struct ptp_ops_s *ops;  /* Lower half driver operations */
+     FAR void *upper;                  /* The upper handle */
+   };
+
+   struct ptp_ops_s
+   {
+     CODE int (*adjfine)(FAR struct ptp_lowerhalf_s *lower, long ppb);
+     CODE int (*adjphase)(FAR struct ptp_lowerhalf_s *lower, int32_t phase);
      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);
+                         FAR struct timespec *ts,
+                         FAR struct ptp_system_timestamp *sts);
+     CODE int (*getcrosststamp)(FAR struct ptp_lowerhalf_s *lower,
+                                FAR struct system_device_crosststamp *cts);
      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);
+     CODE int (*getres)(FAR struct ptp_lowerhalf_s *lower,
+                        FAR struct timespec *res);
+     CODE int (*control)(FAR struct ptp_lowerhalf_s *lower,
+                         int cmd, unsigned long arg);
    };
 
+The units are:
+
+- ``adjfine``: the frequency offset from the nominal frequency, in parts per
+  billion. The upper half converts the ``freq`` field of ``struct timex``
+  (parts per million with 16 fractional bits) and rejects values beyond the
+  ``max_adj`` given at registration before calling it.
+- ``adjphase``: the change to apply to the phase, in nanoseconds.
+- ``adjtime``: the change to apply to the time, in nanoseconds.
+
+The upper half calls ``adjtime`` unconditionally, so every driver has to
+provide it. The other operations are optional: ``gettime``, ``settime`` and
+``getres`` make the matching ioctl return ``-ENOTSUP`` when they are missing,
+``adjfine`` and ``adjphase`` make the corresponding ``clock_adjtime()`` mode
+fail the same way, and ``getcrosststamp`` is reported through the
+``cross_timestamping`` capability. The capabilities are built by the upper
+half from which operations exist and from ``max_adj``, so there is no
+operation to report them. Ioctl commands that the upper half does not know
+are passed to ``control``.
+
 Configuration Options
 =====================
 
@@ -64,7 +93,7 @@ The PTP clock framework can be enabled with the following 
Kconfig options:
 ``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.
+  using ``CLOCK_REALTIME`` as the time base.
 
 ``CONFIG_CLOCK_ADJTIME``
   Enable the ``clock_adjtime()`` system call, required for frequency and
@@ -90,18 +119,18 @@ The following ioctl commands are supported:
 
   .. code-block:: c
 
-     struct ptp_clock_time time;
-     ioctl(fd, PTP_CLOCK_GETTIME, &time);
+     struct timespec ts;
+     ioctl(fd, PTP_CLOCK_GETTIME, &ts);
 
 ``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);
+     struct timespec ts;
+     ts.tv_sec  = 1234567890;
+     ts.tv_nsec = 123456789;
+     ioctl(fd, PTP_CLOCK_SETTIME, &ts);
 
 ``PTP_CLOCK_GETRES``
   Get the resolution of the PTP clock.
@@ -119,7 +148,7 @@ The following ioctl commands are supported:
      struct timex tx;
      memset(&tx, 0, sizeof(tx));
      tx.modes = ADJ_FREQUENCY;
-     tx.freq = 10000000;  /* +10 PPM */
+     tx.freq = 655360;  /* +10 ppm, in ppm scaled by 65536 */
      ioctl(fd, PTP_CLOCK_ADJTIME, &tx);
 
 ``PTP_CLOCK_GETCAPS``
@@ -148,59 +177,87 @@ The following ioctl commands are supported:
      struct ptp_sys_offset_precise precise;
      ioctl(fd, PTP_SYS_OFFSET_PRECISE, &precise);
 
-POSIX Clock API (CLOCKFD)
+``PTP_SYS_OFFSET_EXTENDED``
+  Like ``PTP_SYS_OFFSET``, but returns for each sample the system time before
+  and after reading the device time (``struct ptp_sys_offset_extended``).
+
+``PTP_CLOCK_GETSTATS`` and ``PTP_CLOCK_SETSTATS``
+  Read or set the drift statistics kept by the upper half
+  (``struct ptp_statistics_s``).
+
+The ``PTP_CLOCK_GETCAPS``, ``PTP_SYS_OFFSET``, ``PTP_SYS_OFFSET_PRECISE`` and
+``PTP_SYS_OFFSET_EXTENDED`` commands also exist with a ``2`` suffix
+(``PTP_CLOCK_GETCAPS2``, ``PTP_SYS_OFFSET2`` and so on), which the upper half
+handles the same way. The time values of ``PTP_CLOCK_GETTIME``,
+``PTP_CLOCK_SETTIME`` and ``PTP_CLOCK_GETRES`` are ``struct timespec``, while
+``struct ptp_clock_time`` is used by the ``PTP_SYS_OFFSET`` family.
+
+POSIX Clock API (CLOCK_FD)
 ==========================
 
-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()``.
+NuttX allows PTP clocks to be accessed through the 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:
+The clock identifier is built from the file descriptor of the opened device
+with the ``CLOCK_SHIFT`` and ``CLOCK_FD`` definitions of
+``<nuttx/clock.h>``:
 
 .. code-block:: c
 
    #include <time.h>
    #include <fcntl.h>
+   #include <sys/timex.h>
    #include <nuttx/clock.h>
 
-   int fd = open("/dev/ptp0", O_RDONLY);
+   int fd = open("/dev/ptp0", O_RDWR);
+   clockid_t clockid = (fd << CLOCK_SHIFT) | CLOCK_FD;
    struct timespec ts;
-   
+
    /* Get PTP clock time using POSIX API */
-   clock_gettime(CLOCKFD(fd), &ts);
-   
+   clock_gettime(clockid, &ts);
+
    /* Set PTP clock time */
-   clock_settime(CLOCKFD(fd), &ts);
-   
+   clock_settime(clockid, &ts);
+
    /* Get PTP clock resolution */
    struct timespec res;
-   clock_getres(CLOCKFD(fd), &res);
-   
+   clock_getres(clockid, &res);
+
    /* Adjust PTP clock frequency */
    struct timex tx = {0};
    tx.modes = ADJ_FREQUENCY;
-   tx.freq = -5000000;  /* -5 PPM */
-   clock_adjtime(CLOCKFD(fd), &tx);
-   
+   tx.freq = -327680;  /* -5 ppm, in ppm scaled by 65536 */
+   clock_adjtime(clockid, &tx);
+
    close(fd);
 
+The clock identifier is only valid while the file descriptor is open.
+
 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
+For a PTP clock, ``clock_adjtime()`` handles one of the following modes per
+call, checked in this order:
+
+- ``ADJ_SETOFFSET``: step the clock by the offset in ``tx.time``. The
+  ``tv_usec`` field holds microseconds, or nanoseconds if ``ADJ_NANO`` is also
+  set. A value of one second or more in that field is rejected with
+  ``-EINVAL``.
+- ``ADJ_FREQUENCY``: set the frequency offset from ``tx.freq``, in parts per
+  million with 16 fractional bits (ppm multiplied by 65536, so +10 ppm is
+  655360). A value beyond the maximum adjustment of the clock returns
+  ``-ERANGE``. This needs the ``adjfine`` operation of the driver.
+- ``ADJ_OFFSET``: adjust the **phase** by ``tx.offset``, in microseconds, or in
+  nanoseconds if ``ADJ_NANO`` is also set. This needs the ``adjphase``
+  operation of the driver.
+- No mode (``tx.modes`` equal to zero): read back the last frequency set in
+  ``tx.freq``.
+
+``ADJ_NANO`` only selects the unit of the two time based modes above.
+``ADJ_MAXERROR``, ``ADJ_ESTERROR``, ``ADJ_STATUS`` and ``ADJ_TIMECONST`` are 
not
+handled for a PTP clock and make the call return ``-ENOTSUP``.
 
 Dummy PTP Clock Driver
 =======================
@@ -240,9 +297,10 @@ Basic Time Operations
    int main(void)
    {
      int fd;
+     clockid_t clockid;
      struct timespec ts;
      struct timespec res;
-     
+
      /* Open PTP clock device */
      fd = open("/dev/ptp0", O_RDWR);
      if (fd < 0)
@@ -250,19 +308,21 @@ Basic Time Operations
          perror("Failed to open PTP clock");
          return -1;
        }
-     
+
+     clockid = (fd << CLOCK_SHIFT) | CLOCK_FD;
+
      /* Get current PTP clock time */
-     if (clock_gettime(CLOCKFD(fd), &ts) == 0)
+     if (clock_gettime(clockid, &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)
+     if (clock_getres(clockid, &res) == 0)
        {
          printf("PTP resolution: %ld.%09ld\n", res.tv_sec, res.tv_nsec);
        }
-     
+
      close(fd);
      return 0;
    }
@@ -281,21 +341,24 @@ Frequency Adjustment
    int main(void)
    {
      int fd;
+     clockid_t clockid;
      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 */
+
+     clockid = (fd << CLOCK_SHIFT) | CLOCK_FD;
+
+     /* 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)
+     tx.freq = 655360;  /* 10 ppm in scaled ppm (65536 * ppm) */
+
+     if (clock_adjtime(clockid, &tx) == 0)
        {
          printf("Frequency adjusted successfully\n");
        }
@@ -303,7 +366,7 @@ Frequency Adjustment
        {
          perror("Failed to adjust frequency");
        }
-     
+
      close(fd);
      return 0;
    }
@@ -322,22 +385,25 @@ Time Offset Adjustment
    int main(void)
    {
      int fd;
+     clockid_t clockid;
      struct timex tx;
-     
+
      fd = open("/dev/ptp0", O_RDWR);
      if (fd < 0)
        {
          perror("Failed to open PTP clock");
          return -1;
        }
-     
+
+     clockid = (fd << CLOCK_SHIFT) | CLOCK_FD;
+
      /* 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)
+
+     if (clock_adjtime(clockid, &tx) == 0)
        {
          printf("Time offset applied successfully\n");
        }
@@ -345,7 +411,7 @@ Time Offset Adjustment
        {
          perror("Failed to apply time offset");
        }
-     
+
      close(fd);
      return 0;
    }
@@ -354,7 +420,8 @@ 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:
+that embeds a ``struct ptp_lowerhalf_s`` and provides the operations of
+``struct ptp_ops_s`` described above:
 
 .. code-block:: c
 
@@ -369,73 +436,78 @@ that implements the ``struct ptp_lowerhalf_s`` interface:
      /* ... */
    };
 
-   /* Implement required operations */
-   static int my_ptp_adjfine(FAR struct ptp_lowerhalf_s *lower,
-                            long scaled_ppm)
+   /* Implement the operations */
+   static int my_ptp_adjfine(FAR struct ptp_lowerhalf_s *lower, long ppb)
    {
      FAR struct my_ptp_lowerhalf_s *priv =
        (FAR struct my_ptp_lowerhalf_s *)lower;
-     
-     /* Adjust hardware clock frequency */
+
+     /* Adjust hardware clock frequency by ppb parts per billion */
      /* ... hardware-specific code ... */
-     
+
      return OK;
    }
 
    static int my_ptp_gettime(FAR struct ptp_lowerhalf_s *lower,
-                            FAR struct timespec *ts)
+                             FAR struct timespec *ts,
+                             FAR struct ptp_system_timestamp *sts)
    {
      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 =
+   /* Define operations structure. Operations that the hardware does not
+    * support are left out (NULL), except adjtime.
+    */
+   static const struct ptp_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,
+     .adjfine = my_ptp_adjfine,
+     .adjtime = my_ptp_adjtime,
+     .gettime = my_ptp_gettime,
+     .settime = my_ptp_settime,
+     .getres  = my_ptp_getres,
    };
 
    /* 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 */
+
+     /* Register as /dev/ptp0, with a maximum adjustment of 500 ppm
+      * (500000 ppb).
+      */
+
+     return ptp_clock_register(&priv->base, 500000, 0);
    }
 
 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)
+The PTP clock framework mirrors the PTP hardware clock interface of Linux
+(``/dev/ptpN`` and ``clock_adjtime()``), so software written for that
+interface is easier to port.
 
-These daemons can use the PTP clock devices through the standard POSIX clock
-APIs via the CLOCKFD mechanism, making porting straightforward.
+The PTP daemon of NuttX is ``ptpd`` (``apps/netutils/ptpd``, see
+:doc:`/applications/system/ptpd/index`). It uses a PTP clock through the
+clock identifier described above, when it is started with the device path, for
+example ``ptpd -p /dev/ptp0``. The ``ptp4l`` and ``timemaster`` programs of
+the linuxptp project are Linux programs and are not part of NuttX.
 
 Performance Considerations
 ==========================
@@ -469,20 +541,12 @@ hardware supports adjustments in the range of:
 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
+Debug output can be enabled with ``CONFIG_DEBUG_PTP`` and the following
+options, which send the messages to the SYSLOG:
 
-   ptpclk: PTP clock registered as /dev/ptp0
-   ptpclk: adjfine: scaled_ppm=656360 (10 PPM)
-   ptpclk: gettime: ts=1234567890.123456789
+- ``CONFIG_DEBUG_PTP_ERROR``: Error messages
+- ``CONFIG_DEBUG_PTP_WARN``: Warning messages
+- ``CONFIG_DEBUG_PTP_INFO``: Informational messages
 
 References
 ==========
@@ -493,5 +557,6 @@ References
 - `Linux PTP Project <https://linuxptp.sourceforge.net/>`_
 
 - ``include/nuttx/timers/ptp_clock.h`` - PTP clock header file
+- ``include/nuttx/clock.h`` - ``CLOCK_FD`` and ``CLOCK_SHIFT`` definitions
 - ``drivers/timers/ptp_clock.c`` - Upper-half driver implementation
 - ``drivers/timers/ptp_clock_dummy.c`` - Dummy driver implementation

Reply via email to