blob: f476eec25471323c1c404a56c430193285e0767c [file] [log] [blame]
use crate::syscalls;
const DRIVER_NUMBER: usize = 0x00004;
mod gpio_commands {
pub const SUBSCRIBE_CALLBACK: 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;
}
pub enum InputMode {
PullUp,
PullDown,
PullNone,
}
pub enum IrqMode {
EitherEdge,
RisingEdge,
FallingEdge,
}
impl InputMode {
fn to_num(&self) -> usize {
match self {
InputMode::PullNone => 0,
InputMode::PullUp => 1,
InputMode::PullDown => 2,
}
}
}
impl IrqMode {
fn to_num(&self) -> usize {
match self {
IrqMode::EitherEdge => 0,
IrqMode::RisingEdge => 1,
IrqMode::FallingEdge => 2,
}
}
}
pub struct GpioPinUnitialized {
number: usize,
}
pub struct GpioPinWrite {
number: usize,
}
pub struct GpioPinRead {
number: usize,
}
impl GpioPinUnitialized {
pub fn new(number: usize) -> GpioPinUnitialized {
GpioPinUnitialized { number }
}
pub fn open_for_write(self) -> Result<GpioPinWrite, &'static str> {
match unsafe {
syscalls::command(DRIVER_NUMBER, gpio_commands::ENABLE_OUTPUT, self.number, 0)
} {
0 => Ok(GpioPinWrite {
number: self.number,
}),
_ => Err("Could not open pin for writing."),
}
}
pub fn open_for_read(
self,
callback: Option<(extern "C" fn(usize, usize, usize, usize), IrqMode)>,
input_mode: InputMode,
) -> Result<GpioPinRead, &'static str> {
let (callback, irq_mode) = callback.unwrap_or((noop_callback, IrqMode::EitherEdge));
self.enable_input(input_mode)
.and_then(|pin| pin.subscribe_callback(callback))
.and_then(|pin| pin.enable_callback(irq_mode))
}
fn subscribe_callback(
self,
callback: extern "C" fn(usize, usize, usize, usize),
) -> Result<GpioPinUnitialized, &'static str> {
if unsafe {
syscalls::subscribe_ptr(
DRIVER_NUMBER,
gpio_commands::SUBSCRIBE_CALLBACK,
callback as *const _,
self.number as usize,
)
} == 0
{
Ok(self)
} else {
Err("Could not subscribe callback.")
}
}
fn enable_input(self, mode: InputMode) -> Result<GpioPinUnitialized, &'static str> {
if unsafe {
syscalls::command(
DRIVER_NUMBER,
gpio_commands::ENABLE_INPUT,
self.number,
mode.to_num(),
)
} == 0
{
Ok(self)
} else {
Err("Could not enable input.")
}
}
fn enable_callback(self, irq_mode: IrqMode) -> Result<GpioPinRead, &'static str> {
if unsafe {
syscalls::command(
DRIVER_NUMBER,
gpio_commands::ENABLE_INTERRUPT,
self.number,
irq_mode.to_num(),
)
} == 0
{
Ok(GpioPinRead {
number: self.number,
})
} else {
Err("Could not enable callback.")
}
}
}
impl GpioPinWrite {
pub fn set_low(&self) {
unsafe {
syscalls::command(DRIVER_NUMBER, gpio_commands::SET_LOW, self.number, 0);
}
}
pub fn set_high(&self) {
unsafe {
syscalls::command(DRIVER_NUMBER, gpio_commands::SET_HIGH, self.number, 0);
}
}
pub fn toggle(&self) {
unsafe {
syscalls::command(DRIVER_NUMBER, gpio_commands::TOGGLE, self.number, 0);
}
}
}
impl GpioPinRead {
pub fn read(&self) -> bool {
unsafe { syscalls::command(DRIVER_NUMBER, gpio_commands::READ, self.number, 0) == 1 }
}
}
impl Drop for GpioPinWrite {
fn drop(&mut self) {
unsafe {
syscalls::command(DRIVER_NUMBER, gpio_commands::DISABLE, self.number, 0);
}
}
}
impl Drop for GpioPinRead {
fn drop(&mut self) {
unsafe {
syscalls::command(
DRIVER_NUMBER,
gpio_commands::DISABLE_INTERRUPT,
self.number,
0,
);
syscalls::command(DRIVER_NUMBER, gpio_commands::DISABLE, self.number, 0);
}
}
}
extern "C" fn noop_callback(_: usize, _: usize, _: usize, _: usize) {}