#![no_std]
use core::pin::pin;
use embassy_futures::select::{Either, select};
use embedded_hal_async::delay::DelayNs;
use transport::IcMsgTransport;
pub use transport::Notifier;
mod loom;
pub mod transport;
#[macro_use]
mod poll;
const MAGIC: [u8; 13] = [
0x45, 0x6d, 0x31, 0x6c, 0x31, 0x4b, 0x30, 0x72, 0x6e, 0x33, 0x6c, 0x69, 0x34,
];
pub struct IcMsg<M, W, const ALIGN: usize>
where
M: Notifier,
W: WaitForNotify,
elain::Align<ALIGN>: elain::Alignment,
{
sender: Sender<M, ALIGN>,
receiver: Receiver<W, ALIGN>,
}
impl<M, W, const ALIGN: usize> IcMsg<M, W, ALIGN>
where
M: Notifier,
W: WaitForNotify,
elain::Align<ALIGN>: elain::Alignment,
{
pub async unsafe fn init(
config: MemoryConfig,
notifier: M,
mut waiter: W,
mut delay: impl DelayNs,
) -> Result<Self, InitError> {
if config.send_buffer_len % 4 != 0 || config.recv_buffer_len % 4 != 0 {
return Err(InitError::InvalidSize);
}
if config.send_buffer_len < 24 || config.recv_buffer_len < 24 {
return Err(InitError::TooSmall);
}
let mut transport = unsafe {
IcMsgTransport::new(
config.send_region,
config.recv_region,
config.send_buffer_len,
config.recv_buffer_len,
notifier,
)
};
transport
.send(&MAGIC)
.map_err(InitError::BondingSendError)?;
{
let mut wait_fut = pin!(waiter.wait_for_notify());
loop {
let timeout = delay.delay_ms(1);
match select(wait_fut.as_mut(), timeout).await {
Either::First(_) => break,
Either::Second(_) => transport.notify(),
}
}
transport.notify();
}
let mut message = [0; 32];
transport
.try_recv(&mut message)
.map_err(InitError::BondingRecvError)?;
if message.get(..MAGIC.len()) != Some(&MAGIC) {
return Err(InitError::BondingWrongMagic);
}
let (s, r) = transport.split();
let sender = Sender { transport: s };
let receiver = Receiver {
transport: r,
waiter,
};
Ok(Self { sender, receiver })
}
pub fn send(&mut self, msg: &[u8]) -> Result<(), transport::SendError> {
self.sender.send(msg)
}
pub fn try_recv(&mut self, msg: &mut [u8]) -> Result<usize, transport::RecvError> {
self.receiver.try_recv(msg)
}
pub fn recv(
&mut self,
msg: &mut [u8],
) -> impl Future<Output = Result<usize, transport::RecvError>> {
self.receiver.recv(msg)
}
pub fn split(self) -> (Sender<M, ALIGN>, Receiver<W, ALIGN>) {
(self.sender, self.receiver)
}
pub fn split_mut(&mut self) -> (&mut Sender<M, ALIGN>, &mut Receiver<W, ALIGN>) {
(&mut self.sender, &mut self.receiver)
}
}
pub struct Sender<M, const ALIGN: usize>
where
M: Notifier,
elain::Align<ALIGN>: elain::Alignment,
{
transport: transport::Sender<M, ALIGN>,
}
impl<M, const ALIGN: usize> Sender<M, ALIGN>
where
M: Notifier,
elain::Align<ALIGN>: elain::Alignment,
{
pub fn send(&mut self, msg: &[u8]) -> Result<(), transport::SendError> {
self.transport.send(msg)
}
}
pub struct Receiver<W, const ALIGN: usize>
where
W: WaitForNotify,
elain::Align<ALIGN>: elain::Alignment,
{
transport: transport::Receiver<ALIGN>,
waiter: W,
}
impl<W, const ALIGN: usize> Receiver<W, ALIGN>
where
W: WaitForNotify,
elain::Align<ALIGN>: elain::Alignment,
{
pub fn try_recv(&mut self, msg: &mut [u8]) -> Result<usize, transport::RecvError> {
self.transport.try_recv(msg)
}
pub async fn recv(&mut self, msg: &mut [u8]) -> Result<usize, transport::RecvError> {
loop {
let mut wait_fut = pin!(self.waiter.wait_for_notify());
let r = poll!(wait_fut.as_mut());
match self.transport.try_recv(msg) {
Ok(n) => return Ok(n),
Err(transport::RecvError::Empty) => {
if r.is_pending() {
wait_fut.await;
}
}
Err(e) => return Err(e),
}
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct MemoryConfig {
pub send_region: *mut (),
pub recv_region: *mut (),
pub send_buffer_len: u32,
pub recv_buffer_len: u32,
}
pub trait WaitForNotify {
fn wait_for_notify(&mut self) -> impl Future<Output = ()>;
}
#[derive(Debug, Copy, Clone)]
pub enum InitError {
TooSmall,
InvalidSize,
BondingSendError(transport::SendError),
BondingRecvError(transport::RecvError),
BondingWrongMagic,
}
#[cfg(test)]
mod tests {
extern crate std;
use embedded_hal_async::delay::DelayNs;
use tokio::sync::Notify;
use crate::{
Notifier, WaitForNotify,
transport::{SharedMemoryRegionHeader, tests::SyncThing},
loom::{alloc, sync::Arc},
};
use super::{IcMsg, MemoryConfig};
use core::{alloc::Layout, time::Duration};
#[cfg(not(loom))]
#[tokio::main]
#[test]
async fn test_bonding() {
let expected_messages: &[&[u8]] = &[
b"",
b"0",
b"01",
b"012",
b"0123",
b"01234",
b"012345",
b"0123456",
b"01234567",
];
const ALIGN: usize = 4;
type Hdr = SharedMemoryRegionHeader<ALIGN>;
let buf_size = 24;
let shared_region_layout =
Layout::from_size_align(size_of::<Hdr>() + buf_size, align_of::<Hdr>()).unwrap();
let shared_region_1 = unsafe { alloc::alloc(shared_region_layout) }.cast::<()>();
let shared_region_2 = unsafe { alloc::alloc(shared_region_layout) }.cast::<()>();
let notify_1 = Arc::new(Notify::new());
let notify_2 = Arc::new(Notify::new());
let recv_task = tokio::spawn(SyncThing({
let notify_1 = Arc::clone(¬ify_1);
let notify_2 = Arc::clone(¬ify_2);
async move {
let config = MemoryConfig {
send_region: shared_region_2,
recv_region: shared_region_1,
send_buffer_len: buf_size as u32,
recv_buffer_len: buf_size as u32,
};
let mut icmsg = unsafe {
IcMsg::<_, _, ALIGN>::init(config, &*notify_2, &*notify_1, TokioDelay)
.await
.unwrap()
};
let mut buf = [0; 8];
for &expected_message in expected_messages {
let n = icmsg.recv(&mut buf).await.unwrap();
std::eprintln!("task 2 recv'd {:?}", &buf[..n]);
assert_eq!(&buf[..n], expected_message);
}
for msg in expected_messages {
loop {
let r = icmsg.send(msg);
if r.is_err() {
tokio::task::yield_now().await;
continue;
}
std::eprintln!("task 2 sent {msg:?}");
break;
}
}
}
}));
let config = MemoryConfig {
send_region: shared_region_1,
recv_region: shared_region_2,
send_buffer_len: buf_size as u32,
recv_buffer_len: buf_size as u32,
};
let mut icmsg = unsafe {
IcMsg::<_, _, ALIGN>::init(config, &*notify_1, &*notify_2, TokioDelay)
.await
.unwrap()
};
for msg in expected_messages {
loop {
let r = icmsg.send(msg);
if r.is_err() {
tokio::task::yield_now().await;
continue;
}
std::eprintln!("task 1 sent {msg:?}");
break;
}
}
let mut buf = [0; 8];
for &expected_message in expected_messages {
let n = icmsg.recv(&mut buf).await.unwrap();
std::eprintln!("task 1 recv'd {:?}", &buf[..n]);
assert_eq!(&buf[..n], expected_message);
}
recv_task.await.unwrap();
unsafe {
alloc::dealloc(shared_region_1.cast(), shared_region_layout);
alloc::dealloc(shared_region_2.cast(), shared_region_layout);
}
}
impl Notifier for &'_ Notify {
fn notify(&mut self) {
self.notify_waiters()
}
}
impl WaitForNotify for &'_ Notify {
fn wait_for_notify(&mut self) -> impl Future<Output = ()> {
self.notified()
}
}
struct TokioDelay;
impl DelayNs for TokioDelay {
fn delay_ns(&mut self, ns: u32) -> impl Future<Output = ()> {
tokio::time::sleep(Duration::from_nanos(ns as u64))
}
fn delay_us(&mut self, us: u32) -> impl Future<Output = ()> {
tokio::time::sleep(Duration::from_micros(us as u64))
}
fn delay_ms(&mut self, ms: u32) -> impl Future<Output = ()> {
tokio::time::sleep(Duration::from_millis(ms as u64))
}
}
}