Files
kaptan/firmware/libs/kaptan-drivers/src/mpu6000/test_bus.rs
T
Onur Özkan d32b03c87f feat(drivers): validate bounded IMU FIFO acquisition
Drain bounded MPU6000 and ICM-42688 FIFO batches. Publish the latest
validated sample only after overflow checks. Reject invalid drains.

Validation: driver, sensor and platform tests plus both ARM release builds
from a fresh build directory.
2026-11-07 18:12:11 +03:00

98 lines
2.8 KiB
Rust

//! Test transport with owned pending bytes, never retained caller pointers.
use kaptan_spi::{CompletionError, SpiCompletion, SpiDevice};
#[derive(Debug, Eq, PartialEq)]
pub(crate) enum FakeError {
Start,
Busy,
InvalidLength,
Quarantined,
}
pub(crate) struct FakeBus {
pub response: [u8; 32],
pub fail_start: bool,
pub fail_completion: Option<CompletionError>,
pub busy: bool,
pub starts: usize,
pub cancellations: usize,
pub transmitted: [u8; 32],
pending: Option<([u8; 32], usize)>,
quarantined: bool,
}
impl FakeBus {
pub fn ready() -> Self {
Self {
response: [0; 32],
fail_start: false,
fail_completion: None,
busy: false,
starts: 0,
cancellations: 0,
transmitted: [0; 32],
pending: None,
quarantined: false,
}
}
}
impl SpiDevice for FakeBus {
type StartError = FakeError;
fn is_busy(error: &Self::StartError) -> bool {
*error == FakeError::Busy
}
fn start_transfer(&mut self, transmit: &[u8]) -> Result<(), Self::StartError> {
if self.quarantined {
return Err(FakeError::Quarantined);
}
if self.pending.is_some() || self.busy {
return Err(FakeError::Busy);
}
if self.fail_start {
return Err(FakeError::Start);
}
if transmit.is_empty() || transmit.len() > self.response.len() {
return Err(FakeError::InvalidLength);
}
// Simulated hardware computes its response at submission, but only the
// accepted completion is allowed to copy those owned bytes to a caller.
self.pending = Some((self.response, transmit.len()));
self.transmitted[..transmit.len()].copy_from_slice(transmit);
self.starts += 1;
Ok(())
}
fn complete_transfer(
&mut self,
completion: SpiCompletion,
receive: &mut [u8],
) -> Result<(), CompletionError> {
if completion == SpiCompletion::NotPending {
return Err(CompletionError::NotPending);
}
if self.quarantined {
return Err(CompletionError::Quarantined);
}
if completion == SpiCompletion::TransferFault {
self.cancellations += 1;
}
let (pending, length) = self.pending.take().ok_or(CompletionError::NotActive)?;
if let Some(error) = self.fail_completion.take() {
self.quarantined = error == CompletionError::Quarantined;
return Err(error);
}
if completion == SpiCompletion::TransferFault {
return Err(CompletionError::TransferFault);
}
if receive.len() != length {
return Err(CompletionError::InvalidLength);
}
receive.copy_from_slice(&pending[..length]);
Ok(())
}
}