feat(firmware): validate configuration and migrate sensor-driven flight APIs
Validate configuration through its owning schemas and keep board declarations separate from shared F722 resource ownership. Bound USB transitions, isolate transport failures and retain restart-only activation. Migrate navigation and flight consumers with the sensor and schema contracts. Require explicit installation data and synchronous output authority; board motor outputs remain inhibited. Validation: assembled-prefix host tests, strict Clippy, platform/USB tests and both ARM release builds. Final-source coverage and scoped Miri/Kani evidence are retained separately from hardware qualification.
This commit is contained in:
1 parent
fb1b8af2d1
commit
f9a61a086f
211 files changed
+45657
-12740
No files matched your search
@@ -0,0 +1,97 @@
|
||||
//! 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; 15],
|
||||
pub fail_start: bool,
|
||||
pub fail_completion: Option<CompletionError>,
|
||||
pub busy: bool,
|
||||
pub starts: usize,
|
||||
pub cancellations: usize,
|
||||
pub transmitted: [u8; 15],
|
||||
pending: Option<([u8; 15], usize)>,
|
||||
quarantined: bool,
|
||||
}
|
||||
|
||||
impl FakeBus {
|
||||
pub fn ready() -> Self {
|
||||
Self {
|
||||
response: [0; 15],
|
||||
fail_start: false,
|
||||
fail_completion: None,
|
||||
busy: false,
|
||||
starts: 0,
|
||||
cancellations: 0,
|
||||
transmitted: [0; 15],
|
||||
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(())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user