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;
pub struct I2cEeprom<P: Programmer> {
programmer: P,
spec: ChipSpec,
}
impl<P: Programmer> I2cEeprom<P> {
pub fn new(programmer: P, spec: ChipSpec) -> Self {
Self { programmer, spec }
}
fn get_addressing_info(&self, address: u32) -> (u8, Vec<u8>) {
let capacity = self.spec.capacity.as_bytes();
let base_addr = I2C_ADDR_24CXX;
if capacity <= 2048 {
let addr_high = (address >> 8) as u8 & 0x07;
let device_addr = base_addr | (addr_high << 1);
let addr_bytes = vec![address as u8];
(device_addr, addr_bytes)
} else {
let device_addr = base_addr;
let addr_bytes = vec![(address >> 8) as u8, address as u8];
(device_addr, addr_bytes)
}
}
fn page_size(&self) -> usize {
self.spec.layout.page_size as usize
}
}
impl<P: Programmer> FlashOperation for I2cEeprom<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 mut result = Vec::with_capacity(length);
const MAX_I2C_READ: usize = 32;
let mut remaining = length;
let mut current_addr = address;
while remaining > 0 {
let read_size = remaining.min(MAX_I2C_READ);
let (device_addr, addr_bytes) = self.get_addressing_info(current_addr);
let mut attempts = 0;
let chunk = loop {
let res = (|| -> Result<Vec<u8>> {
self.programmer.i2c_write(device_addr, &addr_bytes)?;
self.programmer.i2c_read(device_addr, read_size)
})();
match res {
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_addr += 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);
let (device_addr, addr_bytes) = self.get_addressing_info(current_addr);
let mut packet = Vec::with_capacity(addr_bytes.len() + bytes_to_write);
packet.extend_from_slice(&addr_bytes);
packet.extend_from_slice(&data[offset..offset + bytes_to_write]);
self.programmer.i2c_write(device_addr, &packet)?;
std::thread::sleep(std::time::Duration::from_millis(10));
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);
let current_addr = offset as u32;
let (device_addr, addr_bytes) = self.get_addressing_info(current_addr);
let mut packet = Vec::with_capacity(addr_bytes.len() + bytes_to_write);
packet.extend_from_slice(&addr_bytes);
packet.extend_from_slice(&fill_data[..bytes_to_write]);
self.programmer.i2c_write(device_addr, &packet)?;
std::thread::sleep(std::time::Duration::from_millis(10));
offset += bytes_to_write;
on_progress(Progress::new(offset as u64, capacity as u64));
}
Ok(())
}
}