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