refactor(radio): chart-first restructure on hsmc 0.3 + soak hardening
Migrate from hsmc 0.2 (local-patched) to hsmc 0.3.0 (crates.io, with embassy + trace-defmt features). Restructure radio chart and bundle several adjacent stability changes that need to land together for soak runs. Radio chart: - Booting (default, no handlers) -> Operational (default Unconfigured; super-state owning CmdPing/CmdGetInfo/Reset/WedgeDetected handlers + Recovering + CheckRecoveryBudget) -> Dead (terminal). - Cold init's during is structurally un-cancellable. - radio_task is a 30-line shim that panics if run() returns (chart is non-terminating). - Internal queue capacity bumped 8 -> 32 to cover action-emit bursts. Host chart: - Connected splits into WaitingForFirstFrame (default, no timer) + Active (chart-owned 1s inactivity timer via on(after) + self- transition on FrameReceived). Manual Timer::after + last_frame_at removed. Soak hardening: - ld/nrf52840-memory.x: app start 0x1000 -> 0x26000 to leave the factory s140 SoftDevice dormant in flash. - hal/nrf52840.rs: dump POWER.RESETREAS on boot -- WARN on DOG/LOCKUP, INFO on RESETPIN/SREQ/wake-from-OFF. - host/usb.rs: read_packet errors discriminated -- Disabled is debug (normal during USB enum), BufferOverflow stays warn. - COBS encode warns now carry type/tag/payload-length for diagnosis. - RADIO_THROTTLED static initializes to true (no proof of host drain at boot). - driver/sh1106.rs: init retries 3x with 100ms backoff (intermittent first-write race on power-up). - Cargo.toml: unexpected_cfgs allowlist for hsmc's journal/trace-* features. docs/semtech_lora_cad_lbt_guidebook.md: vendor the Semtech CAD/LBT reference for offline lookup.
This commit is contained in:
16 files changed
+2417
-262
No files matched your search
+28
-3
@@ -54,7 +54,16 @@ pub async fn dispatch_frame(
|
||||
}
|
||||
|
||||
/// Emit an async framing error (tag = 0x0000) per spec §2.5.
|
||||
///
|
||||
/// Backpressure: if the host transport's IN endpoint is observed-dead
|
||||
/// (RADIO_THROTTLED set by host_task), drop silently. Without this gate,
|
||||
/// a bad-frame burst from a wedged host fills `OutboundChannel` faster
|
||||
/// than host_task can drain it (because it can't drain it at all when
|
||||
/// write_packet keeps failing), producing the warn flood seen in soak.
|
||||
pub async fn emit_async_err(outbound: &OutboundChannel, code: ErrorCode) {
|
||||
if crate::channel::RADIO_THROTTLED.load(portable_atomic::Ordering::Acquire) {
|
||||
return;
|
||||
}
|
||||
let frame = OutboundFrame {
|
||||
tag: 0,
|
||||
msg: DeviceMessage::Err(code),
|
||||
@@ -106,15 +115,31 @@ pub fn encode_outbound(frame: &OutboundFrame, out: &mut [u8; MAX_WIRE_FRAME]) ->
|
||||
) {
|
||||
Ok(n) => Some(n),
|
||||
Err(FrameEncodeError::BufferTooSmall) => {
|
||||
warn!("encode_frame: output buffer too small");
|
||||
warn!(
|
||||
"encode_frame: output buffer too small (type={=u8}, tag={=u16}, payload={=usize}B, out_buf={=usize}B)",
|
||||
frame.msg.type_id(),
|
||||
frame.tag,
|
||||
payload_len,
|
||||
out.len(),
|
||||
);
|
||||
None
|
||||
}
|
||||
Err(FrameEncodeError::PayloadTooLarge) => {
|
||||
warn!("encode_frame: payload exceeds MAX_PAYLOAD_FIELD");
|
||||
warn!(
|
||||
"encode_frame: payload exceeds MAX_PAYLOAD_FIELD (type={=u8}, tag={=u16}, payload={=usize}B)",
|
||||
frame.msg.type_id(),
|
||||
frame.tag,
|
||||
payload_len,
|
||||
);
|
||||
None
|
||||
}
|
||||
Err(FrameEncodeError::CobsEncode) => {
|
||||
warn!("encode_frame: COBS encode refused");
|
||||
warn!(
|
||||
"encode_frame: COBS encode refused (type={=u8}, tag={=u16}, payload={=usize}B)",
|
||||
frame.msg.type_id(),
|
||||
frame.tag,
|
||||
payload_len,
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user