xous_api_ticktimer/
lib.rs

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