#![cfg_attr(target_os = "none", no_std)]
pub mod api;
use core::fmt::Write;
use api::Disconnect;
use num_traits::ToPrimitive;
use xous_ipc::Buffer;
#[repr(C, align(4096))]
struct ConnectRequest {
name: [u8; 64],
len: u32,
_padding: [u8; 4096 - 4 - 64],
}
impl Default for ConnectRequest {
fn default() -> Self { ConnectRequest { name: [0u8; 64], len: 0, _padding: [0u8; 4096 - 4 - 64] } }
}
#[doc = include_str!("../README.md")]
#[derive(Debug)]
pub struct XousNames {
conn: xous::CID,
}
impl XousNames {
pub fn new() -> Result<Self, xous::Error> {
REFCOUNT.fetch_add(1, Ordering::Relaxed);
let conn = xous::connect(xous::SID::from_bytes(b"xous-name-server").unwrap())
.expect("Couldn't connect to XousNames");
Ok(XousNames { conn })
}
pub fn unregister_server(&self, sid: xous::SID) -> Result<(), xous::Error> {
let s = sid.to_array();
let response = xous::send_message(
self.conn,
xous::Message::new_blocking_scalar(
api::Opcode::Unregister.to_usize().unwrap(),
s[0] as usize,
s[1] as usize,
s[2] as usize,
s[3] as usize,
),
)
.expect("unregistration failed");
if let xous::Result::Scalar1(result) = response {
if result != 0 { Ok(()) } else { Err(xous::Error::ServerNotFound) }
} else {
Err(xous::Error::InternalError)
}
}
pub fn register_name(&self, name: &str, max_conns: Option<u32>) -> Result<xous::SID, xous::Error> {
let mut registration = api::Registration { name: String::new(), conn_limit: max_conns };
write!(registration.name, "{}", name).expect("name probably too long");
let mut buf = Buffer::into_buf(registration).or(Err(xous::Error::InternalError))?;
buf.lend_mut(self.conn, api::Opcode::Register.to_u32().unwrap())
.or(Err(xous::Error::InternalError))?;
match buf.to_original().unwrap() {
api::Return::SID(sid_raw) => {
let sid = sid_raw.into();
xous::create_server_with_sid(sid).expect("can't auto-register server");
Ok(sid)
}
api::Return::Failure => Err(xous::Error::InternalError),
_ => unimplemented!("unimplemented return codes"),
}
}
pub fn request_connection_with_token(
&self,
name: &str,
) -> Result<(xous::CID, Option<[u32; 4]>), xous::Error> {
let mut lookup_name = String::new();
write!(lookup_name, "{}", name).expect("name probably too long");
let mut buf = Buffer::into_buf(lookup_name).or(Err(xous::Error::InternalError))?;
buf.lend_mut(self.conn, api::Opcode::Lookup.to_u32().unwrap()).or(Err(xous::Error::InternalError))?;
match buf.to_original().unwrap() {
api::Return::CID((cid, token)) => Ok((cid, token)),
_ => Err(xous::Error::ServerNotFound),
}
}
pub fn disconnect_with_token(&self, name: &str, token: [u32; 4]) -> Result<(), xous::Error> {
let disconnect = Disconnect { name: String::from(name), token };
let mut buf = Buffer::into_buf(disconnect).or(Err(xous::Error::InternalError))?;
buf.lend_mut(self.conn, api::Opcode::Disconnect.to_u32().unwrap())
.or(Err(xous::Error::InternalError))?;
match buf.to_original().unwrap() {
api::Return::Success => Ok(()),
_ => Err(xous::Error::ServerNotFound),
}
}
pub fn request_connection(&self, name: &str) -> Result<xous::CID, xous::Error> {
let mut lookup_name = String::new();
write!(lookup_name, "{}", name).expect("name probably too long");
let mut buf = Buffer::into_buf(lookup_name).or(Err(xous::Error::InternalError))?;
buf.lend_mut(self.conn, api::Opcode::Lookup.to_u32().unwrap()).or(Err(xous::Error::InternalError))?;
match buf.to_original().unwrap() {
api::Return::CID((cid, _)) => Ok(cid),
_ => Err(xous::Error::ServerNotFound),
}
}
pub fn request_connection_blocking(&self, name: &str) -> Result<xous::CID, xous::Error> {
let mut cr: ConnectRequest = Default::default();
let name_bytes = name.as_bytes();
cr.len = cr.name.len().min(name_bytes.len()) as u32;
for (&src_byte, dest_byte) in name_bytes.iter().zip(&mut cr.name) {
*dest_byte = src_byte;
}
log::debug!("connection requested {}", name);
let msg = xous::MemoryMessage {
id: api::Opcode::BlockingConnect.to_usize().unwrap(),
buf: unsafe {
xous::MemoryRange::new(
&mut cr as *mut _ as *mut u8 as usize,
core::mem::size_of::<ConnectRequest>(),
)?
},
offset: None,
valid: xous::MemorySize::new(cr.len as usize),
};
xous::send_message(self.conn, xous::Message::MutableBorrow(msg))?;
let response_ptr = &cr as *const ConnectRequest as *const u32;
let result = unsafe { response_ptr.read() }; if result == 0 {
let cid = unsafe { response_ptr.add(1).read() }.into(); log::debug!("connected to {}:{}", name, cid);
Ok(cid)
} else {
Err(xous::Error::InternalError)
}
}
pub fn trusted_init_done(&self) -> Result<bool, xous::Error> {
let response = xous::send_message(
self.conn,
xous::Message::new_blocking_scalar(api::Opcode::TrustedInitDone.to_usize().unwrap(), 0, 0, 0, 0),
)
.expect("couldn't query trusted_init_done");
if let xous::Result::Scalar1(result) = response {
if result == 1 { Ok(true) } else { Ok(false) }
} else {
Err(xous::Error::InternalError)
}
}
}
use core::sync::atomic::{AtomicU32, Ordering};
static REFCOUNT: AtomicU32 = AtomicU32::new(0);
impl Drop for XousNames {
fn drop(&mut self) {
if REFCOUNT.fetch_sub(1, Ordering::Relaxed) == 1 {
unsafe {
xous::disconnect(self.conn).unwrap();
}
}
}
}