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}