Skip to content

Commit

Permalink
Merge pull request #4 from pendulum-project/sys-offset-support
Browse files Browse the repository at this point in the history
Added call for measuring offset to system clock.
  • Loading branch information
folkertdev authored Oct 4, 2023
2 parents d36a9d1 + 4bb24bb commit 8ca7481
Showing 1 changed file with 92 additions and 12 deletions.
104 changes: 92 additions & 12 deletions src/unix.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
use crate::{Clock, LeapIndicator, TimeOffset, Timestamp};
use std::time::Duration;
#[cfg(target_os = "linux")]
use std::{
os::unix::io::IntoRawFd,
os::unix::prelude::{FromRawFd, RawFd},
os::unix::io::{IntoRawFd, RawFd},
path::Path,
time::Duration,
};

/// A Unix OS clock
#[derive(Debug, Clone, Copy)]
pub struct UnixClock {
clock: libc::clockid_t,
#[cfg(target_os = "linux")]
fd: Option<RawFd>,
}

impl UnixClock {
Expand All @@ -29,6 +31,8 @@ impl UnixClock {
/// ```
pub const CLOCK_REALTIME: Self = UnixClock {
clock: libc::CLOCK_REALTIME,
#[cfg(target_os = "linux")]
fd: None,
};

/// Open a clock device.
Expand All @@ -45,20 +49,102 @@ impl UnixClock {
/// Ok(())
/// }
/// ```
#[cfg(target_os = "linux")]
pub fn open(path: impl AsRef<Path>) -> std::io::Result<Self> {
let file = std::fs::File::open(path)?;
let file = std::fs::OpenOptions::new()
.write(true)
.read(true)
.open(path)?;

// we need an owned fd. the file will be closed when the process exits.
Ok(Self::safe_from_raw_fd(file.into_raw_fd()))
}

// Consume the fd and produce a clock id. Clock id is only valid
// Consume an fd and produce a clock id. Clock id is only valid
// so long as the fd is open, so the RawFd here should
// not be borrowed.
#[cfg(target_os = "linux")]
fn safe_from_raw_fd(fd: RawFd) -> Self {
let clock = ((!(fd as libc::clockid_t)) << 3) | 3;

Self { clock }
Self {
clock,
fd: Some(fd),
}
}

/// Determine offset between file clock and system clock (if any)
/// Returns two system timestamps sandwhiching a timestamp from the
/// hardware clock.
#[cfg(target_os = "linux")]
pub fn system_offset(&self) -> Result<(Timestamp, Timestamp, Timestamp), Error> {
let Some(fd) = self.fd else {
return Err(Error::Invalid);
};

// TODO: remove type and constant definitions once libc updates
#[repr(C)]
#[derive(Default, Clone, Copy, PartialEq, Eq)]
#[allow(non_camel_case_types)]
struct ptp_clock_time {
sec: libc::__s64,
nsec: libc::__u32,
reserved: libc::__u32,
}

#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq)]
#[allow(non_camel_case_types)]
struct ptp_sys_offset {
n_samples: libc::c_uint,
rsv: [libc::c_uint; 3],
ts: [ptp_clock_time; 51],
}

// Needed as arrays larger than 32 elements don't implement default.
impl Default for ptp_sys_offset {
fn default() -> Self {
Self {
n_samples: Default::default(),
rsv: Default::default(),
ts: [Default::default(); 51],
}
}
}

const PTP_SYS_OFFSET: libc::c_uint = 0x43403d05;

let mut offset = ptp_sys_offset {
n_samples: 1,
..Default::default()
};

// # Safety
// Safe since PTP_SYS_OFFSET expects as argument a mutable pointer to
// ptp_sys_offset and offset is valid during the call
if unsafe { libc::ioctl(fd, PTP_SYS_OFFSET as _, &mut offset as *mut ptp_sys_offset) } != 0
{
let t1 = Self::CLOCK_REALTIME.now();
let tp = self.now();
let t2 = Self::CLOCK_REALTIME.now();

Ok((t1?, tp?, t2?))
} else {
Ok((
Timestamp {
seconds: offset.ts[0].sec as _,
nanos: offset.ts[0].nsec as _,
},
Timestamp {
seconds: offset.ts[1].sec as _,
nanos: offset.ts[1].nsec as _,
},
Timestamp {
seconds: offset.ts[2].sec as _,
nanos: offset.ts[2].nsec as _,
},
))
}
}

fn clock_adjtime(&self, timex: &mut libc::timex) -> Result<(), Error> {
Expand Down Expand Up @@ -349,12 +435,6 @@ impl UnixClock {
}
}

impl FromRawFd for UnixClock {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self::safe_from_raw_fd(fd)
}
}

impl Clock for UnixClock {
type Error = Error;

Expand Down

0 comments on commit 8ca7481

Please sign in to comment.