-
Notifications
You must be signed in to change notification settings - Fork 109
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
367: GPIO driver r=hudson-ayers a=alexandruradovici This PR implements an initial version of the GPIO driver. # Roadmap: - [x] implement pin controls (input / output) - [x] ~implement iterator~ - [x] implement interrupts # Iterator Implementation I am not quite sure how to implement the iterator in such a way as to prevent its multiple instantiation. The previous version of libtock was using the driver factory idea and used mutability and lifetimes to prevent this. As the current driver model is using associated functions instead of methods, this does not seem possible. ```rust impl<S: Syscalls> Gpio<S> { pub fn gpios() -> Gpios<'static, S> { let num_gpios = S::command(DRIVER_ID, GPIO_COUNT, 0, 0) .get_success_u32() .unwrap_or_default() as usize; Gpios { num_gpios, current_gpio: 0, _syscalls: &PhantomData, } } } ``` One idea is to use runtime checks and a mutable static, but this (I think) implies unsafe code. Any suggestions are very much welcome. # Usage Examples ## Output ```rust let gpios = Gpio::gpios(); let output_pin: Output<...> = gpios.nth(i)?.into(); output_pin.set(); ``` ## Input ```rust let gpios = Gpio::gpios(); let mut input_pin: Input<PullUp> = gpios.nth(i)?.into(); input_pin.read(); ``` Co-authored-by: Alexandru Radovici <[email protected]> Co-authored-by: Alexandru Radovici <[email protected]>
- Loading branch information
Showing
7 changed files
with
1,237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[package] | ||
name = "libtock_gpio" | ||
version = "0.1.0" | ||
authors = ["Tock Project Developers <[email protected]>"] | ||
license = "MIT/Apache-2.0" | ||
edition = "2018" | ||
repository = "https://www.github.com/tock/libtock-rs" | ||
description = "libtock gpio driver" | ||
|
||
[dependencies] | ||
libtock_platform = { path = "../../platform" } | ||
|
||
[dev-dependencies] | ||
libtock_unittest = { path = "../../unittest" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
#![no_std] | ||
|
||
use core::marker::PhantomData; | ||
|
||
use libtock_platform::{ | ||
share::Handle, subscribe::OneId, DefaultConfig, ErrorCode, Subscribe, Syscalls, Upcall, | ||
}; | ||
|
||
/// The Gpio driver | ||
/// | ||
/// # Example | ||
/// ```ignore | ||
/// use libtock2::Gpios; | ||
/// | ||
/// // Turn on led 0 | ||
/// let pin = Gpios::get_pin(0)?; | ||
/// | ||
/// ``` | ||
|
||
#[derive(Copy, Clone, PartialEq, Debug)] | ||
pub enum GpioState { | ||
Low = 0, | ||
High = 1, | ||
} | ||
|
||
pub enum PinInterruptEdge { | ||
Either = 0, | ||
Rising = 1, | ||
Falling = 2, | ||
} | ||
|
||
pub enum Error { | ||
Invalid, | ||
Failed, | ||
} | ||
|
||
pub trait Pull { | ||
const MODE: u32; | ||
} | ||
|
||
pub struct PullUp; | ||
impl Pull for PullUp { | ||
const MODE: u32 = 1; | ||
} | ||
|
||
pub struct PullDown; | ||
impl Pull for PullDown { | ||
const MODE: u32 = 2; | ||
} | ||
|
||
pub struct PullNone; | ||
impl Pull for PullNone { | ||
const MODE: u32 = 0; | ||
} | ||
|
||
pub struct Gpio<S: Syscalls>(S); | ||
|
||
impl<S: Syscalls> Gpio<S> { | ||
/// Run a check against the gpio capsule to ensure it is present. | ||
/// | ||
/// Returns true` if the driver was present. This does not necessarily mean | ||
/// that the driver is working, as it may still fail to allocate grant | ||
/// memory. | ||
pub fn count() -> Result<u32, ErrorCode> { | ||
S::command(DRIVER_NUM, GPIO_COUNT, 0, 0).to_result() | ||
} | ||
|
||
pub fn get_pin(pin: u32) -> Result<Pin<S>, ErrorCode> { | ||
Self::disable(pin)?; | ||
Ok(Pin { | ||
pin_number: pin, | ||
_syscalls: PhantomData, | ||
}) | ||
} | ||
|
||
/// Register an interrupt listener | ||
/// | ||
/// There can be only one single listener registered at a time. | ||
/// Each time this function is used, it will replace the | ||
/// previously registered listener. | ||
pub fn register_listener<'share, F: Fn(u32, GpioState)>( | ||
listener: &'share GpioInterruptListener<F>, | ||
subscribe: Handle<Subscribe<'share, S, DRIVER_NUM, 0>>, | ||
) -> Result<(), ErrorCode> { | ||
S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, listener) | ||
} | ||
|
||
/// Unregister the interrupt listener | ||
/// | ||
/// This function may be used even if there was no | ||
/// previously registered listener. | ||
pub fn unregister_listener() { | ||
S::unsubscribe(DRIVER_NUM, 0) | ||
} | ||
} | ||
|
||
/// A wrapper around a closure to be registered and called when | ||
/// a gpio interrupt occurs. | ||
/// | ||
/// ```ignore | ||
/// let listener = GpioInterruptListener(|gpio, interrupt_edge| { | ||
/// // make use of the button's state | ||
/// }); | ||
/// ``` | ||
pub struct GpioInterruptListener<F: Fn(u32, GpioState)>(pub F); | ||
|
||
impl<F: Fn(u32, GpioState)> Upcall<OneId<DRIVER_NUM, 0>> for GpioInterruptListener<F> { | ||
fn upcall(&self, gpio_index: u32, value: u32, _arg2: u32) { | ||
self.0(gpio_index, value.into()) | ||
} | ||
} | ||
|
||
impl From<u32> for GpioState { | ||
fn from(original: u32) -> GpioState { | ||
match original { | ||
0 => GpioState::Low, | ||
_ => GpioState::High, | ||
} | ||
} | ||
} | ||
|
||
pub struct Pin<S: Syscalls> { | ||
pin_number: u32, | ||
_syscalls: PhantomData<S>, | ||
} | ||
|
||
impl<S: Syscalls> Pin<S> { | ||
pub fn make_output(&mut self) -> Result<OutputPin<S>, ErrorCode> { | ||
Gpio::<S>::enable_gpio_output(self.pin_number)?; | ||
Ok(OutputPin { pin: self }) | ||
} | ||
|
||
pub fn make_input<P: Pull>(&self) -> Result<InputPin<S, P>, ErrorCode> { | ||
Gpio::<S>::enable_gpio_input(self.pin_number, P::MODE)?; | ||
Ok(InputPin { | ||
pin: self, | ||
_pull: PhantomData, | ||
}) | ||
} | ||
} | ||
|
||
pub struct OutputPin<'a, S: Syscalls> { | ||
pin: &'a Pin<S>, | ||
} | ||
|
||
impl<'a, S: Syscalls> OutputPin<'a, S> { | ||
pub fn toggle(&mut self) -> Result<(), ErrorCode> { | ||
Gpio::<S>::toggle(self.pin.pin_number) | ||
} | ||
pub fn set(&mut self) -> Result<(), ErrorCode> { | ||
Gpio::<S>::write(self.pin.pin_number, GpioState::High) | ||
} | ||
pub fn clear(&mut self) -> Result<(), ErrorCode> { | ||
Gpio::<S>::write(self.pin.pin_number, GpioState::Low) | ||
} | ||
} | ||
|
||
pub struct InputPin<'a, S: Syscalls, P: Pull> { | ||
pin: &'a Pin<S>, | ||
_pull: PhantomData<P>, | ||
} | ||
|
||
impl<'a, S: Syscalls, P: Pull> InputPin<'a, S, P> { | ||
pub fn read(&self) -> Result<GpioState, ErrorCode> { | ||
Gpio::<S>::read(self.pin.pin_number) | ||
} | ||
|
||
pub fn enable_interrupts(&self, edge: PinInterruptEdge) -> Result<(), ErrorCode> { | ||
Gpio::<S>::enable_interrupts(self.pin.pin_number, edge) | ||
} | ||
|
||
pub fn disable_interrupts(&self) -> Result<(), ErrorCode> { | ||
Gpio::<S>::disable_interrupts(self.pin.pin_number) | ||
} | ||
} | ||
|
||
impl<S: Syscalls> Drop for OutputPin<'_, S> { | ||
fn drop(&mut self) { | ||
let _ = Gpio::<S>::disable(self.pin.pin_number); | ||
} | ||
} | ||
|
||
impl<S: Syscalls, P: Pull> Drop for InputPin<'_, S, P> { | ||
fn drop(&mut self) { | ||
let _ = Gpio::<S>::disable(self.pin.pin_number); | ||
} | ||
} | ||
|
||
// ----------------------------------------------------------------------------- | ||
// Implementation details below | ||
// ----------------------------------------------------------------------------- | ||
|
||
impl<S: Syscalls> Gpio<S> { | ||
fn enable_gpio_output(pin: u32) -> Result<(), ErrorCode> { | ||
S::command(DRIVER_NUM, GPIO_ENABLE_OUTPUT, pin, 0).to_result() | ||
} | ||
|
||
fn enable_gpio_input(pin: u32, mode: u32) -> Result<(), ErrorCode> { | ||
S::command(DRIVER_NUM, GPIO_ENABLE_INPUT, pin, mode).to_result() | ||
} | ||
|
||
fn write(pin: u32, state: GpioState) -> Result<(), ErrorCode> { | ||
let action = match state { | ||
GpioState::Low => GPIO_CLEAR, | ||
_ => GPIO_SET, | ||
}; | ||
S::command(DRIVER_NUM, action, pin, 0).to_result() | ||
} | ||
|
||
fn read(pin: u32) -> Result<GpioState, ErrorCode> { | ||
let pin_state: u32 = S::command(DRIVER_NUM, GPIO_READ_INPUT, pin, 0).to_result()?; | ||
Ok(pin_state.into()) | ||
} | ||
|
||
fn toggle(pin: u32) -> Result<(), ErrorCode> { | ||
S::command(DRIVER_NUM, GPIO_TOGGLE, pin, 0).to_result() | ||
} | ||
|
||
fn disable(pin: u32) -> Result<(), ErrorCode> { | ||
S::command(DRIVER_NUM, GPIO_DISABLE, pin, 0).to_result() | ||
} | ||
|
||
fn enable_interrupts(pin: u32, edge: PinInterruptEdge) -> Result<(), ErrorCode> { | ||
S::command(DRIVER_NUM, GPIO_ENABLE_INTERRUPTS, pin, edge as u32).to_result() | ||
} | ||
|
||
fn disable_interrupts(pin: u32) -> Result<(), ErrorCode> { | ||
S::command(DRIVER_NUM, GPIO_DISABLE_INTERRUPTS, pin, 0).to_result() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
|
||
// ----------------------------------------------------------------------------- | ||
// Driver number and command IDs | ||
// ----------------------------------------------------------------------------- | ||
|
||
const DRIVER_NUM: u32 = 4; | ||
|
||
// Command IDs | ||
const GPIO_COUNT: u32 = 0; | ||
|
||
const GPIO_ENABLE_OUTPUT: u32 = 1; | ||
const GPIO_SET: u32 = 2; | ||
const GPIO_CLEAR: u32 = 3; | ||
const GPIO_TOGGLE: u32 = 4; | ||
|
||
const GPIO_ENABLE_INPUT: u32 = 5; | ||
const GPIO_READ_INPUT: u32 = 6; | ||
|
||
const GPIO_ENABLE_INTERRUPTS: u32 = 7; | ||
const GPIO_DISABLE_INTERRUPTS: u32 = 8; | ||
|
||
const GPIO_DISABLE: u32 = 9; |
Oops, something went wrong.