use std::collections::BTreeMap;
use gpio_cdev::{Chip, LineHandle, LineRequestFlags};
use crate::Level;
const CONSUMER: &str = "ados-gpio";
struct DrivenLine {
handle: LineHandle,
level: Level,
}
#[derive(Default)]
pub struct GpioOutput {
chips: BTreeMap<u32, Chip>,
lines: BTreeMap<(u32, u32), DrivenLine>,
}
impl GpioOutput {
pub fn new() -> Self {
Self::default()
}
pub fn set(&mut self, chip: u32, pin: u32, level: Level) -> anyhow::Result<()> {
let key = (chip, pin);
if !self.lines.contains_key(&key) {
let handle = self.request_line(chip, pin)?;
self.lines.insert(
key,
DrivenLine {
handle,
level: Level::Low,
},
);
}
let line = self.lines.get_mut(&key).expect("just inserted");
line.handle.set_value(level.value())?;
line.level = level;
Ok(())
}
fn request_line(&mut self, chip: u32, pin: u32) -> anyhow::Result<LineHandle> {
let dev = format!("/dev/gpiochip{chip}");
let chip_ref = match self.chips.get_mut(&chip) {
Some(c) => c,
None => {
let opened = Chip::new(&dev).map_err(|e| anyhow::anyhow!("open {dev}: {e}"))?;
self.chips.entry(chip).or_insert(opened)
}
};
let line = chip_ref
.get_line(pin)
.map_err(|e| anyhow::anyhow!("get line {pin} on {dev}: {e}"))?;
let handle = line
.request(LineRequestFlags::OUTPUT, Level::Low.value(), CONSUMER)
.map_err(|e| anyhow::anyhow!("request output {pin} on {dev}: {e}"))?;
Ok(handle)
}
pub fn level_of(&self, chip: u32, pin: u32) -> Option<Level> {
self.lines.get(&(chip, pin)).map(|l| l.level)
}
pub fn snapshot(&self) -> Vec<(u32, u32, Level)> {
self.lines
.iter()
.map(|((chip, pin), l)| (*chip, *pin, l.level))
.collect()
}
pub fn all_low(&mut self) {
for ((chip, pin), line) in self.lines.iter_mut() {
if let Err(e) = line.handle.set_value(Level::Low.value()) {
tracing::debug!(chip, pin, error = %e, "failed to drive line low on shutdown");
} else {
line.level = Level::Low;
}
}
}
}