diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index e2334cff7..ed55f7aa4 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1785,7 +1785,7 @@ bool MyMesh::resolveChannelAlias(ChannelDetails& channel) const { })) return false; const auto kind = parseCompanionChannelAlias(channel.name).kind; #if MESH_ENABLE_MESHTEXT_CHAT - if (kind == CompanionChannelKind::Meshtext) channel.channel.tx_radio = mesh::RADIO_TX_OFF; + if (isMeshtextChannel(channel)) channel.channel.tx_radio = mesh::RADIO_TX_OFF; #endif if (isAliasRadio2Defined() && (kind == CompanionChannelKind::Radio2 || kind == CompanionChannelKind::Meshtext)) { @@ -4078,11 +4078,11 @@ bool MyMesh::handleTxRoutingCommand(const char* command, char* reply, size_t rep return true; } -__attribute__((noinline, cold)) bool MyMesh::handleLocalControlCommand(const char* command, char* reply, size_t reply_size) { if (!command || !reply || reply_size == 0) return false; while (*command == ' ') command++; + if (mesh::handleUsbModeCommand(command, reply, reply_size, Serial)) return true; #if defined(ESP32_PLATFORM) && defined(WIFI_SSID) if (strncmp(command, "password", 8) == 0 && (command[8] == 0 || command[8] == ' ' || command[8] == '\t')) { @@ -11139,8 +11139,6 @@ bool MyMesh::handleCommand(const char* command, uint32_t sender_timestamp, while (*command == ' ' || *command == '\t') command++; } - if (mesh::handleUsbModeCommand(command, reply, reply_capacity, Serial)) return true; - #if defined(ESP32_PLATFORM) // Catch remote requests before the direct-transport router below. That // router is also used by USB/BLE/browser commands without a timestamp. diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 24682ed61..dce9ba037 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -3560,7 +3560,6 @@ bool CommonCLI::handleUDPBridgeCommand(uint32_t sender_timestamp, void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* reply) { PrefsSaveReplyGuard save_reply(_prefs_save_failures, reply); mesh::cli::normalizeCommandVerb(command); - if (mesh::handleUsbModeCommand(command, reply, 160, Serial)) return; #if MESH_ENABLE_HOST_KISS if (mesh::hostkiss::handleCommand(command, reply, 157, sender_timestamp == 0)) return; #endif @@ -5992,6 +5991,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* reply) { + if (mesh::handleUsbModeCommand(command, reply, 160, Serial)) return; const char* config = &command[4]; #if defined(RAK_4631) if (mesh::peripherals::handleCommand(_prefs->peripherals_shared, command, diff --git a/src/helpers/CompanionChannelAlias.h b/src/helpers/CompanionChannelAlias.h index 1c741bcc3..7c434b595 100644 --- a/src/helpers/CompanionChannelAlias.h +++ b/src/helpers/CompanionChannelAlias.h @@ -34,12 +34,9 @@ inline CompanionChannelAlias parseCompanionChannelAlias(const char* name) { // Only exact named radio2 aliases request RF setup. Ordinary channels and // arbitrary r2 labels keep the operator's existing profile. inline const char* companionChannelRadio2Preset(const char* name) { - if (!name) return nullptr; - if (*name == '#') ++name; - if (strncmp(name, "r2-", 3)) return nullptr; - name += 3; - // An exact preset tail also excludes every invalid/nested alias form. - return mesh::cli::radio2PresetTuple(name) ? name : nullptr; + const auto alias = parseCompanionChannelAlias(name); + return alias.kind == CompanionChannelKind::Radio2 + && mesh::cli::radio2PresetTuple(alias.tail) ? alias.tail : nullptr; } inline bool companionChannelCanonicalName(const char* name, char* out, size_t size) { diff --git a/test/fixtures/companion_rxps_getters.cpp b/test/fixtures/companion_rxps_getters.cpp index e2d4cf373..71bdd4a49 100644 --- a/test/fixtures/companion_rxps_getters.cpp +++ b/test/fixtures/companion_rxps_getters.cpp @@ -6,6 +6,7 @@ #include #include #include "helpers/radiolib/RXPowerSaving.h" +#include "usb_mode_console.h" struct Radio { bool supported = true; diff --git a/test/test_usb_mode.py b/test/test_usb_mode.py index 60ce40840..843abbe84 100644 --- a/test/test_usb_mode.py +++ b/test/test_usb_mode.py @@ -127,18 +127,26 @@ class UsbModeTests(unittest.TestCase): self.compile_and_run(directory, "alias" + str(index), flags, expected, primary=primary) def test_actual_common_and_companion_dispatch_including_v14_request_prefix(self): - common = method((ROOT / "src/helpers/CommonCLI.cpp").read_text(), - "void CommonCLI::handleCommand(") - companion = method((ROOT / "examples/companion_radio/MyMesh.cpp").read_text(), - "bool MyMesh::handleCommand(") - call = "if (mesh::handleUsbModeCommand(command, reply, reply_capacity, Serial)) return true;" - self.assertIn(call, companion) - # Execute the real early dispatch bodies, before unrelated board/radio - # services. The framed V14 command path invokes this same handleCommand. - common = common.split("#if MESH_ENABLE_HOST_KISS", 1)[0] + "\n}" - companion = companion[:companion.index(call) + len(call)] + "\nreturn false;\n}" - # The omitted unrelated dispatch branches normally use this argument. + common_source = (ROOT / "src/helpers/CommonCLI.cpp").read_text() + companion_source = (ROOT / "examples/companion_radio/MyMesh.cpp").read_text() + common = method(common_source, "void CommonCLI::handleCommand(") + getter = method(common_source, "void CommonCLI::handleGetCmd(") + companion = method(companion_source, "bool MyMesh::handleCommand(") + control = method(companion_source, "bool MyMesh::handleLocalControlCommand(") + self.assertNotIn("handleUsbModeCommand", common) + self.assertNotIn("handleUsbModeCommand", companion) + self.assertIn("handleUsbModeCommand", getter) + self.assertIn("handleUsbModeCommand", control) + # Execute each actual entry and getter registration, retaining the V14 + # prefix/capacity handling while omitting unrelated hardware branches. + common = common.split("#if MESH_ENABLE_HOST_KISS", 1)[0] + method( + common, 'if (memcmp(command, "get ", 4) == 0)') + "\n}" + getter = getter[:getter.index(" const char* config")] + "\n}" + control = control[:control.index("#if defined(ESP32_PLATFORM)")] + "return false;\n}" + companion = companion[:companion.index("#if defined(ESP32_PLATFORM)")] + companion += " return handleLocalControlCommand(command, reply, reply_capacity);\n}" common = common.replace("{", "{ (void)sender_timestamp;", 1) + getter = getter.replace("{", "{ (void)sender_timestamp;", 1) companion = companion.replace("{", "{ (void)sender_timestamp;", 1) dispatch = r''' struct PrefsSaveReplyGuard { @@ -148,8 +156,12 @@ namespace mesh { namespace cli { void normalizeCommandVerb(char*) {} } } struct CommonCLI { unsigned _prefs_save_failures = 0; void handleCommand(uint32_t, char*, char*); + void handleGetCmd(uint32_t, char*, char*); +}; +struct MyMesh { + bool handleCommand(const char*, uint32_t, char*); + bool handleLocalControlCommand(const char*, char*, size_t); }; -struct MyMesh { bool handleCommand(const char*, uint32_t, char*); }; ''' cases = r''' CommonCLI common; @@ -164,8 +176,13 @@ struct MyMesh { bool handleCommand(const char*, uint32_t, char*); }; assert(!companion.handleCommand("set usb.mode tinyusb", 0, reply)); ''' with tempfile.TemporaryDirectory(prefix="meshcore-usb-mode-dispatch-") as directory: - self.compile_and_run(directory, "dispatch", ["ESP32", "ARDUINO_USB_MODE=1", - "ARDUINO_USB_CDC_ON_BOOT=1"], "hardware-cdc-jtag", dispatch + common + companion, cases) + for index, (flags, expected) in enumerate(( + (["ESP32", "ARDUINO_USB_MODE=1", "ARDUINO_USB_CDC_ON_BOOT=1"], "hardware-cdc-jtag"), + (["ESP32", "ARDUINO_USB_MODE=0", "ARDUINO_USB_CDC_ON_BOOT=1"], "tinyusb"), + (["NRF52_PLATFORM", "USE_TINYUSB", "MESH_DUAL_CDC_LOGGING"], "tinyusb"), + (["STM32_PLATFORM"], "unknown"))): + self.compile_and_run(directory, "dispatch" + str(index), flags, expected, + dispatch + getter + control + common + companion, cases) full = (ROOT / "examples/companion_radio/MyMesh.cpp").read_text() frame = full[full.index("mesh::companion::isRunCliFrame"):] self.assertIn("handleCommand(text, 0, reply_buf)", frame)