use crc::crc32::checksum_ieee;
use mwatch_kernel_api::{Context, ServiceFn, SetupFn, Ssd1351, InputFn, InputEvent};
pub struct ApplicationManager {
ram: Ram,
target_cs: [u8; 4],
target_cs_idx: usize,
service_fn: Option<ServiceFn>,
input_fn: Option<InputFn>,
status: Status,
}
#[derive(Debug, Copy, Clone)]
pub enum Error {
Executing,
ChecksumFailed,
NoApplication,
InvalidServiceFn,
InvalidInputFn,
}
#[derive(Debug, Copy, Clone)]
pub struct Status {
pub is_loaded: bool,
pub is_running: bool,
pub ram_used: usize,
pub service_result: i32,
}
impl Default for Status {
fn default() -> Status {
Status {
is_loaded: false,
is_running: false,
service_result: -1,
ram_used: 0,
}
}
}
impl ApplicationManager {
pub fn new(ram: &'static mut [u8]) -> Self {
Self {
ram: Ram::new(ram),
target_cs: [0u8; 4],
target_cs_idx: 0,
service_fn: None,
input_fn: None,
status: Status::default(),
}
}
pub fn write_ram_byte(&mut self, byte: u8) -> Result<(), Error> {
self.ram.write(byte)?;
Ok(())
}
pub fn write_checksum_byte(&mut self, byte: u8) -> Result<(), Error> {
self.target_cs[self.target_cs_idx] = byte;
self.target_cs_idx += 1;
Ok(())
}
pub fn verify(&mut self) -> Result<(), Error> {
let digest = ((self.target_cs[0] as u32) << 24)
| ((self.target_cs[1] as u32) << 16)
| ((self.target_cs[2] as u32) << 8)
| ((self.target_cs[3] as u32) << 0);
info!("Digest: {}", digest);
let ram_cs = self.ram.cs();
info!("Current Ram Digest: {}", ram_cs);
if digest == ram_cs {
self.status.is_loaded = true;
Ok(())
} else {
error!("Application checksum failed!");
Err(Error::ChecksumFailed)
}
}
pub fn execute(&mut self) -> Result<(), Error> {
if !self.status.is_loaded {
return Err(Error::NoApplication);
}
let setup_ptr = Self::fn_ptr_from_slice(&self.ram.as_ref()[..4]);
let service_ptr = Self::fn_ptr_from_slice(&self.ram.as_ref()[4..8]);
let input_ptr = Self::fn_ptr_from_slice(&self.ram.as_ref()[8..12]);
let _result = unsafe {
let setup: SetupFn = ::core::mem::transmute(setup_ptr);
let service: ServiceFn = ::core::mem::transmute(service_ptr);
let input: InputFn = ::core::mem::transmute(input_ptr);
self.service_fn = Some(service);
self.input_fn = Some(input);
setup()
};
self.status.is_running = true;
Ok(())
}
pub fn service(&mut self, display: &mut Ssd1351) -> Result<(), Error> {
if let Some(service_fn) = self.service_fn {
let mut ctx = Context {
display: display,
log: application_logger,
};
self.status.service_result = service_fn(&mut ctx);
Ok(())
} else {
Err(Error::InvalidServiceFn)
}
}
pub fn service_input(&mut self, display: &mut Ssd1351, input: InputEvent) -> Result<(), Error> {
if let Some(input_fn) = self.input_fn {
let mut ctx = Context {
display: display,
log: application_logger,
};
let _ = input_fn(&mut ctx, input);
Ok(())
} else {
Err(Error::InvalidInputFn)
}
}
pub fn pause(&mut self) {
self.status.is_running = false;
}
pub fn stop(&mut self) -> Result<(), Error> {
self.ram.reset();
self.target_cs_idx = 0;
self.status.is_loaded = false;
self.status.is_running = false;
self.input_fn = None;
self.service_fn = None;
Ok(())
}
pub fn status(&self) -> Status {
self.status
}
fn fn_ptr_from_slice(bytes: &[u8]) -> *const () {
assert!(bytes.len() == 4);
let addr = ((bytes[3] as u32) << 24)
| ((bytes[2] as u32) << 16)
| ((bytes[1] as u32) << 8)
| ((bytes[0] as u32) << 0);
addr as *const ()
}
}
pub struct Ram {
ram: &'static mut [u8],
ram_idx: usize,
}
impl Ram {
pub fn new(ram: &'static mut [u8]) -> Self {
Self {
ram: ram,
ram_idx: 0,
}
}
pub fn write(&mut self, byte: u8) -> Result<(), Error> {
self.ram[self.ram_idx] = byte;
self.ram_idx += 1;
Ok(())
}
pub fn cs(&self) -> u32 {
checksum_ieee(&self.ram[..self.ram_idx])
}
pub fn reset(&mut self) {
self.ram_idx = 0;
self.wipe();
}
fn wipe(&mut self) {
for i in 0..self.ram_idx {
self.ram[i] = 0u8;
}
}
pub fn as_ref(&self) -> &[u8] {
&self.ram
}
}
extern "C" fn application_logger(string: &str) -> i32 {
info!("{}", string);
0
}