| 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, | |
| } |