blob: 40db0a5f24008b529ab3c515807aba8040f4e6c0 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
use anyhow::{bail, ensure, Context, Result};
use safe_ftdi as ftdi;
use std::cell::RefCell;
use std::rc::Rc;
use crate::io::gpio::GpioPin;
use crate::io::spi::Target;
use crate::transport::{
Capabilities, Capability, Transport, TransportError, TransportInterfaceType,
};
use crate::transport::ultradebug::mpsse;
pub mod gpio;
pub mod spi;
#[derive(Default)]
pub struct Nexus {
pub usb_vid: Option<u16>,
pub usb_pid: Option<u16>,
pub usb_serial: Option<String>,
mpsse_a: RefCell<Option<Rc<RefCell<mpsse::Context>>>>,
inner: RefCell<Inner>,
}
#[derive(Default)]
struct Inner {
gpio: Option<Rc<gpio::NexusGpio>>,
spi: Option<Rc<dyn Target>>,
}
impl Nexus {
/// Create a new `Nexus` struct, optionally specifying the USB vid/pid/serial number.
pub fn new(usb_vid: Option<u16>, usb_pid: Option<u16>, usb_serial: Option<String>) -> Self {
Nexus {
usb_vid,
usb_pid,
usb_serial,
..Default::default()
}
}
/// Construct an `ftdi::Device` for the specified `interface` on the Nexus device.
pub fn from_interface(&self, interface: ftdi::Interface) -> Result<ftdi::Device> {
Ok(ftdi::Device::from_description_serial(
interface,
self.usb_vid.unwrap_or(0x0403),
self.usb_pid.unwrap_or(0x6011),
None,
self.usb_serial.clone(),
)
.context("FTDI error")?)
}
fn mpsse_interface_a(&self) -> Result<Rc<RefCell<mpsse::Context>>> {
let mut mpsse_a = self.mpsse_a.borrow_mut();
if mpsse_a.is_none() {
let device = self.from_interface(ftdi::Interface::A)?;
device.set_timeouts(5000, 5000);
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_a = Some(Rc::new(RefCell::new(mpdev)));
}
Ok(Rc::clone(mpsse_a.as_ref().unwrap()))
}
/// Construct an `mpsse::Context` for the requested interface.
pub fn mpsse(&self, interface: ftdi::Interface) -> Result<Rc<RefCell<mpsse::Context>>> {
match interface {
ftdi::Interface::A => self.mpsse_interface_a(),
_ => {
bail!(TransportError::UsbOpenError(format!(
"I don't know how to create an MPSSE context for interface {:?}",
interface
)));
}
}
}
}
impl Transport for Nexus {
fn capabilities(&self) -> Result<Capabilities> {
Ok(Capabilities::new(
Capability::GPIO | Capability::SPI,
))
}
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::NexusGpio::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::NexusSpi::open(self)?));
}
Ok(Rc::clone(inner.spi.as_ref().unwrap()))
}
}