use std::error;
use std::fmt;
use std::io;
use std::ops::Not;
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, Weak};
use std::time::Duration;
use lazy_static::lazy_static;
mod epoll;
#[cfg(feature = "hal")]
mod hal;
#[cfg(feature = "hal-unproven")]
mod hal_unproven;
mod interrupt;
mod ioctl;
mod mem;
mod pin;
mod soft_pwm;
use crate::system;
pub use self::pin::{InputPin, IoPin, OutputPin, Pin};
#[derive(Debug)]
pub enum Error {
UnknownModel,
PinNotAvailable(u8),
PermissionDenied(String),
Io(io::Error),
ThreadPanic,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Error::UnknownModel => write!(f, "Unknown Raspberry Pi model"),
Error::PinNotAvailable(pin) => write!(f, "Pin {} is not available", pin),
Error::PermissionDenied(ref path) => write!(f, "Permission denied: {}", path),
Error::Io(ref err) => write!(f, "I/O error: {}", err),
Error::ThreadPanic => write!(f, "Thread panicked"),
}
}
}
impl error::Error for Error {}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}
impl From<system::Error> for Error {
fn from(_err: system::Error) -> Error {
Error::UnknownModel
}
}
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[repr(u8)]
pub enum Mode {
Input = 0b000,
Output = 0b001,
Alt0 = 0b100,
Alt1 = 0b101,
Alt2 = 0b110,
Alt3 = 0b111,
Alt4 = 0b011,
Alt5 = 0b010,
}
impl fmt::Display for Mode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Mode::Input => write!(f, "In"),
Mode::Output => write!(f, "Out"),
Mode::Alt0 => write!(f, "Alt0"),
Mode::Alt1 => write!(f, "Alt1"),
Mode::Alt2 => write!(f, "Alt2"),
Mode::Alt3 => write!(f, "Alt3"),
Mode::Alt4 => write!(f, "Alt4"),
Mode::Alt5 => write!(f, "Alt5"),
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[repr(u8)]
pub enum Level {
Low = 0,
High = 1,
}
impl fmt::Display for Level {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Level::Low => write!(f, "Low"),
Level::High => write!(f, "High"),
}
}
}
impl Not for Level {
type Output = Level;
fn not(self) -> Level {
match self {
Level::Low => Level::High,
Level::High => Level::Low,
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum PullUpDown {
Off = 0b00,
PullDown = 0b01,
PullUp = 0b10,
}
impl fmt::Display for PullUpDown {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
PullUpDown::Off => write!(f, "Off"),
PullUpDown::PullDown => write!(f, "PullDown"),
PullUpDown::PullUp => write!(f, "PullUp"),
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Trigger {
Disabled = 0,
RisingEdge = 1,
FallingEdge = 2,
Both = 3,
}
impl fmt::Display for Trigger {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Trigger::Disabled => write!(f, "Disabled"),
Trigger::RisingEdge => write!(f, "RisingEdge"),
Trigger::FallingEdge => write!(f, "FallingEdge"),
Trigger::Both => write!(f, "Both"),
}
}
}
pub(crate) struct GpioState {
gpio_mem: mem::GpioMem,
cdev: std::fs::File,
sync_interrupts: Mutex<interrupt::EventLoop>,
pins_taken: [AtomicBool; pin::MAX],
}
impl fmt::Debug for GpioState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventLoop")
.field("gpio_mem", &self.gpio_mem)
.field("cdev", &self.cdev)
.field("sync_interrupts", &self.sync_interrupts)
.field("pins_taken", &format_args!("{{ .. }}"))
.finish()
}
}
lazy_static! {
static ref GPIO_STATE: Mutex<Weak<GpioState>> = Mutex::new(Weak::new());
}
#[derive(Clone, Debug)]
pub struct Gpio {
inner: Arc<GpioState>,
}
impl Gpio {
pub fn new() -> Result<Gpio> {
let mut static_state = GPIO_STATE.lock().unwrap();
if let Some(ref state) = static_state.upgrade() {
Ok(Gpio {
inner: state.clone(),
})
} else {
let gpio_mem = mem::GpioMem::open()?;
let cdev = ioctl::find_gpiochip()?;
let sync_interrupts =
Mutex::new(interrupt::EventLoop::new(cdev.as_raw_fd(), pin::MAX)?);
let pins_taken = init_array!(AtomicBool::new(false), pin::MAX);
let gpio_state = Arc::new(GpioState {
gpio_mem,
cdev,
sync_interrupts,
pins_taken,
});
*static_state = Arc::downgrade(&gpio_state);
Ok(Gpio { inner: gpio_state })
}
}
pub fn output(pin : u8) -> Result<pin::OutputPin>{
let gp = Gpio::new()?.get(pin)?.into_output();
Ok(gp)
}
pub fn input(pin : u8) -> Result<pin::InputPin>{
let gp = Gpio::new()?.get(pin)?.into_input();
Ok(gp)
}
pub fn io(pin : u8, mode : Mode) -> Result<pin::IoPin>{
let gp = Gpio::new()?.get(pin)?.into_io(mode);
Ok(gp)
}
pub fn pulldown(pin : u8) -> Result<pin::InputPin>{
let gp = Gpio::new()?.get(pin)?.into_input_pulldown();
Ok(gp)
}
pub fn pullup(pin : u8) -> Result<pin::InputPin>{
let gp = Gpio::new()?.get(pin)?.into_input_pullup();
Ok(gp)
}
pub fn get(&self, pin: u8) -> Result<Pin> {
if pin as usize >= pin::MAX {
return Err(Error::PinNotAvailable(pin));
}
if self.inner.pins_taken[pin as usize].compare_and_swap(false, true, Ordering::SeqCst) {
Err(Error::PinNotAvailable(pin))
} else {
Ok(Pin::new(pin, self.inner.clone()))
}
}
pub fn poll_interrupts<'a>(
&self,
pins: &[&'a InputPin],
reset: bool,
timeout: Option<Duration>,
) -> Result<Option<(&'a InputPin, Level)>> {
(*self.inner.sync_interrupts.lock().unwrap()).poll(pins, reset, timeout)
}
}