Omit unsupported regulator vtable slots on integrated STM32WL

This commit is contained in:
mikecarper committed 2026-10-18 22:00:03 -07:00
1 parent 758004373d
commit a886f618f1
6 files changed
+133 -15

No files matched your search

+4 -4
View File
@@ -37,12 +37,12 @@ public:
*/
class Radio {
public:
virtual uint8_t regulatorModes() const { return 0; }
virtual power::RegulatorMode defaultRegulatorMode() const { return power::RegulatorMode::Default; }
MESH_REGULATOR_VIRTUAL uint8_t regulatorModes() const { return 0; }
MESH_REGULATOR_VIRTUAL power::RegulatorMode defaultRegulatorMode() const { return power::RegulatorMode::Default; }
// Cache a selection before the first hardware initialization. Save later
// choices for the next reboot; never change a live RX/TX regulator here.
virtual bool configureBootRegulator(power::RegulatorMode) { return false; }
virtual power::RegulatorMode activeRegulatorMode() const { return power::RegulatorMode::Default; }
MESH_REGULATOR_VIRTUAL bool configureBootRegulator(power::RegulatorMode) { return false; }
MESH_REGULATOR_VIRTUAL power::RegulatorMode activeRegulatorMode() const { return power::RegulatorMode::Default; }
static constexpr uint32_t CarrierWaveDefaultMillis = 10000;
static constexpr uint32_t CarrierWaveMaxMillis = 60000;
virtual bool supportsCarrierWave() const { return false; }
+4 -4
View File
@@ -146,12 +146,12 @@ public:
virtual void onBeforeTransmit() { }
virtual void onAfterTransmit() { }
virtual void reboot() = 0;
virtual uint8_t mcuRegulatorModes() const { return 0; }
virtual power::RegulatorMode defaultMcuRegulatorMode() const {
MESH_REGULATOR_VIRTUAL uint8_t mcuRegulatorModes() const { return 0; }
MESH_REGULATOR_VIRTUAL power::RegulatorMode defaultMcuRegulatorMode() const {
return power::RegulatorMode::Default;
}
virtual bool applyMcuRegulator(power::RegulatorMode) { return false; }
virtual power::RegulatorMode activeMcuRegulatorMode() const {
MESH_REGULATOR_VIRTUAL bool applyMcuRegulator(power::RegulatorMode) { return false; }
MESH_REGULATOR_VIRTUAL power::RegulatorMode activeMcuRegulatorMode() const {
return power::RegulatorMode::Default;
}
// Reboot into a UF2-capable bootloader when the platform supports the
+18
View File
@@ -1,6 +1,24 @@
#pragma once
#include <stdint.h>
// The current integrated STM32WL driver has no selectable supply domain:
// MCU and radio share a regulator and need coordinated support first. Keep
// its unsupported defaults out of every board/radio vtable as well as the
// settings implementation. Other drivers/platforms retain virtual dispatch.
#define MESH_REGULATOR_FIXED_RADIO_CustomSTM32WLx 1
#define MESH_REGULATOR_FIXED_RADIO_I(type) MESH_REGULATOR_FIXED_RADIO_##type
#define MESH_REGULATOR_FIXED_RADIO(type) MESH_REGULATOR_FIXED_RADIO_I(type)
#if defined(STM32_PLATFORM) && MESH_REGULATOR_FIXED_RADIO(RADIO_CLASS)
#define MESH_REGULATOR_FIXED_SUPPLY 1
#define MESH_REGULATOR_VIRTUAL
#else
#define MESH_REGULATOR_FIXED_SUPPLY 0
#define MESH_REGULATOR_VIRTUAL virtual
#endif
#undef MESH_REGULATOR_FIXED_RADIO
#undef MESH_REGULATOR_FIXED_RADIO_I
#undef MESH_REGULATOR_FIXED_RADIO_CustomSTM32WLx
namespace mesh { namespace power {
enum class RegulatorMode : uint8_t { Default = 0, Dcdc = 1, Ldo = 2 };
+1 -7
View File
@@ -17,10 +17,7 @@
// nRF52 plus an external radio. Do not retain the unreachable persistence and
// recovery machinery on these flash-constrained targets. Match this exact
// driver: an STM32 target with a different radio keeps the ordinary path.
#define MESH_REGULATOR_FIXED_RADIO_CustomSTM32WLx 1
#define MESH_REGULATOR_FIXED_RADIO_I(type) MESH_REGULATOR_FIXED_RADIO_##type
#define MESH_REGULATOR_FIXED_RADIO(type) MESH_REGULATOR_FIXED_RADIO_I(type)
#if defined(STM32_PLATFORM) && MESH_REGULATOR_FIXED_RADIO(RADIO_CLASS)
#if MESH_REGULATOR_FIXED_SUPPLY
namespace mesh { namespace power {
bool beginRegulatorSettings(MainBoard&, Radio&, FILESYSTEM*) { return true; }
void serviceRegulatorSettings() {}
@@ -320,9 +317,6 @@ void resumeRegulatorRecovery() {
}} // namespace mesh::power
#endif
#undef MESH_REGULATOR_FIXED_RADIO
#undef MESH_REGULATOR_FIXED_RADIO_I
#undef MESH_REGULATOR_FIXED_RADIO_CustomSTM32WLx
extern "C" void meshRegulatorCleanReset() {
// Intentional resets can happen before the settings/filesystem is ready.
+62
View File
@@ -0,0 +1,62 @@
#include <cassert>
#include <MeshCore.h>
#include <Dispatcher.h>
using mesh::power::RegulatorMode;
#if MESH_REGULATOR_FIXED_SUPPLY && !defined(REQUIRE_VIRTUAL_REGULATOR_API)
#define REGULATOR_OVERRIDE
#else
#define REGULATOR_OVERRIDE override
#endif
struct BoardDefaults : mesh::MainBoard {
uint16_t getBattMilliVolts() override { return 0; }
const char* getManufacturerName() const override { return "test"; }
uint8_t getStartupReason() const override { return 0; }
void reboot() override {}
void sleep(uint32_t) override {} // The host must not assemble the MCU's WFI.
};
struct BoardSelection : BoardDefaults {
uint8_t mcuRegulatorModes() const REGULATOR_OVERRIDE { return 3; }
RegulatorMode defaultMcuRegulatorMode() const REGULATOR_OVERRIDE { return RegulatorMode::Dcdc; }
bool applyMcuRegulator(RegulatorMode) REGULATOR_OVERRIDE { return true; }
RegulatorMode activeMcuRegulatorMode() const REGULATOR_OVERRIDE { return RegulatorMode::Ldo; }
};
struct RadioDefaults : mesh::Radio {
int recvRaw(uint8_t*, int) override { return 0; }
uint32_t getEstAirtimeFor(int) override { return 0; }
float packetScore(float, int) override { return 0; }
bool startSendRaw(const uint8_t*, int) override { return false; }
bool isSendComplete() override { return true; }
void onSendFinished() override {}
bool isInRecvMode() const override { return false; }
};
struct RadioSelection : RadioDefaults {
uint8_t regulatorModes() const REGULATOR_OVERRIDE { return 3; }
RegulatorMode defaultRegulatorMode() const REGULATOR_OVERRIDE { return RegulatorMode::Dcdc; }
bool configureBootRegulator(RegulatorMode) REGULATOR_OVERRIDE { return true; }
RegulatorMode activeRegulatorMode() const REGULATOR_OVERRIDE { return RegulatorMode::Ldo; }
};
// Exercise the actual production base declarations through references. On
// fixed STM32WL the shadows cannot add a runtime capability; everywhere else
// all eight overrides must remain virtual and reachable through the API.
__attribute__((noinline)) void check(mesh::MainBoard& board, mesh::Radio& radio) {
constexpr bool fixed = EXPECT_FIXED_REGULATOR_API;
assert(board.mcuRegulatorModes() == (fixed ? 0 : 3));
assert(radio.regulatorModes() == (fixed ? 0 : 3));
assert(board.defaultMcuRegulatorMode() == (fixed ? RegulatorMode::Default : RegulatorMode::Dcdc));
assert(radio.defaultRegulatorMode() == (fixed ? RegulatorMode::Default : RegulatorMode::Dcdc));
assert(board.applyMcuRegulator(RegulatorMode::Dcdc) == !fixed);
assert(radio.configureBootRegulator(RegulatorMode::Dcdc) == !fixed);
assert(board.activeMcuRegulatorMode() == (fixed ? RegulatorMode::Default : RegulatorMode::Ldo));
assert(radio.activeRegulatorMode() == (fixed ? RegulatorMode::Default : RegulatorMode::Ldo));
}
int main() {
static_assert(MESH_REGULATOR_FIXED_SUPPLY == EXPECT_FIXED_REGULATOR_API,
"the fixed supply guard must match this exact platform/driver");
BoardSelection board;
RadioSelection radio;
assert(board.mcuRegulatorModes() == 3 && radio.regulatorModes() == 3);
check(board, radio);
}
+44
View File
@@ -276,5 +276,49 @@ class RegulatorSettingsTests(unittest.TestCase):
self.assertNotIn('beginRegulatorSettings(',other)
class RegulatorAPIDispatchTests(unittest.TestCase):
def compile_api(self, flags, fixed, require_virtual=False):
compiler = os.environ.get('CXX') or shutil.which('g++') or shutil.which('clang++')
if not compiler:
self.skipTest('native C++ compiler unavailable')
with tempfile.TemporaryDirectory(prefix='mesh-regulator-api-') as directory:
folder = Path(directory)
(folder/'Stream.h').write_text('#pragma once\nclass Stream {};\n')
binary = folder/'test'
command = [compiler,'-std=c++17','-O2','-Wall','-Wextra','-Werror',
'-Wno-unused-parameter','-Wno-reorder',*SANITIZER_FLAGS,*flags,
f'-DEXPECT_FIXED_REGULATOR_API={int(fixed)}',
'-I',str(folder),'-I',str(ROOT/'src'),
str(ROOT/'test/fixtures/regulator_api_dispatch.cpp'),'-o',str(binary)]
if require_virtual:
command.append('-DREQUIRE_VIRTUAL_REGULATOR_API=1')
result = subprocess.run(command,text=True,capture_output=True,timeout=45)
if require_virtual and fixed:
self.assertNotEqual(result.returncode,0)
for method in ('mcuRegulatorModes','defaultMcuRegulatorMode',
'applyMcuRegulator','activeMcuRegulatorMode','regulatorModes',
'defaultRegulatorMode','configureBootRegulator','activeRegulatorMode'):
self.assertIn(method,result.stderr)
else:
self.assertEqual(result.returncode,0,result.stdout+result.stderr)
result = subprocess.run([str(binary)],text=True,capture_output=True,timeout=10)
self.assertEqual(result.returncode,0,result.stdout+result.stderr)
def test_actual_fixed_stm32wl_base_defaults_are_not_overridden(self):
self.compile_api(['-DSTM32_PLATFORM=1','-DRADIO_CLASS=CustomSTM32WLx'],True)
def test_all_eight_fixed_stm32wl_regulator_methods_reject_override_annotations(self):
self.compile_api(['-DSTM32_PLATFORM=1','-DRADIO_CLASS=CustomSTM32WLx'],True,True)
def test_other_platforms_and_future_stm32_radios_keep_all_eight_virtual_methods(self):
for flags in ([],['-DESP32=1','-DRADIO_CLASS=CustomSTM32WLx'],
['-DNRF52_PLATFORM=1','-DRADIO_CLASS=CustomSX1262'],
['-DRP2040_PLATFORM=1','-DRADIO_CLASS=CustomSX1262'],
['-DSTM32_PLATFORM=1','-DRADIO_CLASS=CustomSX1262'],
['-DSTM32_PLATFORM=1']):
with self.subTest(flags=flags):
self.compile_api(flags,False,True)
if __name__ == '__main__':
unittest.main()