use std::cell::RefCell;
use std::rc::Rc;
use anyhow::{bail, ensure, Context, Result};
use crate::io::gpio::GpioPin;
use crate::io::spi::Target;
use crate::io::uart::Uart;
use crate::transport::{
Capabilities, Capability, Transport, TransportError, TransportInterfaceType,
};
pub mod gpio;
pub mod mpsse;
pub mod spi;
pub mod uart;
#[derive(Default)]
pub struct Ultradebug {
pub usb_vid: Option<u16>,
pub usb_pid: Option<u16>,
pub usb_serial: Option<String>,
mpsse_b: RefCell<Option<Rc<RefCell<mpsse::Context>>>>,
inner: RefCell<Inner>,
}
#[derive(Default)]
struct Inner {
gpio: Option<Rc<gpio::UltradebugGpio>>,
spi: Option<Rc<dyn Target>>,
uart: Option<Rc<dyn Uart>>,
}
impl Ultradebug {
pub const VID_GOOGLE: u16 = 0x18d1;
pub const PID_ULTRADEBUG: u16 = 0x0304;
pub fn new(usb_vid: Option<u16>, usb_pid: Option<u16>, usb_serial: Option<String>) -> Self {
Ultradebug {
usb_vid,
usb_pid,
usb_serial,
..Default::default()
}
}
pub fn from_interface(&self, interface: ftdi::Interface) -> Result<ftdi::Device> {
let vid = self.usb_vid.unwrap_or(Ultradebug::VID_GOOGLE);
let pid = self.usb_pid.unwrap_or(Ultradebug::PID_ULTRADEBUG);
let mut opener = ftdi::find_by_vid_pid(vid, pid).interface(interface);
if let Some(serial) = &self.usb_serial {
opener = opener.serial(serial);
}
opener.open().context("FTDI error")
}
fn mpsse_interface_b(&self) -> Result<Rc<RefCell<mpsse::Context>>> {
let mut mpsse_b = self.mpsse_b.borrow_mut();
if mpsse_b.is_none() {
let device = self.from_interface(ftdi::Interface::B)?;
let mut mpdev = mpsse::Context::new(device).context("FTDI error")?;
mpdev.gpio_direction.insert(
mpsse::GpioDirection::OUT_0 | mpsse::GpioDirection::OUT_1 | mpsse::GpioDirection::OUT_3 | mpsse::GpioDirection::OUT_4 | mpsse::GpioDirection::OUT_5 | mpsse::GpioDirection::OUT_6 | mpsse::GpioDirection::OUT_7, );
let _ = mpdev.gpio_get().context("FTDI error")?;
mpdev.gpio_value &= 0xF8;
*mpsse_b = Some(Rc::new(RefCell::new(mpdev)));
}
Ok(Rc::clone(mpsse_b.as_ref().unwrap()))
}
pub fn mpsse(&self, interface: ftdi::Interface) -> Result<Rc<RefCell<mpsse::Context>>> {
match interface {
ftdi::Interface::B => self.mpsse_interface_b(),
_ => {
bail!(TransportError::UsbOpenError(format!(
"I don't know how to create an MPSSE context for interface {:?}",
interface
)));
}
}
}
}
impl Transport for Ultradebug {
fn capabilities(&self) -> Result<Capabilities> {
Ok(Capabilities::new(
Capability::UART | Capability::GPIO | Capability::SPI,
))
}
fn uart(&self, instance: &str) -> Result<Rc<dyn Uart>> {
ensure!(
instance == "0",
TransportError::InvalidInstance(TransportInterfaceType::Uart, instance.to_string())
);
let mut inner = self.inner.borrow_mut();
if inner.uart.is_none() {
inner.uart = Some(Rc::new(uart::UltradebugUart::open(self)?));
}
Ok(Rc::clone(inner.uart.as_ref().unwrap()))
}
fn gpio_pin(&self, instance: &str) -> Result<Rc<dyn GpioPin>> {
let mut inner = self.inner.borrow_mut();
if inner.gpio.is_none() {
inner.gpio = Some(Rc::new(gpio::UltradebugGpio::open(self)?));
}
Ok(Rc::new(inner.gpio.as_ref().unwrap().pin(instance)?))
}
fn spi(&self, instance: &str) -> Result<Rc<dyn Target>> {
ensure!(
instance == "0",
TransportError::InvalidInstance(TransportInterfaceType::Spi, instance.to_string())
);
let mut inner = self.inner.borrow_mut();
if inner.spi.is_none() {
inner.spi = Some(Rc::new(spi::UltradebugSpi::open(self)?));
}
Ok(Rc::clone(inner.spi.as_ref().unwrap()))
}
}