#![allow(clippy::missing_transmute_annotations)]
#![allow(static_mut_refs)]
use std::error;
use std::fmt;
use std::io;
use std::mem::MaybeUninit;
use std::ops::Not;
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, Once, Weak};
use std::time::Duration;
mod epoll;
mod gpiomem;
#[cfg(any(
feature = "embedded-hal-0",
feature = "embedded-hal",
feature = "embedded-hal-nb"
))]
mod hal;
#[cfg(feature = "hal-unproven")]
mod hal_unproven;
mod interrupt;
mod ioctl;
mod pin;
mod soft_pwm;
use crate::system;
use crate::system::DeviceInfo;
pub use self::pin::{InputPin, IoPin, OutputPin, Pin};
#[derive(Debug)]
pub enum Error {
UnknownModel,
PinUsed(u8),
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::PinUsed(pin) => write!(f, "Pin {} is already in use", pin),
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,
Output,
Alt0,
Alt1,
Alt2,
Alt3,
Alt4,
Alt5,
Alt6,
Alt7,
Alt8,
Null,
}
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"),
Mode::Alt6 => write!(f, "Alt6"),
Mode::Alt7 => write!(f, "Alt7"),
Mode::Alt8 => write!(f, "Alt8"),
Mode::Null => write!(f, "Null"),
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[repr(u8)]
pub enum Level {
Low = 0,
High = 1,
}
impl From<bool> for Level {
fn from(e: bool) -> Level {
if e {
Level::High
} else {
Level::Low
}
}
}
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 From<u8> for Level {
fn from(value: u8) -> Self {
if value == 0 {
Level::Low
} else {
Level::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 Bias {
Off = 0b00,
PullDown = 0b01,
PullUp = 0b10,
}
impl fmt::Display for Bias {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Bias::Off => write!(f, "Off"),
Bias::PullDown => write!(f, "PullDown"),
Bias::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"),
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct Event {
pub timestamp: Duration,
pub seqno: u32,
pub trigger: Trigger,
}
impl Default for Event {
fn default() -> Self {
Self {
timestamp: Duration::default(),
seqno: 0,
trigger: Trigger::Both,
}
}
}
pub(crate) struct GpioState {
gpio_mem: Box<dyn gpiomem::GpioRegisters>,
cdev: std::fs::File,
sync_interrupts: Mutex<interrupt::EventLoop>,
pins_taken: [AtomicBool; u8::MAX as usize],
gpio_lines: u8,
}
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!("{{ .. }}"))
.field("gpio_lines", &self.gpio_lines)
.finish()
}
}
#[derive(Clone, Debug)]
pub struct Gpio {
inner: Arc<GpioState>,
}
impl Gpio {
pub fn new() -> Result<Gpio> {
static mut GPIO_STATE: MaybeUninit<Mutex<Weak<GpioState>>> = MaybeUninit::uninit();
static ONCE: Once = Once::new();
let mut weak_state = unsafe {
ONCE.call_once(|| {
GPIO_STATE.write(Mutex::new(Weak::new()));
});
GPIO_STATE.assume_init_ref().lock().unwrap()
};
if let Some(ref state) = weak_state.upgrade() {
Ok(Gpio {
inner: state.clone(),
})
} else {
let device_info = DeviceInfo::new().map_err(|_| Error::UnknownModel)?;
let gpio_mem: Box<dyn gpiomem::GpioRegisters> = match device_info.gpio_interface() {
system::GpioInterface::Bcm => Box::new(gpiomem::bcm::GpioMem::open()?),
system::GpioInterface::Rp1 => Box::new(gpiomem::rp1::GpioMem::open()?),
};
let cdev = ioctl::find_gpiochip()?;
let sync_interrupts = Mutex::new(interrupt::EventLoop::new(
cdev.as_raw_fd(),
u8::MAX as usize,
)?);
let pins_taken = init_array!(AtomicBool::new(false), u8::MAX as usize);
let gpio_lines = device_info.gpio_lines();
let gpio_state = Arc::new(GpioState {
gpio_mem,
cdev,
sync_interrupts,
pins_taken,
gpio_lines,
});
*weak_state = Arc::downgrade(&gpio_state);
Ok(Gpio { inner: gpio_state })
}
}
pub fn get(&self, pin: u8) -> Result<Pin> {
if pin >= self.inner.gpio_lines {
return Err(Error::PinNotAvailable(pin));
}
if self.inner.pins_taken[pin as usize]
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.is_err()
{
Err(Error::PinUsed(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, Event)>> {
(*self.inner.sync_interrupts.lock().unwrap()).poll(pins, reset, timeout)
}
}