std: xous: add services support
Xous has a concept of `services` that provide various features. Processes may connect to these services by name or by address. Most services require a name server in order to connect. Add a file with the most common services, and provide a way to connect to a service by querying the name server. Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
parent
dfff5bf62f
commit
10dad67f89
@ -3,6 +3,9 @@
|
||||
|
||||
pub mod ffi;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub mod services;
|
||||
|
||||
/// A prelude for conveniently writing platform-specific code.
|
||||
///
|
||||
/// Includes all extension traits, and some important type definitions.
|
||||
|
132
library/std/src/os/xous/services.rs
Normal file
132
library/std/src/os/xous/services.rs
Normal file
@ -0,0 +1,132 @@
|
||||
use crate::os::xous::ffi::Connection;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
mod log;
|
||||
pub(crate) use log::*;
|
||||
|
||||
mod systime;
|
||||
pub(crate) use systime::*;
|
||||
|
||||
mod ticktimer;
|
||||
pub(crate) use ticktimer::*;
|
||||
|
||||
mod ns {
|
||||
const NAME_MAX_LENGTH: usize = 64;
|
||||
use crate::os::xous::ffi::{lend_mut, Connection};
|
||||
// By making this repr(C), the layout of this struct becomes well-defined
|
||||
// and no longer shifts around.
|
||||
// By marking it as `align(4096)` we define that it will be page-aligned,
|
||||
// meaning it can be sent between processes. We make sure to pad out the
|
||||
// entire struct so that memory isn't leaked to the name server.
|
||||
#[repr(C, align(4096))]
|
||||
struct ConnectRequest {
|
||||
data: [u8; 4096],
|
||||
}
|
||||
|
||||
impl ConnectRequest {
|
||||
pub fn new(name: &str) -> Self {
|
||||
let mut cr = ConnectRequest { data: [0u8; 4096] };
|
||||
let name_bytes = name.as_bytes();
|
||||
|
||||
// Copy the string into our backing store.
|
||||
for (&src_byte, dest_byte) in name_bytes.iter().zip(&mut cr.data[0..NAME_MAX_LENGTH]) {
|
||||
*dest_byte = src_byte;
|
||||
}
|
||||
|
||||
// Set the string length to the length of the passed-in String,
|
||||
// or the maximum possible length. Which ever is smaller.
|
||||
for (&src_byte, dest_byte) in (name.len().min(NAME_MAX_LENGTH) as u32)
|
||||
.to_le_bytes()
|
||||
.iter()
|
||||
.zip(&mut cr.data[NAME_MAX_LENGTH..])
|
||||
{
|
||||
*dest_byte = src_byte;
|
||||
}
|
||||
cr
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect_with_name_impl(name: &str, blocking: bool) -> Option<Connection> {
|
||||
let mut request = ConnectRequest::new(name);
|
||||
let opcode = if blocking {
|
||||
6 /* BlockingConnect */
|
||||
} else {
|
||||
7 /* TryConnect */
|
||||
};
|
||||
let cid = if blocking { super::name_server() } else { super::try_name_server()? };
|
||||
|
||||
lend_mut(cid, opcode, &mut request.data, 0, name.len().min(NAME_MAX_LENGTH))
|
||||
.expect("unable to perform lookup");
|
||||
|
||||
// Read the result code back from the nameserver
|
||||
let result = u32::from_le_bytes(request.data[0..4].try_into().unwrap());
|
||||
if result == 0 {
|
||||
// If the result was successful, then the CID is stored in the next 4 bytes
|
||||
Some(u32::from_le_bytes(request.data[4..8].try_into().unwrap()).into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect_with_name(name: &str) -> Option<Connection> {
|
||||
connect_with_name_impl(name, true)
|
||||
}
|
||||
|
||||
pub fn try_connect_with_name(name: &str) -> Option<Connection> {
|
||||
connect_with_name_impl(name, false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to connect to a server by name. If the server does not exist, this will
|
||||
/// block until the server is created.
|
||||
///
|
||||
/// Note that this is different from connecting to a server by address. Server
|
||||
/// addresses are always 16 bytes long, whereas server names are arbitrary-length
|
||||
/// strings up to 64 bytes in length.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn connect(name: &str) -> Option<Connection> {
|
||||
ns::connect_with_name(name)
|
||||
}
|
||||
|
||||
/// Attempt to connect to a server by name. If the server does not exist, this will
|
||||
/// immediately return `None`.
|
||||
///
|
||||
/// Note that this is different from connecting to a server by address. Server
|
||||
/// addresses are always 16 bytes long, whereas server names are arbitrary-length
|
||||
/// strings.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn try_connect(name: &str) -> Option<Connection> {
|
||||
ns::try_connect_with_name(name)
|
||||
}
|
||||
|
||||
static NAME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
/// Return a `Connection` to the name server. If the name server has not been started,
|
||||
/// then this call will block until the name server has been started. The `Connection`
|
||||
/// will be shared among all connections in a process, so it is safe to call this
|
||||
/// multiple times.
|
||||
pub(crate) fn name_server() -> Connection {
|
||||
let cid = NAME_SERVER_CONNECTION.load(Ordering::Relaxed);
|
||||
if cid != 0 {
|
||||
return cid.into();
|
||||
}
|
||||
|
||||
let cid = crate::os::xous::ffi::connect("xous-name-server".try_into().unwrap()).unwrap();
|
||||
NAME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
|
||||
cid
|
||||
}
|
||||
|
||||
fn try_name_server() -> Option<Connection> {
|
||||
let cid = NAME_SERVER_CONNECTION.load(Ordering::Relaxed);
|
||||
if cid != 0 {
|
||||
return Some(cid.into());
|
||||
}
|
||||
|
||||
if let Ok(Some(cid)) = crate::os::xous::ffi::try_connect("xous-name-server".try_into().unwrap())
|
||||
{
|
||||
NAME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
|
||||
Some(cid)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
63
library/std/src/os/xous/services/log.rs
Normal file
63
library/std/src/os/xous/services/log.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use crate::os::xous::ffi::Connection;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
/// Group `usize` bytes into a `usize` and return it, beginning
|
||||
/// from `offset` * sizeof(usize) bytes from the start. For example,
|
||||
/// `group_or_null([1,2,3,4,5,6,7,8], 1)` on a 32-bit system will
|
||||
/// return a usize with 5678 packed into it.
|
||||
fn group_or_null(data: &[u8], offset: usize) -> usize {
|
||||
let start = offset * core::mem::size_of::<usize>();
|
||||
let mut out_array = [0u8; core::mem::size_of::<usize>()];
|
||||
if start < data.len() {
|
||||
for (dest, src) in out_array.iter_mut().zip(&data[start..]) {
|
||||
*dest = *src;
|
||||
}
|
||||
}
|
||||
usize::from_le_bytes(out_array)
|
||||
}
|
||||
|
||||
pub(crate) enum LogScalar<'a> {
|
||||
/// A panic occurred, and a panic log is forthcoming
|
||||
BeginPanic,
|
||||
|
||||
/// Some number of bytes will be appended to the log message
|
||||
AppendPanicMessage(&'a [u8]),
|
||||
}
|
||||
|
||||
impl<'a> Into<[usize; 5]> for LogScalar<'a> {
|
||||
fn into(self) -> [usize; 5] {
|
||||
match self {
|
||||
LogScalar::BeginPanic => [1000, 0, 0, 0, 0],
|
||||
LogScalar::AppendPanicMessage(c) =>
|
||||
// Text is grouped into 4x `usize` words. The id is 1100 plus
|
||||
// the number of characters in this message.
|
||||
// Ignore errors since we're already panicking.
|
||||
{
|
||||
[
|
||||
1100 + c.len(),
|
||||
group_or_null(&c, 0),
|
||||
group_or_null(&c, 1),
|
||||
group_or_null(&c, 2),
|
||||
group_or_null(&c, 3),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a `Connection` to the log server, which is used for printing messages to
|
||||
/// the console and reporting panics. If the log server has not yet started, this
|
||||
/// will block until the server is running. It is safe to call this multiple times,
|
||||
/// because the address is shared among all threads in a process.
|
||||
pub(crate) fn log_server() -> Connection {
|
||||
static LOG_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
let cid = LOG_SERVER_CONNECTION.load(Ordering::Relaxed);
|
||||
if cid != 0 {
|
||||
return cid.into();
|
||||
}
|
||||
|
||||
let cid = crate::os::xous::ffi::connect("xous-log-server ".try_into().unwrap()).unwrap();
|
||||
LOG_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
|
||||
cid
|
||||
}
|
28
library/std/src/os/xous/services/systime.rs
Normal file
28
library/std/src/os/xous/services/systime.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use crate::os::xous::ffi::{connect, Connection};
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
pub(crate) enum SystimeScalar {
|
||||
GetUtcTimeMs,
|
||||
}
|
||||
|
||||
impl Into<[usize; 5]> for SystimeScalar {
|
||||
fn into(self) -> [usize; 5] {
|
||||
match self {
|
||||
SystimeScalar::GetUtcTimeMs => [3, 0, 0, 0, 0],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a `Connection` to the systime server. This server is used for reporting the
|
||||
/// realtime clock.
|
||||
pub(crate) fn systime_server() -> Connection {
|
||||
static SYSTIME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
|
||||
let cid = SYSTIME_SERVER_CONNECTION.load(Ordering::Relaxed);
|
||||
if cid != 0 {
|
||||
return cid.into();
|
||||
}
|
||||
|
||||
let cid = connect("timeserverpublic".try_into().unwrap()).unwrap();
|
||||
SYSTIME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
|
||||
cid
|
||||
}
|
38
library/std/src/os/xous/services/ticktimer.rs
Normal file
38
library/std/src/os/xous/services/ticktimer.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use crate::os::xous::ffi::Connection;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
pub(crate) enum TicktimerScalar {
|
||||
ElapsedMs,
|
||||
SleepMs(usize),
|
||||
LockMutex(usize /* cookie */),
|
||||
UnlockMutex(usize /* cookie */),
|
||||
WaitForCondition(usize /* cookie */, usize /* timeout (ms) */),
|
||||
NotifyCondition(usize /* cookie */, usize /* count */),
|
||||
}
|
||||
|
||||
impl Into<[usize; 5]> for TicktimerScalar {
|
||||
fn into(self) -> [usize; 5] {
|
||||
match self {
|
||||
TicktimerScalar::ElapsedMs => [0, 0, 0, 0, 0],
|
||||
TicktimerScalar::SleepMs(msecs) => [1, msecs, 0, 0, 0],
|
||||
TicktimerScalar::LockMutex(cookie) => [6, cookie, 0, 0, 0],
|
||||
TicktimerScalar::UnlockMutex(cookie) => [7, cookie, 0, 0, 0],
|
||||
TicktimerScalar::WaitForCondition(cookie, timeout_ms) => [8, cookie, timeout_ms, 0, 0],
|
||||
TicktimerScalar::NotifyCondition(cookie, count) => [9, cookie, count, 0, 0],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a `Connection` to the ticktimer server. This server is used for synchronization
|
||||
/// primitives such as sleep, Mutex, and Condvar.
|
||||
pub(crate) fn ticktimer_server() -> Connection {
|
||||
static TICKTIMER_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
|
||||
let cid = TICKTIMER_SERVER_CONNECTION.load(Ordering::Relaxed);
|
||||
if cid != 0 {
|
||||
return cid.into();
|
||||
}
|
||||
|
||||
let cid = crate::os::xous::ffi::connect("ticktimer-server".try_into().unwrap()).unwrap();
|
||||
TICKTIMER_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
|
||||
cid
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user