radiolib: bring LinuxSX1262 to parity with CustomSX1262 - #3
Open
mmmorks wants to merge 1 commit into
Open
Conversation
mmmorks
force-pushed
the
pr/02-config-validation
branch
from
September 8, 2026 04:11
b61c48c to
037b93f
Compare
mmmorks
force-pushed
the
pr/03-sx1262-parity
branch
from
September 8, 2026 04:11
d1082d7 to
294f8c5
Compare
LinuxSX1262 derived from SX1262 directly and carried its own two-line isReceiving() plus a copy of getRxBoostedGainMode(). Upstream's RX watchdog -- a startReceive() that also enables the preamble IRQ, the deadlines that bound a header or preamble which never becomes a packet, and the millis setters that size them -- landed in CustomSX1262 and never here. That left the Linux repeater with an unbounded isReceiving(): nothing cleared a latched HEADER_VALID, so a header whose packet never arrived reported busy forever, deferring every transmit until getCADFailMaxDuration() expired and forced it through. The other half of that check was dead code -- plain SX1262::startReceive() enables RADIOLIB_IRQ_RX_DEFAULT_FLAGS, which does not include PREAMBLE_DETECTED, so the bit could never latch. Deriving from CustomSX1262 both enables it and bounds it. None of that logic is Linux-specific, and upstream keeps fixing it. Derive from CustomSX1262 instead. std_init() stays, shadowing the non-virtual base, because that part genuinely differs: the MCU variants pick regulator, RF switch and gain per board at compile time, while one binary here serves every HAT and reads all of it from meshcored.ini. Two of those compile-time knobs had no Linux equivalent at all, so they become keys: use_regulator_ldo (SX126X_USE_REGULATOR_LDO, begin()'s useRegulatorLDO) rx_register_patch (SX126X_REGISTER_PATCH, bit 0 of register 0x8B5) Both default to the compile-time defaults (DC-DC, no patch), so existing installs are unaffected. LinuxSX1262Wrapper follows the same way. It hand-implemented eleven RadioLibWrapper virtuals; ten of them were the CustomSX1262Wrapper version with a different cast spelled out, so it now derives from CustomSX1262Wrapper and inherits them. That is what makes the wrapper track upstream rather than drift from it -- a method added to RadioLibWrapper now arrives here implemented, instead of breaking the Linux build when it is pure virtual or silently no-opping on Linux alone when it has a default -- and it is why the README's "Upstream-sync fragility" known gap goes away. powerOff()'s cold sleep comes along with it; nothing on Linux calls it today. One side effect: CustomSX1262Wrapper.h defines USE_SX1262, which on Linux guards only a _prefs.rx_boosted_gain default that MyMesh::begin() already overwrites from meshcored.ini. The eleventh virtual, doResetAGC(), stays overridden, and Linux gains it at all for the first time: without it the daemon fell through to RadioLibWrapper's bare sleep(), so `set agc.reset.interval` was quietly a weaker knob here than anywhere else. The full reset (warm sleep, Calibrate(0x7F), calibrateImage() for the band) drops DIO2-as-RF-switch, RX boosted gain and the 0x8B5 patch, and the inherited version restores DIO2 and the patch from SX126X_* build flags this variant does not define. So SX126xReset.h gains a second entry point taking the settings as a struct, with the recalibration and the settings re-application each factored into one helper; the MCU overload keeps its #ifdef path and its behaviour. sx126xApplyRegisterPatch() replaces the register-poke that had grown to two verbatim copies. The boosted gain is read back off the chip here, as every other SX126x wrapper does it -- the ini value is only the starting point, and both the persisted pref and `set radio.rxgain` change it afterwards, so restoring the ini value would revert them at the first reset tick. Also names the downcast once: _radio is held as the base mesh::Radio, and the wrapper repeated ((LinuxSX1262 *)_radio)-> twelve times. Inheriting removes all of them; the two uses left, both in doResetAGC(), go through an r() accessor. Verified: clean linux_repeater build in the arm64 container. Neither new key is exercised on hardware -- that needs a module that wants them -- and the reset path is untested on hardware.
mmmorks
force-pushed
the
pr/02-config-validation
branch
from
September 12, 2026 21:21
037b93f to
8ecdd24
Compare
mmmorks
force-pushed
the
pr/03-sx1262-parity
branch
from
September 12, 2026 21:21
294f8c5 to
b1efd99
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
LinuxSX1262carried a hand-copied subset ofCustomSX1262(the RX watchdog, the millis setters, the boosted-gain readback) and had drifted from it. This derives it fromCustomSX1262instead, keeps only the genuinely Linux-specific part (astd_init()that reads its settings frommeshcored.iniat runtime), and closes the two gaps that drift had opened: two SX126x build-flag knobs with no Linux equivalent, and an AGC reset that was a baresleep()on Linux.What changed
src/helpers/radiolib/LinuxSX1262.h:class LinuxSX1262 : public CustomSX1262. Upstream's RX-IRQ-timeout state machine (bounding a latchedPREAMBLE_DETECTEDthat never becomes a packet) now applies on Linux too; before this,isReceiving()here could report busy forever after a preamble that went nowhere, deferring every transmit untilgetCADFailMaxDuration().std_init()shadows the non-virtual base and takesLinuxConfigby reference.meshcored.inikeys, mirroring the compile-time flags the MCU variants use, defaulting to the compile-time defaults (DC-DC, no patch) so existing installs are unaffected:use_regulator_ldo(SX126X_USE_REGULATOR_LDO,begin()'suseRegulatorLDO)rx_register_patch(SX126X_REGISTER_PATCH, bit 0 of register0x8B5)LinuxSX1262Wrapper:doResetAGC()override running the full SX126x reset (warm sleep,Calibrate(0x7F),calibrateImage()for the configured band), like every other SX126x board. Without it,set agc_intfell through toRadioLibWrapper's baresleep()fallback.powerOff()for parity withCustomSX1262Wrapper(cold sleep). Nothing calls it on Linux today.((LinuxSX1262 *)_radio)->casts become oner()accessor.src/helpers/radiolib/SX126xReset.h: recalibration drops DIO2-as-RF-switch, RX boosted gain and the0x8B5patch, and the existingsx126xResetAGC(radio, bool)restores them fromSX126X_*build flags. A runtime-configured target has none of those, so this adds a second overload taking anSX126xRxSettingsstruct, factors the recalibration and the settings re-application intosx126xRecalibrate()/sx126xApplyRxSettings(), and replaces the three verbatim copies of the register poke withsx126xApplyRegisterPatch(). The MCU overload keeps its#ifdefpath and behaviour.CustomSX1262.h: usessx126xApplyRegisterPatch()(no behaviour change).Why
Everything in the copied code is shared SX126x logic that upstream keeps fixing (the RX timeout handling, which IRQ flag a timeout clears, the setters themselves all changed this year). Keeping a fork of it meant re-applying each fix by hand and silently missing the ones nobody noticed; the missed RX-timeout port above is the concrete example.
How it was tested
linux_repeaterbuild for arm64 in the container (build-docker.sh).Dependencies
Stacked on
pr/02-config-validation(uses itsparse_bool()for the two new keys). Review the last commit only until that merges.Shared code touched
src/helpers/radiolib/SX126xReset.h(additive: struct, two helpers, a secondsx126xResetAGCoverload; the existing overload's behaviour is unchanged)src/helpers/radiolib/CustomSX1262.h(register poke replaced by the helper; no behaviour change)src/helpers/radiolib/LinuxSX1262.h,LinuxSX1262Wrapper.h(Linux-only)Noticed in passing, not changed here:
CustomLLCC68Wrapper.hcallssx126xResetAGC((SX126x *)_radio)with one argument, which does not match either overload's signature. That is pre-existing in this tree.