use anyhow::{bail, Context, Result};
use std::cell::RefCell;
use std::rc::Rc;
use crate::io::spi::{
AssertChipSelect, ClockPolarity, MaxSizes, SpiError, Target, TargetChipDeassert, Transfer,
TransferMode,
};
use crate::transport::ultradebug::mpsse;
use crate::transport::ultradebug::Ultradebug;
use crate::transport::TransportError;
struct Inner {
mode: TransferMode,
cs_asserted_count: u32,
}
pub struct UltradebugSpi {
pub device: Rc<RefCell<mpsse::Context>>,
inner: RefCell<Inner>,
}
impl UltradebugSpi {
pub const PIN_CLOCK: u8 = 0;
pub const PIN_MOSI: u8 = 1;
pub const PIN_MISO: u8 = 2;
pub const PIN_CHIP_SELECT: u8 = 3;
pub const MASK_CHIP_SELECT: u8 = 1u8 << Self::PIN_CHIP_SELECT;
pub const PIN_SPI_ZB: u8 = 4;
pub fn open(ultradebug: &Ultradebug) -> Result<Self> {
let mpsse = ultradebug.mpsse(ftdi::Interface::B)?;
log::debug!("Setting SPI_ZB");
mpsse
.borrow_mut()
.gpio_set(UltradebugSpi::PIN_SPI_ZB, false)
.context("FTDI error")?;
Ok(UltradebugSpi {
device: mpsse,
inner: RefCell::new(Inner {
mode: TransferMode::Mode0,
cs_asserted_count: 0,
}),
})
}
fn do_assert_cs(&self, assert: bool) -> Result<()> {
let mut device = self.device.borrow_mut();
let mut command = [mpsse::Command::SetLowGpio(
device.gpio_direction,
if assert {
device.gpio_value & !Self::MASK_CHIP_SELECT
} else {
device.gpio_value | Self::MASK_CHIP_SELECT
},
)];
device.execute(&mut command).context("FTDI error")?;
Ok(())
}
}
impl Target for UltradebugSpi {
fn get_transfer_mode(&self) -> Result<TransferMode> {
Ok(self.inner.borrow().mode)
}
fn set_transfer_mode(&self, mode: TransferMode) -> Result<()> {
self.inner.borrow_mut().mode = mode;
Ok(())
}
fn get_bits_per_word(&self) -> Result<u32> {
Ok(8)
}
fn set_bits_per_word(&self, bits_per_word: u32) -> Result<()> {
match bits_per_word {
8 => Ok(()),
_ => Err(SpiError::InvalidWordSize(bits_per_word).into()),
}
}
fn get_max_speed(&self) -> Result<u32> {
Ok(self.device.borrow().max_clock_frequency)
}
fn set_max_speed(&self, frequency: u32) -> Result<()> {
let mut device = self.device.borrow_mut();
device
.set_clock_frequency(frequency)
.context("FTDI error")?;
Ok(())
}
fn supports_bidirectional_transfer(&self) -> Result<bool> {
Ok(true)
}
fn supports_tpm_poll(&self) -> Result<bool> {
Ok(false)
}
fn get_max_transfer_count(&self) -> Result<usize> {
Ok(42)
}
fn get_max_transfer_sizes(&self) -> Result<MaxSizes> {
Ok(MaxSizes {
read: 65536,
write: 65536,
})
}
fn run_transaction(&self, transaction: &mut [Transfer]) -> Result<()> {
let (rdedge, wredge) = match self.inner.borrow().mode.polarity() {
ClockPolarity::IdleLow => (mpsse::ClockEdge::Rising, mpsse::ClockEdge::Falling),
ClockPolarity::IdleHigh => (mpsse::ClockEdge::Falling, mpsse::ClockEdge::Rising),
};
let mut command = Vec::new();
let mut device = self.device.borrow_mut();
let cs_not_already_asserted = self.inner.borrow().cs_asserted_count == 0;
if cs_not_already_asserted {
command.push(mpsse::Command::SetLowGpio(
device.gpio_direction,
device.gpio_value & !Self::MASK_CHIP_SELECT,
));
}
for transfer in transaction.iter_mut() {
command.push(match transfer {
Transfer::Read(buf) => mpsse::Command::ReadData(
mpsse::DataShiftOptions {
read_clock_edge: rdedge,
read_data: true,
..Default::default()
},
buf,
),
Transfer::Write(buf) => mpsse::Command::WriteData(
mpsse::DataShiftOptions {
write_clock_edge: wredge,
write_data: true,
..Default::default()
},
buf,
),
Transfer::Both(wbuf, rbuf) => mpsse::Command::TransactData(
mpsse::DataShiftOptions {
write_clock_edge: wredge,
write_data: true,
..Default::default()
},
wbuf,
mpsse::DataShiftOptions {
read_clock_edge: rdedge,
read_data: true,
..Default::default()
},
rbuf,
),
_ => bail!(TransportError::UnsupportedOperation),
});
}
if cs_not_already_asserted {
command.push(mpsse::Command::SetLowGpio(
device.gpio_direction,
device.gpio_value | Self::MASK_CHIP_SELECT,
));
}
device.execute(&mut command).context("FTDI error")?;
Ok(())
}
fn assert_cs(self: Rc<Self>) -> Result<AssertChipSelect> {
{
let mut inner = self.inner.borrow_mut();
if inner.cs_asserted_count == 0 {
self.do_assert_cs(true)?;
}
inner.cs_asserted_count += 1;
}
Ok(AssertChipSelect::new(self))
}
}
impl TargetChipDeassert for UltradebugSpi {
fn deassert_cs(&self) {
let mut inner = self.inner.borrow_mut();
inner.cs_asserted_count -= 1;
if inner.cs_asserted_count == 0 {
self.do_assert_cs(false)
.expect("Error while deasserting CS");
}
}
}