fix(radio): race condition on pulse buffer (#6634)
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
enum ChannelsProtocols {
|
||||
PROTOCOL_CHANNELS_UNINITIALIZED,
|
||||
@@ -62,6 +63,9 @@ struct etx_proto_driver_t {
|
||||
// Some module settings may have been modified
|
||||
void (*onConfigChange)(void* ctx);
|
||||
|
||||
// Previous Frame already sent?
|
||||
bool (*txCompleted)(void* ctx);
|
||||
|
||||
// Module state mode has been modified
|
||||
// void (*onModeChange)(void* ctx, uint8_t old_mode, uint8_t new_mode);
|
||||
};
|
||||
|
||||
@@ -297,6 +297,22 @@ uint8_t modulePortGetModule(etx_module_state_t* st)
|
||||
return st - _module_states;
|
||||
}
|
||||
|
||||
bool modulePortSerialTxCompleted(void* ctx)
|
||||
{
|
||||
auto st = (etx_module_state_t*)ctx;
|
||||
auto drv = modulePortGetSerialDrv(st->tx);
|
||||
auto drv_ctx = modulePortGetCtx(st->tx);
|
||||
return drv->txCompleted(drv_ctx);
|
||||
}
|
||||
|
||||
bool modulePortTimerTxCompleted(void* ctx)
|
||||
{
|
||||
auto st = (etx_module_state_t*)ctx;
|
||||
auto drv = modulePortGetTimerDrv(st->tx);
|
||||
auto drv_ctx = modulePortGetCtx(st->tx);
|
||||
return drv->txCompleted(drv_ctx);
|
||||
}
|
||||
|
||||
bool modulePortIsPortUsedByModule(uint8_t module, uint8_t port)
|
||||
{
|
||||
auto mod_st = modulePortGetState(module);
|
||||
|
||||
@@ -162,6 +162,9 @@ inline void* modulePortGetCtx(const etx_module_driver_t& d) {
|
||||
return d.ctx;
|
||||
}
|
||||
|
||||
bool modulePortSerialTxCompleted(void* ctx);
|
||||
bool modulePortTimerTxCompleted(void* ctx);
|
||||
|
||||
bool modulePortIsPortUsedByModule(uint8_t module, uint8_t port);
|
||||
bool modulePortIsPortUsed(uint8_t port);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
enum SerialEncoding {
|
||||
@@ -79,7 +80,7 @@ typedef struct {
|
||||
void (*sendBuffer)(void* ctx, const uint8_t* data, uint32_t size);
|
||||
|
||||
// Is TX phase completed
|
||||
uint8_t (*txCompleted)(void* ctx);
|
||||
bool (*txCompleted)(void* ctx);
|
||||
|
||||
// Wait for last send operation to complete
|
||||
void (*waitForTxCompleted)(void* ctx);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
@@ -33,4 +34,5 @@ typedef struct {
|
||||
void* (*init)(void* hw_def, const etx_timer_config_t* cfg);
|
||||
void (*deinit)(void* ctx);
|
||||
void (*send)(void* ctx, const etx_timer_config_t* cfg, const void* pulses, uint16_t length);
|
||||
bool (*txCompleted)(void* ctx);
|
||||
} etx_timer_driver_t;
|
||||
|
||||
@@ -262,6 +262,8 @@ class ProtoState
|
||||
|
||||
void applyConfigFromModel();
|
||||
|
||||
bool fifoFull() { return trsp.fifoFull(); }
|
||||
|
||||
protected:
|
||||
|
||||
void resetConfig(uint8_t version);
|
||||
@@ -1159,6 +1161,13 @@ static void sendPulses(void* ctx, uint8_t* buffer, int16_t* channels,
|
||||
p_state->sendFrame();
|
||||
}
|
||||
|
||||
static bool txCompleted(void* ctx)
|
||||
{
|
||||
auto mod_st = (etx_module_state_t*)ctx;
|
||||
auto p_state = (ProtoState*)mod_st->user_data;
|
||||
return !p_state->fifoFull();
|
||||
}
|
||||
|
||||
etx_proto_driver_t ProtoDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_AFHDS3,
|
||||
.init = initModule,
|
||||
@@ -1167,6 +1176,7 @@ etx_proto_driver_t ProtoDriver = {
|
||||
.processData = processTelemetryData,
|
||||
.processFrame = nullptr,
|
||||
.onConfigChange = nullptr,
|
||||
.txCompleted = txCompleted,
|
||||
};
|
||||
|
||||
} // namespace afhds3
|
||||
|
||||
@@ -129,6 +129,7 @@ struct CommandFifo {
|
||||
}
|
||||
|
||||
inline bool isEmpty() const { return (getIndex == setIndex); }
|
||||
inline bool isFull() const { return nextIndex(setIndex) == getIndex; }
|
||||
|
||||
inline void skip() { getIndex = nextIndex(getIndex); }
|
||||
|
||||
@@ -204,6 +205,8 @@ class Transport
|
||||
|
||||
void sendBuffer();
|
||||
|
||||
bool fifoFull() { return fifo.isFull(); }
|
||||
|
||||
/**
|
||||
* Process retransmissions
|
||||
* error: transport is in error state
|
||||
|
||||
@@ -452,4 +452,5 @@ const etx_proto_driver_t CrossfireDriver = {
|
||||
.processData = nullptr,
|
||||
.processFrame = crossfireProcessFrame,
|
||||
.onConfigChange = nullptr,
|
||||
.txCompleted = modulePortSerialTxCompleted,
|
||||
};
|
||||
|
||||
@@ -298,6 +298,7 @@ const etx_proto_driver_t DSM2Driver = {
|
||||
.processData = nullptr,
|
||||
.processFrame = nullptr,
|
||||
.onConfigChange = dsm2ConfigChange,
|
||||
.txCompleted = modulePortSerialTxCompleted,
|
||||
};
|
||||
|
||||
const etx_proto_driver_t DSMPDriver = {
|
||||
@@ -308,4 +309,5 @@ const etx_proto_driver_t DSMPDriver = {
|
||||
.processData = dsmpProcessData,
|
||||
.processFrame = nullptr,
|
||||
.onConfigChange = nullptr,
|
||||
.txCompleted = modulePortSerialTxCompleted,
|
||||
};
|
||||
|
||||
@@ -255,4 +255,5 @@ const etx_proto_driver_t GhostDriver = {
|
||||
.processData = ghostProcessData,
|
||||
.processFrame = nullptr,
|
||||
.onConfigChange = nullptr,
|
||||
.txCompleted = modulePortSerialTxCompleted,
|
||||
};
|
||||
|
||||
@@ -288,6 +288,7 @@ const etx_proto_driver_t MultiDriver = {
|
||||
.processData = multiProcessData,
|
||||
.processFrame = nullptr,
|
||||
.onConfigChange = nullptr,
|
||||
.txCompleted = modulePortSerialTxCompleted,
|
||||
};
|
||||
|
||||
static void sendChannels(uint8_t*& p_buf, uint8_t module)
|
||||
|
||||
@@ -244,4 +244,5 @@ const etx_proto_driver_t PpmDriver = {
|
||||
.processData = ppmProcessTelemetryData,
|
||||
.processFrame = nullptr,
|
||||
.onConfigChange = ppmOnConfigChange,
|
||||
.txCompleted = modulePortTimerTxCompleted,
|
||||
};
|
||||
|
||||
@@ -551,6 +551,9 @@ void pulsesSendNextFrame(uint8_t module)
|
||||
state.settings_updated = 0;
|
||||
}
|
||||
|
||||
// if previous frame not completed, skip this one
|
||||
if (!drv->txCompleted(ctx)) return;
|
||||
|
||||
uint8_t channelStart = g_model.moduleData[module].channelsStart;
|
||||
int16_t* channels = &channelOutputs[channelStart];
|
||||
uint8_t nChannels = 16; // TODO: MAX_CHANNELS - channelsStart
|
||||
|
||||
@@ -416,4 +416,5 @@ const etx_proto_driver_t Pxx1Driver = {
|
||||
.processData = pxx1ProcessData,
|
||||
.processFrame = nullptr,
|
||||
.onConfigChange = nullptr,
|
||||
.txCompleted = modulePortSerialTxCompleted,
|
||||
};
|
||||
|
||||
@@ -833,7 +833,6 @@ static void pxx2ProcessData(void* ctx, uint8_t data, uint8_t* buffer, uint8_t* l
|
||||
}
|
||||
|
||||
#include "hal/module_driver.h"
|
||||
// #include "extmodule_serial_driver.h"
|
||||
|
||||
const etx_proto_driver_t Pxx2Driver = {
|
||||
.protocol = PROTOCOL_CHANNELS_PXX2,
|
||||
@@ -843,4 +842,5 @@ const etx_proto_driver_t Pxx2Driver = {
|
||||
.processData = pxx2ProcessData,
|
||||
.processFrame = nullptr,
|
||||
.onConfigChange = nullptr,
|
||||
.txCompleted = modulePortSerialTxCompleted,
|
||||
};
|
||||
|
||||
@@ -217,4 +217,5 @@ const etx_proto_driver_t SBusDriver = {
|
||||
.processData = sbusProcessTelemetryData,
|
||||
.processFrame = nullptr,
|
||||
.onConfigChange = sbusOnConfigChange,
|
||||
.txCompleted = modulePortSerialTxCompleted,
|
||||
};
|
||||
|
||||
@@ -274,6 +274,13 @@ static void set_oc_mode(const stm32_pulse_timer_t* tim, uint32_t ocmode)
|
||||
LL_TIM_OC_SetMode(tim->TIMx, channel, ocmode);
|
||||
}
|
||||
|
||||
bool stm32_pulse_is_completed(const stm32_pulse_timer_t* tim)
|
||||
{
|
||||
uint32_t channel = _get_base_channel(tim->TIM_Channel);
|
||||
return !LL_TIM_IsEnabledCounter(tim->TIMx) ||
|
||||
LL_TIM_OC_GetMode(tim->TIMx, channel) == LL_TIM_OCMODE_FORCED_INACTIVE;
|
||||
}
|
||||
|
||||
void stm32_pulse_wait_for_completed(const stm32_pulse_timer_t* tim)
|
||||
{
|
||||
uint32_t channel = _get_base_channel(tim->TIM_Channel);
|
||||
|
||||
@@ -69,6 +69,7 @@ void stm32_pulse_set_cmp_val(const stm32_pulse_timer_t* tim, uint32_t cmp_val);
|
||||
void stm32_pulse_start(const stm32_pulse_timer_t* tim);
|
||||
void stm32_pulse_stop(const stm32_pulse_timer_t* tim);
|
||||
|
||||
bool stm32_pulse_is_completed(const stm32_pulse_timer_t* tim);
|
||||
void stm32_pulse_wait_for_completed(const stm32_pulse_timer_t* tim);
|
||||
|
||||
// return true if it could be disabled without interrupting a pulse train, false otherwise
|
||||
|
||||
@@ -383,7 +383,7 @@ static void stm32_serial_send_buffer(void* ctx, const uint8_t* data, uint32_t si
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t stm32_serial_tx_completed(void* ctx)
|
||||
static bool stm32_serial_tx_completed(void* ctx)
|
||||
{
|
||||
auto st = (stm32_serial_state*)ctx;
|
||||
if (!st) return 1;
|
||||
|
||||
@@ -494,6 +494,13 @@ static void stm32_softserial_tx_send_buffer(void* ctx, const uint8_t* data, uint
|
||||
stm32_pulse_start_dma_req(timer, pulses, length, ocmode, cmp_val);
|
||||
}
|
||||
|
||||
static bool stm32_softserial_tx_completed(void* ctx)
|
||||
{
|
||||
auto port = (const stm32_softserial_tx_port*)ctx;
|
||||
auto timer = port->tim;
|
||||
return stm32_pulse_is_completed(timer);
|
||||
}
|
||||
|
||||
void stm32_softserial_set_polarity(void* ctx, uint8_t polarity)
|
||||
{
|
||||
auto port = (const stm32_softserial_tx_port*)ctx;
|
||||
@@ -507,7 +514,7 @@ const etx_serial_driver_t STM32SoftSerialTxDriver = {
|
||||
.deinit = stm32_softserial_tx_deinit,
|
||||
.sendByte = stm32_softserial_tx_send_byte,
|
||||
.sendBuffer = stm32_softserial_tx_send_buffer,
|
||||
.txCompleted = nullptr,
|
||||
.txCompleted = stm32_softserial_tx_completed,
|
||||
.waitForTxCompleted = stm32_softserial_tx_wait,
|
||||
.enableRx = nullptr, // TODO: combine with EXTI / Timer implementation? (S.PORT INV RX)
|
||||
.getByte = nullptr,
|
||||
|
||||
@@ -86,6 +86,7 @@ static void deinit(void* ctx)
|
||||
|
||||
static void sendByte(void*, uint8_t) {}
|
||||
static void sendBuffer(void*, const uint8_t*, uint32_t) {}
|
||||
static bool txCompleted(void*) { return true; }
|
||||
static void waitForTxCompleted(void*) {}
|
||||
static int getByte(void*,uint8_t*) { return -1; }
|
||||
|
||||
@@ -94,7 +95,7 @@ const etx_serial_driver_t _fakeSerialDriver = {
|
||||
.deinit = deinit,
|
||||
.sendByte = sendByte,
|
||||
.sendBuffer = sendBuffer,
|
||||
.txCompleted = nullptr,
|
||||
.txCompleted = txCompleted,
|
||||
.waitForTxCompleted = waitForTxCompleted,
|
||||
.enableRx = nullptr,
|
||||
.getByte = getByte,
|
||||
|
||||
@@ -381,6 +381,7 @@ static void* null_drv_init(void* hw_def, const etx_serial_init* dev) { return nu
|
||||
static void null_drv_deinit(void* ctx) { }
|
||||
static void null_drv_send_byte(void* ctx, uint8_t b) { }
|
||||
static void null_drv_send_buffer(void* ctx, const uint8_t* b, uint32_t l) { }
|
||||
static bool null_drv_tx_completed(void* ctx) { return true; }
|
||||
static int null_drv_get_byte(void* ctx, uint8_t* b) { return 0; }
|
||||
static void null_drv_set_baudrate(void* ctx, uint32_t baudrate) { }
|
||||
|
||||
@@ -389,7 +390,7 @@ const etx_serial_driver_t null_drv = {
|
||||
.deinit = null_drv_deinit,
|
||||
.sendByte = null_drv_send_byte,
|
||||
.sendBuffer = null_drv_send_buffer,
|
||||
.txCompleted = nullptr,
|
||||
.txCompleted = null_drv_tx_completed,
|
||||
.waitForTxCompleted = nullptr,
|
||||
.enableRx = nullptr,
|
||||
.getByte = null_drv_get_byte,
|
||||
|
||||
Reference in New Issue
Block a user