use std::time::{Duration, Instant};
use crate::domain::chip::ChipSpec;
use crate::domain::{EraseRequest, FlashOperation, Progress, ReadRequest, WriteRequest};
use crate::error::{Error, Result};
use crate::infrastructure::flash_protocol::commands::*;
use crate::infrastructure::programmer::Programmer;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddressMode {
OneByte,
TwoByte,
ThreeByte,
}
impl AddressMode {
pub fn from_capacity(capacity_bytes: u32) -> Self {
if capacity_bytes <= 512 {
AddressMode::OneByte
} else if capacity_bytes <= 65536 {
AddressMode::TwoByte
} else {
AddressMode::ThreeByte
}
}
pub fn address_bytes(&self) -> usize {
match self {
AddressMode::OneByte => 1,
AddressMode::TwoByte => 2,
AddressMode::ThreeByte => 3,
}
}
}
pub struct SpiEeprom<P: Programmer> {
programmer: P,
spec: ChipSpec,
address_mode: AddressMode,
}
impl<P: Programmer> SpiEeprom<P> {
pub fn new(programmer: P, spec: ChipSpec) -> Self {
let address_mode = AddressMode::from_capacity(spec.capacity.as_bytes());
Self {
programmer,
spec,
address_mode,
}
}
pub fn spec(&self) -> &ChipSpec {
&self.spec
}
pub fn address_mode(&self) -> AddressMode {
self.address_mode
}
fn wait_ready(&mut self) -> Result<()> {
let start = Instant::now();
let timeout = Duration::from_millis(100);
loop {
let status = self.read_status()?;
if status & STATUS_EEPROM_WIP == 0 {
return Ok(());
}
if start.elapsed() > timeout {
return Err(Error::Timeout);
}
std::thread::sleep(Duration::from_micros(50));
}
}
fn read_status(&mut self) -> Result<u8> {
self.programmer.set_cs(true)?;
self.programmer.spi_write(&[CMD_EEPROM_RDSR])?;
let data = self.programmer.spi_read(1)?;
self.programmer.set_cs(false)?;
Ok(data[0])
}
fn write_enable(&mut self) -> Result<()> {
self.programmer.set_cs(true)?;
self.programmer.spi_write(&[CMD_EEPROM_WREN])?;
self.programmer.set_cs(false)?;
Ok(())
}
fn addr_to_bytes(&self, addr: u32) -> (u8, Vec<u8>) {
match self.address_mode {
AddressMode::OneByte => {
let cmd_mask = if addr >= 256 { 0x08 } else { 0x00 };
(cmd_mask, vec![addr as u8])
}
AddressMode::TwoByte => (0x00, vec![(addr >> 8) as u8, addr as u8]),
AddressMode::ThreeByte => (
0x00,
vec![(addr >> 16) as u8, (addr >> 8) as u8, addr as u8],
),
}
}
fn page_size(&self) -> usize {
self.spec.layout.page_size as usize
}
}
impl<P: Programmer> FlashOperation for SpiEeprom<P> {
fn read(&mut self, request: ReadRequest, on_progress: &dyn Fn(Progress)) -> Result<Vec<u8>> {
let address = request.address.as_u32();
let length = request.length as usize;
let chunk_size = self.programmer.max_bulk_transfer_size().min(4096);
let mut result = Vec::with_capacity(length);
let mut remaining = length;
let mut current_offset = 0u32;
while remaining > 0 {
let read_size = remaining.min(chunk_size);
let current_addr = address + current_offset;
let (cmd_mask, addr_bytes) = self.addr_to_bytes(current_addr);
let mut cmd = vec![CMD_EEPROM_READ | cmd_mask];
cmd.extend(addr_bytes);
let mut attempts = 0;
let chunk = loop {
match self.programmer.spi_transaction(&cmd, read_size) {
Ok(data) => break data,
Err(e) => {
if attempts < request.retry_count {
attempts += 1;
log::warn!(
"Read error at 0x{:08X}, retrying (attempt {}): {}",
current_addr,
attempts,
e
);
continue;
} else {
return Err(e);
}
}
}
};
result.extend_from_slice(&chunk);
remaining -= read_size;
current_offset += read_size as u32;
on_progress(Progress::new(result.len() as u64, length as u64));
}
Ok(result)
}
fn write(&mut self, request: WriteRequest, on_progress: &dyn Fn(Progress)) -> Result<()> {
let data = request.data;
let page_size = self.page_size();
let mut offset = 0usize;
let mut current_addr = request.address.as_u32();
while offset < data.len() {
let page_offset = (current_addr as usize) % page_size;
let bytes_in_page = page_size - page_offset;
let bytes_to_write = bytes_in_page.min(data.len() - offset);
self.write_enable()?;
let (cmd_mask, addr_bytes) = self.addr_to_bytes(current_addr);
let mut cmd = vec![CMD_EEPROM_WRITE | cmd_mask];
cmd.extend(addr_bytes);
cmd.extend_from_slice(&data[offset..offset + bytes_to_write]);
self.programmer.spi_transaction_write(&cmd)?;
self.wait_ready()?;
offset += bytes_to_write;
current_addr += bytes_to_write as u32;
on_progress(Progress::new(offset as u64, data.len() as u64));
}
if request.verify {
let verify_req = ReadRequest {
address: request.address,
length: request.data.len() as u32,
use_ecc: request.use_ecc,
ignore_ecc_errors: request.ignore_ecc_errors,
oob_mode: request.oob_mode,
bad_block_strategy: request.bad_block_strategy,
bbt: None,
retry_count: request.retry_count,
};
let read_back = self.read(verify_req, &|_| {})?;
if read_back != request.data {
for (i, (&actual, &expected)) in
read_back.iter().zip(request.data.iter()).enumerate()
{
if actual != expected {
return Err(Error::VerificationFailed {
address: request.address.as_u32() + i as u32,
expected,
actual,
});
}
}
}
}
Ok(())
}
fn erase(&mut self, _request: EraseRequest, on_progress: &dyn Fn(Progress)) -> Result<()> {
let capacity = self.spec.capacity.as_bytes() as usize;
let page_size = self.page_size();
let fill_data = vec![0xFF; page_size];
let mut offset = 0usize;
while offset < capacity {
let bytes_to_write = page_size.min(capacity - offset);
self.write_enable()?;
let (cmd_mask, addr_bytes) = self.addr_to_bytes(offset as u32);
let mut cmd = vec![CMD_EEPROM_WRITE | cmd_mask];
cmd.extend(addr_bytes);
cmd.extend_from_slice(&fill_data[..bytes_to_write]);
self.programmer.spi_transaction_write(&cmd)?;
self.wait_ready()?;
offset += bytes_to_write;
on_progress(Progress::new(offset as u64, capacity as u64));
}
Ok(())
}
fn get_status(&mut self) -> Result<Vec<u8>> {
Ok(vec![self.read_status()?])
}
fn set_status(&mut self, status: &[u8]) -> Result<()> {
if status.is_empty() {
return Ok(());
}
self.write_enable()?;
self.programmer
.spi_transaction_write(&[CMD_EEPROM_WRSR, status[0]])?;
self.wait_ready()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_address_mode_from_capacity() {
assert_eq!(AddressMode::from_capacity(128), AddressMode::OneByte);
assert_eq!(AddressMode::from_capacity(256), AddressMode::OneByte);
assert_eq!(AddressMode::from_capacity(512), AddressMode::OneByte);
assert_eq!(AddressMode::from_capacity(1024), AddressMode::TwoByte);
assert_eq!(AddressMode::from_capacity(2048), AddressMode::TwoByte);
assert_eq!(AddressMode::from_capacity(65536), AddressMode::TwoByte);
assert_eq!(AddressMode::from_capacity(131072), AddressMode::ThreeByte);
}
#[test]
fn test_address_bytes() {
assert_eq!(AddressMode::OneByte.address_bytes(), 1);
assert_eq!(AddressMode::TwoByte.address_bytes(), 2);
assert_eq!(AddressMode::ThreeByte.address_bytes(), 3);
}
}