Skip to main content

ove/
net_sntp.rs

1// Copyright (C) 2026 Kamil Lulko <kamil.lulko@gmail.com>
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4//
5// This file is part of oveRTOS.
6
7//! Blocking SNTP time synchronization client.
8//!
9//! Provides a safe Rust API for the oveRTOS SNTP subsystem.  A single NTP
10//! query is sent to a time server and the resulting UTC offset is stored
11//! internally.  Useful for wall-clock timestamps, TLS certificate
12//! validation, and log correlation.
13//!
14//! ## Async alternative
15//!
16//! For async SNTP on top of [`crate::async_net`] use the
17//! [`sntpc`](https://crates.io/crates/sntpc) crate from crates.io
18//! with the `embassy-socket` feature. Single-shot NTP query against
19//! any UDP-reachable server; no global UTC-offset state.
20//!
21//! # Example
22//!
23//! ```ignore
24//! use ove::net_sntp;
25//!
26//! let cfg = net_sntp::Config {
27//!     server: b"pool.ntp.org\0",
28//!     timeout: core::time::Duration::from_secs(5),
29//! };
30//! net_sntp::sync(&cfg).unwrap();
31//! let utc = net_sntp::get_utc().unwrap();
32//! ```
33
34use crate::bindings;
35use crate::error::{Error, Result};
36
37// ---------------------------------------------------------------------------
38// Config
39// ---------------------------------------------------------------------------
40
41/// SNTP client configuration.
42///
43/// `server` must be a null-terminated byte string (e.g. `b"pool.ntp.org\0"`).
44/// A `timeout` of `Duration::ZERO` uses the default (5 s).
45pub struct Config<'a> {
46    /// NTP server hostname (null-terminated).
47    pub server: &'a [u8],
48    /// Query timeout (`Duration::ZERO` selects the default of 5 s).
49    pub timeout: core::time::Duration,
50}
51
52impl Default for Config<'_> {
53    fn default() -> Self {
54        Self {
55            server: b"pool.ntp.org\0",
56            timeout: core::time::Duration::from_secs(5),
57        }
58    }
59}
60
61// ---------------------------------------------------------------------------
62// API
63// ---------------------------------------------------------------------------
64
65/// Synchronize with an NTP server.
66///
67/// Sends a single NTP request and stores the computed UTC offset.
68/// Subsequent calls update the stored offset.
69///
70/// # Errors
71/// Returns an error if the NTP query fails.
72pub fn sync(cfg: &Config) -> Result<()> {
73    let c_cfg = bindings::ove_sntp_config_t {
74        server: cfg.server.as_ptr() as *const _,
75        timeout_ns: crate::time::dur_to_ns(cfg.timeout),
76    };
77    let rc = unsafe { bindings::ove_sntp_sync(&c_cfg) };
78    Error::from_code(rc)
79}
80
81/// Get the UTC offset computed by the last successful sync.
82///
83/// The offset can be added to `ove_time_get_us()` to approximate
84/// wall-clock time (microseconds since Unix epoch).
85///
86/// # Errors
87/// Returns `Error::NotSupported` if no sync has been performed.
88pub fn get_offset_us() -> Result<i64> {
89    let mut offset: i64 = 0;
90    let rc = unsafe { bindings::ove_sntp_get_offset_us(&mut offset) };
91    Error::from_code(rc)?;
92    Ok(offset)
93}
94
95/// Get the current UTC time in seconds since Unix epoch.
96///
97/// Convenience function: returns monotonic time + NTP offset.
98///
99/// # Errors
100/// Returns `Error::NotSupported` if no sync has been performed.
101pub fn get_utc() -> Result<u32> {
102    let mut utc: u32 = 0;
103    let rc = unsafe { bindings::ove_sntp_get_utc(&mut utc) };
104    Error::from_code(rc)?;
105    Ok(utc)
106}