use core::cell::RefCell;
use avr_oxide::devices::serialbus::{SerialBusClient,UsesSerialBusClient};
use avrox_storage::{RandomRead, RandomWrite, SAddr, SSize};
use avrox_storage::random::{AccessHint, Storage};
use avr_oxide::OxideResult::{Ok,Err};
pub struct SerPromD8A16be<const PROM_SIZE: u32, const PAGESIZE: u16, BC: SerialBusClient> {
bus: RefCell<BC>
}
impl<const PROM_SIZE:u32, const PAGESIZE:u16, BC> UsesSerialBusClient<BC> for SerPromD8A16be<PROM_SIZE,PAGESIZE,BC>
where
BC: SerialBusClient
{
fn using_client(client: BC) -> Self {
SerPromD8A16be {
bus: RefCell::new(client)
}
}
}
impl<const PROM_SIZE:u32, const PAGESIZE:u16, BC> SerPromD8A16be<PROM_SIZE,PAGESIZE,BC>
where
BC: SerialBusClient
{
fn reduce_len_to_page_boundary(&self, addr: u16, len: u16) -> u16 {
let page_max = (((addr / PAGESIZE) + 1) * PAGESIZE)-1;
let max_len = (page_max - addr)+1;
if max_len > len {
len
} else {
max_len
}
}
}
impl<const PROM_SIZE:u32, const PAGESIZE:u16, BC> Storage for SerPromD8A16be<PROM_SIZE,PAGESIZE,BC>
where
BC: SerialBusClient
{
const ADDRESSABLE_BYTES: SSize = PROM_SIZE as SSize;
}
impl<const PROM_SIZE:u32, const PAGESIZE:u16, BC> RandomRead for SerPromD8A16be<PROM_SIZE,PAGESIZE,BC>
where
BC: SerialBusClient
{
fn read_at_hinted(&self, addr: SAddr, buf: &mut [u8], _hint: AccessHint) -> avr_oxide::io::Result<usize> {
if addr < PROM_SIZE {
let addr = addr as u16;
let max_bytes = self.reduce_len_to_page_boundary(addr, buf.len() as u16);
let data_buf = &mut buf[0..max_bytes as usize];
let addr_buf = [
(addr >> 8) as u8,
addr as u8
];
match self.bus.borrow_mut().write_then_read(&addr_buf, data_buf) {
Ok(bytes) => {
Ok(bytes)
},
Err(_) => {
Err(avr_oxide::io::IoError::Unknown)
}
}
} else {
Err(avr_oxide::io::IoError::EndOfFile)
}
}
}
impl<const PROM_SIZE:u32, const PAGESIZE:u16, BC> RandomWrite for SerPromD8A16be<PROM_SIZE,PAGESIZE,BC>
where
BC: SerialBusClient
{
fn write_at_hinted(&mut self, addr: SAddr, buf: &[u8], _hint: AccessHint) -> avr_oxide::io::Result<usize> {
if addr < PROM_SIZE {
let addr = addr as u16;
let max_bytes = self.reduce_len_to_page_boundary(addr, buf.len() as u16);
let command_buf = [
&[
(addr >> 8) as u8,
addr as u8
],
&buf[0..max_bytes as usize]
];
match self.bus.borrow_mut().write_from_multiple(&command_buf) {
Ok(_) => {
Ok(max_bytes as usize)
},
Err(_) => {
Err(avr_oxide::io::IoError::Unknown)
}
}
} else {
Err(avr_oxide::io::IoError::NoFreeSpace)
}
}
fn flush(&mut self) -> avr_oxide::io::Result<()> {
Ok(())
}
}
#[cfg(test)]
pub mod dummy {
use avr_oxide::devices::serialbus::SerialBusClient;
use avr_oxide::hal::generic::twi::{TwiAddr, TwiError};
use avr_oxide::OxideResult;
use avr_oxide::OxideResult::{Ok,Err};
pub struct DummyPromBusClient<const BASEADDR: u8,const SIZE: usize> {
i2c_addr: TwiAddr,
page_addr_register: u32,
byte_addr_register: u8,
data: [u8; SIZE]
}
impl<const BASEADDR: u8, const SIZE: usize> DummyPromBusClient<BASEADDR,SIZE> {
pub fn new() -> Self {
Self {
i2c_addr: TwiAddr::addr(BASEADDR),
page_addr_register: 0xdeadbeef,
byte_addr_register: 0xff,
data: [0xff; SIZE]
}
}
fn get_addr_postincrement(&mut self) -> u32 {
let addr = self.page_addr_register + self.byte_addr_register as u32;
self.byte_addr_register = self.byte_addr_register.overflowing_add(1).0;
addr
}
fn set_addr(&mut self, addr_buffer: &[u8]) {
self.page_addr_register = (addr_buffer[0] as u32) << 8;
self.byte_addr_register = addr_buffer[1];
}
}
impl<const BASEADDR: u8, const SIZE: usize> SerialBusClient for DummyPromBusClient<BASEADDR,SIZE> {
fn get_bus_addr(&self) -> TwiAddr {
self.i2c_addr
}
fn clone_with_bus_addr(&self, addr: TwiAddr) -> Self {
Self {
i2c_addr: addr,
page_addr_register: 0xdeadbeef,
byte_addr_register: 0xff,
data: [0xff; SIZE]
}
}
fn write_from(&mut self, buffer: &[u8]) -> OxideResult<(), TwiError> {
for byte in buffer {
let addr = self.get_addr_postincrement();
self.data[addr as usize] = *byte;
}
Ok(())
}
fn write_from_multiple(&mut self, buffers: &[&[u8]]) -> OxideResult<(), TwiError> {
self.set_addr(buffers[0]);
for buffer in &buffers[1..] {
self.write_from(*buffer)?;
}
Ok(())
}
fn read_into(&mut self, buffer: &mut [u8]) -> OxideResult<usize, TwiError> {
let mut bytes = 0usize;
for byte in buffer {
let addr = self.get_addr_postincrement();
*byte = self.data[addr as usize];
bytes += 1;
}
Ok(bytes)
}
fn write_then_read(&mut self, command: &[u8], result: &mut [u8]) -> OxideResult<usize, TwiError> {
self.set_addr(&command);
self.read_into(result)
}
fn write_multiple_then_read(&mut self, commands: &[&[u8]], result: &mut [u8]) -> OxideResult<usize, TwiError> {
unimplemented!()
}
}
}
#[cfg(test)]
pub mod tests {
use avrox_storage::serprom::generic::dummy::DummyPromBusClient;
use avrox_storage::serprom::generic::SerPromD8A16be;
use avr_oxide::devices::serialbus::UsesSerialBusClient;
use avrox_storage::{RandomRead,RandomWrite};
pub type TestProm = SerPromD8A16be<65536,256,DummyPromBusClient<0x50,65536>>;
#[test]
fn test_basic_prom_operations() {
let mut test_prom = TestProm::using_client(DummyPromBusClient::new());
let mut buffer = [ 0x00u8, 0x00u8, 0x00u8, 0x00u8 ];
test_prom.write_all_at(0x0000u32, &[ 0x01u8, 0x02u8, 0x03u8, 0x04u8 ]).unwrap();
test_prom.read_full_at(0x0000u32, &mut buffer).unwrap();
assert_eq!(buffer, [ 0x01u8, 0x02u8, 0x03u8, 0x04u8 ]);
test_prom.write_all_at(0x0123u32, &[ 0x01u8, 0x02u8, 0x03u8, 0x04u8 ]).unwrap();
test_prom.read_full_at(0x0123u32, &mut buffer).unwrap();
assert_eq!(buffer, [ 0x01u8, 0x02u8, 0x03u8, 0x04u8 ]);
}
#[test]
fn test_prom_page_boundaries() {
let mut test_prom = TestProm::using_client(DummyPromBusClient::new());
let mut buffer = [ 0x00u8, 0x00u8, 0x00u8, 0x00u8 ];
test_prom.write_all_at(0x02feu32, &[ 0xde, 0xad, 0xbe, 0xef ]).unwrap();
test_prom.read_full_at(0x02feu32, &mut buffer).unwrap();
assert_eq!(buffer, [ 0xde, 0xad, 0xbe, 0xef ]);
test_prom.read_full_at(0x0300u32, &mut buffer).unwrap();
assert_eq!(buffer, [ 0xbe, 0xef, 0xff, 0xff ]);
test_prom.read_full_at(0x0200u32, &mut buffer).unwrap();
assert_eq!(buffer, [ 0xff, 0xff, 0xff, 0xff ]);
}
}