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}