mirror of
https://github.com/meshtastic/firmware.git
synced 2026-11-06 22:34:13 -05:00
* test: pin the CAD commands and LR20x0Interface's busy check against a modelled chip The LR2021 refuses SetLoraCadParams exit mode 0x02, the value of RadioLib's RADIOLIB_LR2021_CAD_EXIT_MODE_RX, with CMD_PERR (-706), and LR20x0Interface::isChannelActive() reads the error as a free channel: on the bench the node transmitted over live frames (16/16), and a Mesh-Tracker X1 gave no busy verdict in 539 scans. RecordingHal gains deferred replies N transactions out (the IRQ status read has no opcode), a rejectUnless() chip model, and an IRQ pin that reads high so scanChannel() returns. lr2021_tests.h and lr11x0_tests.h pin the CAD command bytes, the exit-mode defines, and that a refused command returns an error and starts no CAD. lr20x0_interface_tests.h runs the real isChannelActive() against the modelled chip: a detected preamble must read busy, and sending 0x02 fails both of those tests (checked by mutating LR20x0Interface.cpp). * test: bring RecordingHal and suite-order comments in line with the code digitalRead() also models the IRQ line now, a deferred reply has a stated precedence when two come due on one transaction, and the LR20x0Interface set runs after the LR2021 set for the same DC-DC reason. * test: three small corrections to the CAD test comments and macros The RADIO macros still passed `true` for reply()'s fourth argument, which this branch turned from a bool into `uint8_t after`, a count of transactions to defer by. It converts to 1, so nothing changed, but 1 and 2 now mean different things - lr2021AnswerIrq() passes 2 - so the call sites should not read as a bool. The comments described the refused exit byte as something develop does. It does not: develop's isChannelActive() sends 0x01 from its own local CAD_EXIT_MODE_RX, and these tests are what keep it there. Reworded in test_main.cpp, lr2021_tests.h and lr20x0_interface_tests.h so a later reader does not go looking for a live bug. test_lr2021_cad_irq_routed_to_dio's comment claimed DIO5 is "the line the interface's ISR watches". 5 is LR2021::irqDioNum's in-class default in RadioLib; the firmware assigns irqDioNum in LR20x0Interface::init() from LR2021_IRQ_DIO_NUM, IRQ_DIO_NUM or portduino_config.irq_dio_num, and falls back to RadioLib's default only when the variant defines neither. The assertion is worth keeping - it just pins RadioLib's default rather than the firmware's choice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011C7D9biGfpxLruq7DFLzsP --------- Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
59 lines
3.1 KiB
C++
59 lines
3.1 KiB
C++
// Unit tests for the RadioLib driver calls Meshtastic's radio interfaces make, one set per chip
|
|
// family: SX126x (sx126x_tests.h), SX127x (sx127x_tests.h), SX128x (sx128x_tests.h), LR11x0
|
|
// (lr11x0_tests.h) and LR2021 (lr2021_tests.h), against the RadioLib pinned in platformio.ini.
|
|
//
|
|
// No chip is needed. RecordingHal (RecordingHal.h) logs every SPI transaction and answers with a
|
|
// status byte every status-byte family reads as success, plus the few scripted replies the setters
|
|
// check before acting (the packet type); for SX127x it keeps a register file instead. That reaches
|
|
// every setter and mode command the interfaces use, and the tests pin what reaches the chip: the
|
|
// opcode, the frame length, and where the value is fixed, the payload.
|
|
//
|
|
// The regressions guarded are driver changes that send the wrong bytes, or read or write past a
|
|
// buffer, on paths no hardware-free test reached before. Under [env:coverage] (-fsanitize=address)
|
|
// an out-of-bounds access aborts the program at the call. The first such bug is jgromes/RadioLib#1864:
|
|
// the LR2021 DC-DC workaround passes sizeof(uint32_t) as a word count, overrunning the stack on
|
|
// every setRxPath() and LoRa modulation change. The pin here, 7.7.1, predates that workaround, so
|
|
// the LR2021 DC-DC tests register only on a RadioLib that has it (see lr2021_tests.h). On one that
|
|
// also carries the overrun (7.8.0) they abort, so the LR2021 set and the LR20x0Interface set, whose
|
|
// setup runs the same workaround, run last and the rest report first.
|
|
//
|
|
// CAD (channel activity detection, the listen-before-talk check) is pinned per family: the command
|
|
// bytes, and that a command the chip refuses comes back as an error, never CHANNEL_FREE. The LR2021
|
|
// refuses SetLoraCadParams exit mode 0x02, the value of RadioLib's RADIOLIB_LR2021_CAD_EXIT_MODE_RX
|
|
// (a SetCadParams value; jgromes/RadioLib#1882 adds the LoRa ones), and LR20x0Interface reads the
|
|
// error as a free channel, so a node sending that define would transmit over live frames. develop
|
|
// sends 0x01 from a local constant and does not, which is what is pinned here. lr2021_tests.h
|
|
// models that refusal; lr20x0_interface_tests.h runs LR20x0Interface::isChannelActive() against it,
|
|
// so a firmware change that sends a refused exit byte fails here.
|
|
//
|
|
// Anything that decodes a chip reply (begin(), readData(), getRSSI(), updateFirmware()) needs replies
|
|
// scripted per opcode; each family's header lists those under "Grows here".
|
|
#include "TestUtil.h"
|
|
#include <RadioLib.h>
|
|
#include <unity.h>
|
|
|
|
#include "lr11x0_tests.h"
|
|
#include "lr2021_tests.h"
|
|
#include "lr20x0_interface_tests.h"
|
|
#include "sx126x_tests.h"
|
|
#include "sx127x_tests.h"
|
|
#include "sx128x_tests.h"
|
|
|
|
void setUp(void) {}
|
|
void tearDown(void) {}
|
|
|
|
void setup()
|
|
{
|
|
initializeTestEnvironment();
|
|
UNITY_BEGIN();
|
|
runSx126xTests();
|
|
runSx127xTests();
|
|
runSx128xTests();
|
|
runLr11x0Tests();
|
|
runLr2021Tests(); // after the rest: its DC-DC tests abort under ASan on a RadioLib with the #1864 overrun
|
|
runLr20x0InterfaceTests(); // its setSpreadingFactor() runs that same DC-DC workaround
|
|
exit(UNITY_END());
|
|
}
|
|
|
|
void loop() {}
|