use anyhow::{bail, ensure, Context, Result};
use std::cell::RefCell;
use std::rc::Rc;
use crate::io::eeprom;
use crate::io::eeprom::Transaction::{Read, WaitForBusyClear, Write};
use crate::io::spi::{
AssertChipSelect, ClockPolarity, MaxSizes, SpiError, Target, TargetChipDeassert, Transfer,
TransferMode,
};
use crate::spiflash::flash::SpiFlash;
use crate::transport::dediprog::{ClockSpeed, Command, Inner};
use crate::util::voltage::Voltage;
pub struct DediprogSpi {
inner: Rc<RefCell<Inner>>,
max_chunk_size: usize,
}
#[repr(u8)]
enum ReadMode {
ReadStandard = 1,
ReadFast = 2,
ReadAtmel45 = 3,
Read4bAddrFast = 4,
Read4bAddrFast0x0c = 5,
}
#[repr(u8)]
enum WriteMode {
WritePageProgram = 1,
WritePageWrite = 2,
Write1bAai = 3,
Write2bAai = 4,
Write128bPage = 5,
WritePageAt26df041 = 6,
WriteSiliconBlueFpga = 7,
Write64bPageNumonyx = 8,
Write4bAddr256bPagePgm = 9,
Write32bPageMxic512k = 10,
Write4bAdr256bPagePgm0x12 = 11,
Write4bAdr256bPagePgmFlags = 12,
}
impl DediprogSpi {
const MAX_GENERIC_DATA_LEN: usize = 16;
const READ_CHUNK_SIZE: usize = 512;
const WRITE_CHUNK_SIZE: usize = 256;
pub fn open(inner: Rc<RefCell<Inner>>) -> Result<Self> {
let this = Self {
inner,
max_chunk_size: 65535 * 256,
};
this.set_spi_clock()?;
Ok(this)
}
fn set_spi_clock(&self) -> Result<()> {
self.inner.borrow().device.write_control(
rusb::request_type(
rusb::Direction::Out,
rusb::RequestType::Vendor,
rusb::Recipient::Endpoint,
),
Command::SetSpiClk as u8,
self.inner.borrow().spi_clock as u16,
0,
&[],
)?;
Ok(())
}
fn read_sfdp(&self, mut address: u32, mut rbuf: &mut [u8]) -> Result<()> {
while !rbuf.is_empty() {
let chunk_size = std::cmp::min(rbuf.len(), Self::MAX_GENERIC_DATA_LEN);
let addr_bytes = address.to_be_bytes();
let sub_cmd: [u8; 5] = [
SpiFlash::READ_SFDP,
addr_bytes[1],
addr_bytes[2],
addr_bytes[3],
0,
];
self.transmit(&sub_cmd, chunk_size)?;
self.receive(&mut rbuf[..chunk_size])?;
rbuf = &mut rbuf[chunk_size..];
address += chunk_size as u32;
}
Ok(())
}
fn transmit(&self, wbuf: &[u8], rbuf_len: usize) -> Result<()> {
self.inner.borrow().device.write_control(
rusb::request_type(
rusb::Direction::Out,
rusb::RequestType::Vendor,
rusb::Recipient::Endpoint,
),
Command::Transceive as u8,
rbuf_len as u16,
0,
wbuf,
)?;
Ok(())
}
fn receive(&self, rbuf: &mut [u8]) -> Result<()> {
self.inner.borrow().device.read_control(
rusb::request_type(
rusb::Direction::In,
rusb::RequestType::Vendor,
rusb::Recipient::Endpoint,
),
Command::Transceive as u8,
0,
0,
rbuf,
)?;
Ok(())
}
fn eeprom_read_transaction(&self, cmd: &eeprom::Cmd, rbuf: &mut [u8]) -> Result<()> {
let mut usbcmd = [0u8; 10];
match (
cmd.get_opcode(),
cmd.get_address_len(),
cmd.get_dummy_cycles(),
) {
([SpiFlash::READ_SFDP], 3, 8) => {
return self.read_sfdp(cmd.get_address(), rbuf);
}
([SpiFlash::READ], 3, 0) => {
usbcmd[3] = ReadMode::ReadStandard as u8;
usbcmd[4] = SpiFlash::READ;
}
([SpiFlash::READ], 4, 0) => {
usbcmd[3] = ReadMode::Read4bAddrFast as u8;
usbcmd[4] = SpiFlash::FAST_READ;
}
_ => bail!(SpiError::InvalidTransferMode(
"Command not supported".to_string()
)),
}
ensure!(
rbuf.len() % Self::READ_CHUNK_SIZE == 0,
SpiError::InvalidTransferMode(format!(
"Read length {} not multiple of {}",
rbuf.len(),
Self::READ_CHUNK_SIZE
))
);
let chunks = (rbuf.len() / Self::READ_CHUNK_SIZE) as u16;
usbcmd[0..2].clone_from_slice(&chunks.to_le_bytes());
usbcmd[6..10].clone_from_slice(&cmd.get_address().to_le_bytes());
self.inner.borrow().device.write_control(
rusb::request_type(
rusb::Direction::Out,
rusb::RequestType::Vendor,
rusb::Recipient::Endpoint,
),
Command::Read as u8,
0,
0,
&usbcmd,
)?;
for chunk in rbuf.chunks_exact_mut(Self::READ_CHUNK_SIZE) {
self.inner
.borrow()
.device
.read_bulk(self.inner.borrow().in_endpoint, chunk)?;
}
Ok(())
}
fn eeprom_write_transaction(
&self,
cmd: &eeprom::Cmd,
wbuf: &[u8],
_wait_for_busy_clear: bool,
) -> Result<()> {
ensure!(
wbuf.len() % Self::WRITE_CHUNK_SIZE == 0,
SpiError::InvalidTransferMode(format!(
"Write length {} not multiple of {}",
wbuf.len(),
Self::WRITE_CHUNK_SIZE
))
);
let chunks = (wbuf.len() / Self::WRITE_CHUNK_SIZE) as u16;
let mut usbcmd = [0u8; 10];
usbcmd[0..2].clone_from_slice(&chunks.to_le_bytes());
match (
cmd.get_opcode(),
cmd.get_address_len(),
cmd.get_dummy_cycles(),
) {
([SpiFlash::PAGE_PROGRAM], 3, 0) => {
usbcmd[3] = WriteMode::WritePageProgram as u8;
usbcmd[4] = SpiFlash::PAGE_PROGRAM;
}
([SpiFlash::PAGE_PROGRAM], 4, 0) => {
usbcmd[3] = WriteMode::Write4bAddr256bPagePgm as u8;
usbcmd[4] = SpiFlash::PAGE_PROGRAM;
}
_ => bail!(SpiError::InvalidTransferMode(
"Command not supported".to_string()
)),
}
usbcmd[6..10].clone_from_slice(&cmd.get_address().to_le_bytes());
self.inner.borrow().device.write_control(
rusb::request_type(
rusb::Direction::Out,
rusb::RequestType::Vendor,
rusb::Recipient::Endpoint,
),
Command::Write as u8,
0,
0,
&usbcmd,
)?;
for chunk in wbuf.chunks_exact(Self::WRITE_CHUNK_SIZE) {
let mut buf = [0xFFu8; 512];
buf[0..Self::WRITE_CHUNK_SIZE].clone_from_slice(chunk);
self.inner
.borrow()
.device
.write_bulk(self.inner.borrow().out_endpoint, &buf)?;
}
Ok(())
}
fn do_run_eeprom_transactions(
&self,
mut transactions: &mut [eeprom::Transaction],
) -> Result<()> {
loop {
match transactions {
[eeprom::Transaction::Command(pre_cmd), Write(cmd, wbuf), WaitForBusyClear, rest @ ..] =>
{
transactions = rest;
if pre_cmd.get_opcode() == [SpiFlash::WRITE_ENABLE] {
} else {
self.run_transaction(&mut [Transfer::Write(cmd.to_bytes()?)])?
}
self.eeprom_write_transaction(cmd, wbuf, true)?;
}
[eeprom::Transaction::Command(cmd), rest @ ..] => {
transactions = rest;
self.run_transaction(&mut [Transfer::Write(cmd.to_bytes()?)])?
}
[Read(cmd, ref mut rbuf), rest @ ..] => {
transactions = rest;
self.eeprom_read_transaction(cmd, rbuf)?;
}
[Write(cmd, wbuf), WaitForBusyClear, rest @ ..] => {
transactions = rest;
self.eeprom_write_transaction(cmd, wbuf, true)?;
}
[Write(cmd, wbuf), rest @ ..] => {
transactions = rest;
self.eeprom_write_transaction(cmd, wbuf, false)?;
}
[WaitForBusyClear, rest @ ..] => {
transactions = rest;
let mut status = eeprom::STATUS_WIP;
while status & eeprom::STATUS_WIP != 0 {
self.run_transaction(&mut [
Transfer::Write(&[eeprom::READ_STATUS]),
Transfer::Read(std::slice::from_mut(&mut status)),
])?;
}
}
[] => return Ok(()),
}
}
}
}
impl Target for DediprogSpi {
fn get_transfer_mode(&self) -> Result<TransferMode> {
unimplemented!();
}
fn set_transfer_mode(&self, _mode: TransferMode) -> Result<()> {
unimplemented!();
}
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(match self.inner.borrow().spi_clock {
ClockSpeed::Clk24Mhz => 24_000_000,
ClockSpeed::Clk12Mhz => 12_000_000,
ClockSpeed::Clk8Mhz => 8_000_000,
ClockSpeed::Clk3Mhz => 3_000_000,
ClockSpeed::Clk2p18Mhz => 2_180_000,
ClockSpeed::Clk1p5Mhz => 1_500_000,
ClockSpeed::Clk750Khz => 750_000,
ClockSpeed::Clk375Khz => 375_000,
})
}
fn set_max_speed(&self, frequency: u32) -> Result<()> {
self.inner.borrow_mut().spi_clock = if frequency >= 24_000_000 {
ClockSpeed::Clk24Mhz
} else if frequency >= 12_000_000 {
ClockSpeed::Clk12Mhz
} else if frequency >= 8_000_000 {
ClockSpeed::Clk8Mhz
} else if frequency >= 3_000_000 {
ClockSpeed::Clk3Mhz
} else if frequency >= 2_180_000 {
ClockSpeed::Clk2p18Mhz
} else if frequency >= 1_500_000 {
ClockSpeed::Clk1p5Mhz
} else if frequency >= 750_000 {
ClockSpeed::Clk750Khz
} else {
ClockSpeed::Clk375Khz
};
self.set_spi_clock()
}
fn supports_bidirectional_transfer(&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: Self::MAX_GENERIC_DATA_LEN,
write: Self::MAX_GENERIC_DATA_LEN,
})
}
fn set_voltage(&self, voltage: Voltage) -> Result<()> {
let mut inner = self.inner.borrow_mut();
inner.voltage = if voltage.as_volts() <= 0.3 {
super::Voltage::V0
} else if voltage.as_volts() >= 1.6 && voltage.as_volts() <= 2.0 {
super::Voltage::V1p8
} else if voltage.as_volts() >= 2.3 && voltage.as_volts() <= 2.7 {
super::Voltage::V2p5
} else if voltage.as_volts() >= 3.0 && voltage.as_volts() <= 3.6 {
super::Voltage::V3p5
} else {
bail!(SpiError::InvalidVoltage(voltage))
};
inner.set_voltage()
}
fn run_transaction(&self, transaction: &mut [Transfer]) -> Result<()> {
match transaction {
[] => (),
[Transfer::Write(wbuf), Transfer::Read(rbuf)] => {
ensure!(
wbuf.len() <= Self::MAX_GENERIC_DATA_LEN,
SpiError::InvalidDataLength(wbuf.len())
);
ensure!(
rbuf.len() <= Self::MAX_GENERIC_DATA_LEN,
SpiError::InvalidDataLength(rbuf.len())
);
self.transmit(wbuf, rbuf.len())?;
self.receive(rbuf)?;
}
[Transfer::Write(wbuf)] => {
ensure!(
wbuf.len() <= Self::MAX_GENERIC_DATA_LEN,
SpiError::InvalidDataLength(wbuf.len())
);
self.transmit(wbuf, 0)?;
}
_ => bail!(SpiError::InvalidTransferMode(
"Unsupported combination".to_string()
)),
}
Ok(())
}
fn get_eeprom_max_transfer_sizes(&self) -> Result<MaxSizes> {
Ok(MaxSizes {
read: self.max_chunk_size,
write: self.max_chunk_size,
})
}
fn run_eeprom_transactions(&self, transactions: &mut [eeprom::Transaction]) -> Result<()> {
self.do_run_eeprom_transactions(transactions)
}
fn assert_cs(self: Rc<Self>) -> Result<AssertChipSelect> {
unimplemented!();
}
}