Files
edgetx-gx12/radio/src/serial.cpp
T
Raphael Coeffic 9c9f77a2a9 Do not allocate memory for LUA Rx fifo from the USB ISR
Rather use explicit allocation when the serial is set as the on-demand method that was used previously.
2022-03-20 16:30:59 +01:00

373 lines
8.2 KiB
C++

/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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.
*/
#include "opentx.h"
#include "serial.h"
#include <stdarg.h>
#include <stdio.h>
#include "aux_serial_driver.h"
#include "hal/serial_port.h"
//#include "cli.h"
#define PRINTF_BUFFER_SIZE 128
static void (*dbg_serial_putc)(void*, uint8_t) = nullptr;
static void* dbg_serial_ctx = nullptr;
void (*dbgSerialGetSendCb())(void*, uint8_t)
{
return dbg_serial_putc;
}
void* dbgSerialGetSendCbCtx()
{
return dbg_serial_ctx;
}
void dbgSerialSetSendCb(void* ctx, void (*cb)(void*, uint8_t))
{
dbg_serial_putc = nullptr;
dbg_serial_ctx = ctx;
dbg_serial_putc = cb;
}
extern "C" void dbgSerialPutc(char c)
{
auto _putc = dbg_serial_putc;
auto _ctx = dbg_serial_ctx;
if (_putc) _putc(_ctx, c);
}
extern "C" void dbgSerialPrintf(const char * format, ...)
{
va_list arglist;
char tmp[PRINTF_BUFFER_SIZE+1];
// no need to do anything if we don't have an output
if (!dbg_serial_putc) return;
va_start(arglist, format);
vsnprintf(tmp, PRINTF_BUFFER_SIZE, format, arglist);
tmp[PRINTF_BUFFER_SIZE] = '\0';
va_end(arglist);
const char *t = tmp;
while (*t && dbg_serial_putc) {
dbg_serial_putc(dbg_serial_ctx, *t++);
}
}
extern "C" void dbgSerialCrlf()
{
dbgSerialPutc('\r');
dbgSerialPutc('\n');
}
int8_t getSerialPortMode(uint8_t port_nr)
{
#if !defined(BOOT)
if (port_nr < MAX_SERIAL_PORTS) {
auto cfg = g_eeGeneral.serialPort;
cfg >>= port_nr * 4; // 4 bits per port
return cfg & 0xF;
}
#endif
return UART_MODE_NONE;
}
struct SerialPortState
{
uint8_t mode;
const etx_serial_port_t* port;
void* usart_ctx;
};
static SerialPortState serialPortStates[MAX_SERIAL_PORTS];
static SerialPortState* getSerialPortState(uint8_t port_nr)
{
if (port_nr >= MAX_SERIAL_PORTS) return nullptr;
return &serialPortStates[port_nr];
}
// TODO: replace serialSetupCallBacks & serialSetupPort
// with usage based handlers.
//
static void serialSetCallBacks(int mode, void* ctx, const etx_serial_port_t* port)
{
void (*sendByte)(void*, uint8_t) = nullptr;
int (*getByte)(void*, uint8_t*) = nullptr;
void (*setRxCb)(void*, void (*)(uint8_t*, uint32_t)) = nullptr;
const etx_serial_driver_t* drv = nullptr;
if (port) {
drv = port->uart;
if (drv) {
sendByte = drv->sendByte;
getByte = drv->getByte;
setRxCb = drv->setReceiveCb;
}
}
// prevent compiler warnings
(void)sendByte;
(void)getByte;
(void)setRxCb;
switch(mode) {
#if defined(DEBUG)
case UART_MODE_DEBUG:
// TODO: should we handle 2 outputs?
dbgSerialSetSendCb(ctx, sendByte);
break;
#endif
#if !defined(BOOT)
#if defined(LUA)
case UART_MODE_LUA:
luaSetSendCb(ctx, sendByte);
if (getByte) {
luaSetGetSerialByte(ctx, getByte);
} else if (setRxCb) {
luaAllocRxFifo();
setRxCb(ctx, luaReceiveData);
} else {
luaFreeRxFifo();
}
break;
#endif
#if defined(SBUS_TRAINER)
case UART_MODE_SBUS_TRAINER:
sbusSetAuxGetByte(ctx, getByte);
// TODO: setRxCb (see MODE_LUA)
break;
#endif
case UART_MODE_TELEMETRY:
telemetrySetGetByte(ctx, getByte);
// TODO: setRxCb (see MODE_LUA)
// de we really need telemetry
// input over USB VCP?
break;
case UART_MODE_TELEMETRY_MIRROR:
telemetrySetMirrorCb(ctx, sendByte);
break;
#if defined(CLI)
case UART_MODE_CLI:
cliSetSerialDriver(ctx, drv);
break;
#endif
#if defined(INTERNAL_GPS)
case UART_MODE_GPS:
gpsSetSerialDriver(ctx, drv);
break;
#endif
#endif
}
}
static void serialSetupPort(int mode, etx_serial_init& params, bool& power_required)
{
switch (mode) {
#if defined(DEBUG) || defined(CLI)
case UART_MODE_DEBUG:
case UART_MODE_CLI:
params.baudrate = DEBUG_BAUDRATE;
break;
#endif
#if !defined(BOOT)
case UART_MODE_TELEMETRY_MIRROR:
// TODO: query telemetry baudrate / add setting for module
power_required = true;
#if defined(CROSSFIRE)
if (modelTelemetryProtocol() == PROTOCOL_TELEMETRY_CROSSFIRE) {
params.baudrate = CROSSFIRE_TELEM_MIRROR_BAUDRATE;
break;
}
#endif
params.baudrate = FRSKY_TELEM_MIRROR_BAUDRATE;
break;
case UART_MODE_TELEMETRY:
if (modelTelemetryProtocol() == PROTOCOL_TELEMETRY_FRSKY_D_SECONDARY) {
params.baudrate = FRSKY_D_BAUDRATE;
params.rx_enable = true;
power_required = true;
}
break;
case UART_MODE_SBUS_TRAINER:
params.baudrate = SBUS_BAUDRATE;
params.word_length = ETX_WordLength_9;
params.parity = ETX_Parity_Even;
params.stop_bits = ETX_StopBits_Two;
params.rx_enable = true;
power_required = true;
break;
#if defined(LUA)
case UART_MODE_LUA:
params.baudrate = LUA_DEFAULT_BAUDRATE;
params.rx_enable = true;
power_required = true;
break;
#endif
#if defined(INTERNAL_GPS)
case UART_MODE_GPS:
params.baudrate = GPS_USART_BAUDRATE;
params.rx_enable = true;
power_required = true;
break;
#endif
#endif
}
}
const etx_serial_port_t* serialGetPort(uint8_t port_nr)
{
const etx_serial_port_t* port = nullptr;
if (port_nr != SP_VCP) {
port = auxSerialGetPort(port_nr);
}
#if defined(USB_SERIAL)
else {
port = &UsbSerialPort;
}
#endif
return port;
}
void serialInit(uint8_t port_nr, int mode)
{
auto state = getSerialPortState(port_nr);
if (!state) return;
const etx_serial_port_t* port = serialGetPort(port_nr);
if (!port) return;
if (state->port) {
auto drv = state->port->uart;
if (drv && drv->deinit) {
drv->deinit(state->usart_ctx);
}
if (state->mode != 0) {
// Clear callbacks
serialSetCallBacks(state->mode, nullptr, nullptr);
}
memset(state, 0, sizeof(SerialPortState));
}
etx_serial_init params = {
.baudrate = 0,
.parity = ETX_Parity_None,
.stop_bits = ETX_StopBits_One,
.word_length = ETX_WordLength_8,
.rx_enable = false,
.on_receive = nullptr,
.on_error = nullptr,
};
bool power_required = false;
serialSetupPort(mode, params, power_required);
if (params.baudrate != 0) {
state->mode = mode;
state->port = port;
if (port) {
if (port->uart && port->uart->init)
state->usart_ctx = port->uart->init(&params);
// Set power on/off
if (port->set_pwr) {
port->set_pwr(power_required);
}
}
// Update callbacks once the port is setup
serialSetCallBacks(mode, state->usart_ctx, state->port);
}
}
void initSerialPorts()
{
memset(serialPortStates, 0, sizeof(serialPortStates));
for (uint8_t port_nr = 0; port_nr < MAX_AUX_SERIAL; port_nr++) {
auto mode = getSerialPortMode(port_nr);
serialInit(port_nr, mode);
}
}
int serialGetMode(uint8_t port_nr)
{
return getSerialPortMode(port_nr);
}
void serialSetMode(uint8_t port_nr, int mode)
{
if (port_nr >= MAX_SERIAL_PORTS) return;
uint16_t m = mode & 0xF;
g_eeGeneral.serialPort =
(g_eeGeneral.serialPort & ~(0xF << port_nr * 4)) | (m << port_nr * 4);
}
// uint8_t serialTracesEnabled(int port_nr)
// {
// #if defined(DEBUG)
// return serialGetMode(port_nr) == UART_MODE_DEBUG;
// #else
// return false;
// #endif
// }
void serialStop(uint8_t port_nr)
{
auto state = getSerialPortState(port_nr);
if (!state) return;
if (state->port) {
auto port = state->port;
auto drv = port->uart;
if (drv && drv->deinit) {
drv->deinit(state->usart_ctx);
}
if (port->set_pwr) {
// Power OFF
port->set_pwr(0);
}
if (state->mode != 0) {
// Clear callbacks
serialSetCallBacks(state->mode, nullptr, nullptr);
}
}
memset(state, 0, sizeof(SerialPortState));
}