rdif-serial 0.9.2

Driver Interface base definition.
Documentation
use axtest::prelude::*;

use crate::{
    Config, ConfigError, DataBits, IrqRxSink, Parity, RxErrorFlags, RxFlag, RxSample,
    SerialEventSet, SerialIrqEvent, SplitUart, StopBits, UartInfo, UartIrq, UartParts, UartPort,
};

#[axtest]
fn config_builder_records_requested_serial_mode() {
    let config = Config::new()
        .baudrate(9_600)
        .data_bits(DataBits::Seven)
        .stop_bits(StopBits::Two)
        .parity(Parity::Even);

    ax_assert_eq!(config.baudrate, Some(9_600));
    ax_assert_eq!(config.data_bits, Some(DataBits::Seven));
    ax_assert_eq!(config.stop_bits, Some(StopBits::Two));
    ax_assert_eq!(config.parity, Some(Parity::Even));
}

#[axtest]
fn event_sets_classify_rx_and_tx_sources() {
    let rx = SerialEventSet::RX_DATA | SerialEventSet::RX_TIMEOUT;
    ax_assert!(rx.has_rx());
    ax_assert!(!rx.has_tx());

    let tx = SerialEventSet::TX_SPACE;
    ax_assert!(!tx.has_rx());
    ax_assert!(tx.has_tx());

    let combined = rx | tx | SerialEventSet::FAULT;
    ax_assert!(combined.contains(SerialEventSet::FAULT));
    ax_assert!(combined.has_rx());
    ax_assert!(combined.has_tx());
}

#[axtest]
fn rx_samples_and_irq_events_keep_normalized_status() {
    let sample = RxSample {
        byte: Some(b'x'),
        flag: RxFlag::Framing,
        overrun: true,
    };

    ax_assert_eq!(sample.byte, Some(b'x'));
    ax_assert_eq!(sample.flag, RxFlag::Framing);
    ax_assert!(sample.overrun);

    let event = SerialIrqEvent {
        events: SerialEventSet::RX_STATUS,
        rx_errors: RxErrorFlags::FRAMING | RxErrorFlags::OVERRUN,
        rearm: SerialEventSet::RX_DATA,
    };

    ax_assert!(event.events.has_rx());
    ax_assert!(event.rx_errors.contains(RxErrorFlags::FRAMING));
    ax_assert!(event.rx_errors.contains(RxErrorFlags::OVERRUN));
    ax_assert_eq!(event.rearm, SerialEventSet::RX_DATA);
}

struct MockPort {
    config: Config,
    tx: [u8; 4],
    tx_len: usize,
    rx: Option<RxSample>,
    masked: bool,
}

impl MockPort {
    const fn new() -> Self {
        Self {
            config: Config::new(),
            tx: [0; 4],
            tx_len: 0,
            rx: None,
            masked: false,
        }
    }
}

impl UartPort for MockPort {
    fn startup(&mut self, config: &Config) -> Result<(), ConfigError> {
        self.config = config.clone();
        self.masked = true;
        Ok(())
    }

    fn shutdown(&mut self) {
        self.masked = true;
    }

    fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
        self.config = config.clone();
        Ok(())
    }

    fn read_rx(&mut self) -> Option<RxSample> {
        self.rx.take()
    }

    fn write_tx(&mut self, bytes: &[u8]) -> usize {
        let count = bytes.len().min(self.tx.len());
        self.tx[..count].copy_from_slice(&bytes[..count]);
        self.tx_len = count;
        count
    }

    fn tx_idle(&mut self) -> bool {
        self.tx_len == 0
    }

    fn mask_all(&mut self) {
        self.masked = true;
    }

    fn rearm(&mut self, sources: SerialEventSet) -> SerialEventSet {
        self.masked = false;
        sources & SerialEventSet::RX
    }
}

struct MockIrq {
    pending: Option<SerialIrqEvent>,
    sample: Option<RxSample>,
}

impl UartIrq for MockIrq {
    fn handle(&mut self, rx: &mut dyn IrqRxSink) -> Option<SerialIrqEvent> {
        if let Some(sample) = self.sample.take() {
            rx.push(sample);
        }
        self.pending.take()
    }
}

struct MockSink {
    sample: Option<RxSample>,
}

impl IrqRxSink for MockSink {
    fn push(&mut self, sample: RxSample) {
        self.sample = Some(sample);
    }
}

struct MockUart;

impl SplitUart for MockUart {
    type Port = MockPort;
    type Irq = MockIrq;

    fn runtime_info(&self) -> UartInfo {
        UartInfo {
            name: "mock-uart",
            register_base: 0x1000,
            initial_baudrate: 115_200,
        }
    }

    fn split(self) -> UartParts<Self::Port, Self::Irq> {
        UartParts::new(
            MockPort::new(),
            MockIrq {
                pending: Some(SerialIrqEvent {
                    events: SerialEventSet::RX_DATA,
                    rx_errors: RxErrorFlags::empty(),
                    rearm: SerialEventSet::RX_DATA,
                }),
                sample: Some(RxSample {
                    byte: Some(b'a'),
                    flag: RxFlag::Normal,
                    overrun: false,
                }),
            },
        )
    }
}

#[axtest]
fn split_uart_exposes_independent_task_and_irq_endpoints() {
    let uart = MockUart;
    let info = uart.runtime_info();
    ax_assert_eq!(info.name, "mock-uart");
    ax_assert_eq!(info.register_base, 0x1000);
    ax_assert_eq!(info.initial_baudrate, 115_200);

    let UartParts { mut port, mut irq } = uart.split();
    let config = Config::new().baudrate(57_600);
    ax_assert!(port.startup(&config).is_ok());
    ax_assert_eq!(port.config.baudrate, Some(57_600));
    ax_assert_eq!(port.write_tx(b"abcdef"), 4);
    ax_assert!(!port.tx_idle());
    ax_assert_eq!(
        port.rearm(SerialEventSet::RX_DATA | SerialEventSet::TX_SPACE),
        SerialEventSet::RX_DATA
    );

    let mut sink = MockSink { sample: None };
    let event = irq.handle(&mut sink).expect("mock IRQ has pending event");
    ax_assert_eq!(sink.sample.expect("IRQ pushed sample").byte, Some(b'a'));
    ax_assert_eq!(event.events, SerialEventSet::RX_DATA);
}