mirror of
https://github.com/meshtastic/firmware.git
synced 2026-10-31 04:19:58 -04:00
Hold the radio-sequence lock on the main loop's radio paths
The lock exists so a readout cannot land between the calls of a RadioLib sequence, where it would clear flags the sequence is about to use. Three paths reached from the main loop still made such sequences without it: - checkCadHandoffTimeout()'s startReceive() after a handoff RX that never completed. - checkStaleRxFlags(), which reads the IRQ flags, draws a verdict from them and then re-arms or clears - all of which a readout in between invalidates, since it retires the very flags being judged. - resetAGC(), which is a sequence end to end: warm sleep, RC standby, CALIBRATE_ALL, the re-applied DIO2/boosted-gain/0x8B5 settings and the startReceive() that resumes. Its own mid-packet bail is a point check, so it cannot exclude a readout that starts just after it. All three predate the readout task, so this is the task's own lock coverage catching up rather than a regression. Guarding resetAGC() at its call site in periodicRadioMaintenance() covers every driver's override. It can hold the lock for the length of a calibration, which delays a readout, but the chip is not readable for any of that anyway - the RX that would read a frame is the startReceive() at the end. checkRxDoneIrqFlag() is deliberately left unguarded: it is a single flag read and a wake, not a sequence, and serialising it behind a readout in flight would only delay the handoff it exists to make. Co-Authored-By: Jonathan Bennett <jbennett@incomsystems.biz> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVTg7Na1pVvCcwZoSv9Hnd
This commit is contained in:
1 parent
af72544ab8
commit
d2fb16a508
1 file changed
+15
-1
@@ -974,6 +974,9 @@ void RadioLibInterface::checkCadHandoffTimeout()
|
||||
if (cadHandoffRxStart && Throttle::hasElapsed(cadHandoffRxStart, 2 * maxPacketTimeMsec)) {
|
||||
LOG_WARN("CAD>RX timeout");
|
||||
cadHandoffRxStart = 0;
|
||||
// Reached from the main loop, which holds no sequence of its own: a readout landing inside this
|
||||
// re-arm would clear the flags it sets up, exactly as it would on the radio thread's paths.
|
||||
RadioSequence seq(this);
|
||||
startReceive();
|
||||
}
|
||||
}
|
||||
@@ -983,6 +986,9 @@ void RadioLibInterface::checkStaleRxFlags()
|
||||
// A handoff RX has its own timeout, and a pending RX_DONE is about to clear every flag itself.
|
||||
if (cadHandoffRxStart)
|
||||
return;
|
||||
// The flag read, the verdict drawn from it and the re-arm or clear that follows are one sequence: a readout
|
||||
// in between retires the very flags being judged, so the verdict would be drawn on flags that are gone.
|
||||
RadioSequence seq(this);
|
||||
const uint32_t irq = iface->getIrqFlags();
|
||||
if (irq & iface->getIrqMapped(1UL << RADIOLIB_IRQ_RX_DONE))
|
||||
return;
|
||||
@@ -1039,7 +1045,15 @@ void RadioLibInterface::periodicRadioMaintenance()
|
||||
const bool hearing = lastRxGoodMs && Throttle::isWithinTimespanMs(lastRxGoodMs, AGC_IDLE_RESET_MS);
|
||||
if (hearing && lastAgcResetMs && Throttle::isWithinTimespanMs(lastAgcResetMs, AGC_FORCED_RESET_MS))
|
||||
return;
|
||||
if (resetAGC()) {
|
||||
// resetAGC() is a sequence end to end - warm sleep, RC standby, CALIBRATE_ALL, the re-applied settings and
|
||||
// the startReceive() that resumes - and the chip is not readable for any of it. Its own mid-packet bail is a
|
||||
// point check, so it cannot exclude a readout that starts just after it: the lock is what does.
|
||||
bool agcReset = false;
|
||||
{
|
||||
RadioSequence seq(this);
|
||||
agcReset = resetAGC();
|
||||
}
|
||||
if (agcReset) {
|
||||
const uint32_t now = millis();
|
||||
lastAgcResetMs = now ? now : 1;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user