2020-11-24 14:45:44 +00:00
|
|
|
#![cfg_attr(target_os = "none", no_std)]
|
|
|
|
|
|
2020-11-24 21:11:32 +00:00
|
|
|
/// This is the API that other servers use to call the COM. Read this code as if you
|
|
|
|
|
/// are calling these functions inside a different process.
|
2020-11-24 14:45:44 +00:00
|
|
|
pub mod api;
|
|
|
|
|
|
2022-08-02 19:32:27 +00:00
|
|
|
use std::cell::RefCell;
|
2024-01-14 14:06:28 +00:00
|
|
|
use std::collections::VecDeque;
|
|
|
|
|
|
|
|
|
|
pub use api::*;
|
2023-05-02 12:33:17 +00:00
|
|
|
pub use com_rs::serdes::Ipv4Conf;
|
2024-01-14 14:06:28 +00:00
|
|
|
use com_rs::{DhcpState, LinkState};
|
|
|
|
|
use num_traits::{FromPrimitive, ToPrimitive};
|
2024-09-23 17:49:45 +00:00
|
|
|
use xous::{CID, Error, Message, msg_scalar_unpack, send_message};
|
2024-09-22 06:20:24 +00:00
|
|
|
use xous_ipc::Buffer;
|
2022-07-10 20:07:00 +00:00
|
|
|
use xous_semver::SemVer;
|
2021-10-08 18:12:12 +00:00
|
|
|
|
2021-04-01 21:43:47 +00:00
|
|
|
/// mapping of the callback function to the library user
|
|
|
|
|
/// this exists in the library user's memory space, so we can have up to one
|
|
|
|
|
/// callback per library user.
|
|
|
|
|
static mut BATTSTATS_CB: Option<fn(BattStats)> = None;
|
2020-11-24 14:45:44 +00:00
|
|
|
|
2021-04-01 21:43:47 +00:00
|
|
|
/// handles callback messages from the COM server, in the library user's process space.
|
|
|
|
|
fn battstats_server(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);
|
|
|
|
|
loop {
|
|
|
|
|
let msg = xous::receive_message(sid).unwrap();
|
|
|
|
|
match FromPrimitive::from_usize(msg.body.id()) {
|
2021-04-02 10:07:39 +00:00
|
|
|
Some(Callback::BattStats) => msg_scalar_unpack!(msg, lo, hi, _, _, {
|
2021-04-02 07:38:15 +00:00
|
|
|
let bs: BattStats = [lo, hi].into();
|
|
|
|
|
unsafe {
|
|
|
|
|
if let Some(cb) = BATTSTATS_CB {
|
|
|
|
|
cb(bs)
|
2021-04-01 21:43:47 +00:00
|
|
|
}
|
|
|
|
|
}
|
2021-04-02 07:38:15 +00:00
|
|
|
}),
|
2021-04-02 10:07:39 +00:00
|
|
|
Some(Callback::Drop) => {
|
2021-04-02 08:40:17 +00:00
|
|
|
break; // this exits the loop and kills the thread
|
|
|
|
|
}
|
|
|
|
|
None => (),
|
2021-04-01 21:43:47 +00:00
|
|
|
}
|
2020-11-24 21:11:32 +00:00
|
|
|
}
|
2021-04-09 10:51:43 +00:00
|
|
|
xous::destroy_server(sid).unwrap();
|
2020-11-24 21:11:32 +00:00
|
|
|
}
|
2021-04-05 15:50:00 +00:00
|
|
|
#[derive(Debug)]
|
2021-04-01 21:43:47 +00:00
|
|
|
pub struct Com {
|
|
|
|
|
conn: CID,
|
|
|
|
|
battstats_sid: Option<xous::SID>,
|
2021-05-23 20:34:50 +00:00
|
|
|
ec_lock_id: Option<[u32; 4]>,
|
|
|
|
|
ec_acquired: bool,
|
2024-01-14 14:06:28 +00:00
|
|
|
/// this is a hack to make loopbacks work on smoltcp. Work-around taken from Redox, but tracking this
|
2024-08-18 19:07:09 +00:00
|
|
|
/// issue as well: <https://github.com/smoltcp-rs/smoltcp/issues/50> and <https://github.com/smoltcp-rs/smoltcp/issues/55>
|
2024-01-14 14:06:28 +00:00
|
|
|
loopback_buf: RefCell<VecDeque<Vec<u8>>>,
|
2020-11-24 14:45:44 +00:00
|
|
|
}
|
2021-04-01 21:43:47 +00:00
|
|
|
impl Com {
|
2021-04-03 18:15:29 +00:00
|
|
|
pub fn new(xns: &xous_names::XousNames) -> Result<Self, xous::Error> {
|
2022-01-31 15:13:10 +00:00
|
|
|
REFCOUNT.fetch_add(1, Ordering::Relaxed);
|
2024-01-14 14:06:28 +00:00
|
|
|
let conn =
|
|
|
|
|
xns.request_connection_blocking(api::SERVER_NAME_COM).expect("Can't connect to COM server");
|
2021-04-01 21:43:47 +00:00
|
|
|
Ok(Com {
|
|
|
|
|
conn,
|
|
|
|
|
battstats_sid: None,
|
2021-05-23 20:34:50 +00:00
|
|
|
ec_lock_id: None,
|
|
|
|
|
ec_acquired: false,
|
2022-08-02 19:32:27 +00:00
|
|
|
loopback_buf: RefCell::new(VecDeque::new()),
|
2021-04-01 21:43:47 +00:00
|
|
|
})
|
|
|
|
|
}
|
2020-12-31 11:31:12 +00:00
|
|
|
|
2024-01-14 14:06:28 +00:00
|
|
|
pub fn conn(&self) -> CID { self.conn }
|
|
|
|
|
|
|
|
|
|
pub fn getop_backlight(&self) -> u32 { Opcode::SetBackLight.to_u32().unwrap() }
|
|
|
|
|
|
|
|
|
|
#[deprecated(
|
|
|
|
|
note = "Uses susres.immediate_poweroff() instead, as power sequencing requirements have changed."
|
|
|
|
|
)]
|
2021-04-01 21:43:47 +00:00
|
|
|
pub fn power_off_soc(&self) -> Result<(), xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(self.conn, Message::new_scalar(Opcode::PowerOffSoc.to_usize().unwrap(), 0, 0, 0, 0))
|
|
|
|
|
.map(|_| ())
|
2020-12-31 11:31:12 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-22 17:49:03 +00:00
|
|
|
/// ship mode is synchronous, so that we can schedule order-of-operation dependent tasks around it
|
2021-04-22 10:07:27 +00:00
|
|
|
pub fn ship_mode(&self) -> Result<(), xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
let response = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::ShipMode.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)?;
|
2021-04-22 17:49:03 +00:00
|
|
|
if let xous::Result::Scalar1(_) = response {
|
|
|
|
|
Ok(())
|
|
|
|
|
} else {
|
|
|
|
|
log::error!("ship_mode failed to execute");
|
|
|
|
|
Err(xous::Error::InternalError)
|
|
|
|
|
}
|
2021-04-22 10:07:27 +00:00
|
|
|
}
|
|
|
|
|
|
2021-09-14 12:15:03 +00:00
|
|
|
pub fn link_reset(&self) -> Result<(), xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::LinkReset.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)
|
|
|
|
|
.map(|_| ())
|
2021-09-14 12:15:03 +00:00
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
2022-02-05 07:44:47 +00:00
|
|
|
pub fn ping(&self, value: usize) -> Result<usize, xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
if let xous::Result::Scalar1(pong) = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::Ping.to_usize().unwrap(), value, 0, 0, 0),
|
2022-02-05 07:44:47 +00:00
|
|
|
)? {
|
|
|
|
|
Ok(pong)
|
|
|
|
|
} else {
|
|
|
|
|
Err(xous::Error::InternalError)
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
2021-09-13 15:28:54 +00:00
|
|
|
pub fn reseed_ec_trng(&self) -> Result<(), xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(self.conn, Message::new_scalar(Opcode::ReseedTrng.to_usize().unwrap(), 0, 0, 0, 0))
|
|
|
|
|
.map(|_| ())
|
2021-09-13 15:28:54 +00:00
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
2021-09-13 15:28:54 +00:00
|
|
|
pub fn get_ec_uptime(&self) -> Result<u64, xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
let response = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::GetUptime.to_usize().unwrap(), 0, 0, 0, 0),
|
2021-09-13 15:28:54 +00:00
|
|
|
)?;
|
|
|
|
|
if let xous::Result::Scalar2(lsb, msb) = response {
|
2024-01-14 14:06:28 +00:00
|
|
|
Ok(lsb as u64 | (msb as u64) << 32)
|
2021-09-13 15:28:54 +00:00
|
|
|
} else {
|
|
|
|
|
Err(xous::Error::InternalError)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 20:36:54 +00:00
|
|
|
pub fn get_wf200_fw_rev(&self) -> Result<SemVer, xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
let response = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::Wf200Rev.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)?;
|
2021-04-01 21:43:47 +00:00
|
|
|
if let xous::Result::Scalar1(rev) = response {
|
2024-01-14 14:06:28 +00:00
|
|
|
Ok(SemVer {
|
|
|
|
|
maj: ((rev >> 16) & 0xFF) as u16,
|
|
|
|
|
min: ((rev >> 8) & 0xFF) as u16,
|
|
|
|
|
rev: (rev & 0xFF) as u16,
|
|
|
|
|
extra: 0,
|
|
|
|
|
commit: None,
|
|
|
|
|
})
|
2021-04-01 21:43:47 +00:00
|
|
|
} else {
|
|
|
|
|
panic!("unexpected return value: {:#?}", response);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-31 11:31:12 +00:00
|
|
|
|
2022-01-13 13:12:14 +00:00
|
|
|
/// this is the Git rev of the *soc*, not the firmware.
|
2021-04-01 21:43:47 +00:00
|
|
|
pub fn get_ec_git_rev(&self) -> Result<(u32, bool), Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
let response = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::EcGitRev.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)?;
|
2021-04-01 21:43:47 +00:00
|
|
|
if let xous::Result::Scalar2(rev, dirty) = response {
|
|
|
|
|
let dirtybool: bool;
|
|
|
|
|
if dirty == 0 {
|
|
|
|
|
dirtybool = false;
|
|
|
|
|
} else {
|
|
|
|
|
dirtybool = true;
|
|
|
|
|
}
|
|
|
|
|
Ok((rev as u32, dirtybool))
|
|
|
|
|
} else {
|
|
|
|
|
panic!("unexpected return value: {:#?}", response);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-12 10:44:02 +00:00
|
|
|
|
2022-07-10 20:07:00 +00:00
|
|
|
/// this is the rev of the firmware
|
|
|
|
|
pub fn get_ec_sw_tag(&self) -> Result<SemVer, Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
let response = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::EcSwTag.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)?;
|
2022-01-13 13:12:14 +00:00
|
|
|
if let xous::Result::Scalar1(rev) = response {
|
2022-07-10 20:07:00 +00:00
|
|
|
Ok(SemVer {
|
|
|
|
|
maj: ((rev >> 24) & 0xff) as u16,
|
|
|
|
|
min: ((rev >> 16) & 0xff) as u16,
|
|
|
|
|
rev: ((rev >> 8) & 0xff) as u16,
|
|
|
|
|
extra: ((rev >> 0) & 0xff) as u16,
|
|
|
|
|
commit: None,
|
|
|
|
|
})
|
2022-01-13 13:12:14 +00:00
|
|
|
} else {
|
|
|
|
|
panic!("unexpected return value: {:#?}", response);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-22 06:20:24 +00:00
|
|
|
pub fn send_pds_line(&self, s: &String) -> Result<(), Error> {
|
2021-04-01 21:43:47 +00:00
|
|
|
use core::fmt::Write;
|
2024-09-22 06:20:24 +00:00
|
|
|
let mut clone_s: String = String::new();
|
|
|
|
|
write!(clone_s, "{}", s.as_str()).map_err(|_| xous::Error::AccessDenied)?;
|
2021-01-12 10:44:02 +00:00
|
|
|
|
2021-04-01 21:43:47 +00:00
|
|
|
let buf = Buffer::into_buf(clone_s).or(Err(xous::Error::InternalError))?;
|
2024-01-14 14:06:28 +00:00
|
|
|
buf.lend(self.conn, Opcode::Wf200PdsLine.to_u32().unwrap()).map(|_| ())
|
2021-04-01 21:43:47 +00:00
|
|
|
}
|
2021-02-12 17:42:31 +00:00
|
|
|
|
2021-04-02 08:40:17 +00:00
|
|
|
/// this kicks off an async callback for battery status at some later time
|
|
|
|
|
pub fn req_batt_stats(&self) -> Result<(), xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(self.conn, Message::new_scalar(Opcode::BattStatsNb.to_usize().unwrap(), 0, 0, 0, 0))
|
|
|
|
|
.map(|_| ())
|
2021-04-01 21:43:47 +00:00
|
|
|
}
|
2021-01-12 10:44:02 +00:00
|
|
|
|
2021-04-02 08:40:17 +00:00
|
|
|
/// this allows the caller to provide a hook to handle the callback
|
|
|
|
|
pub fn hook_batt_stats(&mut self, cb: fn(BattStats)) -> Result<(), xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
if unsafe { BATTSTATS_CB }.is_some() {
|
|
|
|
|
return Err(xous::Error::MemoryInUse);
|
2021-04-02 08:40:17 +00:00
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
unsafe { BATTSTATS_CB = Some(cb) };
|
2021-04-02 08:40:17 +00:00
|
|
|
if self.battstats_sid.is_none() {
|
|
|
|
|
let sid = xous::create_server().unwrap();
|
|
|
|
|
self.battstats_sid = Some(sid);
|
|
|
|
|
let sid_tuple = sid.to_u32();
|
2024-01-14 14:06:28 +00:00
|
|
|
xous::create_thread_4(
|
|
|
|
|
battstats_server,
|
|
|
|
|
sid_tuple.0 as usize,
|
|
|
|
|
sid_tuple.1 as usize,
|
|
|
|
|
sid_tuple.2 as usize,
|
|
|
|
|
sid_tuple.3 as usize,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
xous::send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_scalar(
|
|
|
|
|
Opcode::RegisterBattStatsListener.to_usize().unwrap(),
|
|
|
|
|
sid_tuple.0 as usize,
|
|
|
|
|
sid_tuple.1 as usize,
|
|
|
|
|
sid_tuple.2 as usize,
|
|
|
|
|
sid_tuple.3 as usize,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
2021-04-02 08:40:17 +00:00
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2021-04-14 14:43:23 +00:00
|
|
|
|
2021-09-05 15:26:07 +00:00
|
|
|
pub fn get_batt_stats_blocking(&mut self) -> Result<BattStats, xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
let response = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::BattStats.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)?;
|
2021-09-05 15:26:07 +00:00
|
|
|
if let xous::Result::Scalar2(rs0, rs1) = response {
|
|
|
|
|
let bs: BattStats = [rs0, rs1].into();
|
|
|
|
|
Ok(bs)
|
|
|
|
|
} else {
|
|
|
|
|
Err(xous::Error::InternalError)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_more_stats(&mut self) -> Result<[u16; 15], xous::Error> {
|
|
|
|
|
let alloc_stats: [u16; 15] = [0; 15];
|
|
|
|
|
let mut buf = Buffer::into_buf(alloc_stats).or(Err(xous::Error::InternalError))?;
|
|
|
|
|
buf.lend_mut(self.conn, Opcode::MoreStats.to_u32().unwrap())?;
|
|
|
|
|
|
|
|
|
|
let stats: [u16; 15] = buf.to_original::<[u16; 15], _>().unwrap();
|
|
|
|
|
Ok(stats)
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-06 06:39:44 +00:00
|
|
|
pub fn poll_usb_cc(&mut self) -> Result<(bool, [u16; 3], u8), xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
let response = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::PollUsbCc.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)?;
|
2021-09-05 15:26:07 +00:00
|
|
|
if let xous::Result::Scalar2(val1, val2) = response {
|
2024-01-14 14:06:28 +00:00
|
|
|
let event = if ((val1 >> 16) & 0xff) == 0 { false } else { true };
|
|
|
|
|
let regs: [u16; 3] =
|
|
|
|
|
[(val1 & 0xFFFF) as u16, (val2 & 0xFFFF) as u16, ((val2 >> 16) & 0xFF) as u16];
|
2021-09-06 06:39:44 +00:00
|
|
|
let rev: u8 = ((val1 >> 24) & 0xff) as u8;
|
|
|
|
|
Ok((event, regs, rev))
|
2021-09-05 15:26:07 +00:00
|
|
|
} else {
|
|
|
|
|
Err(xous::Error::InternalError)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-14 14:43:23 +00:00
|
|
|
pub fn wifi_disable(&self) -> Result<(), xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(self.conn, Message::new_scalar(Opcode::Wf200Disable.to_usize().unwrap(), 0, 0, 0, 0))
|
|
|
|
|
.map(|_| ())
|
2021-04-14 14:43:23 +00:00
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
2021-04-14 14:43:23 +00:00
|
|
|
// as wifi_reset() re-initializes the wifi chip, call this after wifi_disable() to re-enable wifi
|
2022-01-30 11:07:29 +00:00
|
|
|
pub fn wifi_reset(&self) -> Result<usize, xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
let ret = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::Wf200Reset.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)
|
|
|
|
|
.expect("couldn't send reset opcode");
|
2022-01-30 11:07:29 +00:00
|
|
|
if let xous::Result::Scalar1(time) = ret {
|
|
|
|
|
log::info!("WF200 reset took {}ms", time);
|
|
|
|
|
Ok(time)
|
|
|
|
|
} else {
|
|
|
|
|
Err(xous::Error::Timeout)
|
|
|
|
|
}
|
2021-04-14 14:43:23 +00:00
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
2021-04-14 20:10:47 +00:00
|
|
|
pub fn set_ssid_scanning(&self, enable: bool) -> Result<(), xous::Error> {
|
|
|
|
|
if enable {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(self.conn, Message::new_scalar(Opcode::ScanOn.to_usize().unwrap(), 0, 0, 0, 0))
|
|
|
|
|
.map(|_| ())
|
2021-04-14 20:10:47 +00:00
|
|
|
} else {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(self.conn, Message::new_scalar(Opcode::ScanOff.to_usize().unwrap(), 0, 0, 0, 0))
|
|
|
|
|
.map(|_| ())
|
2021-04-14 20:10:47 +00:00
|
|
|
}
|
|
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
2022-01-26 19:56:39 +00:00
|
|
|
// this function no longer works, must rely on the event-based response via ComInt mechanism
|
|
|
|
|
#[deprecated]
|
2021-04-16 15:07:31 +00:00
|
|
|
pub fn ssid_scan_updated(&self) -> Result<bool, xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
if let xous::Result::Scalar1(avail) = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::SsidCheckUpdate.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)
|
|
|
|
|
.unwrap()
|
|
|
|
|
{
|
|
|
|
|
if avail != 0 { Ok(true) } else { Ok(false) }
|
2021-04-16 15:07:31 +00:00
|
|
|
} else {
|
|
|
|
|
Err(xous::Error::InternalError)
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
2022-01-13 13:12:14 +00:00
|
|
|
// superceded by ssid_fetch_as_list in versions later 0.9.5 (non-inclusive)
|
|
|
|
|
#[deprecated]
|
2024-09-22 06:20:24 +00:00
|
|
|
pub fn ssid_fetch_as_string(&self) -> Result<String, xous::Error> {
|
|
|
|
|
let ssid_list = String::new();
|
2021-04-16 15:07:31 +00:00
|
|
|
let mut buf = Buffer::into_buf(ssid_list).or(Err(xous::Error::InternalError))?;
|
2024-01-14 14:06:28 +00:00
|
|
|
buf.lend_mut(self.conn, Opcode::SsidFetchAsString.to_u32().unwrap())
|
|
|
|
|
.or(Err(xous::Error::InternalError))?;
|
2024-09-22 06:20:24 +00:00
|
|
|
let response = buf.to_original::<String, _>().unwrap();
|
2021-04-16 15:07:31 +00:00
|
|
|
Ok(response)
|
|
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
2022-01-13 13:12:14 +00:00
|
|
|
/// returns a vector of `(u8, String)` tuples that represent rssi + AP name
|
2022-01-30 08:53:15 +00:00
|
|
|
/// Note: this only returns the very most recent incremental scan results from the wifi chip directly.
|
2024-01-14 14:06:28 +00:00
|
|
|
/// The aggregated results of multiple scan passes are accessible from the connection manager via the
|
|
|
|
|
/// NetMgr object.
|
2022-01-13 13:12:14 +00:00
|
|
|
pub fn ssid_fetch_as_list(&self) -> Result<Vec<(u8, std::string::String)>, xous::Error> {
|
|
|
|
|
let ssid_alloc = SsidReturn::default();
|
|
|
|
|
let mut buf = Buffer::into_buf(ssid_alloc).or(Err(xous::Error::InternalError))?;
|
2024-01-14 14:06:28 +00:00
|
|
|
buf.lend_mut(self.conn, Opcode::SsidFetchAsStringV2.to_u32().unwrap())
|
|
|
|
|
.or(Err(xous::Error::InternalError))?;
|
2022-01-13 13:12:14 +00:00
|
|
|
let response = buf.to_original::<SsidReturn, _>().unwrap();
|
|
|
|
|
let mut ret = Vec::<(u8, std::string::String)>::new();
|
|
|
|
|
for ssid in response.list {
|
2024-09-22 06:20:24 +00:00
|
|
|
ret.push((ssid.rssi, ssid.name));
|
2022-01-13 13:12:14 +00:00
|
|
|
}
|
|
|
|
|
Ok(ret)
|
|
|
|
|
}
|
2021-04-14 14:43:23 +00:00
|
|
|
|
2021-04-16 11:58:05 +00:00
|
|
|
pub fn get_standby_current(&self) -> Result<Option<i16>, xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
if let xous::Result::Scalar2(valid, current) = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::StandbyCurrent.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)
|
|
|
|
|
.unwrap()
|
|
|
|
|
{
|
|
|
|
|
if valid != 0 { Ok(Some(current as i16)) } else { Ok(None) }
|
2021-04-16 11:58:05 +00:00
|
|
|
} else {
|
|
|
|
|
Err(xous::Error::InternalError)
|
|
|
|
|
}
|
|
|
|
|
}
|
debugging BOOST mode glitch issue
Here's an arcane bug for you:
When powering down, the Spartan 7 3.3V I/O drivers will
output a roughly 800us wide glitch as 3.3V falls below around
1.7V, if the 0.95V core voltage comes down first (which it does
because we have an active discharge on 0.95V, but not on 3.3V).
In this case, it's just wide and high enough to cause the
BOOST mode line to engage, which takes the BOOST output
and ties it to VBUS, which is just enough for the system to
think you're powering it back on again. Which is not what
you want when powering down.
The ultimate fix was to put a heftier pull-down on the BOOST
line so that it can't trip the BOOST output into the VBUS.
Another option could be more active discharge FETs, but that
would require a full board spin and we are already at certification,
so we'll stick with the stronger pull-down on BOOST. It seems to
be a robust fix in the worst corner cases we can find.
The downside is that when powering an external USB device,
you are burning an extra 30mW of power overcoming the pull-down,
but typically, any external USB devices plugged into Precursor
will burn far in excess of that, so the delta should be negligible.
2021-04-23 07:33:14 +00:00
|
|
|
|
|
|
|
|
pub fn set_boost(&self, on: bool) -> Result<(), xous::Error> {
|
|
|
|
|
if on {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(self.conn, Message::new_scalar(Opcode::BoostOn.to_usize().unwrap(), 0, 0, 0, 0))
|
|
|
|
|
.map(|_| ())
|
debugging BOOST mode glitch issue
Here's an arcane bug for you:
When powering down, the Spartan 7 3.3V I/O drivers will
output a roughly 800us wide glitch as 3.3V falls below around
1.7V, if the 0.95V core voltage comes down first (which it does
because we have an active discharge on 0.95V, but not on 3.3V).
In this case, it's just wide and high enough to cause the
BOOST mode line to engage, which takes the BOOST output
and ties it to VBUS, which is just enough for the system to
think you're powering it back on again. Which is not what
you want when powering down.
The ultimate fix was to put a heftier pull-down on the BOOST
line so that it can't trip the BOOST output into the VBUS.
Another option could be more active discharge FETs, but that
would require a full board spin and we are already at certification,
so we'll stick with the stronger pull-down on BOOST. It seems to
be a robust fix in the worst corner cases we can find.
The downside is that when powering an external USB device,
you are burning an extra 30mW of power overcoming the pull-down,
but typically, any external USB devices plugged into Precursor
will burn far in excess of that, so the delta should be negligible.
2021-04-23 07:33:14 +00:00
|
|
|
} else {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(self.conn, Message::new_scalar(Opcode::BoostOff.to_usize().unwrap(), 0, 0, 0, 0))
|
|
|
|
|
.map(|_| ())
|
debugging BOOST mode glitch issue
Here's an arcane bug for you:
When powering down, the Spartan 7 3.3V I/O drivers will
output a roughly 800us wide glitch as 3.3V falls below around
1.7V, if the 0.95V core voltage comes down first (which it does
because we have an active discharge on 0.95V, but not on 3.3V).
In this case, it's just wide and high enough to cause the
BOOST mode line to engage, which takes the BOOST output
and ties it to VBUS, which is just enough for the system to
think you're powering it back on again. Which is not what
you want when powering down.
The ultimate fix was to put a heftier pull-down on the BOOST
line so that it can't trip the BOOST output into the VBUS.
Another option could be more active discharge FETs, but that
would require a full board spin and we are already at certification,
so we'll stick with the stronger pull-down on BOOST. It seems to
be a robust fix in the worst corner cases we can find.
The downside is that when powering an external USB device,
you are burning an extra 30mW of power overcoming the pull-down,
but typically, any external USB devices plugged into Precursor
will burn far in excess of that, so the delta should be negligible.
2021-04-23 07:33:14 +00:00
|
|
|
}
|
|
|
|
|
}
|
2021-05-15 20:29:12 +00:00
|
|
|
|
|
|
|
|
// numbers from 0-255 represent backlight brightness. Note that only the top 5 bits are used.
|
|
|
|
|
pub fn set_backlight(&self, main: u8, secondary: u8) -> Result<(), xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_scalar(
|
|
|
|
|
Opcode::SetBackLight.to_usize().unwrap(),
|
2021-05-15 20:29:12 +00:00
|
|
|
(main >> 3) as usize,
|
|
|
|
|
(secondary >> 3) as usize,
|
2024-01-14 14:06:28 +00:00
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.map(|_| ())
|
2021-05-15 20:29:12 +00:00
|
|
|
}
|
2021-05-16 08:30:58 +00:00
|
|
|
|
|
|
|
|
pub fn is_charging(&self) -> Result<bool, xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
if let xous::Result::Scalar1(state) = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::IsCharging.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)
|
|
|
|
|
.unwrap()
|
|
|
|
|
{
|
|
|
|
|
if state != 0 { Ok(true) } else { Ok(false) }
|
2021-05-16 08:30:58 +00:00
|
|
|
} else {
|
|
|
|
|
Err(xous::Error::InternalError)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn request_charging(&self) -> Result<(), xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(self.conn, Message::new_scalar(Opcode::RequestCharging.to_usize().unwrap(), 0, 0, 0, 0))
|
|
|
|
|
.map(|_| ())
|
2021-05-16 08:30:58 +00:00
|
|
|
}
|
2021-05-16 08:52:27 +00:00
|
|
|
|
|
|
|
|
pub fn gyro_read_blocking(&self) -> Result<(u16, u16, u16, u16), xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
if let xous::Result::Scalar2(x_y, z_id) = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::ImuAccelReadBlocking.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)
|
|
|
|
|
.unwrap()
|
|
|
|
|
{
|
2021-05-16 08:52:27 +00:00
|
|
|
let x = (x_y >> 16) as u16;
|
|
|
|
|
let y = (x_y & 0xffff) as u16;
|
|
|
|
|
let z = (z_id >> 16) as u16;
|
|
|
|
|
let id = (z_id & 0xffff) as u16;
|
|
|
|
|
Ok((x, y, z, id))
|
|
|
|
|
} else {
|
|
|
|
|
Err(xous::Error::InternalError)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-23 20:34:50 +00:00
|
|
|
pub fn flash_acquire(&mut self) -> Result<bool, xous::Error> {
|
|
|
|
|
let (id0, id1, id2, id3) = xous::create_server_id()?.to_u32();
|
|
|
|
|
self.ec_lock_id = Some([id0, id1, id2, id3]);
|
2024-01-14 14:06:28 +00:00
|
|
|
if let xous::Result::Scalar1(acquired) = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(
|
|
|
|
|
Opcode::FlashAcquire.to_usize().unwrap(),
|
|
|
|
|
id0 as usize,
|
|
|
|
|
id1 as usize,
|
|
|
|
|
id2 as usize,
|
|
|
|
|
id3 as usize,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.unwrap()
|
|
|
|
|
{
|
2021-05-23 20:34:50 +00:00
|
|
|
if acquired != 0 {
|
|
|
|
|
self.ec_acquired = true;
|
|
|
|
|
Ok(true)
|
|
|
|
|
} else {
|
|
|
|
|
self.ec_acquired = false;
|
|
|
|
|
Ok(false)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
self.ec_acquired = false;
|
|
|
|
|
Err(xous::Error::InternalError)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn flash_erase(&mut self, addr: u32, len: u32) -> Result<bool, xous::Error> {
|
|
|
|
|
if !self.ec_acquired {
|
2024-01-14 14:06:28 +00:00
|
|
|
return Err(xous::Error::AccessDenied);
|
2021-05-23 20:34:50 +00:00
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
let flashop = api::FlashRecord { id: self.ec_lock_id.unwrap(), op: api::FlashOp::Erase(addr, len) };
|
2021-05-23 20:34:50 +00:00
|
|
|
let mut buf = Buffer::into_buf(flashop).or(Err(xous::Error::InternalError))?;
|
2024-01-14 14:06:28 +00:00
|
|
|
buf.lend_mut(self.conn, Opcode::FlashOp.to_u32().unwrap())
|
|
|
|
|
.expect("couldn't send flash erase command");
|
2021-05-23 20:34:50 +00:00
|
|
|
match buf.to_original().unwrap() {
|
2024-01-14 14:06:28 +00:00
|
|
|
api::FlashResult::Pass => Ok(true),
|
|
|
|
|
api::FlashResult::Fail => Ok(false),
|
2021-05-23 20:34:50 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn flash_program(&mut self, addr: u32, page: [Option<[u8; 256]>; 4]) -> Result<bool, xous::Error> {
|
|
|
|
|
if !self.ec_acquired {
|
2024-01-14 14:06:28 +00:00
|
|
|
return Err(xous::Error::AccessDenied);
|
2021-05-23 20:34:50 +00:00
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
let flashop =
|
|
|
|
|
api::FlashRecord { id: self.ec_lock_id.unwrap(), op: api::FlashOp::Program(addr, page) };
|
2021-05-23 20:34:50 +00:00
|
|
|
let mut buf = Buffer::into_buf(flashop).or(Err(xous::Error::InternalError))?;
|
2024-01-14 14:06:28 +00:00
|
|
|
buf.lend_mut(self.conn, Opcode::FlashOp.to_u32().unwrap())
|
|
|
|
|
.expect("couldn't send flash program command");
|
2021-05-23 20:34:50 +00:00
|
|
|
match buf.to_original().unwrap() {
|
2024-01-14 14:06:28 +00:00
|
|
|
api::FlashResult::Pass => Ok(true),
|
|
|
|
|
api::FlashResult::Fail => Ok(false),
|
2021-05-23 20:34:50 +00:00
|
|
|
}
|
|
|
|
|
}
|
2021-07-06 08:29:58 +00:00
|
|
|
|
2022-07-09 17:04:34 +00:00
|
|
|
/// Reads a page of data out of the EC, starting at address `addr`. Always reads 256 bytes.
|
|
|
|
|
/// The address is in absolute addressing in the EC space, which means this routine, rather
|
|
|
|
|
/// deliberately, could be used to also read RAM and CSRs in the EC...
|
|
|
|
|
pub fn flash_verify(&mut self, addr: u32, page: &mut [u8; 256]) -> Result<(), xous::Error> {
|
|
|
|
|
if !self.ec_acquired {
|
2024-01-14 14:06:28 +00:00
|
|
|
return Err(xous::Error::AccessDenied);
|
2022-07-09 17:04:34 +00:00
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
let flashop =
|
|
|
|
|
api::FlashRecord { id: self.ec_lock_id.unwrap(), op: api::FlashOp::Verify(addr, [0u8; 256]) };
|
2022-07-09 17:04:34 +00:00
|
|
|
let mut buf = Buffer::into_buf(flashop).or(Err(xous::Error::InternalError))?;
|
2024-01-14 14:06:28 +00:00
|
|
|
buf.lend_mut(self.conn, Opcode::FlashOp.to_u32().unwrap())
|
|
|
|
|
.expect("couldn't send flash program command");
|
2022-07-10 20:07:00 +00:00
|
|
|
let ret = buf.to_original::<api::FlashRecord, _>().unwrap();
|
|
|
|
|
match ret.op {
|
2022-07-09 17:04:34 +00:00
|
|
|
FlashOp::Verify(_a, d) => {
|
|
|
|
|
page.copy_from_slice(&d);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
_ => Err(xous::Error::InternalError),
|
2022-07-09 17:04:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-20 11:36:55 +00:00
|
|
|
/// This blocks until the COM responds from initializing the Wifi chip
|
2021-07-06 08:29:58 +00:00
|
|
|
pub fn wlan_set_on(&mut self) -> Result<xous::Result, xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(self.conn, Message::new_blocking_scalar(Opcode::WlanOn.to_usize().unwrap(), 0, 0, 0, 0))
|
2021-07-06 08:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn wlan_set_off(&mut self) -> Result<xous::Result, xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(self.conn, Message::new_scalar(Opcode::WlanOff.to_usize().unwrap(), 0, 0, 0, 0))
|
2021-07-06 08:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-08 17:29:28 +00:00
|
|
|
pub fn wlan_set_ssid(&mut self, s: &str) -> Result<xous::Result, xous::Error> {
|
2021-07-06 08:29:58 +00:00
|
|
|
use core::fmt::Write;
|
2021-07-07 03:34:30 +00:00
|
|
|
// Enforce WF200 driver API length limit
|
2022-01-08 17:29:28 +00:00
|
|
|
if s.len() > api::WF200_SSID_MAX_LEN {
|
2021-07-06 08:29:58 +00:00
|
|
|
return Err(xous::Error::InvalidString);
|
|
|
|
|
}
|
2024-09-22 06:20:24 +00:00
|
|
|
let mut copy = String::new();
|
2021-07-06 08:29:58 +00:00
|
|
|
let _ = write!(copy, "{}", s);
|
|
|
|
|
let buf = Buffer::into_buf(copy).or(Err(xous::Error::InternalError))?;
|
|
|
|
|
buf.lend(self.conn, Opcode::WlanSetSSID.to_u32().unwrap())
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-08 17:29:28 +00:00
|
|
|
pub fn wlan_set_pass(&mut self, s: &str) -> Result<xous::Result, xous::Error> {
|
2021-07-06 08:29:58 +00:00
|
|
|
use core::fmt::Write;
|
2021-07-07 03:34:30 +00:00
|
|
|
// Enforce WF200 driver API length limit
|
2022-01-08 17:29:28 +00:00
|
|
|
if s.len() > api::WF200_PASS_MAX_LEN {
|
2021-07-06 08:29:58 +00:00
|
|
|
return Err(xous::Error::InvalidString);
|
|
|
|
|
}
|
2024-09-22 06:20:24 +00:00
|
|
|
let mut copy = String::new();
|
2021-07-06 08:29:58 +00:00
|
|
|
let _ = write!(copy, "{}", s);
|
|
|
|
|
let buf = Buffer::into_buf(copy).or(Err(xous::Error::InternalError))?;
|
|
|
|
|
buf.lend(self.conn, Opcode::WlanSetPass.to_u32().unwrap())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn wlan_join(&mut self) -> Result<xous::Result, xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(self.conn, Message::new_scalar(Opcode::WlanJoin.to_usize().unwrap(), 0, 0, 0, 0))
|
2021-07-06 08:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn wlan_leave(&mut self) -> Result<xous::Result, xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
send_message(self.conn, Message::new_scalar(Opcode::WlanLeave.to_usize().unwrap(), 0, 0, 0, 0))
|
2021-07-06 08:29:58 +00:00
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
|
|
|
|
/// Note: applications should poll the `NetManager::read_wifi_state()` call for wifi status information,
|
|
|
|
|
/// not the COM directly. This is because the `wlan_status` call is fairly heavy weight, and the
|
|
|
|
|
/// `NetManager::read_wifi_state()` will cache this information making the status check lighter-weight
|
|
|
|
|
/// overall.
|
2022-09-20 11:36:55 +00:00
|
|
|
pub fn wlan_status(&self) -> Result<WlanStatus, xous::Error> {
|
2022-01-13 18:23:57 +00:00
|
|
|
let status = WlanStatusIpc::default();
|
2021-07-07 08:43:20 +00:00
|
|
|
let mut buf = Buffer::into_buf(status).or(Err(xous::Error::InternalError))?;
|
|
|
|
|
buf.lend_mut(self.conn, Opcode::WlanStatus.to_u32().unwrap()).or(Err(xous::Error::InternalError))?;
|
2022-01-13 18:23:57 +00:00
|
|
|
let response = buf.to_original::<WlanStatusIpc, _>().unwrap();
|
|
|
|
|
Ok(WlanStatus::from_ipc(response))
|
2021-07-06 08:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
2021-10-08 18:12:12 +00:00
|
|
|
pub fn wlan_get_config(&self) -> Result<Ipv4Conf, xous::Error> {
|
|
|
|
|
let prealloc = Ipv4Conf::default().encode_u16();
|
|
|
|
|
let mut buf = Buffer::into_buf(prealloc).or(Err(xous::Error::InternalError))?;
|
2024-01-14 14:06:28 +00:00
|
|
|
buf.lend_mut(self.conn, Opcode::WlanGetConfig.to_u32().expect("WlanGetConfig failed"))
|
|
|
|
|
.or(Err(xous::Error::InternalError))?;
|
2021-10-10 13:12:09 +00:00
|
|
|
let response = buf.to_original().expect("Couldn't convert WlanGetConfig buffer");
|
2021-10-08 18:12:12 +00:00
|
|
|
let config = Ipv4Conf::decode_u16(&response);
|
2024-01-14 14:06:28 +00:00
|
|
|
if (config.mac[0] & 0xFE) == 0x01 || (config.addr[0] & 0xF0) == 0xE0 {
|
2022-07-16 15:11:13 +00:00
|
|
|
// something is wrong with the COM; probably the link is being reset or not in a proper state.
|
|
|
|
|
Err(xous::Error::BadAddress)
|
|
|
|
|
} else {
|
|
|
|
|
Ok(config)
|
|
|
|
|
}
|
2021-10-08 18:12:12 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-14 07:27:24 +00:00
|
|
|
pub fn wlan_debug(&self) -> Result<WlanDebug, xous::Error> {
|
|
|
|
|
let prealloc = WlanDebug::default();
|
|
|
|
|
let mut buf = Buffer::into_buf(prealloc).or(Err(xous::Error::InternalError))?;
|
2024-01-14 14:06:28 +00:00
|
|
|
buf.lend_mut(self.conn, Opcode::WlanDebug.to_u32().expect("WlanDebug failed"))
|
|
|
|
|
.or(Err(xous::Error::InternalError))?;
|
2022-01-14 07:27:24 +00:00
|
|
|
let response = buf.to_original().expect("Couldn't convert WlanDebug buffer");
|
|
|
|
|
Ok(response)
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-08 18:12:12 +00:00
|
|
|
pub fn wlan_fetch_packet(&self, pkt: &mut [u8]) -> Result<(), xous::Error> {
|
|
|
|
|
if pkt.len() > NET_MTU {
|
2024-01-14 14:06:28 +00:00
|
|
|
return Err(xous::Error::OutOfMemory);
|
2021-10-08 18:12:12 +00:00
|
|
|
}
|
|
|
|
|
let mut prealloc: [u8; NET_MTU] = [0; NET_MTU];
|
|
|
|
|
let len_bytes = (pkt.len() as u16).to_be_bytes();
|
|
|
|
|
prealloc[0] = len_bytes[0];
|
|
|
|
|
prealloc[1] = len_bytes[1];
|
|
|
|
|
let mut buf = Buffer::into_buf(prealloc).or(Err(xous::Error::InternalError))?;
|
2024-01-14 14:06:28 +00:00
|
|
|
buf.lend_mut(self.conn, Opcode::WlanFetchPacket.to_u32().expect("WlanFetchPacket failed"))
|
|
|
|
|
.or(Err(xous::Error::InternalError))?;
|
2021-10-10 13:12:09 +00:00
|
|
|
let response = buf.as_flat::<[u8; NET_MTU], _>().expect("couldn't convert WlanFetchPacket buffer");
|
2021-10-08 18:12:12 +00:00
|
|
|
for (&src, dst) in response.iter().zip(pkt.iter_mut()) {
|
|
|
|
|
*dst = src;
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-05 07:12:57 +00:00
|
|
|
pub fn wlan_fetch_loopback_packet(&self, pkt: &mut [u8]) -> Result<(), xous::Error> {
|
2024-01-14 14:06:28 +00:00
|
|
|
// this is a hack to make loopbacks work on smoltcp. Work-around taken from Redox, but tracking this
|
|
|
|
|
// issue as well: https://github.com/smoltcp-rs/smoltcp/issues/50 and https://github.com/smoltcp-rs/smoltcp/issues/55
|
2022-08-05 07:12:57 +00:00
|
|
|
// Inject the loopback packets. loopback packets always take priority, for now.
|
|
|
|
|
if let Some(loop_packet) = self.loopback_buf.borrow_mut().pop_front() {
|
2024-01-14 14:06:28 +00:00
|
|
|
// pkt.len() is almost always not loop_packet.len(), just copy the first bit that can fit and the
|
|
|
|
|
// rest is garbarge...
|
2022-08-05 07:12:57 +00:00
|
|
|
for (&s, d) in loop_packet.iter().zip(pkt.iter_mut()) {
|
|
|
|
|
*d = s;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-14 14:06:28 +00:00
|
|
|
// this is a hack to make loopbacks work on smoltcp. Work-around taken from Redox, but tracking this issue
|
|
|
|
|
// as well: https://github.com/smoltcp-rs/smoltcp/issues/50 and https://github.com/smoltcp-rs/smoltcp/issues/55
|
2022-08-02 19:32:27 +00:00
|
|
|
// this function handles enqueuing packets for local injection
|
2024-01-14 14:06:28 +00:00
|
|
|
pub fn wlan_queue_loopback(&self, pk: &[u8]) { self.loopback_buf.borrow_mut().push_back(pk.to_vec()); }
|
2022-08-02 19:32:27 +00:00
|
|
|
|
2021-10-09 17:57:31 +00:00
|
|
|
pub fn wlan_send_packet(&self, pkt: &[u8]) -> Result<(), xous::Error> {
|
|
|
|
|
if pkt.len() > NET_MTU {
|
2024-01-14 14:06:28 +00:00
|
|
|
return Err(xous::Error::OutOfMemory);
|
2021-10-09 17:57:31 +00:00
|
|
|
}
|
|
|
|
|
let mut prealloc: [u8; NET_MTU + 2] = [0; NET_MTU + 2];
|
|
|
|
|
let len_bytes = (pkt.len() as u16).to_be_bytes();
|
|
|
|
|
prealloc[0] = len_bytes[0];
|
|
|
|
|
prealloc[1] = len_bytes[1];
|
|
|
|
|
for (&src, dst) in pkt.iter().zip(prealloc[2..].iter_mut()) {
|
|
|
|
|
*dst = src;
|
|
|
|
|
}
|
|
|
|
|
let buf = Buffer::into_buf(prealloc).or(Err(xous::Error::InternalError))?;
|
2024-01-14 14:06:28 +00:00
|
|
|
buf.send(self.conn, Opcode::WlanSendPacket.to_u32().expect("WlanSendPacket failed"))
|
|
|
|
|
.or(Err(xous::Error::InternalError))?;
|
2021-10-09 17:57:31 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
2022-01-26 19:56:39 +00:00
|
|
|
/// signal strength in -dBm (pre-negated, for "proper" reporting, add a - sign)
|
|
|
|
|
pub fn wlan_get_rssi(&self) -> Result<u8, xous::Error> {
|
|
|
|
|
let response = send_message(
|
|
|
|
|
self.conn,
|
2024-01-14 14:06:28 +00:00
|
|
|
Message::new_blocking_scalar(Opcode::WlanRssi.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)
|
|
|
|
|
.expect("couldn't send WlanRssi message");
|
2022-01-26 19:56:39 +00:00
|
|
|
if let xous::Result::Scalar1(rssi_usize) = response {
|
|
|
|
|
if rssi_usize & 0xFF_00 != 0 {
|
|
|
|
|
log::error!("got an error code in fetching the RSSI data: 0x{:x}", rssi_usize);
|
|
|
|
|
Err(xous::Error::UnknownError)
|
|
|
|
|
} else {
|
2022-12-12 07:31:06 +00:00
|
|
|
// must convert raw code to signal strength here
|
|
|
|
|
let rssi = 110u8 - (rssi_usize & 0xFF) as u8;
|
|
|
|
|
log::debug!("RSSI (lib): -{}dBm", rssi);
|
|
|
|
|
Ok(rssi)
|
2022-01-26 19:56:39 +00:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Err(xous::Error::InternalError)
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
2022-01-26 19:56:39 +00:00
|
|
|
pub fn wlan_sync_state(&self) -> Result<(LinkState, DhcpState), xous::Error> {
|
|
|
|
|
let response = send_message(
|
|
|
|
|
self.conn,
|
2024-01-14 14:06:28 +00:00
|
|
|
Message::new_blocking_scalar(Opcode::WlanSyncState.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)
|
|
|
|
|
.expect("couldn't send WlanRssi message");
|
2022-01-26 19:56:39 +00:00
|
|
|
if let xous::Result::Scalar2(link, dhcp) = response {
|
|
|
|
|
Ok((LinkState::decode_u16(link as u16), DhcpState::decode_u16(dhcp as u16)))
|
|
|
|
|
} else {
|
|
|
|
|
Err(xous::Error::InternalError)
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
2022-09-20 11:36:55 +00:00
|
|
|
pub fn wlan_is_reset_hold(&self) -> Result<bool, xous::Error> {
|
|
|
|
|
let status = self.wlan_status()?;
|
2024-01-14 14:06:28 +00:00
|
|
|
if status.link_state == LinkState::ResetHold { Ok(true) } else { Ok(false) }
|
2022-09-20 11:36:55 +00:00
|
|
|
}
|
2021-10-09 17:57:31 +00:00
|
|
|
|
2021-10-08 12:01:13 +00:00
|
|
|
pub fn ints_enable(&self, int_list: &[ComIntSources]) {
|
|
|
|
|
let mut mask_val: u16 = 0;
|
|
|
|
|
for &item in int_list.iter() {
|
|
|
|
|
let item_as_u16: u16 = item.into();
|
|
|
|
|
mask_val |= item_as_u16;
|
|
|
|
|
}
|
|
|
|
|
let _ = send_message(
|
|
|
|
|
self.conn,
|
2024-01-14 14:06:28 +00:00
|
|
|
Message::new_blocking_scalar(Opcode::IntSetMask.to_usize().unwrap(), mask_val as usize, 0, 0, 0),
|
|
|
|
|
)
|
|
|
|
|
.expect("couldn't send IntSetMask message");
|
2021-10-08 12:01:13 +00:00
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
|
|
|
|
pub fn ints_get_enabled(&self, int_list: &mut Vec<ComIntSources>) {
|
|
|
|
|
let response = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::IntGetMask.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)
|
|
|
|
|
.expect("Couldn't get IntGetMask");
|
2021-10-08 12:01:13 +00:00
|
|
|
if let xous::Result::Scalar1(raw_mask) = response {
|
|
|
|
|
let mut mask_bit: u16 = 1;
|
|
|
|
|
for _ in 0..16 {
|
|
|
|
|
let int_src = ComIntSources::from(mask_bit & raw_mask as u16);
|
|
|
|
|
if int_src != ComIntSources::Invalid {
|
|
|
|
|
int_list.push(int_src);
|
|
|
|
|
}
|
|
|
|
|
mask_bit <<= 1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
panic!("failed to send IntGetmask message");
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
2021-10-08 12:01:13 +00:00
|
|
|
pub fn ints_ack(&self, int_list: &[ComIntSources]) {
|
|
|
|
|
let mut ack_val: u16 = 0;
|
|
|
|
|
for &item in int_list.iter() {
|
|
|
|
|
let item_as_u16: u16 = item.into();
|
|
|
|
|
ack_val |= item_as_u16;
|
|
|
|
|
}
|
|
|
|
|
let _ = send_message(
|
|
|
|
|
self.conn,
|
2024-01-14 14:06:28 +00:00
|
|
|
Message::new_blocking_scalar(Opcode::IntAck.to_usize().unwrap(), ack_val as usize, 0, 0, 0),
|
|
|
|
|
)
|
|
|
|
|
.expect("couldn't send IntSetMask message");
|
2021-10-08 12:01:13 +00:00
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
|
|
|
|
|
pub fn ints_get_active(
|
|
|
|
|
&self,
|
|
|
|
|
int_list: &mut Vec<ComIntSources>,
|
|
|
|
|
) -> Result<(Option<u16>, usize, usize), xous::Error> {
|
|
|
|
|
let response = send_message(
|
|
|
|
|
self.conn,
|
|
|
|
|
Message::new_blocking_scalar(Opcode::IntFetchVector.to_usize().unwrap(), 0, 0, 0, 0),
|
|
|
|
|
)
|
|
|
|
|
.expect("couldn't get IntFetchVector");
|
2021-10-08 12:01:13 +00:00
|
|
|
let mut rxlen: Option<u16> = None;
|
|
|
|
|
if let xous::Result::Scalar2(ints, maybe_rxlen) = response {
|
THE BALDEST YAK: keyboard injection now working
I wrote in a commit yesterday how there was no ROI on doing this
yet somehow I kept on doing it. I blame poor impulse control.
Turns out there was a series of race conditions and bugs that
caused this thing to "mostly work" on the first go at it but
really hard to debug and get very right.
1. Litex UART has a weird undocumented API. You advance the Rx
FIFO by writing `1` to the PENDING bit, and no other way. However,
you determine if there are any more characters to receive by
reading the EMPTY bit and seeing if it is 1. The previous method
wrote `1` to the PENDING bit and then used the PENDING bit to
see if a character was available, under the theory that the
PENDING bit would be set again if there was still a character.
This is somewhat true, except that the PENDING bit is set only
some amount of time later which is about comparable to a few
CPU instructions, so if you had a fast loop path the PENDING
bit would show as cleared and you'd exit, which would cause
no further interrupts to fire because the interrupt was
effecitvely not acknowledged.
This isn't written up anywhere but I figured this out by
just reading the source code for Litex UART and BIOS:
https://github.com/enjoy-digital/litex/blob/e8be3915041707860e70b15d062661fc1f67a09f/litex/soc/cores/uart.py#L264-L279
https://github.com/enjoy-digital/litex/blob/ccd3ab17be1f8770c59ec1c62e0285d3bac3cc26/litex/soc/software/libbase/uart.c#L38-L44
2. I had the Key Injector IP block in the `sys` domain, which
would have its clock cut when the CPU went to sleep, which would
cause input characters to disappear when the CPU went into power
saving. moving this to "always on" domain fixes that.
3. I had the UART PHY in the always on block (so you could
send or receive exactly one character), but not the
separate UART block (which contained the FIFOs).
The UART block would also go into power
savings, and this would cause FIFO entries to go missing.
4. Escape sequences get sent as a burst of characters. The Key Injector
originally did not have a FIFO of its own, so it would only
remember the *last* key of the sequence that was written, causing
some keys to be dropped occassionally. Adding a 16-deep FIFO
to the injector fixes this.
5. The code I had to read out the injector's results was designed with
the "strobe" bit (to indicate that the FIFO had characters) in the
same regsiter as the character itself. Thus the loop that 1. checked
if the fifo was empty then 2. read and forwarded the character
would actually perform two reads from the FIFO. However, this "mostly worked"
I think because the FIFO retains the last value that was written
even when it was empty, so if only one character was read at a time
everything was fine. It only didn't work when you stuffed two characters
in there, as you'd drop the first one. Reading the FIFO once and
then interpreting the code with a bitmask fixes this.
6. The escape sequences, in their full glory with no dropped characters,
required a refactored parsing routine, which I have now added.
7. backspace->delete mappings, the bane of computer engineers since 1960.
OK. very bald yak. Here I lay down the shears next to several
mooshi pillows stuffed with the finest Yak hair.
2022-01-28 07:17:47 +00:00
|
|
|
if ints == 0xDDDD {
|
|
|
|
|
log::warn!("IntFetchVector: 0xDDDD sentinel returned");
|
|
|
|
|
return Err(xous::Error::Timeout);
|
|
|
|
|
}
|
2021-10-08 12:01:13 +00:00
|
|
|
let mut mask_bit: u16 = 1;
|
|
|
|
|
for _ in 0..16 {
|
|
|
|
|
let int_src = ComIntSources::from(mask_bit & ints as u16);
|
|
|
|
|
if int_src != ComIntSources::Invalid {
|
|
|
|
|
int_list.push(int_src);
|
2021-10-21 20:08:43 +00:00
|
|
|
if maybe_rxlen > NET_MTU {
|
2024-01-14 14:06:28 +00:00
|
|
|
log::error!(
|
|
|
|
|
"got an RX_LEN bigger than NET_MTU: {}, squashing packet; ints vector: 0x{:x?}",
|
|
|
|
|
maybe_rxlen,
|
|
|
|
|
ints
|
|
|
|
|
);
|
2021-10-21 20:08:43 +00:00
|
|
|
rxlen = None;
|
|
|
|
|
} else {
|
|
|
|
|
if int_src == ComIntSources::WlanRxReady {
|
2022-01-26 19:56:39 +00:00
|
|
|
rxlen = Some(maybe_rxlen as u16);
|
|
|
|
|
} else if int_src == ComIntSources::Connect {
|
|
|
|
|
rxlen = Some(maybe_rxlen as u16);
|
2021-10-21 20:08:43 +00:00
|
|
|
}
|
2021-10-08 12:01:13 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mask_bit <<= 1;
|
|
|
|
|
}
|
THE BALDEST YAK: keyboard injection now working
I wrote in a commit yesterday how there was no ROI on doing this
yet somehow I kept on doing it. I blame poor impulse control.
Turns out there was a series of race conditions and bugs that
caused this thing to "mostly work" on the first go at it but
really hard to debug and get very right.
1. Litex UART has a weird undocumented API. You advance the Rx
FIFO by writing `1` to the PENDING bit, and no other way. However,
you determine if there are any more characters to receive by
reading the EMPTY bit and seeing if it is 1. The previous method
wrote `1` to the PENDING bit and then used the PENDING bit to
see if a character was available, under the theory that the
PENDING bit would be set again if there was still a character.
This is somewhat true, except that the PENDING bit is set only
some amount of time later which is about comparable to a few
CPU instructions, so if you had a fast loop path the PENDING
bit would show as cleared and you'd exit, which would cause
no further interrupts to fire because the interrupt was
effecitvely not acknowledged.
This isn't written up anywhere but I figured this out by
just reading the source code for Litex UART and BIOS:
https://github.com/enjoy-digital/litex/blob/e8be3915041707860e70b15d062661fc1f67a09f/litex/soc/cores/uart.py#L264-L279
https://github.com/enjoy-digital/litex/blob/ccd3ab17be1f8770c59ec1c62e0285d3bac3cc26/litex/soc/software/libbase/uart.c#L38-L44
2. I had the Key Injector IP block in the `sys` domain, which
would have its clock cut when the CPU went to sleep, which would
cause input characters to disappear when the CPU went into power
saving. moving this to "always on" domain fixes that.
3. I had the UART PHY in the always on block (so you could
send or receive exactly one character), but not the
separate UART block (which contained the FIFOs).
The UART block would also go into power
savings, and this would cause FIFO entries to go missing.
4. Escape sequences get sent as a burst of characters. The Key Injector
originally did not have a FIFO of its own, so it would only
remember the *last* key of the sequence that was written, causing
some keys to be dropped occassionally. Adding a 16-deep FIFO
to the injector fixes this.
5. The code I had to read out the injector's results was designed with
the "strobe" bit (to indicate that the FIFO had characters) in the
same regsiter as the character itself. Thus the loop that 1. checked
if the fifo was empty then 2. read and forwarded the character
would actually perform two reads from the FIFO. However, this "mostly worked"
I think because the FIFO retains the last value that was written
even when it was empty, so if only one character was read at a time
everything was fine. It only didn't work when you stuffed two characters
in there, as you'd drop the first one. Reading the FIFO once and
then interpreting the code with a bitmask fixes this.
6. The escape sequences, in their full glory with no dropped characters,
required a refactored parsing routine, which I have now added.
7. backspace->delete mappings, the bane of computer engineers since 1960.
OK. very bald yak. Here I lay down the shears next to several
mooshi pillows stuffed with the finest Yak hair.
2022-01-28 07:17:47 +00:00
|
|
|
Ok((rxlen, ints, maybe_rxlen))
|
2021-10-08 12:01:13 +00:00
|
|
|
} else {
|
|
|
|
|
panic!("failed to send IntGetmask message");
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-01 21:43:47 +00:00
|
|
|
}
|
2021-04-02 08:40:17 +00:00
|
|
|
|
2021-05-21 08:24:30 +00:00
|
|
|
use core::sync::atomic::{AtomicU32, Ordering};
|
|
|
|
|
static REFCOUNT: AtomicU32 = AtomicU32::new(0);
|
2021-04-02 08:40:17 +00:00
|
|
|
impl Drop for Com {
|
|
|
|
|
fn drop(&mut self) {
|
2021-04-02 11:01:11 +00:00
|
|
|
// if we have callbacks, destroy the battstats callback server
|
|
|
|
|
if let Some(sid) = self.battstats_sid.take() {
|
|
|
|
|
// no need to tell the COM server we're quitting: the next time a callback processes,
|
|
|
|
|
// it will automatically remove my entry as it will receive a ServerNotFound error.
|
|
|
|
|
|
|
|
|
|
// tell my handler thread to quit
|
|
|
|
|
let cid = xous::connect(sid).unwrap();
|
2024-01-14 14:06:28 +00:00
|
|
|
xous::send_message(cid, Message::new_scalar(api::Callback::Drop.to_usize().unwrap(), 0, 0, 0, 0))
|
|
|
|
|
.unwrap();
|
|
|
|
|
unsafe {
|
|
|
|
|
xous::disconnect(cid).unwrap();
|
|
|
|
|
}
|
2021-04-02 11:01:11 +00:00
|
|
|
}
|
2024-01-14 14:06:28 +00:00
|
|
|
// now de-allocate myself. It's unsafe because we are responsible to make sure nobody else is using
|
|
|
|
|
// the connection.
|
2022-01-31 15:13:10 +00:00
|
|
|
if REFCOUNT.fetch_sub(1, Ordering::Relaxed) == 1 {
|
2024-01-14 14:06:28 +00:00
|
|
|
unsafe {
|
|
|
|
|
xous::disconnect(self.conn).unwrap();
|
|
|
|
|
}
|
2021-05-21 08:24:30 +00:00
|
|
|
}
|
2021-04-02 08:40:17 +00:00
|
|
|
}
|
|
|
|
|
}
|