blob: e69d5ee8c99b3e139a94868372897bd8d520d930 [file] [log] [blame]
use crate::callback::CallbackSubscription;
use crate::callback::Consumer;
use crate::result::OtherError;
use crate::result::TockResult;
use crate::syscalls;
use core::marker::PhantomData;
const DRIVER_NUMBER: usize = 0x00004;
mod command_nr {
pub const COUNT: usize = 0;
pub const ENABLE_OUTPUT: usize = 1;
pub const SET_HIGH: usize = 2;
pub const SET_LOW: usize = 3;
pub const TOGGLE: usize = 4;
pub const ENABLE_INPUT: usize = 5;
pub const READ: usize = 6;
pub const ENABLE_INTERRUPT: usize = 7;
pub const DISABLE_INTERRUPT: usize = 8;
pub const DISABLE: usize = 9;
}
mod subscribe_nr {
pub const SUBSCRIBE_CALLBACK: usize = 0;
}
#[non_exhaustive]
pub struct GpioDriverFactory;
impl GpioDriverFactory {
pub fn init_driver(&mut self) -> TockResult<GpioDriver> {
let driver = GpioDriver {
num_gpios: syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?,
lifetime: PhantomData,
};
Ok(driver)
}
}
pub struct GpioDriver<'a> {
num_gpios: usize,
lifetime: PhantomData<&'a ()>,
}
impl<'a> GpioDriver<'a> {
pub fn num_gpios(&self) -> usize {
self.num_gpios
}
pub fn gpios(&mut self) -> Gpios {
Gpios {
num_gpios: self.num_gpios(),
curr_gpio: 0,
lifetime: PhantomData,
}
}
pub fn subscribe<CB: Fn(usize, GpioState)>(
&self,
callback: &'a mut CB,
) -> TockResult<CallbackSubscription> {
syscalls::subscribe::<GpioEventConsumer, _>(
DRIVER_NUMBER,
subscribe_nr::SUBSCRIBE_CALLBACK,
callback,
)
.map_err(Into::into)
}
}
struct GpioEventConsumer;
impl<CB: Fn(usize, GpioState)> Consumer<CB> for GpioEventConsumer {
fn consume(callback: &mut CB, gpio_num: usize, gpio_state: usize, _: usize) {
let gpio_state = match gpio_state {
0 => GpioState::Low,
1 => GpioState::High,
_ => return,
};
callback(gpio_num, gpio_state);
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum GpioState {
Low,
High,
}
impl From<GpioState> for bool {
fn from(gpio_state: GpioState) -> Self {
match gpio_state {
GpioState::Low => false,
GpioState::High => true,
}
}
}
impl From<bool> for GpioState {
fn from(from_value: bool) -> Self {
if from_value {
GpioState::Low
} else {
GpioState::High
}
}
}
pub struct Gpios<'a> {
num_gpios: usize,
curr_gpio: usize,
lifetime: PhantomData<&'a ()>,
}
impl<'a> Iterator for Gpios<'a> {
type Item = Gpio<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.curr_gpio < self.num_gpios {
let item = Gpio {
gpio_num: self.curr_gpio,
lifetime: PhantomData,
};
self.curr_gpio += 1;
Some(item)
} else {
None
}
}
}
pub struct Gpio<'a> {
gpio_num: usize,
lifetime: PhantomData<&'a ()>,
}
impl<'a> Gpio<'a> {
pub fn enable_output(&mut self) -> TockResult<GpioWrite> {
syscalls::command(DRIVER_NUMBER, command_nr::ENABLE_OUTPUT, self.gpio_num, 0)?;
let gpio_write = GpioWrite {
gpio_num: self.gpio_num,
lifetime: PhantomData,
};
Ok(gpio_write)
}
pub fn enable_input(&mut self, resistor_mode: ResistorMode) -> TockResult<GpioRead> {
syscalls::command(
DRIVER_NUMBER,
command_nr::ENABLE_INPUT,
self.gpio_num,
resistor_mode as usize,
)?;
let gpio_read = GpioRead {
gpio_num: self.gpio_num,
lifetime: PhantomData,
};
Ok(gpio_read)
}
}
pub struct GpioWrite<'a> {
gpio_num: usize,
lifetime: PhantomData<&'a ()>,
}
impl<'a> GpioWrite<'a> {
pub fn gpio_num(&self) -> usize {
self.gpio_num
}
pub fn set(&self, state: impl Into<GpioState>) -> TockResult<()> {
match state.into() {
GpioState::Low => self.set_low(),
GpioState::High => self.set_high(),
}
}
pub fn set_low(&self) -> TockResult<()> {
syscalls::command(DRIVER_NUMBER, command_nr::SET_LOW, self.gpio_num, 0)?;
Ok(())
}
pub fn set_high(&self) -> TockResult<()> {
syscalls::command(DRIVER_NUMBER, command_nr::SET_HIGH, self.gpio_num, 0)?;
Ok(())
}
pub fn toggle(&self) -> TockResult<()> {
syscalls::command(DRIVER_NUMBER, command_nr::TOGGLE, self.gpio_num, 0)?;
Ok(())
}
}
impl<'a> Drop for GpioWrite<'a> {
fn drop(&mut self) {
let _ = syscalls::command(DRIVER_NUMBER, command_nr::DISABLE, self.gpio_num, 0);
}
}
pub struct GpioRead<'a> {
gpio_num: usize,
lifetime: PhantomData<&'a ()>,
}
impl<'a> GpioRead<'a> {
pub fn gpio_num(&self) -> usize {
self.gpio_num
}
pub fn read(&self) -> TockResult<GpioState> {
let button_state = syscalls::command(DRIVER_NUMBER, command_nr::READ, self.gpio_num, 0)?;
match button_state {
0 => Ok(GpioState::Low),
1 => Ok(GpioState::High),
_ => Err(OtherError::GpioDriverInvalidState.into()),
}
}
pub fn enable_interrupt(&self, trigger_type: TriggerType) -> TockResult<()> {
syscalls::command(
DRIVER_NUMBER,
command_nr::ENABLE_INTERRUPT,
self.gpio_num,
trigger_type as usize,
)?;
Ok(())
}
pub fn disable_interrupt(&self, trigger_type: TriggerType) -> TockResult<()> {
syscalls::command(
DRIVER_NUMBER,
command_nr::DISABLE_INTERRUPT,
self.gpio_num,
trigger_type as usize,
)?;
Ok(())
}
}
impl<'a> Drop for GpioRead<'a> {
fn drop(&mut self) {
let _ = syscalls::command(DRIVER_NUMBER, command_nr::DISABLE, self.gpio_num, 0);
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ResistorMode {
PullNone = 0,
PullUp = 1,
PullDown = 2,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum TriggerType {
EitherEdge = 0,
RisingEdge = 1,
FallingEdge = 2,
}