Skip to content

Commit

Permalink
Implemented basic RTXLink for OpenRTX devices. Addresses #251.
Browse files Browse the repository at this point in the history
  • Loading branch information
hmatuschek committed Mar 7, 2023
1 parent 87838df commit f9c66f3
Show file tree
Hide file tree
Showing 8 changed files with 337 additions and 30 deletions.
4 changes: 2 additions & 2 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ SET(libdmrconf_SOURCES
gd77.cc gd77_codeplug.cc gd77_callsigndb.cc gd77_filereader.cc gd77_limits.cc
opengd77.cc opengd77_interface.cc opengd77_codeplug.cc opengd77_extension.cc
opengd77_callsigndb.cc opengd77_limits.cc
openrtx.cc openrtx_interface.cc openrtx_codeplug.cc
openrtx.cc openrtx_link.cc openrtx_interface.cc openrtx_codeplug.cc
anytone_interface.cc anytone_radio.cc anytone_codeplug.cc anytone_extension.cc anytone_limits.cc
d868uv.cc d868uv_codeplug.cc d868uv_callsigndb.cc d868uv_limits.cc
d878uv.cc d878uv_codeplug.cc d878uv_limits.cc
Expand All @@ -50,7 +50,7 @@ SET(libdmrconf_MOC_HEADERS
gd77.hh gd77_codeplug.hh gd77_callsigndb.hh gd77_limits.hh
opengd77.hh opengd77_interface.hh opengd77_codeplug.hh opengd77_extension.hh
opengd77_callsigndb.hh opengd77_limits.hh
openrtx.hh openrtx_interface.hh openrtx_codeplug.hh
openrtx.hh openrtx_link.hh openrtx_interface.hh openrtx_codeplug.hh
anytone_interface.hh anytone_radio.hh anytone_codeplug.hh anytone_extension.hh anytone_limits.hh
d868uv.hh d868uv_codeplug.hh d868uv_callsigndb.hh d868uv_limits.hh
d878uv.hh d878uv_codeplug.hh d878uv_limits.hh
Expand Down
6 changes: 3 additions & 3 deletions lib/openrtx_interface.cc
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
#include "openrtx_interface.hh"

OpenRTXInterface::OpenRTXInterface(const USBDeviceDescriptor &descr, const ErrorStack &err, QObject *parent)
: DFUDevice(descr, err, parent), RadioInterface()
: USBSerial(descr, err, parent)
{
// pass...
}


bool
OpenRTXInterface::isOpen() const {
return DFUDevice::isOpen();
return USBSerial::isOpen();
}

void
OpenRTXInterface::close() {
DFUDevice::close();
USBSerial::close();
}


Expand Down
16 changes: 12 additions & 4 deletions lib/openrtx_interface.hh
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,26 @@
#define OPENRTXINTERFACE_HH

#include "radiointerface.hh"
#include "dfu_libusb.hh"
#include "packetstream.hh"
#include "usbserial.hh"
#include "xmodem.hh"

/** Implements the communication interface to radios running the OpenRTX firmware.
*
* The protocol is called rtxlink and is documented at https://openrtx.org/#/rtxlink. The protocol
* has several layers. The lowest is a serial interface either as a VCOM (UBS CDC-ACM) or a proper
* hardware UART. Ontop of that, there is SLIP. Followed by a simple framing layer, that determines
* the higher-level protocol.
* @ingroup ortx */
class OpenRTXInterface : public DFUDevice, public RadioInterface
class OpenRTXInterface : public USBSerial
{
Q_OBJECT

public:
/** Constructor.
* @param descr The USB device descriptor. Used to identify a specific USB device.
* @param err The stack of error messages.
* @param parent The QObject parent. */
explicit OpenRTXInterface(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent = nullptr);

bool isOpen() const;
Expand All @@ -28,8 +38,6 @@ public:
bool write_finish(const ErrorStack &err=ErrorStack());

bool reboot(const ErrorStack &err=ErrorStack());


};

#endif // OPENRTXINTERFACE_HH
165 changes: 165 additions & 0 deletions lib/openrtx_link.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#include "openrtx_link.hh"


/* ******************************************************************************************** *
* Implements the OpenRTX link stream socket
* ******************************************************************************************** */
OpenRTXLinkStream::OpenRTXLinkStream(OpenRTXLink::Protocol proto, OpenRTXLink *link)
: QIODevice(link), _proto(proto), _inBuffer(), _link(link)
{
// pass...
}

qint64
OpenRTXLinkStream::writeData(const char *data, qint64 len) {
QByteArray buffer(data, len);

ErrorStack err;
if (! _link->send(OpenRTXLink::Protocol::Stdio, buffer, -1, err)) {
setErrorString(err.format());
return -1;
}

return len;
}

qint64
OpenRTXLinkStream::readData(char *data, qint64 maxlen) {
ErrorStack err;
qint64 n_read = 0, n_toread=maxlen;

while (0 < n_toread) {
if (_inBuffer.size()) {
qint64 n = std::min((qint64)_inBuffer.size(), n_toread);
memcpy(data, _inBuffer.data(), n);
_inBuffer.remove(0, n);
n_toread -= n; data+= n; n_read += n;
}
if (0 == n_toread)
return n_read;
if (! _link->receive(OpenRTXLink::Protocol::Stdio, _inBuffer, -1, err)) {
setErrorString(err.format());
return -1;
}
}

return n_read;
}



/* ******************************************************************************************** *
* Implements the OpenRTX link datagram socket
* ******************************************************************************************** */
OpenRTXLinkDatagram::OpenRTXLinkDatagram(OpenRTXLink::Protocol proto, OpenRTXLink *link)
: PacketStream(link), _proto(proto), _link(link)
{
// pass...
}

bool
OpenRTXLinkDatagram::receive(QByteArray &buffer, int timeout, const ErrorStack &err) {
return _link->receive(_proto, buffer, timeout, err);
}

bool
OpenRTXLinkDatagram::send(const QByteArray &buffer, int timeout, const ErrorStack &err) {
return _link->send(_proto, buffer, timeout, err);
}


/* ******************************************************************************************** *
* Implements the OpenRTX CAT interface
* ******************************************************************************************** */
OpenRTXCAT::OpenRTXCAT(OpenRTXLink *link)
: OpenRTXLinkDatagram(OpenRTXLink::Protocol::CAT, link)
{
// pass...
}


/* ******************************************************************************************** *
* Implements the OpenRTX link protocol dispatcher
* ******************************************************************************************** */
OpenRTXLink::OpenRTXLink(PacketStream *link, QObject *parent)
: QObject{parent}, _link(link),
_stdio(new OpenRTXLinkStream(Protocol::Stdio, this)),
_cat(new OpenRTXCAT(this)),
_fmp(new OpenRTXLinkDatagram(Protocol::FMP, this)),
_xmodem(new OpenRTXLinkStream(Protocol::XMODEM, this))
{
// pass...
}

OpenRTXLinkStream *
OpenRTXLink::stdio() const {
return _stdio;
}

OpenRTXCAT *OpenRTXLink::cat() const {
return _cat;
}

OpenRTXLinkDatagram *
OpenRTXLink::fmp() const {
return _fmp;
}

OpenRTXLinkStream *
OpenRTXLink::xmodem() const {
return _xmodem;
}

uint8_t
OpenRTXLink::crc8(const QByteArray &data) {
constexpr uint8_t poly = 0xA6;
uint8_t crc = 0;
for (int i=0; i<data.size(); i++) {
crc ^= data[i];
for (int j=0; j<8; j++) {
if (crc & 0x80)
crc = ( (crc<<1) ^ poly );
else
crc <<= 1;
}
}
return crc;
}

bool
OpenRTXLink::send(Protocol proto, const QByteArray &data, int timeout, const ErrorStack &err) {
QByteArray packet; packet.reserve(data.size()+2);
packet.append((char)proto);
packet.append(data);
packet.append(crc8(packet));
return this->_link->send(packet, timeout, err);
}

bool
OpenRTXLink::receive(Protocol proto, QByteArray &data, int timeout, const ErrorStack &err) {
QByteArray buffer;
while (true) {
// Receive a datagram
if (! this->_link->receive(buffer, timeout, err))
return false;

// Check CRC
if (0 != crc8(buffer)) {
errMsg(err) << "Invalid CRC in RTXLink packet.";
return false;
}

// Dispatch by type
Protocol rxProto = (Protocol)buffer.at(0);
// If requested type matches -> done
if (proto == rxProto) {
data = buffer.mid(1, buffer.size()-2);
return true;
}

// Otherwise, store and receive next
if (! _inBuffers.contains((unsigned int)rxProto))
_inBuffers.insert((unsigned int)rxProto, QList<QByteArray>());
_inBuffers[(unsigned int)rxProto].append(buffer.mid(1,buffer.size()-2));
}
}
133 changes: 133 additions & 0 deletions lib/openrtx_link.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#ifndef OPENRTXLINK_HH
#define OPENRTXLINK_HH

#include <QObject>

#include "packetstream.hh"

// Forward declaration
class OpenRTXLink;
class OpenRTXLinkStream;
class OpenRTXLinkDatagram;
class OpenRTXCAT;


/** Implements the OpenRTX link protocol. This is a datagram-oriented protocol, that dispatches
* several different protocols to talk to the radio. It provides a stream, to the stdio of the
* radio, a CAT interface, a file-system management protocol (FMP) as well as XMODEM to transfer
* files. The file transfer must be initialized via the FMP.
*
* All these protocols are exposed through specialized interface objects, accessible through this
* classs.
*
* @code
* +------------------+-------------------+-------------------+------------------+
* | stdio | CAT | FMP | XMODEM |
* +------------------+-------------------+-------------------+------------------+
* | OpenRTXLink |
* +-----------------------------------------------------------------------------+
* | SLIP (SlipStream) |
* +-----------------------------------------------------------------------------+
* | USB CDC-ACM (VCOM/Serial-over-USB, USBSerial) |
* +-----------------------------------------------------------------------------+
* @endcode
* @ingroup ortx */
class OpenRTXLink: public QObject
{
Q_OBJECT

protected:
/** The possible protocols, encapsulated in OpenRTX link. */
enum class Protocol {
Stdio = 0, CAT = 1, FMP = 2, XMODEM = 3
};

public:
/** Constructor.
* @param link Specifies the datagram socket to talk to the radio (SLIP).
* @param parent Specifies the QObject parent. */
explicit OpenRTXLink(PacketStream *link, QObject *parent = nullptr);

/** Returns a stream to the stdio of the radio. Implements the @c QIODevice interface. */
OpenRTXLinkStream *stdio() const;
/** The CAT interface to the radio. */
OpenRTXCAT *cat() const;
/** The file-system management protocol interface to the radio. */
OpenRTXLinkDatagram *fmp() const;
/** An XMODEM channel to transfer files. */
OpenRTXLinkStream *xmodem() const;

protected:
/** Dispatcher to receive datagrams over OpenRTXLink. */
bool receive(Protocol proto, QByteArray &data, int timeout=-1, const ErrorStack &err=ErrorStack());
/** Dispatcher to send datagrams over OpenRTXLink. */
bool send(Protocol proto, const QByteArray &data, int timeout=-1, const ErrorStack &err=ErrorStack());

static uint8_t crc8(const QByteArray &data);

protected:
PacketStream *_link;
QHash<unsigned int, QList<QByteArray>> _inBuffers;
OpenRTXLinkStream *_stdio;
OpenRTXCAT *_cat;
OpenRTXLinkDatagram *_fmp;
OpenRTXLinkStream *_xmodem;

friend class OpenRTXLinkStream;
friend class OpenRTXLinkDatagram;
friend class OpenRTXCAT;
};


class OpenRTXLinkStream: public QIODevice
{
Q_OBJECT

protected:
OpenRTXLinkStream(OpenRTXLink::Protocol proto, OpenRTXLink *link);

protected:
qint64 writeData(const char *data, qint64 len);
qint64 readData(char *data, qint64 maxlen);

protected:
OpenRTXLink::Protocol _proto;
QByteArray _inBuffer;
OpenRTXLink *_link;

friend class OpenRTXLink;
};


class OpenRTXLinkDatagram: public PacketStream
{
Q_OBJECT

protected:
OpenRTXLinkDatagram(OpenRTXLink::Protocol proto, OpenRTXLink *link);

public:
bool receive(QByteArray &buffer, int timeout, const ErrorStack &err);
bool send(const QByteArray &buffer, int timeout, const ErrorStack &err);

protected:
OpenRTXLink::Protocol _proto;
OpenRTXLink *_link;

friend class OpenRTXLink;
};


class OpenRTXCAT: public OpenRTXLinkDatagram
{
Q_OBJECT

protected:
/** Hidden constrcutor. An instance of this class can be obtained from @c OpenRTXLink.
* @param link Specifies the unerlying RTX-link to the the device. */
explicit OpenRTXCAT(OpenRTXLink *link);

friend class OpenRTXLink;
};

#endif // OPENRTXLINK_HH
2 changes: 2 additions & 0 deletions lib/packetstream.hh
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,6 @@ private:
static const char ESCAPED_DB = '\xDD';
};



#endif // PACKETSTREAM_HH
Loading

0 comments on commit f9c66f3

Please sign in to comment.