diff --git a/docs/Serial.md b/docs/Serial.md index ef10d4d0a3e..7450ec5e8a9 100644 --- a/docs/Serial.md +++ b/docs/Serial.md @@ -48,6 +48,7 @@ e.g. after configuring a port for GPS enable the GPS feature. * All telemetry systems except MSP will ignore any attempts to override the baudrate. * MSP/CLI can be shared with EITHER Blackbox OR telemetry (LTM or MAVlink, not RX telemetry). In shared mode blackbox or telemetry will be output only when armed. * Smartport telemetry cannot be shared with MSP. +* SBUS servo output (`SERVO_SERIAL`) can only be assigned to one port. The CLI refuses a second assignment; previously it was accepted and shown in the port list, but the second port was never opened. * No other serial port sharing combinations are valid. * You can use as many different telemetry systems as you like at the same time. * You can only use each telemetry system once. e.g. FrSky telemetry cannot be used on two port, but LTN Telemetry and FrSky on two different ports is fine. diff --git a/src/main/fc/cli.c b/src/main/fc/cli.c index 718dadca046..01be003f49a 100644 --- a/src/main/fc/cli.c +++ b/src/main/fc/cli.c @@ -929,6 +929,12 @@ static void cliSerial(char *cmdline) return; } + const uint32_t duplicatedFunctions = serialDuplicatedSinglePortFunctions(&portConfig); + if (duplicatedFunctions) { + cliPrintErrorLinef("Function %d is already assigned to another port", (int)duplicatedFunctions); + return; + } + memcpy(currentConfig, &portConfig, sizeof(portConfig)); } diff --git a/src/main/io/serial.c b/src/main/io/serial.c index d88f071e7cd..9574ed363c2 100644 --- a/src/main/io/serial.c +++ b/src/main/io/serial.c @@ -313,6 +313,25 @@ bool isSerialConfigValid(const serialConfig_t *serialConfigToCheck) return true; } +// Functions that are resolved with findSerialPortConfig() and therefore only ever use the +// first port they are assigned to. Assigning one of them to a second port has no effect at +// all, so the configuration layer refuses it instead of ignoring it silently. +#define SERIAL_SINGLE_PORT_FUNCTIONS (FUNCTION_SERVO_SERIAL) + +uint32_t serialDuplicatedSinglePortFunctions(const serialPortConfig_t *portConfigToCheck) +{ + uint32_t functionMaskOfOtherPorts = 0; + + for (int index = 0; index < SERIAL_PORT_COUNT; index++) { + const serialPortConfig_t *portConfig = &serialConfig()->portConfigs[index]; + if (portConfig->identifier != portConfigToCheck->identifier) { + functionMaskOfOtherPorts |= portConfig->functionMask; + } + } + + return portConfigToCheck->functionMask & functionMaskOfOtherPorts & SERIAL_SINGLE_PORT_FUNCTIONS; +} + serialPortConfig_t *serialFindPortConfiguration(serialPortIdentifier_e identifier) { for (int index = 0; index < SERIAL_PORT_COUNT; index++) { diff --git a/src/main/io/serial.h b/src/main/io/serial.h index 36f2e02328a..2274ec6e36e 100644 --- a/src/main/io/serial.h +++ b/src/main/io/serial.h @@ -159,6 +159,7 @@ void serialRemovePort(serialPortIdentifier_e identifier); uint8_t serialGetAvailablePortCount(void); bool serialIsPortAvailable(serialPortIdentifier_e identifier); bool isSerialConfigValid(const serialConfig_t *serialConfig); +uint32_t serialDuplicatedSinglePortFunctions(const serialPortConfig_t *portConfigToCheck); serialPortConfig_t *serialFindPortConfiguration(serialPortIdentifier_e identifier); bool doesConfigurationUsePort(serialPortIdentifier_e portIdentifier); serialPortConfig_t *findSerialPortConfig(serialPortFunction_e function);