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}