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 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 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/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 60fdda858e900..0f1370b71bedd 100644 --- a/drivers/timers/CMakeLists.txt +++ b/drivers/timers/CMakeLists.txt @@ -90,4 +90,12 @@ if(CONFIG_GOLDFISH_TIMER) list(APPEND SRCS goldfish_timer.c) endif() +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 8916a22594396..6e276e3299db6 100644 --- a/drivers/timers/Kconfig +++ b/drivers/timers/Kconfig @@ -551,4 +551,27 @@ 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. + +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 ddf2620ffaca3..b9ab14e0fbdaf 100644 --- a/drivers/timers/Make.defs +++ b/drivers/timers/Make.defs @@ -124,6 +124,14 @@ ifeq ($(CONFIG_GOLDFISH_TIMER),y) TMRVPATH = :timers endif +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.c b/drivers/timers/ptp_clock.c new file mode 100644 index 0000000000000..90555363b766f --- /dev/null +++ b/drivers/timers/ptp_clock.c @@ -0,0 +1,482 @@ +/**************************************************************************** + * 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, ppb); + 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: + { + if (lower->ops->control) + { + ret = lower->ops->control(lower, cmd, arg); + } + } + 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/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/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..71718f450642e 100644 --- a/include/nuttx/clock.h +++ b/include/nuttx/clock.h @@ -29,6 +29,7 @@ #include +#include #include #include #include @@ -91,11 +92,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 @@ -823,7 +828,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 @@ -833,7 +838,19 @@ 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); + +/**************************************************************************** + * 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/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..f3277b070d3d6 --- /dev/null +++ b/include/nuttx/timers/ptp_clock.h @@ -0,0 +1,399 @@ +/**************************************************************************** + * 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) + +#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 + ****************************************************************************/ + +/**************************************************************************** + * 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) + */ + +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 + * 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 ppb); + + /************************************************************************** + * 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); + + /************************************************************************** + * 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 + * 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/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 */ 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/include/sys/timex.h b/include/sys/timex.h new file mode 100644 index 0000000000000..bbd8b16a9032e --- /dev/null +++ b/include/sys/timex.h @@ -0,0 +1,129 @@ +/**************************************************************************** + * 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 + +/**************************************************************************** + * 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) +} +#endif + +#endif /* __INCLUDE_SYS_TIMEX_H */ 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/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/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/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 */ 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; diff --git a/sched/clock/clock_gettime.c b/sched/clock/clock_gettime.c index 89ebde1766529..198b88c59baab 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,21 @@ void nxclock_gettime(clockid_t clock_id, FAR struct timespec *tp) { up_perf_convert(tcb->run_time, tp); } + else + { + ret = -EINVAL; + } } + else + { + return -EINVAL; + } +#else + ret = -EINVAL; #endif } + + return ret; } /**************************************************************************** @@ -181,12 +217,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; } 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*"