xous-core/services/net/src/lib.rs
bunnie 562a930ce0 add APIs to pause the connection manager, and pause the CM while doing EC updates
It's not a good time to bug the EC to do network stuff when it's updating
itself.

...might even want to send the network into a disconnected state,
but, let's start with just turning off the connection manager
for now.
2022-02-02 02:38:19 +08:00

178 lines
7.7 KiB
Rust

#![cfg_attr(target_os = "none", no_std)]
pub mod api;
use com::{Ipv4Conf, SsidRecord};
use xous::{CID, send_message, Message};
use xous_ipc::Buffer;
use num_traits::*;
pub mod protocols;
pub use protocols::*;
pub use smoltcp::time::Duration;
pub use api::*;
pub use smoltcp::wire::IpEndpoint;
/// NetConn is a crate-level structure that just counts the number of connections from this process to
/// the Net server. It's not mean to be created by user-facing code, so the visibility is (crate).
#[derive(Debug)]
pub(crate) struct NetConn {
conn: CID,
}
impl NetConn {
pub(crate) fn new(xns: &xous_names::XousNames) -> Result<Self, xous::Error> {
REFCOUNT.fetch_add(1, Ordering::Relaxed);
let conn = xns.request_connection_blocking(api::SERVER_NAME_NET).expect("Can't connect to Net server");
Ok(NetConn {
conn,
})
}
pub(crate) fn conn(&self) -> CID {
self.conn
}
}
use core::sync::atomic::{AtomicU32, Ordering};
static REFCOUNT: AtomicU32 = AtomicU32::new(0);
impl Drop for NetConn {
fn drop(&mut self) {
// the connection to the server side must be reference counted, so that multiple instances of this object within
// a single process do not end up de-allocating the CID on other threads before they go out of scope.
// Note to future me: you want this. Don't get rid of it because you think, "nah, nobody will ever make more than one copy of this object".
if REFCOUNT.fetch_sub(1, Ordering::Relaxed) == 1 {
unsafe{xous::disconnect(self.conn).unwrap();}
}
// if there was object-specific state (such as a one-time use server for async callbacks, specific to the object instance),
// de-allocate those items here. They don't need a reference count because they are object-specific
}
}
#[derive(Debug)]
pub struct NetManager {
netconn: NetConn,
wifi_state_cid: Option<CID>,
wifi_state_sid: Option<xous::SID>,
}
impl NetManager {
pub fn new() -> NetManager {
NetManager {
netconn: NetConn::new(&xous_names::XousNames::new().unwrap()).expect("can't connect to Net Server"),
wifi_state_cid: None,
wifi_state_sid: None,
}
}
pub fn get_ipv4_config(&self) -> Option<Ipv4Conf> {
let storage = Some(Ipv4Conf::default().encode_u16());
let mut buf = Buffer::into_buf(storage).expect("Couldn't convert to memory structure");
buf.lend_mut(self.netconn.conn(), Opcode::GetIpv4Config.to_u32().unwrap()).expect("Couldn't execute GetIpv4Config opcode");
let maybe_config = buf.to_original().expect("couldn't restore config structure");
if let Some(config) = maybe_config {
let ipv4 = Ipv4Conf::decode_u16(&config);
Some(ipv4)
} else {
None
}
}
pub fn reset(&self) {
send_message(
self.netconn.conn(),
Message::new_blocking_scalar(Opcode::Reset.to_usize().unwrap(), 0, 0, 0, 0),
).expect("couldn't send reset");
}
/// This is the function that the system should be using to check the wifi state -- it will read
/// the cached value from the connection manager. The direct call to the COM could cause too much
/// congestion.
pub fn wifi_state_subscribe(&mut self, return_cid: CID, opcode: u32) -> Result<(), xous::Error> {
if self.wifi_state_cid.is_none() {
let onetime_sid = xous::create_server().unwrap();
let sub = WifiStateSubscription {
sid: onetime_sid.to_array(),
opcode
};
let buf = Buffer::into_buf(sub).or(Err(xous::Error::InternalError))?;
buf.send(self.netconn.conn(), Opcode::SubscribeWifiStats.to_u32().unwrap()).or(Err(xous::Error::InternalError))?;
self.wifi_state_cid = Some(xous::connect(onetime_sid).unwrap());
self.wifi_state_sid = Some(onetime_sid);
let _ = std::thread::spawn({
let onetime_sid = onetime_sid.clone();
let opcode = opcode.clone();
move || {
loop {
let msg = xous::receive_message(onetime_sid).unwrap();
match FromPrimitive::from_usize(msg.body.id()) {
Some(WifiStateCallback::Update) => {
let buffer = unsafe {
Buffer::from_memory_message(msg.body.memory_message().unwrap())
};
log::debug!("got state_subscribe callback {} {}", return_cid, opcode);
// have to transform it through the local memory space because you can't re-lend pages
let sub = buffer.to_original::<com::WlanStatusIpc, _>().unwrap();
let buf = Buffer::into_buf(sub).expect("couldn't convert to memory message");
buf.lend(return_cid, opcode).expect("couldn't forward state update");
}
Some(WifiStateCallback::Drop) => {
xous::return_scalar(msg.sender, 1).unwrap();
break;
}
_ => {
log::error!("got unknown opcode: {:?}", msg);
}
}
}
log::info!("destroying callback server");
xous::destroy_server(onetime_sid).unwrap();
}
});
Ok(())
} else {
// you can only hook this once per object
Err(xous::Error::ServerExists)
}
}
/// If we're not already subscribed, returns without error.
pub fn wifi_state_unsubscribe(&mut self) -> Result<(), xous::Error> {
if let Some(handler) = self.wifi_state_cid.take() {
if let Some(sid) = self.wifi_state_sid.take() {
let s = sid.to_array();
send_message(self.netconn.conn(),
Message::new_blocking_scalar(Opcode::UnsubWifiStats.to_usize().unwrap(),
s[0] as usize,
s[1] as usize,
s[2] as usize,
s[3] as usize,
)
).expect("couldn't unsubscribe");
}
send_message(handler, Message::new_blocking_scalar(WifiStateCallback::Drop.to_usize().unwrap(), 0, 0, 0, 0)).ok();
unsafe{xous::disconnect(handler).ok()};
}
Ok(())
}
pub fn wifi_get_ssid_list(&self) -> Result<Vec::<SsidRecord>, xous::Error> {
let alloc = SsidList::default();
let mut buf = Buffer::into_buf(alloc).map_err(|_| xous::Error::InternalError)?;
buf.lend_mut(self.netconn.conn(), Opcode::FetchSsidList.to_u32().unwrap())?;
let ssid_list = buf.to_original::<SsidList, _>().map_err(|_| xous::Error::InternalError)?;
let mut ret = Vec::<SsidRecord>::new();
for maybe_item in ssid_list.list.iter() {
if let Some(item) = maybe_item {
ret.push(*item);
}
}
Ok(ret)
}
pub fn connection_manager_stop(&self) -> Result<(), xous::Error> {
send_message(self.netconn.conn(),
Message::new_scalar(Opcode::ConnMgrStartStop.to_usize().unwrap(), 0, 0,0, 0)
).map(|_| ())
}
pub fn connection_manager_run(&self) -> Result<(), xous::Error> {
send_message(self.netconn.conn(),
Message::new_scalar(Opcode::ConnMgrStartStop.to_usize().unwrap(), 1, 0,0, 0)
).map(|_| ())
}
}
impl Drop for NetManager {
fn drop(&mut self) {
self.wifi_state_unsubscribe().unwrap();
}
}