Internal and external module buffers are now private
This commit is contained in:
committed by
Raphael Coeffic
parent
ab6033e4d5
commit
f952c5f7c7
+1
-1
@@ -1143,7 +1143,7 @@ int cliSerialPassthrough(const char **argv)
|
||||
while (cdcConnected) {
|
||||
|
||||
uint8_t data;
|
||||
if (intmoduleFifo.pop(data)) {
|
||||
if (IntmoduleSerialDriver.getByte(uart_ctx, &data) > 0) {
|
||||
|
||||
uint8_t timeout = 10; // 10 ms
|
||||
while(!usbSerialFreeSpace() && (timeout > 0)) {
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ class DMAFifo
|
||||
|
||||
void clear()
|
||||
{
|
||||
ridx = 0;
|
||||
ridx = N - stream->NDTR;
|
||||
}
|
||||
|
||||
uint32_t size()
|
||||
|
||||
@@ -99,6 +99,11 @@ class Fifo
|
||||
}
|
||||
}
|
||||
|
||||
T * buffer()
|
||||
{
|
||||
return fifo;
|
||||
}
|
||||
|
||||
protected:
|
||||
T fifo[N];
|
||||
volatile uint32_t widx;
|
||||
|
||||
@@ -62,5 +62,8 @@ struct etx_module_driver_t {
|
||||
|
||||
// Send the pulses
|
||||
void (*sendPulses)(void* context);
|
||||
|
||||
// Fetch telemetry byte
|
||||
int (*getByte)(void* context, uint8_t* data);
|
||||
};
|
||||
|
||||
|
||||
@@ -75,6 +75,9 @@ typedef struct {
|
||||
// Fetch byte from internal buffer
|
||||
int (*getByte)(void* ctx, uint8_t* data);
|
||||
|
||||
// Clear internal buffer
|
||||
void (*clearRxBuffer)(void* ctx);
|
||||
|
||||
// Get current baudrate
|
||||
uint32_t (*getBaudrate)(void*);
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ bool FrskyDeviceFirmwareUpdate::readBuffer(uint8_t * buffer, uint8_t count, uint
|
||||
uint32_t elapsed = 0;
|
||||
uint8_t index = 0;
|
||||
while (index < count && elapsed < timeout) {
|
||||
if (intmoduleFifo.pop(buffer[index])) {
|
||||
if (IntmoduleSerialDriver.getByte(uart_ctx, &(buffer[index]))) {
|
||||
++index;
|
||||
}
|
||||
else {
|
||||
@@ -141,14 +141,15 @@ bool FrskyDeviceFirmwareUpdate::readBuffer(uint8_t * buffer, uint8_t count, uint
|
||||
}
|
||||
#endif
|
||||
|
||||
const uint8_t * FrskyDeviceFirmwareUpdate::readFullDuplexFrame(ModuleFifo & fifo, uint32_t timeout)
|
||||
const uint8_t * FrskyDeviceFirmwareUpdate::readFullDuplexFrame(uint32_t timeout)
|
||||
{
|
||||
#if defined(INTMODULE_USART)
|
||||
uint8_t len = 0;
|
||||
bool bytestuff = false;
|
||||
while (len < 10) {
|
||||
uint32_t elapsed = 0;
|
||||
uint8_t byte;
|
||||
while (!fifo.pop(byte)) {
|
||||
uint8_t byte = 0;
|
||||
while (IntmoduleSerialDriver.getByte(uart_ctx, &byte) == 0) {
|
||||
RTOS_WAIT_MS(1);
|
||||
if (elapsed++ >= timeout) {
|
||||
return nullptr;
|
||||
@@ -170,6 +171,9 @@ const uint8_t * FrskyDeviceFirmwareUpdate::readFullDuplexFrame(ModuleFifo & fifo
|
||||
}
|
||||
}
|
||||
return &frame[1];
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
const uint8_t * FrskyDeviceFirmwareUpdate::readHalfDuplexFrame(uint32_t timeout)
|
||||
@@ -193,7 +197,7 @@ const uint8_t * FrskyDeviceFirmwareUpdate::readFrame(uint32_t timeout)
|
||||
switch (module) {
|
||||
#if defined(INTERNAL_MODULE_PXX2)
|
||||
case INTERNAL_MODULE:
|
||||
return readFullDuplexFrame(intmoduleFifo, timeout);
|
||||
return readFullDuplexFrame(timeout);
|
||||
#endif
|
||||
|
||||
default:
|
||||
|
||||
@@ -24,10 +24,6 @@
|
||||
|
||||
#include "dataconstants.h"
|
||||
#include "definitions.h"
|
||||
#include "frsky_pxx2.h"
|
||||
#include "pulses/modules_helpers.h"
|
||||
#include "ff.h"
|
||||
#include "popups.h"
|
||||
|
||||
enum FrskyFirmwareProductFamily {
|
||||
FIRMWARE_FAMILY_INTERNAL_MODULE,
|
||||
@@ -143,7 +139,7 @@ class FrskyDeviceFirmwareUpdate {
|
||||
void sendFrame();
|
||||
|
||||
bool readBuffer(uint8_t * buffer, uint8_t count, uint32_t timeout);
|
||||
const uint8_t * readFullDuplexFrame(ModuleFifo & fifo, uint32_t timeout);
|
||||
const uint8_t * readFullDuplexFrame(uint32_t timeout);
|
||||
const uint8_t * readHalfDuplexFrame(uint32_t timeout);
|
||||
const uint8_t * readFrame(uint32_t timeout);
|
||||
bool waitState(State state, uint32_t timeout);
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _IO_PXX2_H_
|
||||
#define _IO_PXX2_H_
|
||||
|
||||
#include "fifo.h"
|
||||
#include "pulses/pxx.h"
|
||||
|
||||
#include "board.h"
|
||||
|
||||
class ModuleFifo : public Fifo<uint8_t, INTMODULE_FIFO_SIZE> {
|
||||
public:
|
||||
bool getFrame(uint8_t * frame)
|
||||
{
|
||||
while (true) {
|
||||
if (isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
else if (fifo[ridx] != 0x7E) {
|
||||
skip();
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t next = nextIndex(ridx);
|
||||
uint8_t len = fifo[next];
|
||||
|
||||
if (len > 40) {
|
||||
clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size() < unsigned(len + 4 /* 2 bytes header + 2 bytes CRC */)) {
|
||||
// frame not fully received
|
||||
return false;
|
||||
}
|
||||
|
||||
// we keep the length in frame[0]
|
||||
frame[0] = fifo[next];
|
||||
next = nextIndex(next);
|
||||
|
||||
uint16_t crc = 0xFFFF;
|
||||
|
||||
for (uint32_t i = 1; i <= len; i++) {
|
||||
frame[i] = fifo[next];
|
||||
crc -= frame[i];
|
||||
next = nextIndex(next);
|
||||
}
|
||||
|
||||
uint8_t crcLow = fifo[next];
|
||||
next = nextIndex(next);
|
||||
uint8_t crcHigh = fifo[next];
|
||||
ridx = nextIndex(next);
|
||||
|
||||
return ((crc >> 8) == crcLow) && ((crc & 0xFF) == crcHigh);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -106,7 +106,7 @@ class MultiInternalUpdateDriver: public MultiFirmwareUpdateDriver
|
||||
|
||||
bool getByte(uint8_t & byte) const override
|
||||
{
|
||||
return intmoduleFifo.pop(byte);
|
||||
return IntmoduleSerialDriver.getByte(uart_ctx, &byte) > 0;
|
||||
}
|
||||
|
||||
void sendByte(uint8_t byte) const override
|
||||
@@ -116,7 +116,7 @@ class MultiInternalUpdateDriver: public MultiFirmwareUpdateDriver
|
||||
|
||||
void clear() const override
|
||||
{
|
||||
intmoduleFifo.clear();
|
||||
IntmoduleSerialDriver.clearRxBuffer(uart_ctx);
|
||||
}
|
||||
|
||||
void deinit(bool inverted) override
|
||||
|
||||
@@ -19,16 +19,15 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include "afhds2.h"
|
||||
#include "flysky.h"
|
||||
|
||||
#include "intmodule_serial_driver.h"
|
||||
#include "mixer_scheduler.h"
|
||||
|
||||
// solely for 'intmoduleFifo'
|
||||
// TODO: remove this non-sense
|
||||
#include "opentx.h"
|
||||
#include "pulses/pxx2.h"
|
||||
#include "pulses/pulses.h"
|
||||
#include "telemetry/telemetry.h"
|
||||
|
||||
afhds2::afhds2() {
|
||||
// TODO Auto-generated constructor stub
|
||||
@@ -53,20 +52,18 @@ static void* afhds2Init(uint8_t module)
|
||||
|
||||
// serial port setup
|
||||
resetPulsesAFHDS2();
|
||||
intmoduleFifo.clear();
|
||||
IntmoduleSerialDriver.init(&afhds2SerialInitParams);
|
||||
void* ctx = IntmoduleSerialDriver.init(&afhds2SerialInitParams);
|
||||
|
||||
// mixer setup
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, AFHDS2_PERIOD);
|
||||
INTERNAL_MODULE_ON();
|
||||
|
||||
return nullptr;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void afhds2DeInit(void* context)
|
||||
{
|
||||
INTERNAL_MODULE_OFF();
|
||||
intmoduleFifo.clear();
|
||||
IntmoduleSerialDriver.deinit(context);
|
||||
|
||||
// mixer setup
|
||||
@@ -96,10 +93,16 @@ static void afhds2SendPulses(void* context)
|
||||
IntmoduleSerialDriver.sendBuffer(context, data, size);
|
||||
}
|
||||
|
||||
static int afhds2GetByte(void* context, uint8_t* data)
|
||||
{
|
||||
return IntmoduleSerialDriver.getByte(context, data);
|
||||
}
|
||||
|
||||
const etx_module_driver_t Afhds2InternalDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_AFHDS2A,
|
||||
.init = afhds2Init,
|
||||
.deinit = afhds2DeInit,
|
||||
.setupPulses = afhds2SetupPulses,
|
||||
.sendPulses = afhds2SendPulses,
|
||||
.sendPulses = afhds2SendPulses,
|
||||
.getByte = afhds2GetByte,
|
||||
};
|
||||
|
||||
@@ -128,12 +128,9 @@ static void* crossfireInitInternal(uint8_t module)
|
||||
// serial port setup
|
||||
etx_serial_init params(intmoduleCrossfireInitParams);
|
||||
params.baudrate = INT_CROSSFIRE_BAUDRATE;
|
||||
|
||||
intmoduleFifo.clear();
|
||||
IntmoduleSerialDriver.init(¶ms);
|
||||
INTERNAL_MODULE_ON();
|
||||
|
||||
return nullptr;
|
||||
return IntmoduleSerialDriver.init(¶ms);
|
||||
}
|
||||
|
||||
static void crossfireDeInitInternal(void* context)
|
||||
@@ -143,7 +140,6 @@ static void crossfireDeInitInternal(void* context)
|
||||
INTERNAL_MODULE_OFF();
|
||||
mixerSchedulerSetPeriod(INTERNAL_MODULE, 0);
|
||||
intmoduleStop();
|
||||
intmoduleFifo.clear();
|
||||
}
|
||||
|
||||
static void crossfireSetupPulsesInternal(void* context, int16_t* channels, uint8_t nChannels)
|
||||
@@ -159,12 +155,18 @@ static void crossfireSendPulsesInternal(void* context)
|
||||
intmodulePulsesData.crossfire.length);
|
||||
}
|
||||
|
||||
static int crossfireGetByteInternal(void* context, uint8_t* data)
|
||||
{
|
||||
return IntmoduleSerialDriver.getByte(context, data);
|
||||
}
|
||||
|
||||
etx_module_driver_t CrossfireInternalDriver = {
|
||||
.protocol = PROTOCOL_CHANNELS_CROSSFIRE,
|
||||
.init = crossfireInitInternal,
|
||||
.deinit = crossfireDeInitInternal,
|
||||
.setupPulses = crossfireSetupPulsesInternal,
|
||||
.sendPulses = crossfireSendPulsesInternal,
|
||||
.getByte = crossfireGetByteInternal,
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ static void sendConfig(uint8_t moduleIdx);
|
||||
static void sendDSM(uint8_t moduleIdx);
|
||||
#endif
|
||||
|
||||
#if defined(INTMODULE_USART)
|
||||
#if defined(INTMODULE_USART) && defined(INTERNAL_MODULE_MULTI)
|
||||
#include "intmodule_serial_driver.h"
|
||||
|
||||
etx_serial_init multiSerialInitParams = {
|
||||
@@ -65,7 +65,7 @@ etx_serial_init multiSerialInitParams = {
|
||||
|
||||
static void sendMulti(uint8_t moduleIdx, uint8_t b)
|
||||
{
|
||||
#if defined(HARDWARE_INTERNAL_MODULE)
|
||||
#if defined(INTERNAL_MODULE_MULTI)
|
||||
if (moduleIdx == INTERNAL_MODULE) {
|
||||
intmodulePulsesData.multi.sendByte(b);
|
||||
}
|
||||
@@ -222,7 +222,6 @@ static void* multiInit(uint8_t module)
|
||||
|
||||
// serial port setup
|
||||
intmodulePulsesData.multi.initFrame();
|
||||
intmoduleFifo.clear();
|
||||
void* uart_ctx = IntmoduleSerialDriver.init(&multiSerialInitParams);
|
||||
|
||||
// mixer setup
|
||||
@@ -266,6 +265,11 @@ static void multiSendPulses(void* context)
|
||||
intmodulePulsesData.multi.getSize());
|
||||
}
|
||||
|
||||
static int multiGetByte(void* context, uint8_t* data)
|
||||
{
|
||||
return IntmoduleSerialDriver.getByte(context, data);
|
||||
}
|
||||
|
||||
#include "hal/module_driver.h"
|
||||
|
||||
const etx_module_driver_t MultiInternalDriver = {
|
||||
@@ -273,7 +277,8 @@ const etx_module_driver_t MultiInternalDriver = {
|
||||
.init = multiInit,
|
||||
.deinit = multiDeInit,
|
||||
.setupPulses = multiSetupPulses,
|
||||
.sendPulses = multiSendPulses
|
||||
.sendPulses = multiSendPulses,
|
||||
.getByte = multiGetByte,
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -356,6 +356,16 @@ uint8_t getRequiredProtocol(uint8_t module)
|
||||
static void* internalModuleContext = nullptr;
|
||||
static const etx_module_driver_t* internalModuleDriver = nullptr;
|
||||
|
||||
const etx_module_driver_t* getIntModuleDriver()
|
||||
{
|
||||
return internalModuleDriver;
|
||||
}
|
||||
|
||||
void* getIntModuleCtx()
|
||||
{
|
||||
return internalModuleContext;
|
||||
}
|
||||
|
||||
static void enablePulsesInternalModule(uint8_t protocol)
|
||||
{
|
||||
// start new protocol hardware here
|
||||
@@ -519,6 +529,16 @@ bool setupPulsesInternalModule()
|
||||
static void* externalModuleContext = nullptr;
|
||||
static const etx_module_driver_t* externalModuleDriver = nullptr;
|
||||
|
||||
const etx_module_driver_t* getExtModuleDriver()
|
||||
{
|
||||
return externalModuleDriver;
|
||||
}
|
||||
|
||||
void* getExtModuleCtx()
|
||||
{
|
||||
return externalModuleContext;
|
||||
}
|
||||
|
||||
void enablePulsesExternalModule(uint8_t protocol)
|
||||
{
|
||||
// start new protocol hardware here
|
||||
|
||||
@@ -229,11 +229,15 @@ extern TrainerPulsesData trainerPulsesData;
|
||||
bool setupPulsesInternalModule();
|
||||
void stopPulsesInternalModule();
|
||||
void intmoduleSendNextFrame();
|
||||
const etx_module_driver_t* getIntModuleDriver();
|
||||
void* getIntModuleCtx();
|
||||
#endif
|
||||
#if defined(HARDWARE_EXTERNAL_MODULE)
|
||||
bool setupPulsesExternalModule();
|
||||
void stopPulsesExternalModule();
|
||||
void extmoduleSendNextFrame();
|
||||
const etx_module_driver_t* getExtModuleDriver();
|
||||
void* getExtModuleCtx();
|
||||
#endif
|
||||
void restartModule(uint8_t idx);
|
||||
void setupPulsesDSM2();
|
||||
@@ -251,7 +255,6 @@ void intmodulePpmStart();
|
||||
void intmodulePxx1PulsesStart();
|
||||
void intmodulePxx1SerialStart();
|
||||
void extmodulePxx1PulsesStart();
|
||||
void extmodulePxx1SerialStart();
|
||||
void extmodulePpmStart();
|
||||
void intmoduleStop();
|
||||
void extmoduleStop();
|
||||
|
||||
@@ -648,7 +648,6 @@ void Pxx2OtaUpdate::flashFirmware(const char * filename, ProgressHandler progres
|
||||
|
||||
static void* pxx2InitInternal(uint8_t moduleType)
|
||||
{
|
||||
intmoduleFifo.clear();
|
||||
void* uart_ctx = IntmoduleSerialDriver.init(&pxx2SerialInitParams);
|
||||
resetAccessAuthenticationCount();
|
||||
|
||||
@@ -697,6 +696,11 @@ static void pxx2SendPulsesInternal(void* context)
|
||||
}
|
||||
}
|
||||
|
||||
static int pxx2GetByteInternal(void* context, uint8_t* data)
|
||||
{
|
||||
return IntmoduleSerialDriver.getByte(context, data);
|
||||
}
|
||||
|
||||
#include "hal/module_driver.h"
|
||||
|
||||
const etx_module_driver_t Pxx2InternalDriver = {
|
||||
@@ -705,6 +709,7 @@ const etx_module_driver_t Pxx2InternalDriver = {
|
||||
.deinit = pxx2DeInitInternal,
|
||||
.setupPulses = pxx2SetupPulsesInternal,
|
||||
.sendPulses = pxx2SendPulsesInternal,
|
||||
.getByte = pxx2GetByteInternal,
|
||||
};
|
||||
|
||||
#if defined(EXTMODULE_USART)
|
||||
|
||||
@@ -23,9 +23,8 @@
|
||||
#define _PULSES_PXX2_H_
|
||||
|
||||
#include "fifo.h"
|
||||
#include "io/frsky_pxx2.h"
|
||||
#include "popups.h"
|
||||
#include "./pxx.h"
|
||||
#include "pxx.h"
|
||||
|
||||
#include "hal/serial_driver.h"
|
||||
#include "hal/module_driver.h"
|
||||
@@ -424,9 +423,6 @@ PXX2ModuleSetup& getPXX2ModuleSetupBuffer();
|
||||
BindInformation& getPXX2BindInformationBuffer();
|
||||
PXX2HardwareAndSettings& getPXX2HardwareAndSettingsBuffer();
|
||||
|
||||
extern ModuleFifo intmoduleFifo;
|
||||
extern ModuleFifo extmoduleFifo;
|
||||
|
||||
class Pxx2CrcMixin {
|
||||
protected:
|
||||
void initCrc()
|
||||
|
||||
@@ -25,9 +25,16 @@
|
||||
|
||||
#if defined(EXTMODULE_USART)
|
||||
#include "stm32_usart_driver.h"
|
||||
#include "fifo.h"
|
||||
|
||||
#include "io/frsky_pxx2.h"
|
||||
ModuleFifo extmoduleFifo;
|
||||
typedef Fifo<uint8_t, INTMODULE_FIFO_SIZE> RxFifo;
|
||||
static RxFifo extmoduleFifo;
|
||||
|
||||
struct ExtmoduleCtx
|
||||
{
|
||||
RxFifo* rxFifo;
|
||||
const stm32_usart_t* usart;
|
||||
};
|
||||
|
||||
static etx_serial_callbacks_t extmodule_driver = {
|
||||
nullptr, nullptr, nullptr
|
||||
@@ -69,6 +76,11 @@ static const stm32_usart_t extmoduleUSART = {
|
||||
.rxDMA_Channel = 0,
|
||||
};
|
||||
|
||||
static const ExtmoduleCtx extmoduleCtx = {
|
||||
.rxFifo = &extmoduleFifo,
|
||||
.usart = &extmoduleUSART,
|
||||
};
|
||||
|
||||
static void* extmoduleSerialStart(const etx_serial_init* params)
|
||||
{
|
||||
if (!params) return nullptr;
|
||||
@@ -78,9 +90,9 @@ static void* extmoduleSerialStart(const etx_serial_init* params)
|
||||
|
||||
// UART config
|
||||
stm32_usart_init(&extmoduleUSART, params);
|
||||
extmoduleFifo.clear();
|
||||
extmoduleCtx.rxFifo->clear();
|
||||
|
||||
return nullptr;
|
||||
return (void*)&extmoduleCtx;
|
||||
}
|
||||
|
||||
void extmoduleInvertedSerialStart(uint32_t baudrate)
|
||||
@@ -91,16 +103,6 @@ void extmoduleInvertedSerialStart(uint32_t baudrate)
|
||||
extmoduleSerialStart(¶ms);
|
||||
}
|
||||
|
||||
#if defined(PXX1)
|
||||
void extmodulePxx1SerialStart()
|
||||
{
|
||||
EXTERNAL_MODULE_ON();
|
||||
etx_serial_init params(extmoduleSerialParams);
|
||||
params.baudrate = EXTMODULE_PXX1_SERIAL_BAUDRATE;
|
||||
ExtmoduleSerialDriver.init(¶ms);
|
||||
}
|
||||
#endif
|
||||
|
||||
void extmoduleSerialStop(void*)
|
||||
{
|
||||
stm32_usart_deinit(&extmoduleUSART);
|
||||
@@ -110,20 +112,37 @@ void extmoduleSerialStop(void*)
|
||||
extmodule_driver.on_error = nullptr;
|
||||
}
|
||||
|
||||
static void extmoduleSendByte(void*, uint8_t byte)
|
||||
static void extmoduleSendByte(void* ctx, uint8_t byte)
|
||||
{
|
||||
stm32_usart_send_byte(&extmoduleUSART, byte);
|
||||
auto modCtx = (ExtmoduleCtx*)ctx;
|
||||
stm32_usart_send_byte(modCtx->usart, byte);
|
||||
}
|
||||
|
||||
static void extmoduleSendBuffer(void*, const uint8_t * data, uint8_t size)
|
||||
static void extmoduleSendBuffer(void* ctx, const uint8_t * data, uint8_t size)
|
||||
{
|
||||
auto modCtx = (ExtmoduleCtx*)ctx;
|
||||
if (size == 0) return;
|
||||
stm32_usart_send_buffer(&extmoduleUSART, data, size);
|
||||
stm32_usart_send_buffer(modCtx->usart, data, size);
|
||||
}
|
||||
|
||||
static void extmoduleWaitForTxCompleted(void*)
|
||||
static void extmoduleWaitForTxCompleted(void* ctx)
|
||||
{
|
||||
stm32_usart_wait_for_tx_dma(&extmoduleUSART);
|
||||
auto modCtx = (ExtmoduleCtx*)ctx;
|
||||
stm32_usart_wait_for_tx_dma(modCtx->usart);
|
||||
}
|
||||
|
||||
static int extmoduleGetByte(void* ctx, uint8_t* data)
|
||||
{
|
||||
auto modCtx = (ExtmoduleCtx*)ctx;
|
||||
if (!modCtx->rxFifo) return -1;
|
||||
return modCtx->rxFifo->pop(*data);
|
||||
}
|
||||
|
||||
static void extmoduleClearRxBuffer(void* ctx)
|
||||
{
|
||||
auto modCtx = (ExtmoduleCtx*)ctx;
|
||||
if (!modCtx->rxFifo) return;
|
||||
modCtx->rxFifo->clear();
|
||||
}
|
||||
|
||||
const etx_serial_driver_t ExtmoduleSerialDriver = {
|
||||
@@ -132,7 +151,8 @@ const etx_serial_driver_t ExtmoduleSerialDriver = {
|
||||
.sendByte = extmoduleSendByte,
|
||||
.sendBuffer = extmoduleSendBuffer,
|
||||
.waitForTxCompleted = extmoduleWaitForTxCompleted,
|
||||
.getByte = nullptr,
|
||||
.getByte = extmoduleGetByte,
|
||||
.clearRxBuffer = extmoduleClearRxBuffer,
|
||||
.getBaudrate = nullptr,
|
||||
.setReceiveCb = nullptr,
|
||||
.setBaudrateCb = nullptr,
|
||||
|
||||
@@ -30,13 +30,4 @@
|
||||
void extmoduleFifoReceive(uint8_t data);
|
||||
void extmoduleFifoError();
|
||||
|
||||
// void extmoduleSerialStart();
|
||||
// void extmoduleInvertedSerialStart(uint32_t baudrate);
|
||||
// void extmoduleSendBuffer(const uint8_t * data, uint8_t size);
|
||||
// void extmoduleSendNextFrame();
|
||||
|
||||
#if defined(PXX1)
|
||||
void extmodulePxx1SerialStart();
|
||||
#endif
|
||||
|
||||
extern const etx_serial_driver_t ExtmoduleSerialDriver;
|
||||
|
||||
@@ -23,9 +23,16 @@
|
||||
#include "intmodule_serial_driver.h"
|
||||
#include "stm32_usart_driver.h"
|
||||
#include "board.h"
|
||||
#include "fifo.h"
|
||||
|
||||
Fifo<uint8_t, INTMODULE_FIFO_SIZE> intmoduleFifo;
|
||||
#include "fifo.h"
|
||||
typedef Fifo<uint8_t, INTMODULE_FIFO_SIZE> RxFifo;
|
||||
static RxFifo intmoduleFifo;
|
||||
|
||||
struct IntmoduleCtx
|
||||
{
|
||||
RxFifo* rxFifo;
|
||||
const stm32_usart_t* usart;
|
||||
};
|
||||
|
||||
#if !defined(INTMODULE_DMA_STREAM)
|
||||
static uint8_t * intmoduleTxBufferData;
|
||||
@@ -88,6 +95,11 @@ static const stm32_usart_t intmoduleUSART = {
|
||||
.rxDMA_Channel = 0,
|
||||
};
|
||||
|
||||
static const IntmoduleCtx intmoduleCtx = {
|
||||
.rxFifo = &intmoduleFifo,
|
||||
.usart = &intmoduleUSART,
|
||||
};
|
||||
|
||||
void intmoduleStop()
|
||||
{
|
||||
stm32_usart_deinit(&intmoduleUSART);
|
||||
@@ -115,7 +127,9 @@ void* intmoduleSerialStart(const etx_serial_init* params)
|
||||
intmodule_driver.on_error = nullptr;
|
||||
|
||||
stm32_usart_init(&intmoduleUSART, params);
|
||||
return nullptr;
|
||||
|
||||
intmoduleCtx.rxFifo->clear();
|
||||
return (void*)&intmoduleCtx;
|
||||
}
|
||||
|
||||
#define USART_FLAG_ERRORS (USART_FLAG_ORE | USART_FLAG_NE | USART_FLAG_FE | USART_FLAG_PE)
|
||||
@@ -126,13 +140,13 @@ extern "C" void INTMODULE_USART_IRQHandler(void)
|
||||
|
||||
void intmoduleSendByte(void* ctx, uint8_t byte)
|
||||
{
|
||||
(void)ctx;
|
||||
stm32_usart_send_byte(&intmoduleUSART, byte);
|
||||
auto modCtx = (IntmoduleCtx*)ctx;
|
||||
stm32_usart_send_byte(modCtx->usart, byte);
|
||||
}
|
||||
|
||||
void intmoduleSendBuffer(void* ctx, const uint8_t * data, uint8_t size)
|
||||
{
|
||||
(void)ctx;
|
||||
auto modCtx = (IntmoduleCtx*)ctx;
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
@@ -140,26 +154,41 @@ void intmoduleSendBuffer(void* ctx, const uint8_t * data, uint8_t size)
|
||||
intmoduleTxBufferData = (uint8_t *)data;
|
||||
intmoduleTxBufferRemaining = size;
|
||||
#endif
|
||||
stm32_usart_send_buffer(&intmoduleUSART, data, size);
|
||||
stm32_usart_send_buffer(modCtx->usart, data, size);
|
||||
}
|
||||
|
||||
void intmoduleWaitForTxCompleted(void* ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
auto modCtx = (IntmoduleCtx*)ctx;
|
||||
#if defined(INTMODULE_DMA_STREAM)
|
||||
stm32_usart_wait_for_tx_dma(&intmoduleUSART);
|
||||
stm32_usart_wait_for_tx_dma(modCtx->usart);
|
||||
#else
|
||||
while (intmoduleTxBufferRemaining > 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int intmoduleGetByte(void* ctx, uint8_t* data)
|
||||
{
|
||||
auto modCtx = (IntmoduleCtx*)ctx;
|
||||
if (!modCtx->rxFifo) return -1;
|
||||
return modCtx->rxFifo->pop(*data);
|
||||
}
|
||||
|
||||
static void intmoduleClearRxBuffer(void* ctx)
|
||||
{
|
||||
auto modCtx = (IntmoduleCtx*)ctx;
|
||||
if (!modCtx->rxFifo) return;
|
||||
modCtx->rxFifo->clear();
|
||||
}
|
||||
|
||||
const etx_serial_driver_t IntmoduleSerialDriver = {
|
||||
.init = intmoduleSerialStart,
|
||||
.deinit = intmoduleStop,
|
||||
.sendByte = intmoduleSendByte,
|
||||
.sendBuffer = intmoduleSendBuffer,
|
||||
.waitForTxCompleted = intmoduleWaitForTxCompleted,
|
||||
.getByte = nullptr,
|
||||
.getByte = intmoduleGetByte,
|
||||
.clearRxBuffer = intmoduleClearRxBuffer,
|
||||
.getBaudrate = nullptr,
|
||||
.setReceiveCb = nullptr,
|
||||
.setBaudrateCb = nullptr,
|
||||
|
||||
@@ -297,15 +297,16 @@ void usbSerialSetBaudRateCb(void*, void (*cb)(uint32_t))
|
||||
// }
|
||||
|
||||
static const etx_serial_driver_t usbSerialDriver = {
|
||||
nullptr,
|
||||
nullptr,
|
||||
usbSerialPutc,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
usbSerialBaudRate,
|
||||
usbSerialSetReceiveDataCb,
|
||||
usbSerialSetBaudRateCb,
|
||||
.init = nullptr,
|
||||
.deinit = nullptr,
|
||||
.sendByte = usbSerialPutc,
|
||||
.sendBuffer = nullptr,
|
||||
.waitForTxCompleted = nullptr,
|
||||
.getByte = nullptr,
|
||||
.clearRxBuffer = nullptr,
|
||||
.getBaudrate = usbSerialBaudRate,
|
||||
.setReceiveCb = usbSerialSetReceiveDataCb,
|
||||
.setBaudrateCb = usbSerialSetBaudRateCb,
|
||||
};
|
||||
|
||||
const etx_serial_port_t UsbSerialPort = {
|
||||
|
||||
@@ -19,14 +19,11 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "hal.h"
|
||||
#include "hal/serial_driver.h"
|
||||
#include "intmodule_serial_driver.h"
|
||||
#include "extmodule_serial_driver.h"
|
||||
|
||||
#include "io/frsky_pxx2.h"
|
||||
ModuleFifo intmoduleFifo;
|
||||
ModuleFifo extmoduleFifo;
|
||||
|
||||
void intmoduleStop() {}
|
||||
void intmoduleFifoError() {}
|
||||
void intmoduleFifoReceive(uint8_t){}
|
||||
@@ -40,7 +37,6 @@ void extmoduleFifoReceive(uint8_t){}
|
||||
|
||||
void extmodulePpmStart(unsigned short, bool) {}
|
||||
void extmoduleSerialStart() {}
|
||||
void extmodulePxx1SerialStart() {}
|
||||
void extmodulePxx1PulsesStart() {}
|
||||
void extmoduleInitTxPin() {}
|
||||
void extmoduleSendInvertedByte(uint8_t) {}
|
||||
@@ -78,6 +74,7 @@ const etx_serial_driver_t IntmoduleSerialDriver = {
|
||||
.sendBuffer = sendBuffer,
|
||||
.waitForTxCompleted = waitForTxCompleted,
|
||||
.getByte = getByte,
|
||||
.clearRxBuffer = nullptr,
|
||||
.getBaudrate = nullptr,
|
||||
.setReceiveCb = nullptr,
|
||||
.setBaudrateCb = nullptr,
|
||||
@@ -90,6 +87,7 @@ const etx_serial_driver_t ExtmoduleSerialDriver = {
|
||||
.sendBuffer = sendBuffer,
|
||||
.waitForTxCompleted = waitForTxCompleted,
|
||||
.getByte = getByte,
|
||||
.clearRxBuffer = nullptr,
|
||||
.getBaudrate = nullptr,
|
||||
.setReceiveCb = nullptr,
|
||||
.setBaudrateCb = nullptr,
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
static_assert(PXX2_FRAME_MAXLENGTH <= INTMODULE_FIFO_SIZE);
|
||||
|
||||
void processGetHardwareInfoFrame(uint8_t module, const uint8_t * frame)
|
||||
static void processGetHardwareInfoFrame(uint8_t module, const uint8_t * frame)
|
||||
{
|
||||
if (moduleState[module].mode != MODULE_MODE_GET_HARDWARE_INFO) {
|
||||
return;
|
||||
@@ -60,7 +60,7 @@ void processGetHardwareInfoFrame(uint8_t module, const uint8_t * frame)
|
||||
}
|
||||
}
|
||||
|
||||
void processModuleSettingsFrame(uint8_t module, const uint8_t * frame)
|
||||
static void processModuleSettingsFrame(uint8_t module, const uint8_t * frame)
|
||||
{
|
||||
if (moduleState[module].mode != MODULE_MODE_MODULE_SETTINGS) {
|
||||
return;
|
||||
@@ -80,7 +80,7 @@ void processModuleSettingsFrame(uint8_t module, const uint8_t * frame)
|
||||
moduleState[module].mode = MODULE_MODE_NORMAL;
|
||||
}
|
||||
|
||||
void processReceiverSettingsFrame(uint8_t module, const uint8_t * frame)
|
||||
static void processReceiverSettingsFrame(uint8_t module, const uint8_t * frame)
|
||||
{
|
||||
if (moduleState[module].mode != MODULE_MODE_RECEIVER_SETTINGS) {
|
||||
return;
|
||||
@@ -117,7 +117,7 @@ void processReceiverSettingsFrame(uint8_t module, const uint8_t * frame)
|
||||
moduleState[module].mode = MODULE_MODE_NORMAL;
|
||||
}
|
||||
|
||||
void processRegisterFrame(uint8_t module, const uint8_t * frame)
|
||||
static void processRegisterFrame(uint8_t module, const uint8_t * frame)
|
||||
{
|
||||
if (moduleState[module].mode != MODULE_MODE_REGISTER) {
|
||||
return;
|
||||
@@ -153,7 +153,7 @@ void processRegisterFrame(uint8_t module, const uint8_t * frame)
|
||||
}
|
||||
}
|
||||
|
||||
void processBindFrame(uint8_t module, const uint8_t * frame)
|
||||
static void processBindFrame(uint8_t module, const uint8_t * frame)
|
||||
{
|
||||
if (moduleState[module].mode != MODULE_MODE_BIND) {
|
||||
return;
|
||||
@@ -204,7 +204,7 @@ void processBindFrame(uint8_t module, const uint8_t * frame)
|
||||
}
|
||||
}
|
||||
|
||||
void processResetFrame(uint8_t module, const uint8_t * frame)
|
||||
static void processResetFrame(uint8_t module, const uint8_t * frame)
|
||||
{
|
||||
if (moduleState[module].mode != MODULE_MODE_RESET) {
|
||||
return;
|
||||
@@ -217,7 +217,7 @@ void processResetFrame(uint8_t module, const uint8_t * frame)
|
||||
moduleState[module].mode = MODULE_MODE_NORMAL;
|
||||
}
|
||||
|
||||
void processTelemetryFrame(uint8_t module, const uint8_t * frame)
|
||||
static void processTelemetryFrame(uint8_t module, const uint8_t * frame)
|
||||
{
|
||||
for (uint8_t i = 0; i < 1 + frame[0]; i++) {
|
||||
telemetryMirrorSend(frame[i]);
|
||||
@@ -237,7 +237,7 @@ extern "C" {
|
||||
|
||||
volatile int16_t authenticateFrames = 0;
|
||||
|
||||
void processAuthenticationFrame(uint8_t module, const uint8_t * frame)
|
||||
static void processAuthenticationFrame(uint8_t module, const uint8_t * frame)
|
||||
{
|
||||
uint8_t cryptoType = frame[3];
|
||||
uint8_t messageDigest[16] = {0};
|
||||
@@ -280,7 +280,7 @@ void processAuthenticationFrame(uint8_t module, const uint8_t * frame)
|
||||
#define processAuthenticationFrame(module, frame)
|
||||
#endif
|
||||
|
||||
void processSpectrumAnalyserFrame(uint8_t module, const uint8_t * frame)
|
||||
static void processSpectrumAnalyserFrame(uint8_t module, const uint8_t * frame)
|
||||
{
|
||||
if (moduleState[module].mode != MODULE_MODE_SPECTRUM_ANALYSER) {
|
||||
return;
|
||||
@@ -307,7 +307,7 @@ void processSpectrumAnalyserFrame(uint8_t module, const uint8_t * frame)
|
||||
}
|
||||
}
|
||||
|
||||
void processPowerMeterFrame(uint8_t module, const uint8_t * frame)
|
||||
static void processPowerMeterFrame(uint8_t module, const uint8_t * frame)
|
||||
{
|
||||
if (moduleState[module].mode != MODULE_MODE_POWER_METER) {
|
||||
return;
|
||||
@@ -319,7 +319,7 @@ void processPowerMeterFrame(uint8_t module, const uint8_t * frame)
|
||||
}
|
||||
}
|
||||
|
||||
void processOtaUpdateFrame(uint8_t module, const uint8_t * frame)
|
||||
static void processOtaUpdateFrame(uint8_t module, const uint8_t * frame)
|
||||
{
|
||||
if (moduleState[module].mode != MODULE_MODE_OTA_UPDATE) {
|
||||
return;
|
||||
@@ -345,7 +345,7 @@ void processOtaUpdateFrame(uint8_t module, const uint8_t * frame)
|
||||
}
|
||||
}
|
||||
|
||||
void processModuleFrame(uint8_t module, const uint8_t *frame)
|
||||
static void processModuleFrame(uint8_t module, const uint8_t *frame)
|
||||
{
|
||||
switch (frame[2]) {
|
||||
case PXX2_TYPE_ID_HW_INFO:
|
||||
@@ -382,7 +382,7 @@ void processModuleFrame(uint8_t module, const uint8_t *frame)
|
||||
}
|
||||
}
|
||||
|
||||
void processToolsFrame(uint8_t module, const uint8_t * frame)
|
||||
static void processToolsFrame(uint8_t module, const uint8_t * frame)
|
||||
{
|
||||
switch (frame[2]) {
|
||||
case PXX2_TYPE_ID_POWER_METER:
|
||||
@@ -395,7 +395,7 @@ void processToolsFrame(uint8_t module, const uint8_t * frame)
|
||||
}
|
||||
}
|
||||
|
||||
void processPXX2Frame(uint8_t module, const uint8_t * frame)
|
||||
static void processPXX2Frame(uint8_t module, const uint8_t * frame)
|
||||
{
|
||||
LOG_TELEMETRY_WRITE_START();
|
||||
for (uint8_t i = 0; i < 1 + frame[0]; i++) {
|
||||
@@ -419,3 +419,50 @@ void processPXX2Frame(uint8_t module, const uint8_t * frame)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void processPXX2TelemetryData(uint8_t data, uint8_t idx)
|
||||
{
|
||||
uint8_t * rxBuffer = getTelemetryRxBuffer(idx);
|
||||
uint8_t &rxBufferCount = getTelemetryRxBufferCount(idx);
|
||||
|
||||
if (rxBufferCount == 0 && data != START_STOP) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rxBufferCount == 1 && data > 40) {
|
||||
rxBufferCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (rxBufferCount < TELEMETRY_RX_PACKET_SIZE) {
|
||||
rxBuffer[rxBufferCount++] = data;
|
||||
}
|
||||
else {
|
||||
TRACE("[PXX2] array size %d error", rxBufferCount);
|
||||
rxBufferCount = 0;
|
||||
}
|
||||
|
||||
uint8_t len = rxBuffer[1];
|
||||
/* 1 byte start + 2 bytes header + 2 bytes CRC = 5 */
|
||||
if (rxBufferCount < unsigned(len + 5)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t crc = 0xFFFF;
|
||||
uint8_t* frame = rxBuffer + 1;
|
||||
|
||||
for (uint32_t i = 1; i <= len; i++) {
|
||||
crc -= frame[i];
|
||||
}
|
||||
|
||||
uint8_t crcHigh = frame[len + 1];
|
||||
uint8_t crcLow = frame[len + 2];
|
||||
|
||||
if (((crc >> 8) != crcHigh) || ((crc & 0xFF) == crcLow)) {
|
||||
TRACE("[PXX2] crc error");
|
||||
rxBufferCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
processPXX2Frame(idx, frame);
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@
|
||||
#include "flysky_ibus.h"
|
||||
#endif
|
||||
|
||||
#if defined(INTMODULE_USART)
|
||||
#include "intmodule_serial_driver.h"
|
||||
#endif
|
||||
|
||||
uint8_t telemetryStreaming = 0;
|
||||
uint8_t telemetryRxBuffer[TELEMETRY_RX_PACKET_SIZE]; // Receive buffer. 9 bytes (full packet), worst case 18 bytes with byte-stuffing (+1)
|
||||
uint8_t telemetryRxBufferCount = 0;
|
||||
@@ -236,28 +240,6 @@ inline bool isBadAntennaDetected()
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(INTERNAL_MODULE_PXX2)
|
||||
static void pollIntPXX2()
|
||||
{
|
||||
uint8_t frame[PXX2_FRAME_MAXLENGTH];
|
||||
|
||||
while (intmoduleFifo.getFrame(frame)) {
|
||||
processPXX2Frame(INTERNAL_MODULE, frame);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PXX2) && defined(EXTMODULE_USART)
|
||||
static void pollExtPXX2()
|
||||
{
|
||||
uint8_t frame[PXX2_FRAME_MAXLENGTH];
|
||||
|
||||
while (extmoduleFifo.getFrame(frame)) {
|
||||
processPXX2Frame(EXTERNAL_MODULE, frame);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MULTI_PROTOLIST)
|
||||
static inline void pollMultiProtolist(uint8_t idx)
|
||||
{
|
||||
@@ -268,19 +250,51 @@ static inline void pollMultiProtolist(uint8_t idx)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void pollIntTelemetry(void (*processData)(uint8_t,uint8_t))
|
||||
static inline void pollIntTelemetry(void (*processData)(uint8_t, uint8_t))
|
||||
{
|
||||
auto drv = getIntModuleDriver();
|
||||
auto ctx = getIntModuleCtx();
|
||||
|
||||
if (!drv || !drv->getByte || !processData) return;
|
||||
|
||||
uint8_t data;
|
||||
if (intmoduleFifo.pop(data)) {
|
||||
if (drv->getByte(ctx, &data) > 0) {
|
||||
LOG_TELEMETRY_WRITE_START();
|
||||
do {
|
||||
telemetryMirrorSend(data);
|
||||
processData(data, INTERNAL_MODULE);
|
||||
LOG_TELEMETRY_WRITE_BYTE(data);
|
||||
} while (intmoduleFifo.pop(data));
|
||||
}
|
||||
} while (drv->getByte(ctx, &data) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(INTERNAL_MODULE_PXX2)
|
||||
static void pollIntPXX2()
|
||||
{
|
||||
pollIntTelemetry(processPXX2TelemetryData);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PXX2) && defined(EXTMODULE_USART)
|
||||
static void pollExtPXX2()
|
||||
{
|
||||
auto drv = getExtModuleDriver();
|
||||
auto ctx = getExtModuleCtx();
|
||||
|
||||
if (!drv || !drv->getByte) return;
|
||||
|
||||
uint8_t data;
|
||||
if (drv->getByte(ctx, &data) > 0) {
|
||||
LOG_TELEMETRY_WRITE_START();
|
||||
do {
|
||||
telemetryMirrorSend(data);
|
||||
processPXX2TelemetryData(data, EXTERNAL_MODULE);
|
||||
LOG_TELEMETRY_WRITE_BYTE(data);
|
||||
} while (drv->getByte(ctx, &data) > 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(INTERNAL_MODULE_MULTI)
|
||||
static void pollIntMulti()
|
||||
{
|
||||
|
||||
@@ -50,7 +50,7 @@ constexpr uint8_t TELEMETRY_TIMEOUT10ms = 100; // 1 second
|
||||
#define TELEMETRY_SERIAL_8E2 1
|
||||
#define TELEMETRY_SERIAL_WITHOUT_DMA 2
|
||||
|
||||
#if defined(CROSSFIRE) || defined(MULTIMODULE) || defined(AFHDS3)
|
||||
#if defined(CROSSFIRE) || defined(MULTIMODULE) || defined(AFHDS3) || defined(PXX2)
|
||||
#define TELEMETRY_RX_PACKET_SIZE 128
|
||||
// multi module Spektrum telemetry is 18 bytes, FlySky is 37 bytes
|
||||
#else
|
||||
@@ -304,7 +304,7 @@ extern OutputTelemetryBuffer outputTelemetryBuffer __DMA;
|
||||
extern Fifo<uint8_t, LUA_TELEMETRY_INPUT_FIFO_SIZE> * luaInputTelemetryFifo;
|
||||
#endif
|
||||
|
||||
void processPXX2Frame(uint8_t module, const uint8_t *frame);
|
||||
void processPXX2TelemetryData(uint8_t data, uint8_t idx);
|
||||
|
||||
// Module pulse synchronization
|
||||
struct ModuleSyncStatus
|
||||
|
||||
Reference in New Issue
Block a user