begin adding support for P25P2 network transiting;

r05a04_dev
Bryan Biedenkapp 3 weeks ago
parent 51cc90df77
commit 6b70ffe435

@ -1510,12 +1510,12 @@ void HostBridge::processDMRNetwork(uint8_t* buffer, uint32_t length)
}
// Individual slot disabling
if (slotNo == 1U && !m_network->getDMRSlot1()) {
if (slotNo == 1U && !m_network->getSlot1()) {
LogError(LOG_DMR, "DMR, invalid slot, slot 1 disabled, slotNo = %u", slotNo);
m_network->resetDMR(1U);
return;
}
if (slotNo == 2U && !m_network->getDMRSlot2()) {
if (slotNo == 2U && !m_network->getSlot2()) {
LogError(LOG_DMR, "DMR, invalid slot, slot 2 disabled, slotNo = %u", slotNo);
m_network->resetDMR(2U);
return;

@ -54,6 +54,7 @@ BaseNetwork::BaseNetwork(uint32_t peerId, bool duplex, bool debug, bool slot1, b
m_frameQueue(nullptr),
m_rxDMRData(NET_RING_BUF_SIZE, "DMR Net Buffer"),
m_rxP25Data(NET_RING_BUF_SIZE, "P25 Net Buffer"),
m_rxP25P2Data(NET_RING_BUF_SIZE, "P25 Phase 2 Net Buffer"),
m_rxNXDNData(NET_RING_BUF_SIZE, "NXDN Net Buffer"),
m_rxAnalogData(NET_RING_BUF_SIZE, "Analog Net Buffer"),
m_random(),
@ -77,6 +78,9 @@ BaseNetwork::BaseNetwork(uint32_t peerId, bool duplex, bool debug, bool slot1, b
m_dmrStreamId[0U] = createStreamId();
m_dmrStreamId[1U] = createStreamId();
m_p25StreamId = createStreamId();
m_p25P2StreamId = new uint32_t[2U];
m_p25P2StreamId[0U] = createStreamId();
m_p25P2StreamId[1U] = createStreamId();
m_nxdnStreamId = createStreamId();
m_analogStreamId = createStreamId();
}
@ -94,6 +98,7 @@ BaseNetwork::~BaseNetwork()
}
delete[] m_dmrStreamId;
delete[] m_p25P2StreamId;
}
/* Writes grant request to the network. */
@ -371,6 +376,27 @@ void BaseNetwork::resetP25()
m_rxP25Data.clear();
}
/* Resets the P25 Phase 2 ring buffer for the given slot. */
void BaseNetwork::resetP25P2(uint32_t slotNo)
{
assert(slotNo == 1U || slotNo == 2U);
if (slotNo == 1U) {
m_p25P2StreamId[0U] = createStreamId();
}
else {
m_p25P2StreamId[1U] = createStreamId();
}
if (m_debug)
LogDebugEx(LOG_NET, "BaseNetwork::resetP25P2()", "reset P25 Phase 2 Slot %u stream ID, streamId = %u", slotNo,
(slotNo == 1U) ? m_p25P2StreamId[0U] : m_p25P2StreamId[1U]);
m_pktSeq = 0U;
m_rxP25P2Data.clear();
}
/* Resets the NXDN ring buffer. */
void BaseNetwork::resetNXDN()
@ -411,6 +437,20 @@ uint32_t BaseNetwork::getDMRStreamId(uint32_t slotNo) const
}
}
/* Gets the current P25 Phase 2 stream ID. */
uint32_t BaseNetwork::getP25P2StreamId(uint32_t slotNo) const
{
assert(slotNo == 1U || slotNo == 2U);
if (slotNo == 1U) {
return m_p25P2StreamId[0U];
}
else {
return m_p25P2StreamId[1U];
}
}
/* Helper to send a data message to the master. */
bool BaseNetwork::writeMaster(FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length, uint16_t pktSeq, uint32_t streamId,
@ -695,6 +735,60 @@ bool BaseNetwork::writeP25PDU(const p25::data::DataHeader& header, const uint8_t
return writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, seq, m_p25StreamId);
}
/* Reads P25 raw frame data from the P25 ring buffer. */
UInt8Array BaseNetwork::readP25P2(bool& ret, uint32_t& frameLength)
{
if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING)
return nullptr;
ret = true;
if (m_rxP25P2Data.isEmpty()) {
ret = false;
return nullptr;
}
uint8_t length = 0U;
m_rxP25P2Data.get(&length, 1U);
if (length == 0U) {
ret = false;
return nullptr;
}
UInt8Array buffer;
frameLength = length;
buffer = std::unique_ptr<uint8_t[]>(new uint8_t[length]);
::memset(buffer.get(), 0x00U, length);
m_rxP25P2Data.get(buffer.get(), length);
return buffer;
}
/* Writes P25 Phase 2 frame data to the network. */
bool BaseNetwork::writeP25P2(const p25::lc::LC& control, p25::defines::P2_DUID::E duid, bool slot, const uint8_t* data,
const uint8_t controlByte)
{
if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING)
return false;
uint8_t slotNo = slot ? 0x00U : 0x01U;
bool resetSeq = false;
if (m_p25P2StreamId[slotNo] = 0U) {
resetSeq = true;
m_p25P2StreamId[slotNo] = createStreamId();
}
uint32_t messageLength = 0U;
UInt8Array message = createP25P2_Message(messageLength, control, duid, slot, data, controlByte);
if (message == nullptr) {
return false;
}
return writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25_P2 }, message.get(), messageLength, pktSeq(resetSeq), m_p25P2StreamId[slotNo]);
}
/* Helper to test if the P25 ring buffer has data. */
bool BaseNetwork::hasP25Data() const
@ -705,6 +799,16 @@ bool BaseNetwork::hasP25Data() const
return true;
}
/* Helper to test if the P25 Phase 2 ring buffer has data. */
bool BaseNetwork::hasP25P2Data() const
{
if (m_rxP25P2Data.isEmpty())
return false;
return true;
}
/* Helper to validate a P25 network frame length. */
bool BaseNetwork::validateP25FrameLength(uint8_t& frameLength, uint32_t len, const P25DEF::DUID::E duid)
@ -1368,6 +1472,41 @@ UInt8Array BaseNetwork::createP25_PDUMessage(uint32_t& length, const p25::data::
return UInt8Array(buffer);
}
/* Creates an P25 Phase 2 frame message. */
UInt8Array BaseNetwork::createP25P2_Message(uint32_t& length, const p25::lc::LC& control, p25::defines::P2_DUID::E duid,
const bool slot, const uint8_t* data, uint8_t controlByte)
{
using namespace p25::defines;
uint8_t* buffer = new uint8_t[DATA_PACKET_LENGTH];
::memset(buffer, 0x00U, DATA_PACKET_LENGTH);
// create dummy low speed data
p25::data::LowSpeedData lsd = p25::data::LowSpeedData();
// construct P25 message header
createP25_MessageHdr(buffer, DUID::PDU, control, lsd, FrameType::DATA_UNIT);
buffer[14U] = controlByte;
buffer[19U] = slot ? 0x00U : 0x80U; // Slot Number
buffer[19U] |= (uint8_t)duid; // Phase 2 DUID
// pack raw P25 Phase 2 bytes
uint32_t count = MSG_HDR_SIZE;
::memcpy(buffer + 24U, data, P25_P2_FRAME_LENGTH_BYTES);
count += P25_P2_FRAME_LENGTH_BYTES;
buffer[23U] = count;
if (m_packetDump)
Utils::dump(1U, "BaseNetwork::createP25P2_Message(), Message, Phase 2", buffer, (count + PACKET_PAD));
length = (count + PACKET_PAD);
return UInt8Array(buffer);
}
/* Writes NXDN frame data to the network. */
UInt8Array BaseNetwork::createNXDN_Message(uint32_t& length, const nxdn::lc::RTCH& lc, const uint8_t* data, const uint32_t len)

