Skip to main content

pps_time/
lib.rs

1use std::{
2    fs::File,
3    io::{Error, Result},
4    mem::MaybeUninit,
5    os::{
6        fd::AsRawFd,
7        raw::{c_uint, c_ulong},
8    },
9    path::PathBuf,
10};
11
12pub mod pps;
13use pps::{pps_fdata, pps_kparams, pps_ktime, PPS_TIME_INVALID};
14
15// These constants rely on macros which bindgen does not export from pps.h
16const PPS_GETPARAMS: c_ulong = 0x800870a1;
17const PPS_SETPARAMS: c_ulong = 0x400870a2;
18const PPS_GETCAP: c_ulong = 0x800870a3;
19const PPS_FETCH: c_ulong = 0xc00870a4;
20
21pub struct PpsDevice(File);
22
23impl PpsDevice {
24    pub fn new(path: PathBuf) -> Result<PpsDevice> {
25        Ok(PpsDevice(File::open(path)?))
26    }
27
28    /// Perform ioctl request and check result for possible errors
29    unsafe fn ioctl<T>(&self, request: c_ulong, value: &mut T) -> Result<()> {
30        match libc::ioctl(self.0.as_raw_fd(), request as _, value) {
31            0 => Ok(()),
32            _ => Err(Error::last_os_error()),
33        }
34    }
35
36    /// Perform ioctl request with uninitialized memory
37    unsafe fn ioctl_uninit<T>(&self, request: c_ulong) -> Result<T> {
38        let mut value: MaybeUninit<T> = MaybeUninit::uninit();
39        self.ioctl(request, &mut value)?;
40        Ok(unsafe { value.assume_init() })
41    }
42
43    pub fn get_params(&self) -> Result<pps_kparams> {
44        // Safety: PPS_GETPARAMS writes pps_kparams, for which memory is allocated and returned by ioctl_uninit
45        unsafe { self.ioctl_uninit(PPS_GETPARAMS) }
46    }
47
48    pub fn set_params(&self, params: &mut pps_kparams) -> Result<()> {
49        // Safety: PPS_SETPARAMS expects pps_kparams, which lives for the duration of the call
50        unsafe { self.ioctl(PPS_SETPARAMS, params) }
51    }
52
53    pub fn get_cap(&self) -> Result<c_uint> {
54        // Safety: PPS_GETCAP writes a c_uint, for which memory is allocated and returned by ioctl_uninit
55        unsafe { self.ioctl_uninit(PPS_GETCAP) }
56    }
57
58    fn fetch(&self, timeout: pps_ktime) -> Result<pps_fdata> {
59        let mut data = pps_fdata {
60            info: Default::default(),
61            timeout,
62        };
63
64        // Safety: PPS_FETCH expects and writes to a pps_fdata, which lives for the duration of the call
65        unsafe { self.ioctl(PPS_FETCH, &mut data)? };
66
67        Ok(data)
68    }
69
70    /// Fetch next PPS event, blocking until it arrives
71    ///
72    /// Device must support PPS_CANWAIT, otherwise it will give an EOPNOTSUPP error
73    pub fn fetch_blocking(&self) -> Result<pps_fdata> {
74        self.fetch(pps_ktime {
75            sec: 0,
76            nsec: 0,
77            flags: PPS_TIME_INVALID,
78        })
79    }
80
81    /// Fetch next PPS event with a timeout, giving an ETIMEDOUT error if event does not come in time
82    ///
83    /// Device must support PPS_CANWAIT, otherwise it will give an EOPNOTSUPP error
84    pub fn fetch_timeout(&self, seconds: i64, nanoseconds: i32) -> Result<pps_fdata> {
85        self.fetch(pps_ktime {
86            sec: seconds,
87            nsec: nanoseconds,
88            flags: 0,
89        })
90    }
91
92    /// Fetch newest PPS event without blocking
93    pub fn fetch_non_blocking(&self) -> Result<pps_fdata> {
94        self.fetch(pps_ktime {
95            sec: 0,
96            nsec: 0,
97            flags: 0,
98        })
99    }
100}