/* * This file is part of Cleanflight. * * Cleanflight is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Cleanflight is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Cleanflight. If not, see . */ #include #include #include #include #include "platform.h" #include "build_config.h" #include "drivers/system.h" #include "drivers/gpio.h" #include "drivers/timer.h" #include "drivers/serial.h" #include "drivers/serial_softserial.h" #include "drivers/serial_uart.h" #include "drivers/serial_usb_vcp.h" #include "io/serial.h" #include "serial_cli.h" #include "serial_msp.h" #include "config/config.h" #ifdef TELEMETRY #include "telemetry/telemetry.h" #endif void updateSerialRxFunctionConstraint(functionConstraint_t *functionConstraintToUpdate); void mspInit(serialConfig_t *serialConfig); void cliInit(serialConfig_t *serialConfig); // this exists so the user can reference scenarios by a number in the CLI instead of an unuser-friendly bitmask. const serialPortFunctionScenario_e serialPortScenarios[SERIAL_PORT_SCENARIO_COUNT] = { SCENARIO_UNUSED, // common scenarios in order of importance SCENARIO_MSP_CLI_TELEMETRY_GPS_PASTHROUGH, SCENARIO_GPS_ONLY, SCENARIO_SERIAL_RX_ONLY, SCENARIO_TELEMETRY_ONLY, // other scenarios SCENARIO_MSP_CLI_GPS_PASTHROUGH, SCENARIO_CLI_ONLY, SCENARIO_GPS_PASSTHROUGH_ONLY, SCENARIO_MSP_ONLY, SCENARIO_SMARTPORT_TELEMETRY_ONLY }; static serialConfig_t *serialConfig; static serialPort_t *serialPorts[SERIAL_PORT_COUNT]; #ifdef STM32F303xC static serialPortFunction_t serialPortFunctions[SERIAL_PORT_COUNT] = { {SERIAL_PORT_USB_VCP, NULL, SCENARIO_UNUSED, FUNCTION_NONE}, {SERIAL_PORT_USART1, NULL, SCENARIO_UNUSED, FUNCTION_NONE}, {SERIAL_PORT_USART2, NULL, SCENARIO_UNUSED, FUNCTION_NONE}, #if (SERIAL_PORT_COUNT > 3) {SERIAL_PORT_USART3, NULL, SCENARIO_UNUSED, FUNCTION_NONE}, #if (SERIAL_PORT_COUNT > 4) {SERIAL_PORT_USART4, NULL, SCENARIO_UNUSED, FUNCTION_NONE} #endif #endif }; const serialPortConstraint_t serialPortConstraints[SERIAL_PORT_COUNT] = { {SERIAL_PORT_USB_VCP, 9600, 115200, SPF_NONE }, {SERIAL_PORT_USART1, 9600, 115200, SPF_NONE | SPF_SUPPORTS_SBUS_MODE | SPF_SUPPORTS_BIDIR_MODE}, {SERIAL_PORT_USART2, 9600, 115200, SPF_SUPPORTS_CALLBACK | SPF_SUPPORTS_SBUS_MODE | SPF_SUPPORTS_BIDIR_MODE}, #if (SERIAL_PORT_COUNT > 3) {SERIAL_PORT_USART3, 9600, 115200, SPF_SUPPORTS_CALLBACK | SPF_SUPPORTS_SBUS_MODE | SPF_SUPPORTS_BIDIR_MODE}, #if (SERIAL_PORT_COUNT > 4) {SERIAL_PORT_USART4, 9600, 115200, SPF_SUPPORTS_CALLBACK} #endif #endif }; #else #ifdef CC3D static serialPortFunction_t serialPortFunctions[SERIAL_PORT_COUNT] = { {SERIAL_PORT_USART1, NULL, SCENARIO_UNUSED, FUNCTION_NONE}, {SERIAL_PORT_USART3, NULL, SCENARIO_UNUSED, FUNCTION_NONE}, {SERIAL_PORT_SOFTSERIAL1, NULL, SCENARIO_UNUSED, FUNCTION_NONE} }; const serialPortConstraint_t serialPortConstraints[SERIAL_PORT_COUNT] = { {SERIAL_PORT_USART1, 9600, 115200, SPF_NONE | SPF_SUPPORTS_SBUS_MODE | SPF_SUPPORTS_BIDIR_MODE}, {SERIAL_PORT_USART3, 9600, 115200, SPF_SUPPORTS_CALLBACK | SPF_SUPPORTS_SBUS_MODE | SPF_SUPPORTS_BIDIR_MODE}, {SERIAL_PORT_SOFTSERIAL1, 9600, 19200, SPF_SUPPORTS_CALLBACK | SPF_IS_SOFTWARE_INVERTABLE} }; #else static serialPortFunction_t serialPortFunctions[SERIAL_PORT_COUNT] = { {SERIAL_PORT_USART1, NULL, SCENARIO_UNUSED, FUNCTION_NONE}, {SERIAL_PORT_USART2, NULL, SCENARIO_UNUSED, FUNCTION_NONE}, #if (SERIAL_PORT_COUNT > 2) {SERIAL_PORT_SOFTSERIAL1, NULL, SCENARIO_UNUSED, FUNCTION_NONE}, {SERIAL_PORT_SOFTSERIAL2, NULL, SCENARIO_UNUSED, FUNCTION_NONE} #endif }; const serialPortConstraint_t serialPortConstraints[SERIAL_PORT_COUNT] = { {SERIAL_PORT_USART1, 9600, 115200, SPF_NONE | SPF_SUPPORTS_SBUS_MODE | SPF_SUPPORTS_BIDIR_MODE}, {SERIAL_PORT_USART2, 9600, 115200, SPF_SUPPORTS_CALLBACK | SPF_SUPPORTS_SBUS_MODE | SPF_SUPPORTS_BIDIR_MODE}, #if (SERIAL_PORT_COUNT > 2) {SERIAL_PORT_SOFTSERIAL1, 9600, 19200, SPF_SUPPORTS_CALLBACK | SPF_IS_SOFTWARE_INVERTABLE}, {SERIAL_PORT_SOFTSERIAL2, 9600, 19200, SPF_SUPPORTS_CALLBACK | SPF_IS_SOFTWARE_INVERTABLE} #endif }; #endif #endif const functionConstraint_t functionConstraints[] = { { FUNCTION_CLI, 9600, 115200, NO_AUTOBAUD, SPF_NONE }, { FUNCTION_GPS, 9600, 115200, AUTOBAUD, SPF_NONE }, { FUNCTION_GPS_PASSTHROUGH, 9600, 115200, NO_AUTOBAUD, SPF_NONE }, { FUNCTION_MSP, 9600, 115200, NO_AUTOBAUD, SPF_NONE }, { FUNCTION_SERIAL_RX, 9600, 115200, NO_AUTOBAUD, SPF_SUPPORTS_SBUS_MODE | SPF_SUPPORTS_CALLBACK }, { FUNCTION_TELEMETRY, 9600, 19200, NO_AUTOBAUD, SPF_NONE }, { FUNCTION_SMARTPORT_TELEMETRY, 57600, 57600, NO_AUTOBAUD, SPF_SUPPORTS_BIDIR_MODE } }; #define FUNCTION_CONSTRAINT_COUNT (sizeof(functionConstraints) / sizeof(functionConstraint_t)) typedef struct serialPortSearchResult_s { serialPortIndex_e portIndex; serialPortFunction_t *portFunction; const serialPortConstraint_t *portConstraint; const functionConstraint_t *functionConstraint; // private uint8_t startSerialPortFunctionIndex; // used by findNextSerialPort } serialPortSearchResult_t; static const serialPortFunctionList_t serialPortFunctionList = { SERIAL_PORT_COUNT, serialPortFunctions }; const serialPortFunctionList_t *getSerialPortFunctionList(void) { return &serialPortFunctionList; } uint8_t lookupScenarioIndex(serialPortFunctionScenario_e scenario) { uint8_t index; for (index = 0; index < SERIAL_PORT_SCENARIO_COUNT; index++) { if (serialPortScenarios[index] == scenario) { break; } } return index; } static serialPortIndex_e lookupSerialPortIndexByIdentifier(serialPortIdentifier_e identifier) { serialPortIndex_e portIndex; for (portIndex = 0; portIndex < SERIAL_PORT_COUNT; portIndex++) { if (serialPortConstraints[portIndex].identifier == identifier) { break; } } return portIndex; } #define IDENTIFIER_NOT_FOUND 0xFF static uint8_t lookupSerialPortFunctionIndexByIdentifier(serialPortIdentifier_e identifier) { uint8_t index; for (index = 0; index < SERIAL_PORT_COUNT; index++) { if (serialPortFunctions[index].identifier == identifier) { return index; } } return IDENTIFIER_NOT_FOUND; } static const functionConstraint_t *findFunctionConstraint(serialPortFunction_e function) { const functionConstraint_t *functionConstraint = NULL; uint8_t functionConstraintIndex; for (functionConstraintIndex = 0; functionConstraintIndex < FUNCTION_CONSTRAINT_COUNT; functionConstraintIndex++) { functionConstraint = &functionConstraints[functionConstraintIndex]; if (functionConstraint->function == function) { return functionConstraint; } } return NULL; } static uint8_t countBits_uint32(uint32_t n) { uint8_t c; // c accumulates the total bits set in n for (c = 0; n; c++) n &= n - 1; // clear the least significant bit set return c; } static int serialPortFunctionMostSpecificFirstComparator(const void *aPtr, const void *bPtr) { serialPortFunction_t *a = (serialPortFunction_t *)aPtr; serialPortFunction_t *b = (serialPortFunction_t *)bPtr; return countBits_uint32(a->scenario) - countBits_uint32(b->scenario); } static void sortSerialPortFunctions(serialPortFunction_t *serialPortFunctions, uint8_t elements) { serialPortFunction_t swap; int index1; int index2; // bubble-sort array (TODO - port selection can be implemented as repeated minimum search with bitmask marking used elements) for (index1 = 0; index1 < (elements - 1); index1++) { for (index2 = 0; index2 < elements - index1 - 1; index2++) { if(serialPortFunctionMostSpecificFirstComparator(&serialPortFunctions[index2], &serialPortFunctions[index2 + 1]) > 0) { swap = serialPortFunctions[index2]; serialPortFunctions[index2] = serialPortFunctions[index2 + 1]; serialPortFunctions[index2 + 1] = swap; } } } } serialPortSearchResult_t *findNextSerialPort(serialPortFunction_e function, const functionConstraint_t *functionConstraint, serialPortSearchResult_t *resultBuffer) { uint8_t serialPortFunctionIndex; serialPortFunction_t *serialPortFunction; for (serialPortFunctionIndex = resultBuffer->startSerialPortFunctionIndex; serialPortFunctionIndex < SERIAL_PORT_COUNT; serialPortFunctionIndex++) { serialPortFunction = &serialPortFunctions[serialPortFunctionIndex]; if (!(serialPortFunction->scenario & function)) { continue; } uint8_t serialPortIndex = lookupSerialPortIndexByIdentifier(serialPortFunction->identifier); const serialPortConstraint_t *serialPortConstraint = &serialPortConstraints[serialPortIndex]; #if defined(CC3D) if (!feature(FEATURE_SOFTSERIAL) && ( serialPortConstraint->identifier == SERIAL_PORT_SOFTSERIAL1)) { continue; } #else #if defined(USE_SOFTSERIAL1) ||(defined(USE_SOFTSERIAL2)) if (!feature(FEATURE_SOFTSERIAL) && ( serialPortConstraint->identifier == SERIAL_PORT_SOFTSERIAL1 || serialPortConstraint->identifier == SERIAL_PORT_SOFTSERIAL2 )) { continue; } #endif #if (defined(NAZE) || defined(OLIMEXINO)) && defined(SONAR) if (feature(FEATURE_SONAR) && !feature(FEATURE_RX_PARALLEL_PWM) && (serialPortConstraint->identifier == SERIAL_PORT_SOFTSERIAL2)) { continue; } #endif #endif if ((serialPortConstraint->feature & functionConstraint->requiredSerialPortFeatures) != functionConstraint->requiredSerialPortFeatures) { continue; } if (functionConstraint->minBaudRate < serialPortConstraint->minBaudRate || functionConstraint->maxBaudRate > serialPortConstraint->maxBaudRate) { continue; } resultBuffer->portIndex = serialPortIndex; resultBuffer->portConstraint = serialPortConstraint; resultBuffer->portFunction = serialPortFunction; resultBuffer->functionConstraint = functionConstraint; uint8_t nextStartIndex = serialPortFunctionIndex + 1; resultBuffer->startSerialPortFunctionIndex = nextStartIndex; return resultBuffer; } return NULL; } /* * since this method, and other methods that use it, use a single instance of * searchPortSearchResult be sure to copy the data out of it before it gets overwritten by another caller. * If this becomes a problem perhaps change the implementation to use a destination argument. */ static serialPortSearchResult_t *findSerialPort(serialPortFunction_e function, const functionConstraint_t *functionConstraint) { static serialPortSearchResult_t serialPortSearchResult; memset(&serialPortSearchResult, 0, sizeof(serialPortSearchResult)); // FIXME this only needs to be done once, after the config has been loaded. sortSerialPortFunctions(serialPortFunctions, SERIAL_PORT_COUNT); return findNextSerialPort(function, functionConstraint, &serialPortSearchResult); } static serialPortFunction_t *findSerialPortFunction(uint16_t functionMask) { serialPortIndex_e portIndex; // find exact match first for (portIndex = 0; portIndex < SERIAL_PORT_COUNT; portIndex++) { serialPortFunction_t *serialPortFunction = &serialPortFunctions[portIndex]; if (serialPortFunction->scenario == functionMask) { return serialPortFunction; } } // find the first port that supports the function requested for (portIndex = 0; portIndex < SERIAL_PORT_COUNT; portIndex++) { serialPortFunction_t *serialPortFunction = &serialPortFunctions[portIndex]; if (serialPortFunction->scenario & functionMask) { return serialPortFunction; } } return NULL; } /* * find a serial port that is: * a) open * b) matches the function mask exactly, or if an exact match is not found the first port that supports the function */ serialPort_t *findOpenSerialPort(uint16_t functionMask) { serialPortFunction_t *function = findSerialPortFunction(functionMask); if (!function) { return NULL; } return function->port; } static serialPortFunction_t * findSerialPortFunctionByPort(serialPort_t *port) { serialPortFunction_t *serialPortFunction; uint8_t functionIndex; for (functionIndex = 0; functionIndex < SERIAL_PORT_COUNT; functionIndex++) { serialPortFunction = &serialPortFunctions[functionIndex]; if (serialPortFunction->port == port) { return serialPortFunction; } } return NULL; } void beginSerialPortFunction(serialPort_t *port, serialPortFunction_e function) { serialPortFunction_t *serialPortFunction = findSerialPortFunctionByPort(port); serialPortFunction->currentFunction = function; } void endSerialPortFunction(serialPort_t *port, serialPortFunction_e function) { UNUSED(function); serialPortFunction_t *serialPortFunction = findSerialPortFunctionByPort(port); serialPortFunction->currentFunction = FUNCTION_NONE; serialPortFunction->port = NULL; } functionConstraint_t *getConfiguredFunctionConstraint(serialPortFunction_e function) { static functionConstraint_t configuredFunctionConstraint; const functionConstraint_t *functionConstraint; functionConstraint = findFunctionConstraint(function); if (!functionConstraint) { return NULL; } memcpy(&configuredFunctionConstraint, functionConstraint, sizeof(functionConstraint_t)); switch(function) { case FUNCTION_MSP: configuredFunctionConstraint.maxBaudRate = serialConfig->msp_baudrate; break; case FUNCTION_CLI: configuredFunctionConstraint.minBaudRate = serialConfig->cli_baudrate; configuredFunctionConstraint.maxBaudRate = configuredFunctionConstraint.minBaudRate; break; case FUNCTION_GPS_PASSTHROUGH: configuredFunctionConstraint.minBaudRate = serialConfig->gps_passthrough_baudrate; configuredFunctionConstraint.maxBaudRate = configuredFunctionConstraint.minBaudRate; break; #ifdef TELEMETRY case FUNCTION_TELEMETRY: case FUNCTION_SMARTPORT_TELEMETRY: configuredFunctionConstraint.minBaudRate = getTelemetryProviderBaudRate(); configuredFunctionConstraint.maxBaudRate = configuredFunctionConstraint.minBaudRate; break; #endif #ifdef SERIAL_RX case FUNCTION_SERIAL_RX: updateSerialRxFunctionConstraint(&configuredFunctionConstraint); break; #endif case FUNCTION_GPS: configuredFunctionConstraint.maxBaudRate = serialConfig->gps_baudrate; break; default: break; } return &configuredFunctionConstraint; } bool isSerialConfigValid(serialConfig_t *serialConfigToCheck) { serialPortSearchResult_t *searchResult; functionConstraint_t *functionConstraint; serialConfig = serialConfigToCheck; functionConstraint = getConfiguredFunctionConstraint(FUNCTION_MSP); searchResult = findSerialPort(FUNCTION_MSP, functionConstraint); if (!searchResult) { return false; } functionConstraint = getConfiguredFunctionConstraint(FUNCTION_CLI); searchResult = findSerialPort(FUNCTION_CLI, functionConstraint); if (!searchResult) { return false; } functionConstraint = getConfiguredFunctionConstraint(FUNCTION_GPS); searchResult = findSerialPort(FUNCTION_GPS, functionConstraint); if (feature(FEATURE_GPS) && !searchResult) { return false; } functionConstraint = getConfiguredFunctionConstraint(FUNCTION_SERIAL_RX); searchResult = findSerialPort(FUNCTION_SERIAL_RX, functionConstraint); if (feature(FEATURE_RX_SERIAL) && !searchResult) { return false; } functionConstraint = getConfiguredFunctionConstraint(FUNCTION_TELEMETRY); searchResult = findSerialPort(FUNCTION_TELEMETRY, functionConstraint); // TODO check explicitly for SmartPort config if (!searchResult) { functionConstraint = getConfiguredFunctionConstraint(FUNCTION_SMARTPORT_TELEMETRY); searchResult = findSerialPort(FUNCTION_SMARTPORT_TELEMETRY, functionConstraint); } if (feature(FEATURE_TELEMETRY) && !searchResult) { return false; } uint8_t functionIndex; uint8_t cliPortCount = 0; uint8_t mspPortCount = 0; for (functionIndex = 0; functionIndex < SERIAL_PORT_COUNT; functionIndex++) { if (serialPortFunctions[functionIndex].scenario & FUNCTION_CLI) { if (++cliPortCount > 1) { return false; } } if (serialPortFunctions[functionIndex].scenario & FUNCTION_MSP) { if (++mspPortCount > MAX_MSP_PORT_COUNT) { return false; } } } return true; } bool doesConfigurationUsePort(serialPortIdentifier_e portIdentifier) { serialPortSearchResult_t *searchResult; const functionConstraint_t *functionConstraint; serialPortFunction_e function; uint8_t index; for (index = 0; index < FUNCTION_CONSTRAINT_COUNT; index++) { function = functionConstraints[index].function; functionConstraint = findFunctionConstraint(function); searchResult = findSerialPort(function, functionConstraint); if (searchResult->portConstraint->identifier == portIdentifier) { return true; } } return false; } bool canOpenSerialPort(serialPortFunction_e function) { functionConstraint_t *functionConstraint = getConfiguredFunctionConstraint(function); serialPortSearchResult_t *result = findSerialPort(function, functionConstraint); return result != NULL; } bool isSerialPortFunctionShared(serialPortFunction_e functionToUse, uint16_t functionMask) { functionConstraint_t *functionConstraint = getConfiguredFunctionConstraint(functionToUse); serialPortSearchResult_t *result = findSerialPort(functionToUse, functionConstraint); if (!result) { return false; } return result->portFunction->scenario & functionMask; } serialPort_t *findSharedSerialPort(serialPortFunction_e functionToUse, uint16_t functionMask) { functionConstraint_t *functionConstraint = getConfiguredFunctionConstraint(functionToUse); serialPortSearchResult_t *result = findSerialPort(functionToUse, functionConstraint); if (result->portFunction->scenario & functionMask) { return result->portFunction->port; } return NULL; } void applySerialConfigToPortFunctions(serialConfig_t *serialConfig) { uint32_t portIndex = 0, serialPortIdentifier, constraintIndex; for (constraintIndex = 0; constraintIndex < SERIAL_PORT_COUNT && portIndex < SERIAL_PORT_COUNT; constraintIndex++) { serialPortIdentifier = serialPortConstraints[constraintIndex].identifier; uint32_t functionIndex = lookupSerialPortFunctionIndexByIdentifier(serialPortIdentifier); if (functionIndex == IDENTIFIER_NOT_FOUND) { continue; } serialPortFunctions[functionIndex].scenario = serialPortScenarios[serialConfig->serial_port_scenario[portIndex++]]; } } serialPort_t *openSerialPort(serialPortFunction_e function, serialReceiveCallbackPtr callback, uint32_t baudRate, portMode_t mode, serialInversion_e inversion) { serialPort_t *serialPort = NULL; functionConstraint_t *initialFunctionConstraint = getConfiguredFunctionConstraint(function); functionConstraint_t updatedFunctionConstraint; memcpy(&updatedFunctionConstraint, initialFunctionConstraint, sizeof(updatedFunctionConstraint)); if (initialFunctionConstraint->autoBaud == NO_AUTOBAUD) { updatedFunctionConstraint.minBaudRate = baudRate; updatedFunctionConstraint.maxBaudRate = baudRate; } functionConstraint_t *functionConstraint = &updatedFunctionConstraint; serialPortSearchResult_t *searchResult = findSerialPort(function, functionConstraint); while(searchResult && searchResult->portFunction->port) { // port is already open, find the next one searchResult = findNextSerialPort(function, functionConstraint, searchResult); } if (!searchResult) { return NULL; } serialPortIndex_e portIndex = searchResult->portIndex; const serialPortConstraint_t *serialPortConstraint = searchResult->portConstraint; serialPortIdentifier_e identifier = serialPortConstraint->identifier; switch(identifier) { #ifdef USE_VCP case SERIAL_PORT_USB_VCP: serialPort = usbVcpOpen(); break; #endif #ifdef USE_USART1 case SERIAL_PORT_USART1: serialPort = uartOpen(USART1, callback, baudRate, mode, inversion); break; #endif #ifdef USE_USART2 case SERIAL_PORT_USART2: serialPort = uartOpen(USART2, callback, baudRate, mode, inversion); break; #endif #ifdef USE_USART3 case SERIAL_PORT_USART3: serialPort = uartOpen(USART3, callback, baudRate, mode, inversion); break; #endif #ifdef USE_SOFTSERIAL1 case SERIAL_PORT_SOFTSERIAL1: serialPort = openSoftSerial(SOFTSERIAL1, callback, baudRate, inversion); serialSetMode(serialPort, mode); break; #endif #ifdef USE_SOFTSERIAL2 case SERIAL_PORT_SOFTSERIAL2: serialPort = openSoftSerial(SOFTSERIAL2, callback, baudRate, inversion); serialSetMode(serialPort, mode); break; #endif default: break; } if (!serialPort) { return NULL; } serialPort->identifier = identifier; serialPorts[portIndex] = serialPort; serialPortFunction_t *serialPortFunction = searchResult->portFunction; serialPortFunction->port = serialPort; //serialPortFunction->identifier = identifier; beginSerialPortFunction(serialPort, function); return serialPort; } void serialInit(serialConfig_t *initialSerialConfig) { serialConfig = initialSerialConfig; applySerialConfigToPortFunctions(serialConfig); #ifdef TELEMETRY if (telemetryAllowsOtherSerial(FUNCTION_MSP)) #endif mspInit(serialConfig); #ifdef TELEMETRY if (telemetryAllowsOtherSerial(FUNCTION_CLI)) #endif cliInit(serialConfig); } void handleSerial(void) { // in cli mode, all serial stuff goes to here. enter cli mode by sending # if (cliMode) { cliProcess(); return; } #ifdef TELEMETRY if (telemetryAllowsOtherSerial(FUNCTION_MSP)) #endif mspProcess(); } void waitForSerialPortToFinishTransmitting(serialPort_t *serialPort) { while (!isSerialTransmitBufferEmpty(serialPort)) { delay(10); }; } // evaluate all other incoming serial data void evaluateOtherData(uint8_t sr) { if (sr == '#') cliProcess(); else if (sr == serialConfig->reboot_character) systemResetToBootloader(); }