mirror of
https://github.com/thelsing/knx.git
synced 2025-09-18 17:52:44 +02:00
* fixed tipo: RepititionAllowed * fixed typo: frameRecieved * fixed typo: individualAddressDuplication * fixed typo: induvidualAddress * fixded Typo: InduvidualAddress Co-authored-by: Dominik Westner <nikwest@github.com>
383 lines
10 KiB
C++
383 lines
10 KiB
C++
#include "cemi_frame.h"
|
|
#include "bits.h"
|
|
#include "string.h"
|
|
#include <stdio.h>
|
|
|
|
/*
|
|
cEMI Frame Format
|
|
|
|
+---------+--------+--------+--------+--------+---------+---------+--------+---------+
|
|
| Header | Msg |Add.Info| Ctrl 1 | Ctrl 2 | Source | Dest. | Data | APDU |
|
|
| | Code | Length | | | Address | Address | Length | |
|
|
+---------+--------+--------+--------+--------+---------+---------+--------+---------+
|
|
6 bytes 1 byte 1 byte 1 byte 1 byte 2 bytes 2 bytes 1 byte 2 bytes
|
|
|
|
Header = See below the structure of a cEMI header
|
|
Message Code = See below. On Appendix A is the list of all existing EMI and cEMI codes
|
|
Add.Info Length = 0x00 - no additional info
|
|
Control Field 1 =
|
|
Control Field 2 =
|
|
Source Address = 0x0000 - filled in by router/gateway with its source address which is
|
|
part of the KNX subnet
|
|
Dest. Address = KNX group or individual address (2 byte)
|
|
Data Length = Number of bytes of data in the APDU excluding the TPCI/APCI bits
|
|
APDU = Application Protocol Data Unit - the actual payload including transport
|
|
protocol control information (TPCI), application protocol control
|
|
information (APCI) and data passed as an argument from higher layers of
|
|
the KNX communication stack
|
|
|
|
Control Field 1
|
|
|
|
Bit |
|
|
------+---------------------------------------------------------------
|
|
7 | Frame Type - 0x0 for extended frame
|
|
| 0x1 for standard frame
|
|
------+---------------------------------------------------------------
|
|
6 | Reserved
|
|
|
|
|
------+---------------------------------------------------------------
|
|
5 | Repeat Flag - 0x0 repeat frame on medium in case of an error
|
|
| 0x1 do not repeat
|
|
------+---------------------------------------------------------------
|
|
4 | System Broadcast - 0x0 system broadcast
|
|
| 0x1 broadcast
|
|
------+---------------------------------------------------------------
|
|
3 | Priority - 0x0 system
|
|
| 0x1 normal
|
|
------+ 0x2 urgent
|
|
2 | 0x3 low
|
|
|
|
|
------+---------------------------------------------------------------
|
|
1 | Acknowledge Request - 0x0 no ACK requested
|
|
| (L_Data.req) 0x1 ACK requested
|
|
------+---------------------------------------------------------------
|
|
0 | Confirm - 0x0 no error
|
|
| (L_Data.con) - 0x1 error
|
|
------+---------------------------------------------------------------
|
|
|
|
Control Field 2
|
|
|
|
Bit |
|
|
------+---------------------------------------------------------------
|
|
7 | Destination Address Type - 0x0 individual address
|
|
| - 0x1 group address
|
|
------+---------------------------------------------------------------
|
|
6-4 | Hop Count (0-7)
|
|
------+---------------------------------------------------------------
|
|
3-0 | Extended Frame Format - 0x0 standard frame
|
|
------+---------------------------------------------------------------
|
|
*/
|
|
|
|
CemiFrame::CemiFrame(uint8_t* data, uint16_t length)
|
|
: _npdu(data + data[1] + NPDU_LPDU_DIFF, *this),
|
|
_tpdu(data + data[1] + TPDU_LPDU_DIFF, *this),
|
|
_apdu(data + data[1] + APDU_LPDU_DIFF, *this)
|
|
{
|
|
_data = data;
|
|
_ctrl1 = data + data[1] + CEMI_HEADER_SIZE;
|
|
_length = length;
|
|
}
|
|
|
|
CemiFrame::CemiFrame(uint8_t apduLength)
|
|
: _data(buffer),
|
|
_npdu(_data + NPDU_LPDU_DIFF, *this),
|
|
_tpdu(_data + TPDU_LPDU_DIFF, *this),
|
|
_apdu(_data + APDU_LPDU_DIFF, *this)
|
|
{
|
|
_ctrl1 = _data + CEMI_HEADER_SIZE;
|
|
_length = 0;
|
|
|
|
memset(_data, 0, apduLength + APDU_LPDU_DIFF);
|
|
_ctrl1[0] |= Broadcast;
|
|
_npdu.octetCount(apduLength);
|
|
}
|
|
|
|
CemiFrame::CemiFrame(const CemiFrame & other)
|
|
: _data(buffer),
|
|
_npdu(_data + NPDU_LPDU_DIFF, *this),
|
|
_tpdu(_data + TPDU_LPDU_DIFF, *this),
|
|
_apdu(_data + APDU_LPDU_DIFF, *this)
|
|
{
|
|
_ctrl1 = _data + CEMI_HEADER_SIZE;
|
|
_length = other._length;
|
|
|
|
memcpy(_data, other._data, other.totalLenght());
|
|
}
|
|
|
|
CemiFrame& CemiFrame::operator=(CemiFrame other)
|
|
{
|
|
_length = other._length;
|
|
_data = buffer;
|
|
_ctrl1 = _data + CEMI_HEADER_SIZE;
|
|
memcpy(_data, other._data, other.totalLenght());
|
|
_npdu._data = _data + NPDU_LPDU_DIFF;
|
|
_tpdu._data = _data + TPDU_LPDU_DIFF;
|
|
_apdu._data = _data + APDU_LPDU_DIFF;
|
|
return *this;
|
|
}
|
|
|
|
MessageCode CemiFrame::messageCode() const
|
|
{
|
|
return (MessageCode)_data[0];
|
|
}
|
|
|
|
void CemiFrame::messageCode(MessageCode msgCode)
|
|
{
|
|
_data[0] = msgCode;
|
|
}
|
|
|
|
uint16_t CemiFrame::totalLenght() const
|
|
{
|
|
uint16_t tmp =
|
|
_npdu.length() + NPDU_LPDU_DIFF;
|
|
return tmp;
|
|
}
|
|
|
|
uint16_t CemiFrame::telegramLengthtTP() const
|
|
{
|
|
if (frameType() == StandardFrame)
|
|
return totalLenght() - 2; /*-AddInfo -MsgCode - only one CTRL + CRC, */
|
|
else
|
|
return totalLenght() - 1; /*-AddInfo -MsgCode + CRC, */
|
|
}
|
|
|
|
void CemiFrame::fillTelegramTP(uint8_t* data)
|
|
{
|
|
uint16_t len = telegramLengthtTP();
|
|
|
|
if (frameType() == StandardFrame)
|
|
{
|
|
uint8_t octet5 = (_ctrl1[1] & 0xF0) | (_ctrl1[6] & 0x0F);
|
|
data[0] = _ctrl1[0]; //CTRL
|
|
memcpy(data + 1, _ctrl1 + 2, 4); // SA, DA
|
|
data[5] = octet5; // LEN; Hopcount, ..
|
|
memcpy(data + 6, _ctrl1 + 7, len - 7); // APDU
|
|
}
|
|
else
|
|
{
|
|
memcpy(data, _ctrl1, len - 1);
|
|
}
|
|
data[len - 1] = calcCrcTP(data, len - 1);
|
|
}
|
|
|
|
#ifdef USE_RF
|
|
|
|
uint16_t CemiFrame::telegramLengthtRF() const
|
|
{
|
|
return totalLenght() - 3;
|
|
}
|
|
|
|
void CemiFrame::fillTelegramRF(uint8_t* data)
|
|
{
|
|
uint16_t len = telegramLengthtRF();
|
|
|
|
// We prepare the actual KNX telegram for RF here only.
|
|
// The packaging into blocks with CRC16 (Format based on FT3 Data Link Layer (IEC 870-5))
|
|
// is done in the RF Data Link Layer code.
|
|
// RF always uses the Extended Frame Format. However, the length field is missing (right before the APDU)
|
|
// as there is already a length field at the beginning of the raw RF frame which is also used by the
|
|
// physical layer to control the HW packet engine of the transceiver.
|
|
|
|
data[0] = _ctrl1[1] & 0x0F; // KNX CTRL field for RF (bits 3..0 EFF only), bits 7..4 are set to 0 for asynchronous RF frames
|
|
memcpy(data + 1, _ctrl1 + 2, 4); // SA, DA
|
|
data[5] = (_ctrl1[1] & 0xF0) | ((_rfLfn & 0x7) << 1) | ((_ctrl1[0] & 0x10) >> 4); // L/NPCI field: AT, Hopcount, LFN, AET
|
|
memcpy(data + 6, _ctrl1 + 7, len - 6); // APDU
|
|
|
|
//printHex("cEMI_fill: ", &data[0], len);
|
|
}
|
|
#endif
|
|
uint8_t* CemiFrame::data()
|
|
{
|
|
return _data;
|
|
}
|
|
|
|
uint16_t CemiFrame::dataLength()
|
|
{
|
|
return _length;
|
|
}
|
|
|
|
uint8_t CemiFrame::calcCrcTP(uint8_t * buffer, uint16_t len)
|
|
{
|
|
uint8_t crc = 0xFF;
|
|
|
|
for (uint16_t i = 0; i < len; i++)
|
|
crc ^= buffer[i];
|
|
|
|
return crc;
|
|
}
|
|
|
|
FrameFormat CemiFrame::frameType() const
|
|
{
|
|
return (FrameFormat)(_ctrl1[0] & StandardFrame);
|
|
}
|
|
|
|
void CemiFrame::frameType(FrameFormat type)
|
|
{
|
|
_ctrl1[0] &= ~StandardFrame;
|
|
_ctrl1[0] |= type;
|
|
}
|
|
|
|
Repetition CemiFrame::repetition() const
|
|
{
|
|
return (Repetition)(_ctrl1[0] & RepetitionAllowed);
|
|
}
|
|
|
|
void CemiFrame::repetition(Repetition rep)
|
|
{
|
|
_ctrl1[0] &= ~RepetitionAllowed;
|
|
_ctrl1[0] |= rep;
|
|
}
|
|
|
|
SystemBroadcast CemiFrame::systemBroadcast() const
|
|
{
|
|
return (SystemBroadcast)(_ctrl1[0] & Broadcast);
|
|
}
|
|
|
|
void CemiFrame::systemBroadcast(SystemBroadcast value)
|
|
{
|
|
_ctrl1[0] &= ~Broadcast;
|
|
_ctrl1[0] |= value;
|
|
}
|
|
|
|
Priority CemiFrame::priority() const
|
|
{
|
|
return (Priority)(_ctrl1[0] & LowPriority);
|
|
}
|
|
|
|
void CemiFrame::priority(Priority value)
|
|
{
|
|
_ctrl1[0] &= ~LowPriority;
|
|
_ctrl1[0] |= value;
|
|
}
|
|
|
|
AckType CemiFrame::ack() const
|
|
{
|
|
return (AckType)(_ctrl1[0] & AckRequested);
|
|
}
|
|
|
|
void CemiFrame::ack(AckType value)
|
|
{
|
|
_ctrl1[0] &= ~AckRequested;
|
|
_ctrl1[0] |= value;
|
|
}
|
|
|
|
Confirm CemiFrame::confirm() const
|
|
{
|
|
return (Confirm)(_ctrl1[0] & ConfirmError);
|
|
}
|
|
|
|
void CemiFrame::confirm(Confirm value)
|
|
{
|
|
_ctrl1[0] &= ~ConfirmError;
|
|
_ctrl1[0] |= value;
|
|
}
|
|
|
|
AddressType CemiFrame::addressType() const
|
|
{
|
|
return (AddressType)(_ctrl1[1] & GroupAddress);
|
|
}
|
|
|
|
void CemiFrame::addressType(AddressType value)
|
|
{
|
|
_ctrl1[1] &= ~GroupAddress;
|
|
_ctrl1[1] |= value;
|
|
}
|
|
|
|
uint8_t CemiFrame::hopCount() const
|
|
{
|
|
return ((_ctrl1[1] >> 4) & 0x7);
|
|
}
|
|
|
|
void CemiFrame::hopCount(uint8_t value)
|
|
{
|
|
_ctrl1[1] &= ~(0x7 << 4);
|
|
_ctrl1[1] |= ((value & 0x7) << 4);
|
|
}
|
|
|
|
uint16_t CemiFrame::sourceAddress() const
|
|
{
|
|
uint16_t addr;
|
|
popWord(addr, _ctrl1 + 2);
|
|
return addr;
|
|
}
|
|
|
|
void CemiFrame::sourceAddress(uint16_t value)
|
|
{
|
|
pushWord(value, _ctrl1 + 2);
|
|
}
|
|
|
|
uint16_t CemiFrame::destinationAddress() const
|
|
{
|
|
uint16_t addr;
|
|
popWord(addr, _ctrl1 + 4);
|
|
return addr;
|
|
}
|
|
|
|
void CemiFrame::destinationAddress(uint16_t value)
|
|
{
|
|
pushWord(value, _ctrl1 + 4);
|
|
}
|
|
#ifdef USE_RF
|
|
uint8_t* CemiFrame::rfSerialOrDoA() const
|
|
{
|
|
return _rfSerialOrDoA;
|
|
}
|
|
|
|
void CemiFrame::rfSerialOrDoA(const uint8_t* rfSerialOrDoA)
|
|
{
|
|
_rfSerialOrDoA = (uint8_t*)rfSerialOrDoA;
|
|
}
|
|
|
|
uint8_t CemiFrame::rfInfo() const
|
|
{
|
|
return _rfInfo;
|
|
}
|
|
|
|
void CemiFrame::rfInfo(uint8_t rfInfo)
|
|
{
|
|
_rfInfo = rfInfo;
|
|
}
|
|
|
|
uint8_t CemiFrame::rfLfn() const
|
|
{
|
|
return _rfLfn;
|
|
}
|
|
|
|
void CemiFrame::rfLfn(uint8_t rfLfn)
|
|
{
|
|
_rfLfn = rfLfn;
|
|
}
|
|
#endif
|
|
NPDU& CemiFrame::npdu()
|
|
{
|
|
return _npdu;
|
|
}
|
|
|
|
TPDU& CemiFrame::tpdu()
|
|
{
|
|
return _tpdu;
|
|
}
|
|
|
|
APDU& CemiFrame::apdu()
|
|
{
|
|
return _apdu;
|
|
}
|
|
|
|
bool CemiFrame::valid() const
|
|
{
|
|
uint8_t addInfoLen = _data[1];
|
|
uint8_t apduLen = _data[_data[1] + NPDU_LPDU_DIFF];
|
|
|
|
if (_length != 0 && _length != (addInfoLen + apduLen + NPDU_LPDU_DIFF + 2))
|
|
return false;
|
|
|
|
if ((_ctrl1[0] & 0x40) > 0 // Bit 6 has do be 0
|
|
|| (_ctrl1[1] & 0xF) > 0 // only standard or extended frames
|
|
|| _npdu.octetCount() == 0xFF // not allowed
|
|
|| (_npdu.octetCount() > 15 && frameType() == StandardFrame)
|
|
)
|
|
return false;
|
|
|
|
return true;
|
|
}
|