1#![cfg_attr(target_os = "none", no_std)]
2#![cfg_attr(not(target_os = "none"), allow(dead_code))]
3#![cfg_attr(not(target_os = "none"), allow(unused_imports))]
4#![cfg_attr(not(target_os = "none"), allow(unused_variables))]
5
6pub mod api;
7pub use api::*;
8use num_traits::{FromPrimitive, ToPrimitive};
9use xous::{CID, Message, msg_scalar_unpack, send_message};
10use xous_ipc::Buffer;
11
12#[derive(Debug)]
13pub struct Susres {
14 conn: CID,
15 suspend_cb_sid: Option<xous::SID>,
16}
17impl Susres {
18 #[cfg(any(feature = "precursor", feature = "renode"))]
19 pub fn new(
27 order: Option<SuspendOrder>,
28 xns: &xous_names::XousNames,
29 cb_discriminant: u32,
30 cid: CID,
31 ) -> Result<Self, xous::Error> {
32 REFCOUNT.fetch_add(1, Ordering::Relaxed);
33 let conn = xns.request_connection_blocking(api::SERVER_NAME_SUSRES).expect("Can't connect to SUSRES");
34
35 let sid = xous::create_server().unwrap();
36 let sid_tuple = sid.to_u32();
37 xous::create_thread_4(
38 suspend_cb_server,
39 sid_tuple.0 as usize,
40 sid_tuple.1 as usize,
41 sid_tuple.2 as usize,
42 sid_tuple.3 as usize,
43 )
44 .unwrap();
45 let hookdata = ScalarHook {
46 sid: sid_tuple,
47 id: cb_discriminant,
48 cid,
49 order: order.unwrap_or(SuspendOrder::Normal),
50 };
51 log::debug!("hooking {:?}", hookdata);
52 let buf = Buffer::into_buf(hookdata).or(Err(xous::Error::InternalError))?;
53 buf.lend(conn, Opcode::SuspendEventSubscribe.to_u32().unwrap())?;
54
55 Ok(Susres { conn, suspend_cb_sid: Some(sid) })
56 }
57
58 #[cfg(not(target_os = "xous"))]
64 pub fn new(
72 _ordering: Option<SuspendOrder>,
73 xns: &xous_names::XousNames,
74 cb_discriminant: u32,
75 cid: CID,
76 ) -> Result<Self, xous::Error> {
77 REFCOUNT.fetch_add(1, Ordering::Relaxed);
78 Ok(Susres { conn: 0, suspend_cb_sid: None })
79 }
80
81 pub fn new_without_hook(xns: &xous_names::XousNames) -> Result<Self, xous::Error> {
85 REFCOUNT.fetch_add(1, Ordering::Relaxed);
86 let conn = xns.request_connection_blocking(api::SERVER_NAME_SUSRES)?;
87 Ok(Susres { conn, suspend_cb_sid: None })
88 }
89
90 pub fn initiate_suspend(&self) -> Result<(), xous::Error> {
98 log::trace!("suspend initiated");
99 match send_message(
100 self.conn,
101 Message::new_blocking_scalar(Opcode::SuspendRequest.to_usize().unwrap(), 0, 0, 0, 0),
102 ) {
103 Ok(xous::Result::Scalar1(result)) => {
104 if result == 1 {
105 Ok(())
106 } else {
107 Err(xous::Error::Timeout)
109 }
110 }
111 _ => Err(xous::Error::InternalError),
112 }
113 }
114
115 pub fn suspend_until_resume(&mut self, token: usize) -> Result<bool, xous::Error> {
125 if self.suspend_cb_sid.is_none() {
126 return Err(xous::Error::UseBeforeInit);
128 }
129 log::debug!("token {} pid {} suspending", token, xous::process::id()); xous::yield_slice();
131 send_message(
133 self.conn,
134 Message::new_scalar(Opcode::SuspendReady.to_usize().unwrap(), token, 0, 0, 0),
135 )
136 .map(|_| ())?;
137 log::trace!("blocking until suspend");
138
139 send_message(
143 self.conn,
144 Message::new_blocking_scalar(Opcode::SuspendingNow.to_usize().unwrap(), 0, 0, 0, 0),
145 )
146 .map(|_| ())?;
147
148 let response = send_message(
149 self.conn,
150 Message::new_blocking_scalar(Opcode::WasSuspendClean.to_usize().unwrap(), token, 0, 0, 0),
151 )
152 .expect("couldn't query if my suspend was successful");
153 if let xous::Result::Scalar1(result) = response {
154 if result != 0 {
155 log::debug!("resume pid {} clean", xous::process::id()); Ok(true)
157 } else {
158 log::debug!("resume pid {} dirty", xous::process::id()); Ok(false)
160 }
161 } else {
162 Err(xous::Error::InternalError)
163 }
164 }
165
166 pub fn set_suspendable(&mut self, allow_suspend: bool) -> Result<(), xous::Error> {
171 if allow_suspend {
172 send_message(self.conn, Message::new_scalar(Opcode::SuspendAllow.to_usize().unwrap(), 0, 0, 0, 0))
173 .map(|_| ())
174 } else {
175 send_message(self.conn, Message::new_scalar(Opcode::SuspendDeny.to_usize().unwrap(), 0, 0, 0, 0))
176 .map(|_| ())
177 }
178 }
179
180 pub fn reboot(&self, whole_soc: bool) -> Result<(), xous::Error> {
183 send_message(self.conn, Message::new_scalar(Opcode::RebootRequest.to_usize().unwrap(), 0, 0, 0, 0))
184 .map(|_| ())?;
185
186 if whole_soc {
187 send_message(
188 self.conn,
189 Message::new_scalar(Opcode::RebootSocConfirm.to_usize().unwrap(), 0, 0, 0, 0),
190 )
191 .map(|_| ())
192 } else {
193 send_message(
194 self.conn,
195 Message::new_scalar(Opcode::RebootCpuConfirm.to_usize().unwrap(), 0, 0, 0, 0),
196 )
197 .map(|_| ())
198 }
199 }
200
201 pub fn immediate_poweroff(&self) -> Result<(), xous::Error> {
203 send_message(self.conn, Message::new_scalar(Opcode::PowerOff.to_usize().unwrap(), 0, 0, 0, 0))
204 .map(|_| ())
205 }
206}
207fn drop_conn(sid: xous::SID) {
208 let cid = xous::connect(sid).unwrap();
209 xous::send_message(cid, Message::new_scalar(SuspendEventCallback::Drop.to_usize().unwrap(), 0, 0, 0, 0))
210 .unwrap();
211 unsafe {
212 xous::disconnect(cid).unwrap();
213 }
214}
215use core::sync::atomic::{AtomicU32, Ordering};
216static REFCOUNT: AtomicU32 = AtomicU32::new(0);
217impl Drop for Susres {
218 fn drop(&mut self) {
219 if let Some(sid) = self.suspend_cb_sid.take() {
220 drop_conn(sid);
221 }
222 if REFCOUNT.fetch_sub(1, Ordering::Relaxed) == 1 {
223 unsafe {
224 xous::disconnect(self.conn).unwrap();
225 }
226 }
227 }
228}
229
230fn suspend_cb_server(sid0: usize, sid1: usize, sid2: usize, sid3: usize) {
231 let sid = xous::SID::from_u32(sid0 as u32, sid1 as u32, sid2 as u32, sid3 as u32);
232 let mut print_once = false;
233 loop {
234 let msg = xous::receive_message(sid).unwrap();
235 match FromPrimitive::from_usize(msg.body.id()) {
236 Some(SuspendEventCallback::Event) => msg_scalar_unpack!(msg, cid, id, token, _, {
237 if !print_once {
239 log::info!("PID {} has s/r token {}", xous::current_pid().unwrap().get(), token); print_once = true;
243 }
244 send_message(cid as u32, Message::new_scalar(id, token, 0, 0, 0)).unwrap();
245 }),
246 Some(SuspendEventCallback::Drop) => {
247 break; }
249 None => (),
250 }
251 }
252 xous::destroy_server(sid).unwrap();
253}