pub trait ScdcTransport {
type Error;
async fn read(&self, reg: u8) -> Result<u8, Self::Error>;
async fn write(&mut self, reg: u8, value: u8) -> Result<(), Self::Error>;
async fn read_block(&self, reg: u8, buf: &mut [u8]) -> Result<(), Self::Error> {
let mut addr = reg;
for byte in buf {
*byte = self.read(addr).await?;
addr = addr.wrapping_add(1);
}
Ok(())
}
}
pub trait SendScdcTransport: Send + Sync {
type Error: Send;
fn read(&self, reg: u8) -> impl Future<Output = Result<u8, Self::Error>> + Send;
fn write(&mut self, reg: u8, value: u8)
-> impl Future<Output = Result<(), Self::Error>> + Send;
fn read_block(
&self,
reg: u8,
buf: &mut [u8],
) -> impl Future<Output = Result<(), Self::Error>> + Send {
async move {
let mut addr = reg;
for byte in buf {
*byte = self.read(addr).await?;
addr = addr.wrapping_add(1);
}
Ok(())
}
}
}
impl<T: SendScdcTransport> ScdcTransport for T {
type Error = <T as SendScdcTransport>::Error;
fn read(&self, reg: u8) -> impl Future<Output = Result<u8, Self::Error>> {
<T as SendScdcTransport>::read(self, reg)
}
fn write(&mut self, reg: u8, value: u8) -> impl Future<Output = Result<(), Self::Error>> {
<T as SendScdcTransport>::write(self, reg, value)
}
fn read_block(&self, reg: u8, buf: &mut [u8]) -> impl Future<Output = Result<(), Self::Error>> {
<T as SendScdcTransport>::read_block(self, reg, buf)
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::cell::Cell;
use core::sync::atomic::{AtomicU32, Ordering};
struct MockTransport {
registers: [u8; 256],
reads: Cell<u32>,
fail_at: Option<u8>,
}
impl MockTransport {
fn new() -> Self {
Self {
registers: [0u8; 256],
reads: Cell::new(0),
fail_at: None,
}
}
fn filled() -> Self {
let mut t = Self::new();
for (i, r) in t.registers.iter_mut().enumerate() {
*r = i as u8;
}
t
}
}
impl ScdcTransport for MockTransport {
type Error = u8;
async fn read(&self, reg: u8) -> Result<u8, Self::Error> {
if self.fail_at == Some(reg) {
return Err(reg);
}
self.reads.set(self.reads.get() + 1);
Ok(self.registers[reg as usize])
}
async fn write(&mut self, reg: u8, value: u8) -> Result<(), Self::Error> {
self.registers[reg as usize] = value;
Ok(())
}
}
#[pollster::test]
async fn write_then_read_returns_written_value() {
let mut t = MockTransport::new();
t.write(0x20, 0xAB).await.unwrap();
assert_eq!(t.read(0x20).await.unwrap(), 0xAB);
}
#[pollster::test]
async fn unwritten_register_reads_zero() {
let t = MockTransport::new();
assert_eq!(t.read(0x31).await.unwrap(), 0x00);
}
#[pollster::test]
async fn write_is_register_specific() {
let mut t = MockTransport::new();
t.write(0x01, 0xFF).await.unwrap();
assert_eq!(t.read(0x00).await.unwrap(), 0x00);
assert_eq!(t.read(0x02).await.unwrap(), 0x00);
}
#[pollster::test]
async fn write_overwrites_previous_value() {
let mut t = MockTransport::new();
t.write(0x20, 0x01).await.unwrap();
t.write(0x20, 0x02).await.unwrap();
assert_eq!(t.read(0x20).await.unwrap(), 0x02);
}
#[pollster::test]
async fn read_block_reads_consecutive_registers() {
let t = MockTransport::filled();
let mut buf = [0u8; 3];
t.read_block(0x40, &mut buf).await.unwrap();
assert_eq!(buf, [0x40, 0x41, 0x42]);
assert_eq!(t.reads.get(), 3);
}
#[pollster::test]
async fn read_block_empty_buffer_reads_nothing() {
let t = MockTransport::filled();
t.read_block(0x40, &mut []).await.unwrap();
assert_eq!(t.reads.get(), 0);
}
#[pollster::test]
async fn read_block_stops_at_first_error() {
let mut t = MockTransport::filled();
t.fail_at = Some(0x41);
let mut buf = [0u8; 3];
assert_eq!(t.read_block(0x40, &mut buf).await, Err(0x41));
assert_eq!(buf, [0x40, 0x00, 0x00]);
assert_eq!(t.reads.get(), 1);
}
#[pollster::test]
async fn read_block_wraps_past_0xff() {
let t = MockTransport::filled();
let mut buf = [0u8; 2];
t.read_block(0xFF, &mut buf).await.unwrap();
assert_eq!(buf, [0xFF, 0x00]);
}
#[pollster::test]
async fn write_then_read_block() {
let mut t = MockTransport::new();
t.write(0x20, 0xAB).await.unwrap();
let mut buf = [0u8; 1];
t.read_block(0x20, &mut buf).await.unwrap();
assert_eq!(buf, [0xAB]);
}
struct SendMockTransport {
registers: [u8; 256],
reads: AtomicU32,
fail_at: Option<u8>,
}
impl SendMockTransport {
fn filled() -> Self {
let mut registers = [0u8; 256];
for (i, r) in registers.iter_mut().enumerate() {
*r = i as u8;
}
Self {
registers,
reads: AtomicU32::new(0),
fail_at: None,
}
}
fn reads(&self) -> u32 {
self.reads.load(Ordering::Relaxed)
}
}
impl SendScdcTransport for SendMockTransport {
type Error = u8;
async fn read(&self, reg: u8) -> Result<u8, Self::Error> {
if self.fail_at == Some(reg) {
return Err(reg);
}
self.reads.fetch_add(1, Ordering::Relaxed);
Ok(self.registers[reg as usize])
}
async fn write(&mut self, reg: u8, value: u8) -> Result<(), Self::Error> {
self.registers[reg as usize] = value;
Ok(())
}
}
struct BlockReadingTransport;
impl SendScdcTransport for BlockReadingTransport {
type Error = ();
async fn read(&self, _reg: u8) -> Result<u8, Self::Error> {
Err(())
}
async fn write(&mut self, _reg: u8, _value: u8) -> Result<(), Self::Error> {
Err(())
}
async fn read_block(&self, reg: u8, buf: &mut [u8]) -> Result<(), Self::Error> {
buf.fill(reg);
Ok(())
}
}
async fn round_trip_plain<T: ScdcTransport>(
t: &mut T,
buf: &mut [u8],
) -> (
Result<(), T::Error>,
Result<u8, T::Error>,
Result<(), T::Error>,
) {
let written = t.write(0x20, 0xAB).await;
let read = t.read(0x20).await;
let block = t.read_block(0x20, buf).await;
(written, read, block)
}
fn spawnable<'a, T: SendScdcTransport>(
t: &'a T,
buf: &'a mut [u8],
) -> impl Future<Output = Result<(), T::Error>> + Send + 'a {
SendScdcTransport::read_block(t, 0x40, buf)
}
fn assert_send<T: Send>(value: T) -> T {
value
}
#[pollster::test]
async fn send_twin_is_usable_as_scdc_transport() {
let mut t = SendMockTransport::filled();
let mut buf = [0u8; 2];
let (written, read, block) = round_trip_plain(&mut t, &mut buf).await;
assert_eq!((written, read, block), (Ok(()), Ok(0xAB), Ok(())));
assert_eq!(buf, [0xAB, 0x21]);
assert_eq!(t.reads(), 3);
}
#[pollster::test]
async fn send_twin_futures_are_send_in_generic_code() {
let t = SendMockTransport::filled();
let mut buf = [0u8; 3];
assert_send(spawnable(&t, &mut buf)).await.unwrap();
assert_eq!(buf, [0x40, 0x41, 0x42]);
}
#[pollster::test]
async fn send_twin_default_read_block_stops_at_first_error() {
let mut t = SendMockTransport::filled();
t.fail_at = Some(0x41);
let mut buf = [0u8; 3];
assert_eq!(
SendScdcTransport::read_block(&t, 0x40, &mut buf).await,
Err(0x41)
);
assert_eq!(buf, [0x40, 0x00, 0x00]);
assert_eq!(t.reads(), 1);
}
#[pollster::test]
async fn send_twin_default_read_block_wraps_past_0xff() {
let t = SendMockTransport::filled();
let mut buf = [0u8; 2];
SendScdcTransport::read_block(&t, 0xFF, &mut buf)
.await
.unwrap();
assert_eq!(buf, [0xFF, 0x00]);
}
#[pollster::test]
async fn send_twin_read_block_override_is_used_through_scdc_transport() {
let mut t = BlockReadingTransport;
let mut buf = [0u8; 2];
let (written, read, block) = round_trip_plain(&mut t, &mut buf).await;
assert_eq!((written, read, block), (Err(()), Err(()), Ok(())));
assert_eq!(buf, [0x20, 0x20]);
}
}