diff --git a/services/llio/src/api.rs b/services/llio/src/api.rs index c4068fa59..bdb89a1f5 100644 --- a/services/llio/src/api.rs +++ b/services/llio/src/api.rs @@ -121,6 +121,9 @@ pub(crate) enum Opcode { /// debug USB status DebugUsbOp, + /// Read the date and time from the RTC. Warning: blocking call, operation takes ~10-20ms + DateTime, + /// SuspendResume callback SuspendResume, diff --git a/services/llio/src/api/rtc_api.rs b/services/llio/src/api/rtc_api.rs index 1cb45dc8d..a8f717727 100644 --- a/services/llio/src/api/rtc_api.rs +++ b/services/llio/src/api/rtc_api.rs @@ -8,6 +8,9 @@ pub enum Weekday { Friday, Saturday, } +impl Default for Weekday { + fn default() -> Self { Weekday::Sunday } +} #[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] pub enum TimeUnits { @@ -16,7 +19,7 @@ pub enum TimeUnits { Hours, } -#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone)] +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone, Default)] pub struct DateTime { pub seconds: u8, pub minutes: u8, @@ -42,6 +45,8 @@ pub(crate) enum RtcOpcode { /// Get datetime. Causes users with registered callbacks to receive the current DateTime RequestDateTime, + /// Get datetime. Blocks until complete. + RequestDateTimeBlocking, /// the datetime response, used internally from the callback manager ResponseDateTime, diff --git a/services/llio/src/llio_lib.rs b/services/llio/src/llio_lib.rs index 1171e2682..9c0b9086e 100644 --- a/services/llio/src/llio_lib.rs +++ b/services/llio/src/llio_lib.rs @@ -382,6 +382,13 @@ impl Llio { Message::new_scalar(Opcode::UartMux.to_usize().unwrap(), arg, 0, 0, 0) ).map(|_| ()) } + pub fn read_rtc_blocking(&self) -> Result { + let alloc = DateTime::default(); + let mut buf = Buffer::into_buf(alloc).expect("couldn't transform into IPC memory"); + buf.lend_mut(self.conn, Opcode::DateTime.to_u32().unwrap()).or(Err(xous::Error::InternalError))?; + let dt = buf.to_original::().expect("couldn't transform back from IPC memory"); + Ok(dt) + } } diff --git a/services/llio/src/main.rs b/services/llio/src/main.rs index 617b6d8f4..6a5e72c4d 100644 --- a/services/llio/src/main.rs +++ b/services/llio/src/main.rs @@ -730,6 +730,7 @@ fn xmain() -> ! { crate::rtc::rtc_server(rtc_sid); } }); + let rtc_conn = xous::connect(rtc_sid).unwrap(); // Create a new llio object let handler_conn = xous::connect(llio_sid).expect("can't create IRQ handler connection"); @@ -755,7 +756,7 @@ fn xmain() -> ! { log::trace!("starting main loop"); loop { - let msg = xous::receive_message(llio_sid).unwrap(); + let mut msg = xous::receive_message(llio_sid).unwrap(); log::trace!("Message: {:?}", msg); match FromPrimitive::from_usize(msg.body.id()) { Some(Opcode::SuspendResume) => xous::msg_scalar_unpack!(msg, token, _, _, _, { @@ -1009,6 +1010,14 @@ fn xmain() -> ! { xous::return_scalar2(msg.sender, is_locked, force_update).expect("couldn't return status"); lockstatus_force_update = false; }), + Some(Opcode::DateTime) => { + let alloc = DateTime::default(); + let mut buf = Buffer::into_buf(alloc).expect("couldn't transform to IPC memory"); + buf.lend_mut(rtc_conn, RtcOpcode::RequestDateTimeBlocking.to_u32().unwrap()).expect("RTC blocking get failed"); + let dt = buf.to_original::().expect("couldn't revert IPC memory"); + let mut buffer = unsafe { Buffer::from_memory_message_mut(msg.body.memory_message_mut().unwrap()) }; + buffer.replace(dt).unwrap(); + } Some(Opcode::Quit) => { log::info!("Received quit opcode, exiting."); let dropconn = xous::connect(i2c_sid).unwrap(); diff --git a/services/llio/src/rtc.rs b/services/llio/src/rtc.rs index 06b386237..03b2bf653 100644 --- a/services/llio/src/rtc.rs +++ b/services/llio/src/rtc.rs @@ -3,19 +3,14 @@ use num_traits::{FromPrimitive, ToPrimitive}; use xous_ipc::Buffer; use xous::{msg_scalar_unpack, msg_blocking_scalar_unpack}; -use core::sync::atomic::{AtomicU32, Ordering}; -static CB_TO_MAIN_CONN: AtomicU32 = AtomicU32::new(0); - #[cfg(any(target_os = "none", target_os = "xous"))] mod implementation { #![allow(dead_code)] use bitflags::*; - use crate::rtc::CB_TO_MAIN_CONN; - use core::sync::atomic::Ordering; - use llio::{I2cStatus, I2cTransaction, I2c}; - use crate::api::{RtcOpcode, DateTime, Weekday}; - use xous_ipc::Buffer; + use llio::{I2cStatus, I2c}; + use crate::api::{RtcOpcode, Weekday}; use num_traits::ToPrimitive; + use crate::rtc::{to_binary, to_weekday, to_bcd}; const BLOCKING_I2C_TIMEOUT_MS: u64 = 50; @@ -180,64 +175,6 @@ mod implementation { const ABRTCMC_TIMERB: u8 = 0x13; // no bitflags, register is timer period in seconds, and the period is N / (source clock frequency) - /// convert binary to BCD - fn to_bcd(binary: u8) -> u8 { - let mut lsd: u8 = binary % 10; - if lsd > 9 { - lsd = 9; - } - let mut msd: u8 = binary / 10; - if msd > 9 { - msd = 9; - } - - (msd << 4) | lsd - } - - fn to_binary(bcd: u8) -> u8 { - (bcd & 0xf) + ((bcd >> 4) * 10) - } - - fn to_weekday(bcd: u8) -> Weekday { - match bcd { - 0 => Weekday::Sunday, - 1 => Weekday::Monday, - 2 => Weekday::Tuesday, - 3 => Weekday::Wednesday, - 4 => Weekday::Thursday, - 5 => Weekday::Friday, - 6 => Weekday::Saturday, - _ => Weekday::Sunday, - } - } - - fn i2c_callback(trans: I2cTransaction) { - if trans.status == I2cStatus::ResponseReadOk { - let cb_to_main_conn = CB_TO_MAIN_CONN.load(Ordering::Relaxed); - if cb_to_main_conn != 0 { - // we expect this to be 0, as we don't set it - assert!(trans.callback_id == 0, "callback ID was incorrect!"); - if let Some(rxbuf) = trans.rxbuf { - let dt = DateTime { - seconds: to_binary(rxbuf[0] & 0x7f), - minutes: to_binary(rxbuf[1] & 0x7f), - hours: to_binary(rxbuf[2] & 0x3f), - days: to_binary(rxbuf[3] & 0x3f), - weekday: to_weekday(rxbuf[4] & 0x7f), - months: to_binary(rxbuf[5] & 0x1f), - years: to_binary(rxbuf[6]), - }; - let buf = Buffer::into_buf(dt).unwrap(); - buf.send(cb_to_main_conn, RtcOpcode::ResponseDateTime.to_u32().unwrap()).unwrap(); - } else { - log::error!("i2c_callback: no rx data to unpack!") - } - } else { - log::error!("i2c_callback happened, but no connection to the main server!"); - } - } - } - pub struct Rtc { i2c: I2c, cb_to_main: xous::CID, @@ -324,6 +261,29 @@ mod implementation { } } + pub fn rtc_get_blocking(&mut self) -> Result { + let mut rxbuf = [0; 7]; + match self.i2c.i2c_read(ABRTCMC_I2C_ADR, ABRTCMC_SECONDS, &mut rxbuf, None ) { + Ok(status) => { + match status { + I2cStatus::ResponseReadOk => { + Ok(crate::DateTime { + seconds: to_binary(rxbuf[0] & 0x7f), + minutes: to_binary(rxbuf[1] & 0x7f), + hours: to_binary(rxbuf[2] & 0x3f), + days: to_binary(rxbuf[3] & 0x3f), + weekday: to_weekday(rxbuf[4] & 0x7f), + months: to_binary(rxbuf[5] & 0x1f), + years: to_binary(rxbuf[6]), + }) + }, + _ => Err(xous::Error::InternalError), + } + } + _ => Err(xous::Error::InternalError) + } + } + // the awkward array syntax is a legacy of a port from a previous implementation // would be fine to clean up method signature as e.g. // blocking_i2c_write2(adr: u8, data: u8) -> bool @@ -447,55 +407,48 @@ mod implementation { mod implementation { use crate::api::Weekday; use chrono::prelude::*; - use crate::rtc::CB_TO_MAIN_CONN; - use core::sync::atomic::Ordering; use num_traits::ToPrimitive; - - fn rtc_thread(sid0: usize, sid1: usize, sid2: usize, sid3: usize) { - let sid = xous::SID::from_u32(sid0 as u32, sid1 as u32, sid2 as u32, sid3 as u32); - log::trace!("rtc callback server started"); - loop { - let msg = xous::receive_message(sid).unwrap(); - log::trace!("rtc callback got msg: {:?}", msg); - // we only have one purpose, and that's to send this message. - let cb_to_main_conn = CB_TO_MAIN_CONN.load(Ordering::Relaxed); - if cb_to_main_conn != 0 { - log::trace!("rtc_get sending time to main server"); - let now = Local::now(); - let wday: Weekday = match now.weekday() { - chrono::Weekday::Mon => Weekday::Monday, - chrono::Weekday::Tue => Weekday::Tuesday, - chrono::Weekday::Wed => Weekday::Wednesday, - chrono::Weekday::Thu => Weekday::Thursday, - chrono::Weekday::Fri => Weekday::Friday, - chrono::Weekday::Sat => Weekday::Saturday, - chrono::Weekday::Sun => Weekday::Sunday, - }; - let dt = crate::api::DateTime { - seconds: now.second() as u8, - minutes: now.minute() as u8, - hours: now.hour() as u8, - months: now.month() as u8, - days: now.day() as u8, - years: (now.year() - 2000) as u8, - weekday: wday, - }; - let buf = xous_ipc::Buffer::into_buf(dt).unwrap(); - buf.send(cb_to_main_conn, crate::api::RtcOpcode::ResponseDateTime.to_u32().unwrap()).unwrap(); - } - } - } - pub struct Rtc { cb_conn: xous::CID, } impl Rtc { - pub fn new(_xns: &xous_names::XousNames) -> Rtc { + pub fn new(_xns: &xous_names::XousNames, cb_to_main: xous::CID) -> Rtc { let sid = xous::create_server().unwrap(); - let sid_tuple = sid.to_u32(); let cid = xous::connect(sid).unwrap(); - xous::create_thread_4(rtc_thread, sid_tuple.0 as usize, sid_tuple.1 as usize, sid_tuple.2 as usize, sid_tuple.3 as usize).unwrap(); + + std::thread::spawn({ + move || { + log::trace!("rtc callback server started"); + loop { + let msg = xous::receive_message(sid).unwrap(); + log::trace!("rtc callback got msg: {:?}", msg); + // we only have one purpose, and that's to send this message. + log::trace!("rtc_get sending time to main server"); + let now = Local::now(); + let wday: Weekday = match now.weekday() { + chrono::Weekday::Mon => Weekday::Monday, + chrono::Weekday::Tue => Weekday::Tuesday, + chrono::Weekday::Wed => Weekday::Wednesday, + chrono::Weekday::Thu => Weekday::Thursday, + chrono::Weekday::Fri => Weekday::Friday, + chrono::Weekday::Sat => Weekday::Saturday, + chrono::Weekday::Sun => Weekday::Sunday, + }; + let dt = crate::api::DateTime { + seconds: now.second() as u8, + minutes: now.minute() as u8, + hours: now.hour() as u8, + months: now.month() as u8, + days: now.day() as u8, + years: (now.year() - 2000) as u8, + weekday: wday, + }; + let buf = xous_ipc::Buffer::into_buf(dt).unwrap(); + buf.send(cb_to_main, crate::api::RtcOpcode::ResponseDateTime.to_u32().unwrap()).unwrap(); + } + } + }); Rtc { cb_conn: cid, } @@ -508,12 +461,32 @@ mod implementation { xous::send_message(self.cb_conn, xous::Message::new_scalar(0, 0, 0, 0, 0)).unwrap(); Ok(()) } + pub fn rtc_get_blocking(&mut self) -> Result { + let now = Local::now(); + let wday: Weekday = match now.weekday() { + chrono::Weekday::Mon => Weekday::Monday, + chrono::Weekday::Tue => Weekday::Tuesday, + chrono::Weekday::Wed => Weekday::Wednesday, + chrono::Weekday::Thu => Weekday::Thursday, + chrono::Weekday::Fri => Weekday::Friday, + chrono::Weekday::Sat => Weekday::Saturday, + chrono::Weekday::Sun => Weekday::Sunday, + }; + let dt = crate::api::DateTime { + seconds: now.second() as u8, + minutes: now.minute() as u8, + hours: now.hour() as u8, + months: now.month() as u8, + days: now.day() as u8, + years: (now.year() - 2000) as u8, + weekday: wday, + }; + Ok(dt) + } pub fn wakeup_alarm(&mut self, _seconds: u8) { } pub fn clear_wakeup_alarm(&mut self) { } pub fn rtc_alarm(&mut self, _seconds: u8) { } pub fn clear_rtc_alarm(&mut self) { } - pub fn rtc_get_ack(&mut self) { - } } } @@ -522,19 +495,15 @@ pub(crate) fn rtc_server(rtc_sid: xous::SID) { log::info!("my PID is {}", xous::process::id()); let xns = xous_names::XousNames::new().unwrap(); - CB_TO_MAIN_CONN.store(xous::connect(rtc_sid).unwrap(), Ordering::Relaxed); + let cb_to_main = xous::connect(rtc_sid).unwrap(); - #[cfg(any(target_os = "none", target_os = "xous"))] - let mut rtc = Rtc::new(&xns, CB_TO_MAIN_CONN.load(Ordering::Relaxed)); // swap the conn variable out later - - #[cfg(not(any(target_os = "none", target_os = "xous")))] - let mut rtc = Rtc::new(&xns); + let mut rtc = Rtc::new(&xns, cb_to_main); let ticktimer = ticktimer_server::Ticktimer::new().expect("can't connect to ticktimer"); let mut dt_cb_conns: [bool; xous::MAX_CID] = [false; xous::MAX_CID]; log::trace!("ready to accept requests"); loop { - let msg = xous::receive_message(rtc_sid).unwrap(); + let mut msg = xous::receive_message(rtc_sid).unwrap(); log::trace!("Message: {:?}", msg); match FromPrimitive::from_usize(msg.body.id()) { Some(RtcOpcode::SetDateTime) => { @@ -639,6 +608,11 @@ pub(crate) fn rtc_server(rtc_sid: xous::SID) { } log::trace!("RequestDateTime completed"); } + Some(RtcOpcode::RequestDateTimeBlocking) => { + let mut buffer = unsafe { Buffer::from_memory_message_mut(msg.body.memory_message_mut().unwrap()) }; + let dt = rtc.rtc_get_blocking().expect("couldn't read RTC"); + buffer.replace(dt).unwrap(); + } Some(RtcOpcode::SetWakeupAlarm) => msg_blocking_scalar_unpack!(msg, delay, _, _, _, { rtc.wakeup_alarm(delay as u8); // this will block until finished, no callbacks used xous::return_scalar(msg.sender, 0).expect("couldn't return to caller"); @@ -668,10 +642,7 @@ pub(crate) fn rtc_server(rtc_sid: xous::SID) { // clean up our program log::trace!("main loop exit, destroying servers"); unsafe{ - let cb_to_main_conn = CB_TO_MAIN_CONN.load(Ordering::Relaxed); - if cb_to_main_conn != 0 { - xous::disconnect(cb_to_main_conn).unwrap(); - } + xous::disconnect(cb_to_main).ok(); } for cid in 1..dt_cb_conns.len() { if dt_cb_conns[cid as usize] { @@ -702,4 +673,19 @@ fn to_weekday(bcd: u8) -> Weekday { 6 => Weekday::Saturday, _ => Weekday::Sunday, } -} \ No newline at end of file +} + +/// convert binary to BCD +#[allow(dead_code)] // unused in hosted mode +fn to_bcd(binary: u8) -> u8 { + let mut lsd: u8 = binary % 10; + if lsd > 9 { + lsd = 9; + } + let mut msd: u8 = binary / 10; + if msd > 9 { + msd = 9; + } + + (msd << 4) | lsd +} diff --git a/services/shellchat/src/cmds/rtc_cmd.rs b/services/shellchat/src/cmds/rtc_cmd.rs index b2f399424..197d68a42 100644 --- a/services/shellchat/src/cmds/rtc_cmd.rs +++ b/services/shellchat/src/cmds/rtc_cmd.rs @@ -26,7 +26,7 @@ impl<'a> ShellCmdApi<'a> for RtcCmd { fn process(&mut self, args: String::<1024>, env: &mut CommonEnv) -> Result>, xous::Error> { use core::fmt::Write; let mut ret = String::<1024>::new(); - let helpstring = "rtc options: set, get"; + let helpstring = "rtc options: set, get, pubget"; let mut tokens = args.as_str().unwrap().split(' '); @@ -37,6 +37,10 @@ impl<'a> ShellCmdApi<'a> for RtcCmd { env.rtc.lock().unwrap().hook_rtc_callback(dt_callback).unwrap(); env.rtc.lock().unwrap().request_datetime().unwrap(); }, + "pubget" => { + // super lazy test routine + write!(ret, "{:?}", env.llio.read_rtc_blocking().unwrap()).unwrap(); + } "set" => { let mut success = true; let mut hour: u8 = 0;