embedded_registers/i2c/codecs/
crc_codec.rs1use core::marker::PhantomData;
2
3use crate::{ReadableRegister, WritableRegister};
4use arrayvec::ArrayVec;
5use bytemuck::Zeroable;
6use crc::Algorithm;
7
8#[derive(Default)]
44pub struct Crc8Codec<const HEADER_SIZE: usize, const CHUNK_SIZE: usize, C: Crc8Algorithm> {
45 _algo: PhantomData<C>,
46}
47
48pub trait Crc8Algorithm: Default {
49 fn instance() -> &'static Algorithm<u8>;
51}
52
53#[maybe_async_cfg::maybe(
54 idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), Codec, I2cBoundBus),
55 sync(feature = "sync"),
56 async(feature = "async"),
57 keep_self
58)]
59impl<const HEADER_SIZE: usize, const CHUNK_SIZE: usize, C: Crc8Algorithm + 'static> crate::i2c::Codec
60 for Crc8Codec<HEADER_SIZE, CHUNK_SIZE, C>
61{
62 #[inline]
63 async fn read_register<R, I, A>(bound_bus: &mut crate::i2c::I2cBoundBus<I, A>) -> Result<R, I::Error>
64 where
65 R: ReadableRegister,
66 I: hal::i2c::I2c<A> + hal::i2c::ErrorType,
67 A: hal::i2c::AddressMode + Copy,
68 {
69 let crc = crc::Crc::<u8>::new(C::instance());
70 let header = &R::ADDRESS.to_be_bytes()[core::mem::size_of_val(&R::ADDRESS) - HEADER_SIZE..];
71
72 let mut array = ArrayVec::<_, 64>::new();
73 unsafe {
74 array.set_len(R::REGISTER_SIZE + R::REGISTER_SIZE / CHUNK_SIZE);
75 }
76
77 bound_bus
78 .interface
79 .write_read(bound_bus.address, header, &mut array)
80 .await?;
81
82 let mut register = R::zeroed();
83 let data = bytemuck::bytes_of_mut(&mut register);
84 for (i, x) in array.chunks(CHUNK_SIZE + 1).enumerate() {
85 let value = &x[0..CHUNK_SIZE];
86 data[i..i + CHUNK_SIZE].copy_from_slice(value);
87 let crc_val = crc.checksum(value);
88 let crc_real = x[CHUNK_SIZE];
89 if crc_real != crc_val {
90 panic!("crc failed")
91 }
92 }
93
94 Ok(register)
95 }
96
97 #[inline]
98 async fn write_register<R, I, A>(
99 bound_bus: &mut crate::i2c::I2cBoundBus<I, A>,
100 register: impl AsRef<R>,
101 ) -> Result<(), I::Error>
102 where
103 R: WritableRegister,
104 I: hal::i2c::I2c<A> + hal::i2c::ErrorType,
105 A: hal::i2c::AddressMode + Copy,
106 {
107 #[repr(C, packed(1))]
108 #[derive(Copy, Clone, bytemuck::Pod, Zeroable)]
109 struct Buffer<const HEADER_SIZE: usize, R> {
110 header: [u8; HEADER_SIZE],
111 register: R,
112 }
113 let crc = crc::Crc::<u8>::new(C::instance());
114
115 let mut buffer = Buffer::<{ HEADER_SIZE }, R> {
116 header: R::ADDRESS.to_be_bytes()[core::mem::size_of_val(&R::ADDRESS) - HEADER_SIZE..]
117 .try_into()
118 .expect("Unexpected compile-time header address size. This is a bug in the chosen Codec or embedded-registers."),
119 register: *register.as_ref(),
120 };
121 let data = bytemuck::bytes_of_mut(&mut buffer);
122
123 let mut array = ArrayVec::<_, 64>::new();
124 for x in data.chunks(CHUNK_SIZE) {
125 array.try_extend_from_slice(x).unwrap();
126 let crc_val = crc.checksum(x);
127 array.push(crc_val);
128 }
129
130 bound_bus.interface.write(bound_bus.address, &array).await
131 }
132}