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:
Onur Özkan committed 2026-11-07 18:12:11 +03:00
1 parent fb1b8af2d1
commit f9a61a086f
211 files changed
+45657 -12740

No files matched your search

@@ -1,13 +1,13 @@
//! Common former-motor-pin interlock for every F722 image.
//!
//! This is intentionally independent of profile initialization and is called
//! as the first PAC access. The GPIO-low interlock is a separate electrical
//! boundary from all telemetry initialization.
//! Motor GPIO-low interlock, called before all fallible initialization.
use crate::MotorPins;
use stm32f7::stm32f722;
/// Drives PB0/PB1/PB4/PB5 low before selecting push-pull GPIO output mode.
pub(crate) fn inhibit_pb0_pb1_pb4_pb5(rcc: &stm32f722::RCC, port: &stm32f722::GPIOB) {
pub(crate) fn inhibit(
rcc: &stm32f722::rcc::RegisterBlock,
port: &stm32f722::gpiob::RegisterBlock,
pins: MotorPins,
) {
rcc.ahb1enr().modify(|_, write| write.gpioben().set_bit());
// RM0431 section 5.2.12 requires two peripheral clock cycles before the
// first access. The volatile enable-register read supplies the standard
@@ -15,7 +15,7 @@ pub(crate) fn inhibit_pb0_pb1_pb4_pb5(rcc: &stm32f722::RCC, port: &stm32f722::GP
// ahead of the clock enable.
let _ = rcc.ahb1enr().read().gpioben().bit_is_set();
port.bsrr().write(|write| {
write
let write = write
.br0()
.set_bit()
.br1()
@@ -23,10 +23,15 @@ pub(crate) fn inhibit_pb0_pb1_pb4_pb5(rcc: &stm32f722::RCC, port: &stm32f722::GP
.br4()
.set_bit()
.br5()
.set_bit()
.set_bit();
if pins == MotorPins::Six {
write.br6().set_bit().br7().set_bit()
} else {
write
}
});
port.otyper().modify(|_, write| {
write
let write = write
.ot0()
.push_pull()
.ot1()
@@ -34,10 +39,15 @@ pub(crate) fn inhibit_pb0_pb1_pb4_pb5(rcc: &stm32f722::RCC, port: &stm32f722::GP
.ot4()
.push_pull()
.ot5()
.push_pull()
.push_pull();
if pins == MotorPins::Six {
write.ot6().push_pull().ot7().push_pull()
} else {
write
}
});
port.ospeedr().modify(|_, write| {
write
let write = write
.ospeedr0()
.low_speed()
.ospeedr1()
@@ -45,10 +55,15 @@ pub(crate) fn inhibit_pb0_pb1_pb4_pb5(rcc: &stm32f722::RCC, port: &stm32f722::GP
.ospeedr4()
.low_speed()
.ospeedr5()
.low_speed()
.low_speed();
if pins == MotorPins::Six {
write.ospeedr6().low_speed().ospeedr7().low_speed()
} else {
write
}
});
port.pupdr().modify(|_, write| {
write
let write = write
.pupdr0()
.floating()
.pupdr1()
@@ -56,10 +71,15 @@ pub(crate) fn inhibit_pb0_pb1_pb4_pb5(rcc: &stm32f722::RCC, port: &stm32f722::GP
.pupdr4()
.floating()
.pupdr5()
.floating()
.floating();
if pins == MotorPins::Six {
write.pupdr6().floating().pupdr7().floating()
} else {
write
}
});
port.moder().modify(|_, write| {
write
let write = write
.moder0()
.output()
.moder1()
@@ -67,6 +87,51 @@ pub(crate) fn inhibit_pb0_pb1_pb4_pb5(rcc: &stm32f722::RCC, port: &stm32f722::GP
.moder4()
.output()
.moder5()
.output()
.output();
if pins == MotorPins::Six {
write.moder6().output().moder7().output()
} else {
write
}
});
}
#[cfg(test)]
mod tests {
use super::*;
use core::mem::MaybeUninit;
#[test]
fn inhibits_only_board_motor_pins() {
for (pins, pin_mask, field_mask, output_mode) in [
(MotorPins::Four, 0x0033, 0x0f0f, 0x0505),
(MotorPins::Six, 0x00f3, 0xff0f, 0x5505),
] {
// SAFETY: these PAC blocks contain u32 volatile cells and reserved
// bytes. Zero is valid, and this test owns their ordinary RAM storage.
let (rcc, port) = unsafe {
(
MaybeUninit::<stm32f722::rcc::RegisterBlock>::zeroed().assume_init(),
MaybeUninit::<stm32f722::gpiob::RegisterBlock>::zeroed().assume_init(),
)
};
// SAFETY: writes initialize only the test's local register storage.
unsafe {
rcc.ahb1enr().write(|write| write.bits(1));
port.moder().write(|write| write.bits(u32::MAX));
port.otyper().write(|write| write.bits(0xffff));
port.ospeedr().write(|write| write.bits(u32::MAX));
port.pupdr().write(|write| write.bits(0xaaaa_aaaa));
}
inhibit(&rcc, &port, pins);
assert_eq!(rcc.ahb1enr().read().bits(), 3);
// SAFETY: this reads the local backing cell, not a hardware write-only register.
let reset = unsafe { port.bsrr().as_ptr().read_volatile() };
assert_eq!(reset, pin_mask << 16);
assert_eq!(port.moder().read().bits(), !field_mask | output_mode);
assert_eq!(port.otyper().read().bits(), 0xffff & !pin_mask);
assert_eq!(port.ospeedr().read().bits(), !field_mask);
assert_eq!(port.pupdr().read().bits(), 0xaaaa_aaaa & !field_mask);
}
}
}