use anyhow::{ensure, Context, Result};
use once_cell::sync::Lazy;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use crate::collection;
use crate::io::gpio::{GpioError, GpioPin, PinMode, PullMode};
use crate::transport::ultradebug::mpsse;
use crate::transport::ultradebug::Ultradebug;
use crate::util::parse_int::ParseInt;
pub struct UltradebugGpio {
pub device: Rc<RefCell<mpsse::Context>>,
}
impl UltradebugGpio {
pub const PIN_SPI_ZB: u8 = 4;
pub const PIN_RESET_B: u8 = 5;
pub const PIN_BOOTSTRAP: u8 = 6;
pub const PIN_TGT_RESET: u8 = 7;
const LAST_PIN_NUM: u8 = 7;
pub fn open(ultradebug: &Ultradebug) -> Result<Self> {
Ok(UltradebugGpio {
device: ultradebug.mpsse(ftdi::Interface::B)?,
})
}
pub fn pin(&self, pinname: &str) -> Result<UltradebugGpioPin> {
Ok(UltradebugGpioPin {
device: self.device.clone(),
pin_id: self.pin_name_to_number(pinname)?,
})
}
pub fn pin_name_to_number(&self, pinname: &str) -> Result<u8> {
if let Ok(pinnum) = u8::from_str(pinname) {
ensure!(
pinnum <= UltradebugGpio::LAST_PIN_NUM,
GpioError::InvalidPinNumber(pinnum)
);
return Ok(pinnum);
}
let pinname = pinname.to_uppercase();
let pn = pinname.as_str();
PIN_NAMES
.get(pn)
.copied()
.ok_or_else(|| GpioError::InvalidPinName(pinname).into())
}
}
pub struct UltradebugGpioPin {
device: Rc<RefCell<mpsse::Context>>,
pin_id: u8,
}
impl GpioPin for UltradebugGpioPin {
fn read(&self) -> Result<bool> {
let bits = self.device.borrow_mut().gpio_get().context("FTDI error")?;
Ok(bits & (1 << self.pin_id) != 0)
}
fn write(&self, value: bool) -> Result<()> {
self.device
.borrow_mut()
.gpio_set(self.pin_id, value)
.context("FTDI error")?;
Ok(())
}
fn set_mode(&self, mode: PinMode) -> Result<()> {
let direction = match mode {
PinMode::Input => false,
PinMode::PushPull => true,
_ => return Err(GpioError::UnsupportedPinMode(mode).into()),
};
self.device
.borrow_mut()
.gpio_set_direction(self.pin_id, direction)
.context("FTDI error")?;
Ok(())
}
fn set_pull_mode(&self, mode: PullMode) -> Result<()> {
Err(GpioError::UnsupportedPullMode(mode).into())
}
}
static PIN_NAMES: Lazy<HashMap<&'static str, u8>> = Lazy::new(|| {
collection! {
"SPI_ZB" => 4,
"RESET_B" => 5,
"BOOTSTRAP" => 6,
"TGT_RESET" => 7,
}
});