socketcan 3.6.1

Linux SocketCAN library. Send and receive CAN frames via CANbus on Linux.
// socketcan/src/timestamp.rs
//
// Timestamp types and helpers for SocketCAN sockets.
//
// This file is part of the Rust 'socketcan-rs' library.
//
// Licensed under the MIT license:
//   <LICENSE or http://opensource.org/licenses/MIT>
// This file may not be copied, modified, or distributed except according
// to those terms.

//! Timestamp support for SocketCAN sockets.
//!
//! Timestamps are delivered atomically with the frame data via `recvmsg()`
//! and ancillary control messages, avoiding the two-syscall race of the old
//! `SIOCGSTAMPNS` approach.
//!
//! # Usage
//!
//! 1. Enable the desired timestamp mode on the socket with
//!    [`SocketOptions::set_recv_timestamp`] or [`SocketOptions::set_timestamping`].
//! 2. Call the corresponding read method on the socket.
//!
//! [`SocketOptions::set_recv_timestamp`]: crate::SocketOptions::set_recv_timestamp
//! [`SocketOptions::set_timestamping`]: crate::SocketOptions::set_timestamping

use std::time::{Duration, SystemTime};

// --------------------------------------------------------------------------
// SOF_TIMESTAMPING_* flags
// TODO: These should be PR'd into libc

/// Hardware transmit timestamp, generated by the network adapter at packet departure.
pub const SOF_TIMESTAMPING_TX_HARDWARE: u32 = 1 << 0;
/// Software transmit timestamp, generated when the packet leaves the network stack.
pub const SOF_TIMESTAMPING_TX_SOFTWARE: u32 = 1 << 1;
/// Hardware receive timestamp.
pub const SOF_TIMESTAMPING_RX_HARDWARE: u32 = 1 << 2;
/// Software receive timestamp, generated when the packet enters the network stack.
pub const SOF_TIMESTAMPING_RX_SOFTWARE: u32 = 1 << 3;
/// Report software timestamps in the ancillary data (distinct from `RX_SOFTWARE`,
/// which selects when the timestamp is taken).
pub const SOF_TIMESTAMPING_SOFTWARE: u32 = 1 << 4;
/// Report the raw hardware clock value (not wall-clock time).
pub const SOF_TIMESTAMPING_RAW_HARDWARE: u32 = 1 << 6;
/// Deliver `SO_TIMESTAMPING` timestamps via a control message on receive.
///
/// Required for RX timestamps to actually appear in the ancillary data
/// returned by `recvmsg()`.
pub const SOF_TIMESTAMPING_OPT_CMSG: u32 = 1 << 10;

// --------------------------------------------------------------------------
// ethtool constants / structs
// TODO: These should be PR'd into libc

pub(crate) const ETHTOOL_GET_TS_INFO: u32 = 0x0000_0041;

/// Mirror of `ethtool_ts_info` from `<linux/ethtool.h>`.
#[repr(C)]
pub(crate) struct EthtoolTsInfo {
    pub cmd: u32,
    pub so_timestamping: u32,
    pub phc_index: i32,
    pub tx_types: u32,
    pub tx_reserved: [u32; 3],
    pub rx_filters: u32,
    pub rx_reserved: [u32; 3],
}

// ===== Private conversion helpers =====

/// Convert a `libc::timespec` to a `SystemTime`.
///
/// Assumes the timespec is a non-negative UNIX timestamp, which is always
/// the case for kernel-generated socket timestamps.
#[inline]
pub(crate) fn timespec_to_system_time(ts: libc::timespec) -> SystemTime {
    SystemTime::UNIX_EPOCH + timespec_to_duration(ts)
}

/// Convert a `libc::timespec` to a `Duration`.
///
/// Used for hardware timestamps, which are reported in the adapter's own
/// clock domain rather than as wall-clock time. Negative `tv_sec` or
/// `tv_nsec` values (which the kernel should never produce here) are
/// clamped to zero so the function never panics on `Duration::new`.
#[inline]
pub(crate) fn timespec_to_duration(ts: libc::timespec) -> Duration {
    let secs = ts.tv_sec.max(0) as u64;
    let nsec = ts.tv_nsec.clamp(0, 999_999_999) as u32;
    Duration::new(secs, nsec)
}

/////////////////////////////////////////////////////////////////////////////

/// Timestamps associated with a received CAN frame.
///
/// Each field is `None` when the corresponding timestamp mode was not enabled
/// on the socket before the frame was read.
///
/// Enable socket-layer timestamps with [`SocketOptions::set_recv_timestamp`]
/// and network-stack / hardware timestamps with [`SocketOptions::set_timestamping`].
///
/// # Limitation
///
/// The kernel reports each unrequested timestamp source as all-zero rather
/// than omitting it from the cmsg. This implementation treats an exactly-zero
/// `sw` or `hw` value as "not delivered" and reports it as `None`, which
/// collapses three otherwise-distinct cases: the source was disabled, the
/// kernel returned zero, or (for `hw` only) the adapter's clock genuinely
/// read zero. In practice this is only ambiguous in the first nanosecond
/// after a hardware clock starts up.
///
/// [`SocketOptions::set_recv_timestamp`]: crate::SocketOptions::set_recv_timestamp
/// [`SocketOptions::set_timestamping`]: crate::SocketOptions::set_timestamping
#[derive(Debug, Clone, Copy, Default)]
pub struct CanTimestamps {
    /// `SO_TIMESTAMPNS` — socket-layer arrival time (wall clock).
    pub socket: Option<SystemTime>,
    /// `SOF_TIMESTAMPING_RX_SOFTWARE` — network-stack entry time (wall clock).
    pub sw: Option<SystemTime>,
    /// `SOF_TIMESTAMPING_RX_HARDWARE` — raw hardware clock value.
    ///
    /// Reported as nanoseconds in the adapter's own clock domain.
    /// This is not a wall-clock time!
    pub hw: Option<Duration>,
}