use anyhow::Result;
use clap::Args;
use serde::{Deserialize, Serialize};
use std::rc::Rc;
use std::str::FromStr;
use thiserror::Error;
use super::{eeprom, gpio};
use crate::app::TransportWrapper;
use crate::impl_serializable_error;
use crate::util::voltage::Voltage;
#[derive(Clone, Debug, Args, Serialize, Deserialize)]
pub struct SpiParams {
#[arg(long)]
pub bus: Option<String>,
#[arg(long)]
pub speed: Option<u32>,
#[arg(long)]
pub chip_select: Option<String>,
#[arg(long)]
pub voltage: Option<Voltage>,
#[arg(long)]
pub mode: Option<TransferMode>,
}
impl SpiParams {
pub fn create(
&self,
transport: &TransportWrapper,
default_instance: &str,
) -> Result<Rc<dyn Target>> {
let spi = transport.spi(self.bus.as_deref().unwrap_or(default_instance))?;
if let Some(ref cs) = self.chip_select {
spi.set_pins(None, None, None, Some(&transport.gpio_pin(cs.as_str())?))?;
}
if let Some(speed) = self.speed {
spi.set_max_speed(speed)?;
}
if let Some(voltage) = self.voltage {
spi.set_voltage(voltage)?;
}
if let Some(mode) = self.mode {
spi.set_transfer_mode(mode)?;
}
Ok(spi)
}
}
#[derive(Error, Debug, Serialize, Deserialize)]
pub enum SpiError {
#[error("Invalid option: {0}")]
InvalidOption(String),
#[error("Invalid word size: {0}")]
InvalidWordSize(u32),
#[error("Invalid speed: {0}")]
InvalidSpeed(u32),
#[error("Invalid data length: {0}")]
InvalidDataLength(usize),
#[error("Invalid data width: {0:?}")]
InvalidDataWidth(eeprom::DataWidth),
#[error("Double transfer rate not supported")]
InvalidDoubleTransferRate(),
#[error("Invalid number of dummy cycles: {0}")]
InvalidDummyCycles(u8),
#[error("Mismatched data length: {0} != {1}")]
MismatchedDataLength(usize, usize),
#[error("Invalid transfer mode: {0}")]
InvalidTransferMode(String),
#[error("Invalid SPI voltage: {0}")]
InvalidVoltage(Voltage),
#[error("Given pin does not support requested SPI function")]
InvalidPin,
}
impl_serializable_error!(SpiError);
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
pub enum TransferMode {
Mode0,
Mode1,
Mode2,
Mode3,
}
impl FromStr for TransferMode {
type Err = SpiError;
fn from_str(s: &str) -> std::result::Result<TransferMode, Self::Err> {
match s {
"Mode0" | "mode0" | "0" => Ok(TransferMode::Mode0),
"Mode1" | "mode1" | "1" => Ok(TransferMode::Mode1),
"Mode2" | "mode2" | "2" => Ok(TransferMode::Mode2),
"Mode3" | "mode3" | "3" => Ok(TransferMode::Mode3),
_ => Err(SpiError::InvalidTransferMode(s.to_string())),
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ClockPhase {
SampleLeading,
SampleTrailing,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ClockPolarity {
IdleLow,
IdleHigh,
}
impl TransferMode {
pub fn phase(&self) -> ClockPhase {
match self {
TransferMode::Mode0 | TransferMode::Mode2 => ClockPhase::SampleLeading,
TransferMode::Mode1 | TransferMode::Mode3 => ClockPhase::SampleTrailing,
}
}
pub fn polarity(&self) -> ClockPolarity {
match self {
TransferMode::Mode0 | TransferMode::Mode1 => ClockPolarity::IdleLow,
TransferMode::Mode2 | TransferMode::Mode3 => ClockPolarity::IdleHigh,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct MaxSizes {
pub read: usize,
pub write: usize,
}
pub enum Transfer<'rd, 'wr> {
Read(&'rd mut [u8]),
Write(&'wr [u8]),
Both(&'wr [u8], &'rd mut [u8]),
}
pub trait Target {
fn get_transfer_mode(&self) -> Result<TransferMode>;
fn set_transfer_mode(&self, mode: TransferMode) -> Result<()>;
fn get_bits_per_word(&self) -> Result<u32>;
fn set_bits_per_word(&self, bits_per_word: u32) -> Result<()>;
fn get_max_speed(&self) -> Result<u32>;
fn set_max_speed(&self, max_speed: u32) -> Result<()>;
fn supports_bidirectional_transfer(&self) -> Result<bool>;
fn set_pins(
&self,
_serial_clock: Option<&Rc<dyn gpio::GpioPin>>,
_host_out_device_in: Option<&Rc<dyn gpio::GpioPin>>,
_host_in_device_out: Option<&Rc<dyn gpio::GpioPin>>,
_chip_select: Option<&Rc<dyn gpio::GpioPin>>,
) -> Result<()> {
Err(SpiError::InvalidPin.into())
}
fn get_max_transfer_count(&self) -> Result<usize>;
fn get_max_transfer_sizes(&self) -> Result<MaxSizes>;
fn set_voltage(&self, _voltage: Voltage) -> Result<()> {
Err(SpiError::InvalidOption("This target does not support set_voltage".to_string()).into())
}
fn run_transaction(&self, transaction: &mut [Transfer]) -> Result<()>;
fn get_eeprom_max_transfer_sizes(&self) -> Result<MaxSizes> {
let spi_max = self.get_max_transfer_sizes()?;
Ok(MaxSizes {
read: spi_max.read,
write: spi_max.write - 6,
})
}
fn run_eeprom_transactions(&self, transactions: &mut [eeprom::Transaction]) -> Result<()> {
eeprom::default_run_eeprom_transactions(self, transactions)
}
fn assert_cs(self: Rc<Self>) -> Result<AssertChipSelect>;
}
pub struct AssertChipSelect {
target: Rc<dyn TargetChipDeassert>,
}
impl AssertChipSelect {
pub fn new(target: Rc<dyn TargetChipDeassert>) -> Self {
Self { target }
}
}
impl Drop for AssertChipSelect {
fn drop(&mut self) {
self.target.deassert_cs()
}
}
pub trait TargetChipDeassert {
fn deassert_cs(&self);
}