mirror of
https://github.com/g4klx/MMDVMHost
synced 2025-12-21 15:09:23 +08:00
Add RAW data in and out of the FM repeater for SVXLink.
This commit is contained in:
@@ -147,7 +147,7 @@ unsigned int CFMControl::readModem(unsigned char* data, unsigned int space)
|
|||||||
space = 240U; // 160 samples 12-bit
|
space = 240U; // 160 samples 12-bit
|
||||||
|
|
||||||
float netData[160U]; // Modem can handle up to 160 samples at a time
|
float netData[160U]; // Modem can handle up to 160 samples at a time
|
||||||
unsigned int length = m_network->read(netData, 160U); // 160 samples 12-bit
|
unsigned int length = m_network->readData(netData, 160U); // 160 samples 12-bit
|
||||||
if (length == 0U)
|
if (length == 0U)
|
||||||
return 0U;
|
return 0U;
|
||||||
|
|
||||||
|
|||||||
344
FMNetwork.cpp
344
FMNetwork.cpp
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020,2021 by Jonathan Naylor G4KLX
|
* Copyright (C) 2020,2021,2023 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -50,8 +50,10 @@ m_seqNo(0U)
|
|||||||
if (pos != std::string::npos)
|
if (pos != std::string::npos)
|
||||||
m_callsign = callsign.substr(0U, pos);
|
m_callsign = callsign.substr(0U, pos);
|
||||||
|
|
||||||
// if (protocol == "USRP")
|
if (protocol == "RAW")
|
||||||
// m_protocol = FMNP_USRP;
|
m_protocol = FMNP_RAW;
|
||||||
|
else
|
||||||
|
m_protocol = FMNP_USRP;
|
||||||
}
|
}
|
||||||
|
|
||||||
CFMNetwork::~CFMNetwork()
|
CFMNetwork::~CFMNetwork()
|
||||||
@@ -75,8 +77,21 @@ bool CFMNetwork::writeData(float* data, unsigned int nSamples)
|
|||||||
assert(data != NULL);
|
assert(data != NULL);
|
||||||
assert(nSamples > 0U);
|
assert(nSamples > 0U);
|
||||||
|
|
||||||
|
if (m_protocol == FMNP_USRP)
|
||||||
|
return writeUSRPData(data, nSamples);
|
||||||
|
else if (m_protocol == FMNP_RAW)
|
||||||
|
return writeRawData(data, nSamples);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFMNetwork::writeUSRPData(float* data, unsigned int nSamples)
|
||||||
|
{
|
||||||
|
assert(data != NULL);
|
||||||
|
assert(nSamples > 0U);
|
||||||
|
|
||||||
if (m_seqNo == 0U) {
|
if (m_seqNo == 0U) {
|
||||||
bool ret = writeStart();
|
bool ret = writeUSRPStart();
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -86,53 +101,51 @@ bool CFMNetwork::writeData(float* data, unsigned int nSamples)
|
|||||||
|
|
||||||
unsigned int length = 0U;
|
unsigned int length = 0U;
|
||||||
|
|
||||||
if (m_protocol == FMNP_USRP) {
|
buffer[length++] = 'U';
|
||||||
buffer[length++] = 'U';
|
buffer[length++] = 'S';
|
||||||
buffer[length++] = 'S';
|
buffer[length++] = 'R';
|
||||||
buffer[length++] = 'R';
|
buffer[length++] = 'P';
|
||||||
buffer[length++] = 'P';
|
|
||||||
|
|
||||||
// Sequence number
|
// Sequence number
|
||||||
buffer[length++] = (m_seqNo >> 24) & 0xFFU;
|
buffer[length++] = (m_seqNo >> 24) & 0xFFU;
|
||||||
buffer[length++] = (m_seqNo >> 16) & 0xFFU;
|
buffer[length++] = (m_seqNo >> 16) & 0xFFU;
|
||||||
buffer[length++] = (m_seqNo >> 8) & 0xFFU;
|
buffer[length++] = (m_seqNo >> 8) & 0xFFU;
|
||||||
buffer[length++] = (m_seqNo >> 0) & 0xFFU;
|
buffer[length++] = (m_seqNo >> 0) & 0xFFU;
|
||||||
|
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
// PTT on
|
// PTT on
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x01U;
|
buffer[length++] = 0x01U;
|
||||||
|
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
// Type, 0 for audio
|
// Type, 0 for audio
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int i = 0U; i < nSamples; i++) {
|
for (unsigned int i = 0U; i < nSamples; i++) {
|
||||||
short val = short(data[i] * 32767.0F + 0.5F); // Changing audio format from float to S16LE
|
short val = short(data[i] * 32767.0F + 0.5F); // Changing audio format from float to S16LE
|
||||||
|
|
||||||
buffer[length++] = (val >> 0) & 0xFFU;
|
buffer[length++] = (val >> 0) & 0xFFU;
|
||||||
buffer[length++] = (val >> 8) & 0xFFU;
|
buffer[length++] = (val >> 8) & 0xFFU;
|
||||||
@@ -146,59 +159,91 @@ bool CFMNetwork::writeData(float* data, unsigned int nSamples)
|
|||||||
return m_socket.write(buffer, length, m_addr, m_addrLen);
|
return m_socket.write(buffer, length, m_addr, m_addrLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CFMNetwork::writeRawData(float* data, unsigned int nSamples)
|
||||||
|
{
|
||||||
|
assert(data != NULL);
|
||||||
|
assert(nSamples > 0U);
|
||||||
|
|
||||||
|
unsigned char buffer[1000U];
|
||||||
|
::memset(buffer, 0x00U, 1000U);
|
||||||
|
|
||||||
|
unsigned int length = 0U;
|
||||||
|
|
||||||
|
for (unsigned int i = 0U; i < nSamples; i++) {
|
||||||
|
short val = short(data[i] * 32767.0F + 0.5F); // Changing audio format from float to S16LE
|
||||||
|
|
||||||
|
buffer[length++] = (val >> 0) & 0xFFU;
|
||||||
|
buffer[length++] = (val >> 8) & 0xFFU;
|
||||||
|
|
||||||
|
buffer[length++] = (val >> 0) & 0xFFU;
|
||||||
|
buffer[length++] = (val >> 8) & 0xFFU;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_debug)
|
||||||
|
CUtils::dump(1U, "FM Network Data Sent", buffer, length);
|
||||||
|
|
||||||
|
return m_socket.write(buffer, length, m_addr, m_addrLen);
|
||||||
|
}
|
||||||
|
|
||||||
bool CFMNetwork::writeEnd()
|
bool CFMNetwork::writeEnd()
|
||||||
|
{
|
||||||
|
if (m_protocol == FMNP_USRP)
|
||||||
|
return writeUSRPEnd();
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFMNetwork::writeUSRPEnd()
|
||||||
{
|
{
|
||||||
unsigned char buffer[500U];
|
unsigned char buffer[500U];
|
||||||
::memset(buffer, 0x00U, 500U);
|
::memset(buffer, 0x00U, 500U);
|
||||||
|
|
||||||
unsigned int length = 0U;
|
unsigned int length = 0U;
|
||||||
|
|
||||||
if (m_protocol == FMNP_USRP) {
|
buffer[length++] = 'U';
|
||||||
buffer[length++] = 'U';
|
buffer[length++] = 'S';
|
||||||
buffer[length++] = 'S';
|
buffer[length++] = 'R';
|
||||||
buffer[length++] = 'R';
|
buffer[length++] = 'P';
|
||||||
buffer[length++] = 'P';
|
|
||||||
|
|
||||||
// Sequence number
|
// Sequence number
|
||||||
buffer[length++] = (m_seqNo >> 24) & 0xFFU;
|
buffer[length++] = (m_seqNo >> 24) & 0xFFU;
|
||||||
buffer[length++] = (m_seqNo >> 16) & 0xFFU;
|
buffer[length++] = (m_seqNo >> 16) & 0xFFU;
|
||||||
buffer[length++] = (m_seqNo >> 8) & 0xFFU;
|
buffer[length++] = (m_seqNo >> 8) & 0xFFU;
|
||||||
buffer[length++] = (m_seqNo >> 0) & 0xFFU;
|
buffer[length++] = (m_seqNo >> 0) & 0xFFU;
|
||||||
|
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
// PTT off
|
// PTT off
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
// Type, 0 for audio
|
// Type, 0 for audio
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
length += 320U;
|
length += 320U;
|
||||||
}
|
|
||||||
|
|
||||||
m_seqNo = 0U;
|
m_seqNo = 0U;
|
||||||
|
|
||||||
@@ -250,10 +295,19 @@ void CFMNetwork::clock(unsigned int ms)
|
|||||||
|
|
||||||
if (type == 0U)
|
if (type == 0U)
|
||||||
m_buffer.addData(buffer + 32U, length - 32U);
|
m_buffer.addData(buffer + 32U, length - 32U);
|
||||||
|
} else if (m_protocol == FMNP_RAW) {
|
||||||
|
for (int i = 0U; i < length; i += 4U) {
|
||||||
|
unsigned char data[2U];
|
||||||
|
|
||||||
|
data[0U] = buffer[i + 0];
|
||||||
|
data[1U] = buffer[i + 1];
|
||||||
|
|
||||||
|
m_buffer.addData(data, 2U);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int CFMNetwork::read(float* data, unsigned int nSamples)
|
unsigned int CFMNetwork::readData(float* data, unsigned int nSamples)
|
||||||
{
|
{
|
||||||
assert(data != NULL);
|
assert(data != NULL);
|
||||||
assert(nSamples > 0U);
|
assert(nSamples > 0U);
|
||||||
@@ -298,94 +352,92 @@ void CFMNetwork::enable(bool enabled)
|
|||||||
m_enabled = enabled;
|
m_enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CFMNetwork::writeStart()
|
bool CFMNetwork::writeUSRPStart()
|
||||||
{
|
{
|
||||||
unsigned char buffer[500U];
|
unsigned char buffer[500U];
|
||||||
::memset(buffer, 0x00U, 500U);
|
::memset(buffer, 0x00U, 500U);
|
||||||
|
|
||||||
unsigned int length = 0U;
|
unsigned int length = 0U;
|
||||||
|
|
||||||
if (m_protocol == FMNP_USRP) {
|
buffer[length++] = 'U';
|
||||||
buffer[length++] = 'U';
|
buffer[length++] = 'S';
|
||||||
buffer[length++] = 'S';
|
buffer[length++] = 'R';
|
||||||
buffer[length++] = 'R';
|
buffer[length++] = 'P';
|
||||||
buffer[length++] = 'P';
|
|
||||||
|
|
||||||
// Sequence number
|
// Sequence number
|
||||||
buffer[length++] = (m_seqNo >> 24) & 0xFFU;
|
buffer[length++] = (m_seqNo >> 24) & 0xFFU;
|
||||||
buffer[length++] = (m_seqNo >> 16) & 0xFFU;
|
buffer[length++] = (m_seqNo >> 16) & 0xFFU;
|
||||||
buffer[length++] = (m_seqNo >> 8) & 0xFFU;
|
buffer[length++] = (m_seqNo >> 8) & 0xFFU;
|
||||||
buffer[length++] = (m_seqNo >> 0) & 0xFFU;
|
buffer[length++] = (m_seqNo >> 0) & 0xFFU;
|
||||||
|
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
// PTT off
|
// PTT off
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
// Type, 2 for metadata
|
// Type, 2 for metadata
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x02U;
|
buffer[length++] = 0x02U;
|
||||||
|
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
// TLV TAG for Metadata
|
// TLV TAG for Metadata
|
||||||
buffer[length++] = 0x08U;
|
buffer[length++] = 0x08U;
|
||||||
|
|
||||||
// TLV Length
|
// TLV Length
|
||||||
buffer[length++] = 3U + 4U + 3U + 1U + 1U + m_callsign.size() + 1U;
|
buffer[length++] = 3U + 4U + 3U + 1U + 1U + m_callsign.size() + 1U;
|
||||||
|
|
||||||
// DMR Id
|
// DMR Id
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
// Rpt Id
|
// Rpt Id
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
// Talk Group
|
// Talk Group
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
// Time Slot
|
// Time Slot
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
// Color Code
|
// Color Code
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
// Callsign
|
// Callsign
|
||||||
for (std::string::const_iterator it = m_callsign.cbegin(); it != m_callsign.cend(); ++it)
|
for (std::string::const_iterator it = m_callsign.cbegin(); it != m_callsign.cend(); ++it)
|
||||||
buffer[length++] = *it;
|
buffer[length++] = *it;
|
||||||
|
|
||||||
// End of Metadata
|
// End of Metadata
|
||||||
buffer[length++] = 0x00U;
|
buffer[length++] = 0x00U;
|
||||||
|
|
||||||
length = 70U;
|
length = 70U;
|
||||||
}
|
|
||||||
|
|
||||||
if (length > 0U) {
|
if (length > 0U) {
|
||||||
if (m_debug)
|
if (m_debug)
|
||||||
|
|||||||
15
FMNetwork.h
15
FMNetwork.h
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020,2021 by Jonathan Naylor G4KLX
|
* Copyright (C) 2020,2021,2023 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -26,7 +26,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
enum FM_NETWORK_PROTOCOL {
|
enum FM_NETWORK_PROTOCOL {
|
||||||
FMNP_USRP
|
FMNP_USRP,
|
||||||
|
FMNP_RAW
|
||||||
};
|
};
|
||||||
|
|
||||||
class CFMNetwork {
|
class CFMNetwork {
|
||||||
@@ -42,7 +43,7 @@ public:
|
|||||||
|
|
||||||
bool writeEnd();
|
bool writeEnd();
|
||||||
|
|
||||||
unsigned int read(float* data, unsigned int nSamples);
|
unsigned int readData(float* data, unsigned int nSamples);
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
@@ -61,7 +62,13 @@ private:
|
|||||||
CRingBuffer<unsigned char> m_buffer;
|
CRingBuffer<unsigned char> m_buffer;
|
||||||
unsigned int m_seqNo;
|
unsigned int m_seqNo;
|
||||||
|
|
||||||
bool writeStart();
|
bool writeUSRPStart();
|
||||||
|
|
||||||
|
bool writeUSRPData(float* data, unsigned int nSamples);
|
||||||
|
bool writeRawData(float* data, unsigned int nSamples);
|
||||||
|
|
||||||
|
bool writeUSRPEnd();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -287,7 +287,8 @@ Debug=0
|
|||||||
|
|
||||||
[FM Network]
|
[FM Network]
|
||||||
Enable=1
|
Enable=1
|
||||||
# Protocol=USRP
|
# Protocol may be USRP or RAW
|
||||||
|
Protocol=USRP
|
||||||
LocalAddress=127.0.0.1
|
LocalAddress=127.0.0.1
|
||||||
LocalPort=3810
|
LocalPort=3810
|
||||||
GatewayAddress=127.0.0.1
|
GatewayAddress=127.0.0.1
|
||||||
|
|||||||
Reference in New Issue
Block a user