Skip to main content

xous_api_ticktimer/
lib.rs

1pub mod api;
2
3use std::str::FromStr;
4
5use num_traits::ToPrimitive;
6use xous::{CID, Error, send_message};
7use xous_semver::SemVer;
8
9#[derive(Debug)]
10pub struct Ticktimer {
11    conn: CID,
12}
13impl Ticktimer {
14    pub fn new() -> Result<Self, Error> {
15        REFCOUNT.fetch_add(1, Ordering::Relaxed);
16        let conn = xous::connect(xous::SID::from_bytes(b"ticktimer-server").unwrap())?;
17        Ok(Ticktimer { conn })
18    }
19
20    /// Return the number of milliseconds that have elapsed since boot. The returned
21    /// value is guaranteed to always be the same or greater than the previous value,
22    /// even through suspend/resume cycles. During suspend, the counter does not
23    /// advance, so loops which rely on this value will not perceive any extra time
24    /// passing through a suspend/resume cycle.
25    ///
26    /// This call is expected to be infalliable, and removing the error handling
27    /// path makes it a little more efficient in a tight loop.
28    ///
29    /// # Returns:
30    ///
31    ///     * A `u64` that is the number of milliseconds elapsed since boot.
32    pub fn elapsed_ms(&self) -> u64 {
33        let response = send_message(
34            self.conn,
35            xous::Message::new_blocking_scalar(api::Opcode::ElapsedMs.to_usize().unwrap(), 0, 0, 0, 0),
36        )
37        .expect("Ticktimer: failure to send message to Ticktimer");
38        if let xous::Result::Scalar2(upper, lower) = response {
39            upper as u64 | ((lower as u64) << 32)
40        } else {
41            panic!("Ticktimer elapsed_ms(): unexpected return value.");
42        }
43    }
44
45    /// Sleep for at least `ms` milliseconds. Blocks until the requested time has passed.
46    ///
47    /// # Arguments:
48    ///
49    ///     * ms: A `usize` specifying how many milliseconds to sleep for
50    pub fn sleep_ms(&self, ms: usize) -> Result<(), Error> {
51        send_message(
52            self.conn,
53            xous::Message::new_blocking_scalar(api::Opcode::SleepMs.to_usize().unwrap(), ms, 0, 0, 0),
54        )
55        .map(|_| ())
56    }
57
58    /// Ping the watchdog timer. Processes may use this to periodically ping the WDT to prevent
59    /// the system from resetting itself. Note that every call to `sleep_ms()` also implicitly
60    /// pings the WDT, so in more complicated systems an explicit call is not needed.
61    pub fn ping_wdt(&self) {
62        send_message(
63            self.conn,
64            xous::Message::new_scalar(api::Opcode::PingWdt.to_usize().unwrap(), 0, 0, 0, 0),
65        )
66        .expect("Couldn't send WDT ping");
67    }
68
69    /// Query version information embedded in this implementation crate by the build system.
70    ///
71    /// # Returns:
72    ///
73    ///     * A `String` containing the version information of the latest build
74    pub fn get_version(&self) -> String {
75        let alloc = api::VersionString { version: String::new() };
76        let mut buf = xous_ipc::Buffer::into_buf(alloc).expect("couldn't convert version request");
77        buf.lend_mut(self.conn, api::Opcode::GetVersion.to_u32().unwrap()).expect("couldn't get version");
78        let v = buf.to_original::<api::VersionString, _>().expect("couldn't revert buffer");
79        v.version
80    }
81
82    pub fn get_version_semver(&self) -> SemVer {
83        SemVer::from_str(self.get_version().lines().next().unwrap()).unwrap()
84    }
85
86    /// Lock the given Mutex. Blocks until the Mutex is locked.
87    ///
88    /// Note that Mutexes start out in a `Locked` state and move into an `Unlocked` state by calling
89    /// `Unlock` on their pointer. For example, the following will probably block forever:
90    ///
91    ///     `TickTimer.lock_mutex(1)`
92    ///
93    /// In order to create a new Mutex, you must first `Unlock` it. For example, the following is
94    /// allowed:
95    ///
96    ///     `TickTimer.unlock_mutex(1)`
97    ///     `TickTimer.lock_mutex(1)`
98    ///     `TickTimer.unlock_mutex(1)`
99    ///
100    /// # Arguments:
101    ///
102    ///     * mtx: A `usize` referring to the Mutex. This is probably a pointer, but can be any `usize`
103    pub fn lock_mutex(&self, mtx: usize) {
104        send_message(
105            self.conn,
106            xous::Message::new_blocking_scalar(api::Opcode::LockMutex.to_usize().unwrap(), mtx, 0, 0, 0),
107        )
108        .expect("couldn't lock mutex");
109    }
110
111    /// Unlock the given Mutex. Does not block. If the Mutex is not locked, then it will be
112    /// "doubly-unlocked". That is, if you Unlock a mutex twice, then you can Lock it twice
113    /// without blocking.
114    ///
115    /// # Arguments:
116    ///
117    ///     * mtx: A `usize` referring to the Mutex. This is probably a pointer, but can be any `usize`
118    pub fn unlock_mutex(&self, mtx: usize) {
119        send_message(
120            self.conn,
121            xous::Message::new_scalar(api::Opcode::UnlockMutex.to_usize().unwrap(), mtx, 0, 0, 0),
122        )
123        .expect("couldn't unlock mutex");
124    }
125
126    /// Wait for a Condition on the given condvar, with an optional Duration
127    ///
128    /// # Arguments:
129    ///
130    ///     * condvar: A `usize` referring to the Condvar. This is probably a pointer, but can be any `usize`
131    ///     * duration: The amount of time to wait for a signal, if any
132    ///
133    /// # Returns:
134    ///
135    ///     * true: the condition was successfully received
136    ///     * false: the condition was not received and the operation itmed out
137    pub fn wait_condition(&self, condvar: usize, duration: Option<core::time::Duration>) -> bool {
138        send_message(
139            self.conn,
140            xous::Message::new_scalar(
141                api::Opcode::WaitForCondition.to_usize().unwrap(),
142                condvar,
143                duration.map(|d| d.as_millis() as usize).unwrap_or(0),
144                0,
145                0,
146            ),
147        )
148        .map(|r| r == xous::Result::Scalar1(0))
149        .expect("couldn't wait for condition")
150    }
151
152    /// Notify a condition to one or more Waiters
153    ///
154    /// # Arguments:
155    ///
156    ///     * condvar: A `usize` referring to the Condvar. This is probably a pointer, but can be any `usize`
157    ///     * count: The number of Waiters to wake up
158    pub fn notify_condition(&self, condvar: usize, count: usize) {
159        send_message(
160            self.conn,
161            xous::Message::new_scalar(api::Opcode::NotifyCondition.to_usize().unwrap(), condvar, count, 0, 0),
162        )
163        .map(|r| r == xous::Result::Scalar1(0))
164        .expect("couldn't notify condition");
165    }
166}
167
168use core::sync::atomic::{AtomicU32, Ordering};
169static REFCOUNT: AtomicU32 = AtomicU32::new(0);
170impl Drop for Ticktimer {
171    fn drop(&mut self) {
172        // de-allocate myself. It's unsafe because we are responsible to make sure nobody else is using the
173        // connection.
174        if REFCOUNT.fetch_sub(1, Ordering::Relaxed) == 1 {
175            unsafe {
176                xous::disconnect(self.conn).unwrap();
177            }
178        }
179    }
180}