#![cfg_attr(not(feature="std"), no_std)]
#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals, clippy::all)]
use core::ptr;
use log::{debug};
pub use cty::{c_char};
pub use wasm_embedded_spec::{self as spec};
pub use spec::api::{gpio_drv_t, spi_drv_t, i2c_drv_t, uart_drv_t};
mod gpio;
mod spi;
mod i2c;
mod uart;
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
const START_STR: &'static [u8] = b"_start\0";
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature="thiserror", derive(thiserror::Error))]
#[cfg_attr(feature="defmt", derive(defmt::Format))]
pub enum Wasm3Err {
#[cfg_attr(feature="thiserror", error("Failed to create context"))]
Ctx,
#[cfg_attr(feature="thiserror", error("I2C init error: {0}"))]
I2c(i32),
#[cfg_attr(feature="thiserror", error("SPI init error: {0}"))]
Spi(i32),
#[cfg_attr(feature="thiserror", error("GPIO init error: {0}"))]
Gpio(i32),
#[cfg_attr(feature="thiserror", error("Execution error: {0}"))]
Exec(i32),
#[cfg_attr(feature="thiserror", error("Driver binding error: {0}"))]
Bind(i32),
}
pub struct Wasm3Runtime {
_task: wasme_task_t,
ctx: *mut wasme_ctx_t,
}
pub trait Engine: spec::gpio::Gpio + spec::i2c::I2c + spec::spi::Spi + spec::uart::Uart {}
impl <T> Engine for T where
T: spec::gpio::Gpio + spec::i2c::I2c + spec::spi::Spi + spec::uart::Uart,
{
}
impl Wasm3Runtime {
pub fn new<E: Engine>(engine: &mut E, data: &[u8]) -> Result<Self, Wasm3Err> {
let task = wasme_task_t{
data: data.as_ptr(),
data_len: data.len() as u32,
};
let ctx = unsafe { WASME_init(&task, 10 * 1024) };
if ctx.is_null() {
return Err(Wasm3Err::Ctx);
}
let mut rt = Self{
_task: task,
ctx,
};
rt.bind::<gpio_drv_t, _>(engine)?;
rt.bind::<spi_drv_t, _>(engine)?;
rt.bind::<i2c_drv_t, _>(engine)?;
rt.bind::<uart_drv_t, _>(engine)?;
Ok(rt)
}
pub fn bind<I, D: Driver<I>>(&mut self, driver: &mut D) -> Result<(), Wasm3Err> {
debug!("Binding {} driver: {}",
core::any::type_name::<I>(),
core::any::type_name::<D>(),
);
let res = driver.bind(self);
if res < 0 {
Err(Wasm3Err::Bind(res))
} else {
Ok(())
}
}
pub fn run(&mut self) -> Result<(), Wasm3Err> {
let entry = START_STR.as_ptr() as *const c_char;
let res = unsafe { WASME_run(self.ctx, entry, 0, ptr::null_mut()) };
if res < 0 {
return Err(Wasm3Err::Exec(res));
}
debug!("WASME execution complete!");
Ok(())
}
}
impl Drop for Wasm3Runtime {
fn drop(&mut self) {
unsafe { WASME_deinit(&mut self.ctx) }
self.ctx = core::ptr::null_mut();
}
}
pub trait Driver<Inner>: Sized {
const DRIVER: Inner;
fn context(&mut self) -> *mut cty::c_void {
self as *mut _ as *mut cty::c_void
}
fn bind(&mut self, rt: &mut Wasm3Runtime) -> i32;
}