use core::marker::PhantomData;
use core::mem::MaybeUninit;
use avr_oxide::devices::serialbus::{SerialBusClient, UsesSerialBusClient};
use avr_oxide::hal::generic::twi::TwiAddr;
use avrox_storage::{RandomRead, RandomWrite, SAddr, SSize};
use avrox_storage::random::{AccessHint, Storage};
use avr_oxide::OxideResult::Ok;
pub struct CompositeSerProm<const ELEMENTS: usize, const I2C_MASK: u8, const I2C_SHIFT: u8, BC, ELEMENT>
where
BC: SerialBusClient,
ELEMENT: UsesSerialBusClient<BC>
{
elements: [ELEMENT; ELEMENTS],
phantom: PhantomData<BC>
}
impl<const ELEMENTS: usize, const I2C_MASK: u8, const I2C_SHIFT: u8, BC, ELEMENT>
UsesSerialBusClient<BC> for CompositeSerProm<ELEMENTS,I2C_MASK,I2C_SHIFT,BC,ELEMENT>
where
BC: SerialBusClient,
ELEMENT: UsesSerialBusClient<BC>
{
fn using_client(base_client: BC) -> Self {
let mut elements: [MaybeUninit<ELEMENT>; ELEMENTS] = unsafe {
MaybeUninit::uninit().assume_init()
};
let mut base_addr = base_client.get_bus_addr();
for element in &mut elements {
let element_client = base_client.clone_with_bus_addr(base_addr);
element.write(ELEMENT::using_client(element_client));
let next_element_addr = ((base_addr.write_addr() & I2C_MASK) >> I2C_SHIFT) + 1;
base_addr = TwiAddr::addr((base_addr.write_addr() & (!I2C_MASK)) | (next_element_addr << I2C_SHIFT));
}
CompositeSerProm {
elements: unsafe { core::mem::transmute_copy::<_,[ELEMENT; ELEMENTS]>(&elements) },
phantom: PhantomData::default()
}
}
}
impl<const ELEMENTS: usize, const I2C_MASK: u8, const I2C_SHIFT: u8, BC, ELEMENT>
RandomRead for CompositeSerProm<ELEMENTS,I2C_MASK,I2C_SHIFT,BC,ELEMENT>
where
BC: SerialBusClient,
ELEMENT: UsesSerialBusClient<BC> + RandomRead + Storage
{
fn read_at_hinted(&self, addr: SAddr, buf: &mut [u8], _hint: AccessHint) -> avr_oxide::io::Result<usize> {
let device = (addr / ELEMENT::addressable_bytes()) as usize;
let device_addr = addr % ELEMENT::addressable_bytes();
self.elements[device].read_at(device_addr, buf)
}
}
impl<const ELEMENTS: usize, const I2C_MASK: u8, const I2C_SHIFT: u8, BC, ELEMENT>
RandomWrite for CompositeSerProm<ELEMENTS,I2C_MASK,I2C_SHIFT,BC,ELEMENT>
where
BC: SerialBusClient,
ELEMENT: UsesSerialBusClient<BC> + RandomWrite + Storage
{
fn write_at_hinted(&mut self, addr: SAddr, buf: &[u8], _hint: AccessHint) -> avr_oxide::io::Result<usize> {
let device = (addr / ELEMENT::addressable_bytes()) as usize;
let device_addr = addr % ELEMENT::addressable_bytes();
self.elements[device].write_at(device_addr, buf)
}
fn flush(&mut self) -> avr_oxide::io::Result<()> {
for device in &mut self.elements {
device.flush()?;
}
Ok(())
}
}
impl<const ELEMENTS: usize, const I2C_MASK: u8, const I2C_SHIFT: u8, BC, ELEMENT>
Storage for CompositeSerProm<ELEMENTS,I2C_MASK,I2C_SHIFT,BC,ELEMENT>
where
BC: SerialBusClient,
ELEMENT: UsesSerialBusClient<BC> + RandomWrite + Storage
{
const ADDRESSABLE_BYTES: SSize = (ELEMENTS as SSize) * ELEMENT::ADDRESSABLE_BYTES;
}
#[cfg(test)]
pub mod tests {
use avrox_storage::serprom::composite::CompositeSerProm;
use avrox_storage::serprom::generic::SerPromD8A16be;
use avrox_storage::serprom::generic::dummy::DummyPromBusClient;
use avr_oxide::devices::serialbus::UsesSerialBusClient;
use avrox_storage::{RandomRead,RandomWrite};
pub type TestBusClient = DummyPromBusClient<0x50,65_536>;
pub type TestCompositeProm = CompositeSerProm<2,0b00000010,1,TestBusClient,SerPromD8A16be<65_536,256,TestBusClient>>;
#[test]
fn test_basic_prom_operations() {
let mut test_prom = TestCompositeProm::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_prom.write_all_at(0x10123u32, &[ 0x08u8, 0x07u8, 0x06u8, 0x05u8 ]).unwrap();
test_prom.read_full_at(0x10123u32, &mut buffer).unwrap();
assert_eq!(buffer, [ 0x08u8, 0x07u8, 0x06u8, 0x05u8 ]);
test_prom.read_full_at(0x0123u32, &mut buffer).unwrap();
assert_eq!(buffer, [ 0x01u8, 0x02u8, 0x03u8, 0x04u8 ]);
}
#[test]
fn test_prom_chip_boundaries() {
let mut test_prom = TestCompositeProm::using_client(DummyPromBusClient::new());
let mut buffer = [ 0x00u8, 0x00u8, 0x00u8, 0x00u8 ];
test_prom.write_all_at(0x00fffeu32, &[ 0xde, 0xad, 0xbe, 0xef ]).unwrap();
test_prom.read_full_at(0x00fffeu32, &mut buffer).unwrap();
assert_eq!(buffer, [ 0xde, 0xad, 0xbe, 0xef ]);
test_prom.read_full_at(0x10000u32, &mut buffer).unwrap();
assert_eq!(buffer, [ 0xbe, 0xef, 0xff, 0xff ]);
}
}