blob: 842a16ac0da388cba76563aa277e6ae6ad23f5a9 [file] [log] [blame]
use core::cell::Cell;
use kernel::{AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
use matcha_hal::dprintf;
use matcha_hal::mailbox_hal::MailboxHAL;
use matcha_config::*;
//------------------------------------------------------------------------------
pub struct AppData {
pub send_callback: Option<Callback>,
pub recv_callback: Option<Callback>,
pub send_buffer: Option<AppSlice<Shared, u8>>,
pub recv_buffer: Option<AppSlice<Shared, u8>>,
}
impl Default for AppData {
fn default() -> AppData {
AppData {
send_callback: None,
recv_callback: None,
send_buffer: None,
recv_buffer: None,
}
}
}
//------------------------------------------------------------------------------
// MailboxCapsule is a global static singleton - its members have to be
// pseudo-const.
pub struct MailboxCapsule {
pub app_data_grant: Grant<AppData>,
pub mailbox_hal: Cell<Option<&'static dyn MailboxHAL>>,
pub current_app: Cell<Option<AppId>>,
}
//----------------------------------------
impl MailboxCapsule {
pub fn new(app_data_grant: Grant<AppData>) -> Self {
return MailboxCapsule {
app_data_grant: app_data_grant,
mailbox_hal: Cell::new(None),
current_app: Cell::new(None),
};
}
pub fn set_mailbox(&self, mailbox: &'static dyn MailboxHAL) {
self.mailbox_hal.set(Some(mailbox));
}
pub fn handle_command(
&self,
app_id: AppId,
app_data: &mut AppData,
cmd_num: usize,
arg2: usize,
_arg3: usize,
) -> ReturnCode {
match cmd_num {
CMD_MAILBOX_INIT => {
self.current_app.set(Some(app_id));
ReturnCode::SUCCESS
}
CMD_MAILBOX_SEND => {
let message_len = arg2;
match &app_data.send_buffer {
Some(buffer) => {
self.mailbox_hal.get().map(|mb| {
let _ = mb.send_message_slice_sync(message_len, buffer);
});
ReturnCode::SUCCESS
}
None => ReturnCode::FAIL,
}
}
CMD_MAILBOX_RECV => {
// Nothing to do here yet?
ReturnCode::SUCCESS
}
_ => {
dprintf!("MailboxCapsule::handle_command({:?}, ????)\n", app_id);
ReturnCode::EINVAL
}
}
}
pub fn handle_subscribe(
&self,
_app_id: AppId,
app_data: &mut AppData,
minor_num: usize,
callback: Option<Callback>,
) -> ReturnCode {
match minor_num {
CMD_MAILBOX_SEND => {
app_data.send_callback = callback;
ReturnCode::SUCCESS
}
CMD_MAILBOX_RECV => {
app_data.recv_callback = callback;
ReturnCode::SUCCESS
}
_ => ReturnCode::EINVAL,
}
}
pub fn handle_allow(
&self,
_app_id: AppId,
app_data: &mut AppData,
minor_num: usize,
slice: Option<AppSlice<Shared, u8>>,
) -> ReturnCode {
match minor_num {
CMD_MAILBOX_SEND => {
app_data.send_buffer = slice;
ReturnCode::SUCCESS
}
CMD_MAILBOX_RECV => {
app_data.recv_buffer = slice;
ReturnCode::SUCCESS
}
_ => ReturnCode::EINVAL,
}
}
}
//------------------------------------------------------------------------------
// Our "driver" trait impl just unwraps the app data grant and delegates to
// MailboxCapsule.
impl Driver for MailboxCapsule {
fn subscribe(&self, minor_num: usize, callback: Option<Callback>, app_id: AppId) -> ReturnCode {
self.app_data_grant
.enter(app_id, |app_data, _| {
self.handle_subscribe(app_id, app_data, minor_num, callback)
})
.unwrap_or_else(|err| err.into())
}
fn command(&self, cmd_num: usize, r2: usize, r3: usize, app_id: AppId) -> ReturnCode {
self.app_data_grant
.enter(app_id, |app_data, _| {
self.handle_command(app_id, app_data, cmd_num, r2, r3)
})
.unwrap_or_else(|err| err.into())
}
fn allow(
&self,
app_id: AppId,
minor_num: usize,
slice: Option<AppSlice<Shared, u8>>,
) -> ReturnCode {
self.app_data_grant
.enter(app_id, |app_data, _| {
self.handle_allow(app_id, app_data, minor_num, slice)
})
.unwrap_or_else(|err| err.into())
}
}
//------------------------------------------------------------------------------
impl matcha_hal::mailbox_hal::MailboxISR for MailboxCapsule {
fn on_wtirq(&self) {
// Nothing to do here at the moment, sends are synchronous and the
// polarity of the ISR is backwards.
}
// When a message arrives, we copy it to the waiting app's buffer (if there
// is one) and then fire the app's callback. Async waiting is handled in
// app/src/mailbox_client.rs
fn on_rtirq(&self) {
// Unwrap all our optionals. There's probably a better way to do this.
self.current_app.get().map(|app_id| {
self.app_data_grant.enter(app_id, |app_data, _| {
app_data.recv_buffer.as_ref().map(|buffer| {
self.mailbox_hal.get().map(|mailbox| {
// Copy the message to the app's buffer and then
// schedule the app callback.
match mailbox.get_message_slice_sync(&buffer) {
Ok(len) => {
let page = mailbox.get_message_page().unwrap_or(0);
app_data.recv_callback.map(|mut callback| {
callback.schedule(1, len, page as usize);
});
}
Err(len) => {
app_data.recv_callback.map(|mut callback| {
callback.schedule(0, len, 0);
});
}
}
});
});
})
});
}
fn on_eirq(&self) {
// We should probably do something here eventually.
}
}