@ -92,6 +92,7 @@ namespace network
const uint32_t P25_LDU2_PACKET_LENGTH = 181U; // 24 byte header + DFSI data + 1 byte frame type
const uint32_t P25_TSDU_PACKET_LENGTH = 69U; // 24 byte header + TSDU data
const uint32_t P25_TDULC_PACKET_LENGTH = 78U; // 24 byte header + TDULC data
const uint32_t P25_P2_PACKET_LENGTH = 66U; // 24 byte header + P25_P2_FRAME_LENGTH_BYTES + 2 byte trailer
const uint32_t NXDN_PACKET_LENGTH = 70U; // 20 byte header + NXDN_FRAME_LENGTH_BYTES + 2 byte trailer
const uint32_t ANALOG_PACKET_LENGTH = 344U; // 20 byte header + AUDIO_SAMPLES_LENGTH_BYTES + 4 byte trailer
@ -376,8 +377,8 @@ namespace network
* @param peerId Unique ID of this modem on the network.
* @param duplex Flag indicating full-duplex operation.
* @param debug Flag indicating whether network debug is enabled.
* @param slot1 Flag indicating whether DMR slot 1 is enabled for network traffic.
* @param slot2 Flag indicating whether DMR slot 2 is enabled for network traffic.
* @param slot1 Flag indicating whether DMR/P25 Phase 2 slot 1 is enabled for network traffic.
* @param slot2 Flag indicating whether DMR/P25 Phase 2 slot 2 is enabled for network traffic.
* @param allowActivityTransfer Flag indicating that the system activity logs will be sent to the network.
* @param allowDiagnosticTransfer Flag indicating that the system diagnostic logs will be sent to the network.
* @param localPort Local port used to listen for incoming data.
@ -707,6 +708,11 @@ namespace network
* @brief Resets the P25 ring buffer.
*/
virtual void resetP25();
/**
* @brief Resets the P25 Phase 2 ring buffer for the given slot.
* @param slotNo P25 Phase 2 slot number.
*/
virtual void resetP25P2(uint32_t slotNo);
/**
* @brief Resets the NXDN ring buffer.
*/
@ -727,6 +733,12 @@ namespace network
* @return uint32_t Stream ID.
*/
uint32_t getP25StreamId() const { return m_p25StreamId; }
/**
* @brief Gets the current P25 Phase 2 stream ID.
* @param slotNo P25 Phase 2 slot to get stream ID for.
* @return uint32_t Stream ID for the given P25 Phase 2 slot.
*/
uint32_t getP25P2StreamId(uint32_t slotNo) const;
/**
* @brief Gets the current NXDN stream ID.
* @return uint32_t Stream ID.
@ -838,12 +850,37 @@ namespace network
virtual bool writeP25PDU(const p25::data::DataHeader& header, const uint8_t currentBlock, const uint8_t* data,
const uint32_t len, bool lastBlock);
/**
* @brief Reads P25 Phase 2 raw frame data from the P25 Phase 2 ring buffer.
* @param[out] ret Flag indicating whether or not data was received.
* @param[out] frameLength Length in bytes of received frame.
* @returns UInt8Array Buffer containing received frame.
*/
virtual UInt8Array readP25P2(bool& ret, uint32_t& frameLength);
/**
* @brief Writes P25 Phase 2 frame data to the network.
* @param[in] control Instance of p25::lc::LC containing link control data.
* @param[in] duid P25 Phase 2 DUID type.
* @param[in] slot DMR slot number.
* @param[in] data Buffer containing P25 Phase 2 data to send.
* @param[in] controlByte DVM control byte.
* @returns bool True, if message was sent, otherwise false.
*/
virtual bool writeP25P2(const p25::lc::LC& control, p25::defines::P2_DUID::E duid, bool slot, const uint8_t* data,
const uint8_t controlByte = 0U);
/**
* @brief Helper to test if the P25 ring buffer has data.
* @returns bool True, if the network P25 ring buffer has data, otherwise false.
*/
bool hasP25Data() const;
/**
* @brief Helper to test if the P25 Phase 2 ring buffer has data.
* @returns bool True, if the network P25 Phase 2 ring buffer has data, otherwise false.
*/
bool hasP25P2Data() const;
/**
* @brief Helper to validate a P25 network frame length.
* @param frameLength P25 encapsulated frame length.
@ -918,13 +955,13 @@ namespace network
DECLARE_PROTECTED_RO_PROPERTY_PLAIN(uint32_t, addrLen);
/**
* @brief Flag indicating whether network DMR slot 1 traffic is permitted.
* @brief Flag indicating whether network DMR/P25 Phase 2 slot 1 traffic is permitted.
*/
DECLARE_PROTECTED_RO_PROPERTY(bool, slot1, DMRSlot1);
DECLARE_PROTECTED_RO_PROPERTY(bool, slot1, Slot1);
/**
* @brief Flag indicating whether network DMR slot 2 traffic is permitted.
* @brief Flag indicating whether network DMR/P25 Phase 2 slot 2 traffic is permitted.
*/
DECLARE_PROTECTED_RO_PROPERTY(bool, slot2, DMRSlot2);
DECLARE_PROTECTED_RO_PROPERTY(bool, slot2, Slot2);
/**
* @brief Flag indicating whether network traffic is duplex.
@ -945,6 +982,7 @@ namespace network
RingBuffer<uint8_t> m_rxDMRData;
RingBuffer<uint8_t> m_rxP25Data;
RingBuffer<uint8_t> m_rxP25P2Data;
RingBuffer<uint8_t> m_rxNXDNData;
RingBuffer<uint8_t> m_rxAnalogData;
@ -952,6 +990,7 @@ namespace network
uint32_t* m_dmrStreamId;
uint32_t m_p25StreamId;
uint32_t* m_p25P2StreamId;
uint32_t m_nxdnStreamId;
uint32_t m_analogStreamId;
@ -1025,11 +1064,13 @@ namespace network
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | System ID | Reserved | Control Flags | MFId |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Network ID | Reserved |
* | Network ID |S|Rsvd |P2 DUID|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | LSD1 | LSD2 | DUID | Frame Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* S = Slot Number (clear Slot 1, set Slot 2)
*
* The data starting at offset 20 for variable number of bytes (DUID dependant)
* is the P25 frame.
*
@ -1168,6 +1209,28 @@ namespace network
UInt8Array createP25_PDUMessage(uint32_t& length, const p25::data::DataHeader& header, const uint8_t currentBlock,
const uint8_t* data, const uint32_t len);
/**
* @brief Creates an P25 Phase 2 frame message.
* \code{.unparsed}
*
* The data packed into a P25 Phase 2 frame message is essentially just a message header with the FEC encoded
* raw Phase 2 data.
*
* The data starting at offset 24 for 40 bytes of the raw P25 Phase 2 frame.
*
* \endcode
* @param[out] length Length of network message buffer.
* @param[in] control Instance of p25::lc::LC containing link control data.
* @param duid P25 Phase 2 DUID.
* @param[in] slot P25 Phase 2 slot (clear Slot 1, set Slot 2).
* @param[in] data Buffer containing P25 LDU2 data to send.
* @param[in] controlByte DVM Network Control Byte.
* @param data Instance of the dmr::data::Data class containing the DMR message.
* @returns UInt8Array Buffer containing the built network message.
*/
UInt8Array createP25P2_Message(uint32_t& length, const p25::lc::LC& control, p25::defines::P2_DUID::E duid,
const bool slot, const uint8_t* data, uint8_t controlByte = 0U);
/**
* @brief Creates an NXDN frame message.
* \code{.unparsed}

@ -94,6 +94,9 @@ Network::Network(const std::string& address, uint16_t port, uint16_t localPort,
m_rxDMRStreamId[0U] = 0U;
m_rxDMRStreamId[1U] = 0U;
m_rxP25StreamId = 0U;
m_rxP25P2StreamId = new uint32_t[2U];
m_rxP25P2StreamId[0U] = 0U;
m_rxP25P2StreamId[1U] = 0U;
m_rxNXDNStreamId = 0U;
m_rxAnalogStreamId = 0U;
@ -107,6 +110,7 @@ Network::~Network()
{
delete[] m_salt;
delete[] m_rxDMRStreamId;
delete[] m_rxP25P2StreamId;
delete m_metadata;
delete m_mux;
}
@ -140,6 +144,24 @@ void Network::resetP25()
LogDebugEx(LOG_NET, "Network::resetP25()", "reset P25 rx stream ID");
}
/* Resets the P25 Phase 2 ring buffer for the given slot. */
void Network::resetP25P2(uint32_t slotNo)
{
assert(slotNo == 1U || slotNo == 2U);
BaseNetwork::resetP25P2(slotNo);
if (slotNo == 1U) {
m_rxP25P2StreamId[0U] = 0U;
}
else {
m_rxP25P2StreamId[1U] = 0U;
}
if (m_debug)
LogDebugEx(LOG_NET, "Network::resetP25P2()", "reset P25 Phase 2 Slot %u rx stream ID", slotNo);
}
/* Resets the NXDN ring buffer. */
void Network::resetNXDN()
@ -507,6 +529,93 @@ void Network::clock(uint32_t ms)
}
break;
case NET_SUBFUNC::PROTOCOL_SUBFUNC_P25_P2: // Encapsulated DMR data frame
{
if (m_enabled && m_p25Enabled) {
uint32_t slotNo = (buffer[19U] & 0x80U) == 0x80U ? 1U : 0U; // this is the raw index for the stream ID array
if (m_debug) {
LogDebug(LOG_NET, "P25 Phase 2 Slot %u, peer = %u, len = %u, pktSeq = %u, streamId = %u",
slotNo + 1U, peerId, length, rtpHeader.getSequence(), streamId);
}
if (m_promiscuousPeer) {
m_rxP25P2StreamId[slotNo] = streamId;
m_pktLastSeq = m_pktSeq;
uint16_t lastRxSeq = 0U;
MULTIPLEX_RET_CODE ret = m_mux->verifyStream(streamId, rtpHeader.getSequence(), fneHeader.getFunction(), &lastRxSeq);
if (ret == MUX_LOST_FRAMES) {
LogError(LOG_NET, "PEER %u stream %u possible lost frames; got %u, expected %u", peerId,
streamId, rtpHeader.getSequence(), lastRxSeq, rtpHeader.getSequence());
}
else if (ret == MUX_OUT_OF_ORDER) {
LogError(LOG_NET, "PEER %u stream %u out-of-order; got %u, expected >%u", peerId,
streamId, rtpHeader.getSequence(), lastRxSeq);
}
#if DEBUG_RTP_MUX
else {
LogDebugEx(LOG_NET, "Network::clock()", "PEER %u valid mux, seq = %u, streamId = %u", peerId, rtpHeader.getSequence(), streamId);
}
#endif
}
else {
if (m_rxP25P2StreamId[slotNo] == 0U) {
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
m_rxP25P2StreamId[slotNo] = 0U;
}
else {
m_rxP25P2StreamId[slotNo] = streamId;
}
m_pktLastSeq = m_pktSeq;
}
else {
if (m_rxP25P2StreamId[slotNo] == streamId) {
uint16_t lastRxSeq = 0U;
MULTIPLEX_RET_CODE ret = verifyStream(&lastRxSeq);
if (ret == MUX_LOST_FRAMES) {
LogWarning(LOG_NET, "DMR Slot %u stream %u possible lost frames; got %u, expected %u",
slotNo, streamId, m_pktSeq, lastRxSeq);
}
else if (ret == MUX_OUT_OF_ORDER) {
LogWarning(LOG_NET, "DMR Slot %u stream %u out-of-order; got %u, expected %u",
slotNo, streamId, m_pktSeq, lastRxSeq);
}
#if DEBUG_RTP_MUX
else {
LogDebugEx(LOG_NET, "Network::clock()", "P25 Phase 2 Slot %u valid seq, seq = %u, streamId = %u", slotNo, rtpHeader.getSequence(), streamId);
}
#endif
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
m_rxP25P2StreamId[slotNo] = 0U;
}
}
}
// check if we need to skip this stream -- a non-zero stream ID means the network client is locked
// to receiving a specific stream; a zero stream ID means the network is promiscuously
// receiving streams sent to this peer
if (m_rxP25P2StreamId[slotNo] != 0U && m_rxP25P2StreamId[slotNo] != streamId &&
rtpHeader.getSequence() != RTP_END_OF_CALL_SEQ) {
break;
}
}
if (m_packetDump)
Utils::dump(1U, "Network::clock(), Network Rx, P25 Phase 2", buffer.get(), length);
if (length > (int)(P25_P2_PACKET_LENGTH + PACKET_PAD))
LogError(LOG_NET, "P25 Phase 2 Stream %u, frame oversized? this shouldn't happen, pktSeq = %u, len = %u", streamId, m_pktSeq, length);
uint8_t len = length;
m_rxP25P2Data.addData(&len, 1U);
m_rxP25P2Data.addData(buffer.get(), len);
}
}
break;
case NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN: // Encapsulated NXDN data frame
{
if (m_enabled && m_nxdnEnabled) {

@ -167,6 +167,10 @@ namespace network
* @brief Resets the P25 ring buffer.
*/
void resetP25() override;
/**
* @brief Resets the P25 Phase 2 ring buffer.
*/
void resetP25P2(uint32_t slotNo) override;
/**
* @brief Resets the NXDN ring buffer.
*/
@ -330,6 +334,7 @@ namespace network
uint32_t* m_rxDMRStreamId;
uint32_t m_rxP25StreamId;
uint32_t* m_rxP25P2StreamId;
uint32_t m_rxNXDNStreamId;
uint32_t m_rxAnalogStreamId;

@ -63,7 +63,7 @@ namespace p25
const uint32_t P25_TDULC_FRAME_LENGTH_BYTES = 54U;
const uint32_t P25_TDULC_FRAME_LENGTH_BITS = P25_TDULC_FRAME_LENGTH_BYTES * 8U;
const uint32_t P25_P2_FRAME_LENGTH_BYTES = 45U;
const uint32_t P25_P2_FRAME_LENGTH_BYTES = 40U;
const uint32_t P25_P2_FRAME_LENGTH_BITS = P25_P2_FRAME_LENGTH_BYTES * 8U;
const uint32_t P25_NID_LENGTH_BYTES = 8U;

@ -723,11 +723,11 @@ void Control::processNetwork()
}
// Individual slot disabling
if (slotNo == 1U && !m_network->getDMRSlot1()) {
if (slotNo == 1U && !m_network->getSlot1()) {
LogError(LOG_DMR, "DMR, invalid slot, slot 1 disabled, slotNo = %u", slotNo);
return;
}
if (slotNo == 2U && !m_network->getDMRSlot2()) {
if (slotNo == 2U && !m_network->getSlot2()) {
LogError(LOG_DMR, "DMR, invalid slot, slot 2 disabled, slotNo = %u", slotNo);
return;
}

@ -674,12 +674,12 @@ void HostPatch::processDMRNetwork(uint8_t* buffer, uint32_t length)
}
// Individual slot disabling
if (slotNo == 1U && !m_network->getDMRSlot1()) {
if (slotNo == 1U && !m_network->getSlot1()) {
LogError(LOG_DMR, "DMR, invalid slot, slot 1 disabled, slotNo = %u", slotNo);
m_network->resetDMR(1U);
return;
}
if (slotNo == 2U && !m_network->getDMRSlot2()) {
if (slotNo == 2U && !m_network->getSlot2()) {
LogError(LOG_DMR, "DMR, invalid slot, slot 2 disabled, slotNo = %u", slotNo);
m_network->resetDMR(2U);
return;

Loading…
Cancel
Save

Powered by TurnKey Linux.