mirror of
https://github.com/thelsing/knx.git
synced 2025-09-28 17:50:25 +02:00
Better Routing and new Tunneling Support (#26)
Support 0x091A IP-Router better, including KNX IP Tunneling and a lot of smaller fixes
This commit is contained in:
parent
194de33ee2
commit
7f119275ee
@ -99,32 +99,50 @@ bool Esp32Platform::sendBytesMultiCast(uint8_t * buffer, uint16_t len)
|
||||
return true;
|
||||
}
|
||||
|
||||
int Esp32Platform::readBytesMultiCast(uint8_t * buffer, uint16_t maxLen)
|
||||
int Esp32Platform::readBytesMultiCast(uint8_t * buffer, uint16_t maxLen, uint32_t& src_addr, uint16_t& src_port)
|
||||
{
|
||||
int len = _udp.parsePacket();
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
|
||||
if (len > maxLen)
|
||||
{
|
||||
KNX_DEBUG_SERIAL.printf("udp buffer to small. was %d, needed %d\n", maxLen, len);
|
||||
fatalError();
|
||||
println("Unexpected UDP data packet length - drop packet");
|
||||
for (size_t i = 0; i < len; i++)
|
||||
_udp.read();
|
||||
return 0;
|
||||
}
|
||||
|
||||
_udp.read(buffer, len);
|
||||
//printHex("-> ", buffer, len);
|
||||
_remoteIP = _udp.remoteIP();
|
||||
_remotePort = _udp.remotePort();
|
||||
src_addr = htonl(_remoteIP);
|
||||
src_port = _remotePort;
|
||||
|
||||
// print("Remote IP: ");
|
||||
// print(_udp.remoteIP().toString().c_str());
|
||||
// printHex("-> ", buffer, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
bool Esp32Platform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len)
|
||||
{
|
||||
IPAddress ucastaddr(htonl(addr));
|
||||
println("sendBytesUniCast endPacket fail");
|
||||
if(_udp.beginPacket(ucastaddr, port) == 1) {
|
||||
|
||||
if(!addr)
|
||||
ucastaddr = _remoteIP;
|
||||
|
||||
if(!port)
|
||||
port = _remotePort;
|
||||
|
||||
if(_udp.beginPacket(ucastaddr, port) == 1)
|
||||
{
|
||||
_udp.write(buffer, len);
|
||||
if(_udp.endPacket() == 0) println("sendBytesUniCast endPacket fail");
|
||||
}
|
||||
else println("sendBytesUniCast beginPacket fail");
|
||||
else
|
||||
println("sendBytesUniCast beginPacket fail");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ public:
|
||||
void setupMultiCast(uint32_t addr, uint16_t port) override;
|
||||
void closeMultiCast() override;
|
||||
bool sendBytesMultiCast(uint8_t* buffer, uint16_t len) override;
|
||||
int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen) override;
|
||||
int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen, uint32_t& src_addr, uint16_t& src_port) override;
|
||||
|
||||
//unicast
|
||||
bool sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) override;
|
||||
@ -38,6 +38,10 @@ public:
|
||||
//memory
|
||||
uint8_t* getEepromBuffer(uint32_t size);
|
||||
void commitToEeprom();
|
||||
|
||||
protected: IPAddress _remoteIP;
|
||||
protected: uint16_t _remotePort;
|
||||
|
||||
private:
|
||||
WiFiUDP _udp;
|
||||
int8_t _rxPin = -1;
|
||||
|
@ -722,6 +722,38 @@ void ApplicationLayer::propertyDescriptionReadResponse(AckType ack, Priority pri
|
||||
individualSend(ack, hopType, priority, asap, apdu, secCtrl);
|
||||
}
|
||||
|
||||
void ApplicationLayer::propertyExtDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
|
||||
uint16_t objectType, uint16_t objectInstance, uint16_t propertyId, uint16_t propertyIndex, uint8_t descriptionType, bool writeEnable, uint8_t type,
|
||||
uint16_t maxNumberOfElements, uint8_t access)
|
||||
{
|
||||
CemiFrame frame(16);
|
||||
APDU& apdu = frame.apdu();
|
||||
apdu.type(PropertyExtDescriptionResponse);
|
||||
uint8_t* data = apdu.data();
|
||||
|
||||
data[1] = (objectType & 0xff00) >> 8;
|
||||
data[2] = (objectType & 0x00ff);
|
||||
|
||||
data[3] = (objectInstance & 0x0ff0) >> 4;
|
||||
data[4] = (objectInstance & 0x000f) << 4 | (propertyId & 0x0f00) >> 8;
|
||||
data[5] = (propertyId & 0x00ff);
|
||||
|
||||
data[6] = (descriptionType & 0x000f) << 4 | (propertyIndex & 0x0f00) >> 8;
|
||||
data[7] = (propertyIndex & 0x00ff);
|
||||
data[8] = 0; // DataPointType ??
|
||||
data[9] = 0; // DataPointType ??
|
||||
data[10] = 0; // DataPointType ??
|
||||
data[11] = 0; // DataPointType ??
|
||||
|
||||
if (writeEnable)
|
||||
data[12] |= 0x80;
|
||||
data[12] |= (type & 0x3f);
|
||||
|
||||
pushWord(maxNumberOfElements & 0xfff, data + 13);
|
||||
data[15] = access;
|
||||
individualSend(ack, hopType, priority, asap, apdu, secCtrl);
|
||||
}
|
||||
|
||||
void ApplicationLayer::memoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress)
|
||||
{
|
||||
@ -740,6 +772,18 @@ void ApplicationLayer::memoryReadResponse(AckType ack, Priority priority, HopCou
|
||||
memorySend(MemoryResponse, ack, priority, hopType, asap, secCtrl, number, memoryAddress, memoryData);
|
||||
}
|
||||
|
||||
void ApplicationLayer::memoryRouterReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t * memoryData)
|
||||
{
|
||||
memoryRouterSend(MemoryRouterReadResponse, ack, priority, hopType, asap, secCtrl, number, memoryAddress, memoryData);
|
||||
}
|
||||
|
||||
void ApplicationLayer::memoryRoutingTableReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t * memoryData)
|
||||
{
|
||||
memoryRoutingTableSend(RoutingTableReadResponse, ack, priority, hopType, asap, secCtrl, number, memoryAddress, memoryData);
|
||||
}
|
||||
|
||||
void ApplicationLayer::memoryExtReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ReturnCodes code,
|
||||
uint8_t number, uint32_t memoryAddress, uint8_t * memoryData)
|
||||
{
|
||||
@ -962,6 +1006,34 @@ void ApplicationLayer::memorySend(ApduType type, AckType ack, Priority priority,
|
||||
individualSend(ack, hopType, priority, asap, apdu, secCtrl);
|
||||
}
|
||||
|
||||
void ApplicationLayer::memoryRouterSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t * memoryData)
|
||||
{
|
||||
CemiFrame frame(4 + number);
|
||||
APDU& apdu = frame.apdu();
|
||||
apdu.type(type);
|
||||
uint8_t* data = apdu.data();
|
||||
data[1] |= (number & 0xf);
|
||||
pushWord(memoryAddress & 0xffff, data + 2);
|
||||
if (number > 0)
|
||||
memcpy(data + 4, memoryData, number);
|
||||
individualSend(ack, hopType, priority, asap, apdu, secCtrl);
|
||||
}
|
||||
|
||||
void ApplicationLayer::memoryRoutingTableSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t * memoryData)
|
||||
{
|
||||
CemiFrame frame(4 + number);
|
||||
APDU& apdu = frame.apdu();
|
||||
apdu.type(type);
|
||||
uint8_t* data = apdu.data();
|
||||
data[1] |= (number & 0xf);
|
||||
pushWord(memoryAddress & 0xffff, data + 2);
|
||||
if (number > 0)
|
||||
memcpy(data + 4, memoryData, number);
|
||||
individualSend(ack, hopType, priority, asap, apdu, secCtrl);
|
||||
}
|
||||
|
||||
void ApplicationLayer::userMemorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
|
||||
uint32_t memoryAddress, uint8_t * memoryData)
|
||||
{
|
||||
@ -1084,6 +1156,17 @@ void ApplicationLayer::individualIndication(HopCountType hopType, Priority prior
|
||||
case PropertyDescriptionRead:
|
||||
_bau.propertyDescriptionReadIndication(priority, hopType, tsap, secCtrl, data[1], data[2], data[3]);
|
||||
break;
|
||||
case PropertyExtDescriptionRead:
|
||||
{
|
||||
ObjectType objectType = (ObjectType)(((data[1] & 0xff) << 8) | (data[2] & 0xff));
|
||||
uint16_t objectInstance = ((data[3] & 0xff) << 4) | ((data[4] & 0xf0) >> 4);
|
||||
uint16_t propertyId = ((data[4] & 0x0f) << 8) | (data[5] & 0xff);
|
||||
uint8_t descriptionType = (data[6] & 0xf0) >> 4;
|
||||
uint16_t propertyIndex = ((data[7] & 0x0f) << 8) | (data[8] & 0xff);
|
||||
|
||||
_bau.propertyExtDescriptionReadIndication(priority, hopType, tsap, secCtrl, objectType, objectInstance, propertyId, descriptionType, propertyIndex);
|
||||
break;
|
||||
}
|
||||
case PropertyDescriptionResponse:
|
||||
_bau.propertyDescriptionReadAppLayerConfirm(priority, hopType, tsap, secCtrl, data[1], data[2], data[3],
|
||||
(data[4] & 0x80) > 0, data[4] & 0x3f, getWord(data + 5) & 0xfff, data[7]);
|
||||
@ -1097,8 +1180,30 @@ void ApplicationLayer::individualIndication(HopCountType hopType, Priority prior
|
||||
case MemoryWrite:
|
||||
_bau.memoryWriteIndication(priority, hopType, tsap, secCtrl, data[0] & 0x3f, getWord(data + 1), data + 3);
|
||||
break;
|
||||
case MemoryExtRead:
|
||||
{
|
||||
|
||||
// EC
|
||||
case MemoryRouterWrite:
|
||||
print("MemoryRouterWrite: ");
|
||||
_bau.memoryRouterWriteIndication(priority, hopType, tsap, secCtrl, data[1], getWord(data + 2), data + 4);
|
||||
break;
|
||||
case MemoryRouterReadResponse:
|
||||
_bau.memoryRouterReadAppLayerConfirm(priority, hopType, tsap, secCtrl, data[1], getWord(data + 2), data + 4);
|
||||
break;
|
||||
case RoutingTableOpen:
|
||||
println("Received OpenRoutingTable APDU, doing nothing");
|
||||
break;
|
||||
case RoutingTableRead:
|
||||
_bau.memoryRoutingTableReadIndication(priority, hopType, tsap, secCtrl, data[1], getWord(data + 2));
|
||||
break;
|
||||
case RoutingTableReadResponse:
|
||||
_bau.memoryRoutingTableReadAppLayerConfirm(priority, hopType, tsap, secCtrl, data[1], getWord(data + 2), data + 4);
|
||||
break;
|
||||
case RoutingTableWrite:
|
||||
_bau.memoryRoutingTableWriteIndication(priority, hopType, tsap, secCtrl, data[1], getWord(data + 2), data + 4);
|
||||
break;
|
||||
// end EC
|
||||
|
||||
case MemoryExtRead: {
|
||||
uint8_t number = data[1];
|
||||
uint32_t memoryAddress = ((data[2] & 0xff) << 16) | ((data[3] & 0xff) << 8) | (data[4] & 0xff);
|
||||
_bau.memoryExtReadIndication(priority, hopType, tsap, secCtrl, number, memoryAddress);
|
||||
@ -1162,7 +1267,7 @@ void ApplicationLayer::individualIndication(HopCountType hopType, Priority prior
|
||||
}
|
||||
default:
|
||||
print("Individual-indication: unhandled APDU-Type: ");
|
||||
println(apdu.type());
|
||||
apdu.printPDU();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1210,6 +1315,9 @@ void ApplicationLayer::individualConfirm(AckType ack, HopCountType hopType, Prio
|
||||
case PropertyDescriptionRead:
|
||||
_bau.propertyDescriptionReadLocalConfirm(ack, priority, hopType, tsap, secCtrl, data[1], data[2], data[3], status);
|
||||
break;
|
||||
case PropertyExtDescriptionRead:
|
||||
_bau.propertyExtDescriptionReadLocalConfirm(ack, priority, hopType, tsap, secCtrl, data[1], data[2], data[3], status);
|
||||
break;
|
||||
case PropertyDescriptionResponse:
|
||||
_bau.propertyDescriptionReadResponseConfirm(ack, priority, hopType, tsap, secCtrl, data[1], data[2], data[3],
|
||||
(data[4] & 0x80) > 0, data[4] & 0x3f, getWord(data + 5) & 0xfff, data[7], status);
|
||||
|
@ -125,10 +125,17 @@ class ApplicationLayer
|
||||
void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
|
||||
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type,
|
||||
uint16_t maxNumberOfElements, uint8_t access);
|
||||
void propertyExtDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
|
||||
uint16_t objectType, uint16_t objectInstance, uint16_t propertyId, uint16_t propertyIndex, uint8_t descriptionType, bool writeEnable, uint8_t type,
|
||||
uint16_t maxNumberOfElements, uint8_t access);
|
||||
void memoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress);
|
||||
void memoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t* data);
|
||||
void memoryRouterReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t* data);
|
||||
void memoryRoutingTableReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t* data);
|
||||
void memoryExtReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ReturnCodes code, uint8_t number,
|
||||
uint32_t memoryAddress, uint8_t* data);
|
||||
void memoryExtWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ReturnCodes code, uint8_t number,
|
||||
@ -195,6 +202,12 @@ class ApplicationLayer
|
||||
uint16_t objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length);
|
||||
void memorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t* memoryData);
|
||||
// Added EC
|
||||
void memoryRouterSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t* memoryData);
|
||||
void memoryRoutingTableSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t* memoryData);
|
||||
//
|
||||
void userMemorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
|
||||
uint8_t number, uint32_t memoryAddress, uint8_t* memoryData);
|
||||
void groupValueSend(ApduType type, AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* data, uint8_t& dataLength);
|
||||
|
@ -6,7 +6,11 @@
|
||||
#include <cstring>
|
||||
|
||||
ApplicationProgramObject::ApplicationProgramObject(Memory& memory)
|
||||
#if MASK_VERSION == 0x091A
|
||||
: TableObject(memory, 0x0100, 0x0100)
|
||||
#else
|
||||
: TableObject(memory)
|
||||
#endif
|
||||
{
|
||||
Property* properties[] =
|
||||
{
|
||||
|
@ -151,10 +151,19 @@ void BusAccessUnit::propertyDescriptionReadLocalConfirm(AckType ack, Priority pr
|
||||
{
|
||||
}
|
||||
|
||||
void BusAccessUnit::propertyExtDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint16_t objectIndex, uint8_t propertyId, uint16_t propertyIndex, bool status)
|
||||
{
|
||||
}
|
||||
|
||||
void BusAccessUnit::propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex)
|
||||
{
|
||||
}
|
||||
|
||||
void BusAccessUnit::propertyExtDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
|
||||
uint16_t objectType, uint16_t objectInstance, uint16_t propertyId, uint8_t descriptionType, uint16_t propertyIndex)
|
||||
{
|
||||
}
|
||||
|
||||
void BusAccessUnit::propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access)
|
||||
{
|
||||
}
|
||||
@ -191,6 +200,22 @@ void BusAccessUnit::memoryWriteIndication(Priority priority, HopCountType hopTyp
|
||||
{
|
||||
}
|
||||
|
||||
void BusAccessUnit::memoryRouterWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data)
|
||||
{
|
||||
}
|
||||
void BusAccessUnit::memoryRouterReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data)
|
||||
{
|
||||
}
|
||||
void BusAccessUnit::memoryRoutingTableReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress)
|
||||
{
|
||||
}
|
||||
void BusAccessUnit::memoryRoutingTableReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data)
|
||||
{
|
||||
}
|
||||
void BusAccessUnit::memoryRoutingTableWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t *data)
|
||||
{
|
||||
}
|
||||
|
||||
void BusAccessUnit::memoryExtReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, bool status)
|
||||
{
|
||||
}
|
||||
|
@ -73,8 +73,12 @@ class BusAccessUnit
|
||||
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool confirmed);
|
||||
virtual void propertyDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
|
||||
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool status);
|
||||
virtual void propertyExtDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
|
||||
uint16_t objectIndex, uint8_t propertyId, uint16_t propertyIndex, bool status);
|
||||
virtual void propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
|
||||
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex);
|
||||
virtual void propertyExtDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
|
||||
uint16_t objectType, uint16_t objectInstance, uint16_t propertyId, uint8_t descriptionType, uint16_t propertyIndex);
|
||||
virtual void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
|
||||
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type,
|
||||
uint16_t maxNumberOfElements, uint8_t access);
|
||||
@ -95,6 +99,13 @@ class BusAccessUnit
|
||||
uint16_t memoryAddress, uint8_t* data, bool status);
|
||||
virtual void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t* data);
|
||||
virtual void memoryRouterWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t* data);
|
||||
virtual void memoryRouterReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t* data);
|
||||
virtual void memoryRoutingTableReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress);
|
||||
virtual void memoryRoutingTableReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t *data);
|
||||
virtual void memoryRoutingTableWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t *data);
|
||||
virtual void memoryExtReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
|
||||
uint32_t memoryAddress, bool status);
|
||||
virtual void memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress);
|
||||
|
@ -10,7 +10,7 @@ using namespace std;
|
||||
|
||||
Bau07B0::Bau07B0(Platform& platform)
|
||||
: BauSystemBDevice(platform),
|
||||
_dlLayer(_deviceObj, _netLayer.getInterface(), _platform, (ITpUartCallBacks&) *this, (DataLinkLayerCallbacks*) this),
|
||||
_dlLayer(_deviceObj, _netLayer.getInterface(), _platform, *this, (ITpUartCallBacks&) *this, (DataLinkLayerCallbacks*) this),
|
||||
DataLinkLayerCallbacks()
|
||||
#ifdef USE_CEMI_SERVER
|
||||
, _cemiServer(*this)
|
||||
@ -78,7 +78,7 @@ InterfaceObject* Bau07B0::getInterfaceObject(uint8_t idx)
|
||||
}
|
||||
}
|
||||
|
||||
InterfaceObject* Bau07B0::getInterfaceObject(ObjectType objectType, uint8_t objectInstance)
|
||||
InterfaceObject* Bau07B0::getInterfaceObject(ObjectType objectType, uint16_t objectInstance)
|
||||
{
|
||||
// We do not use it right now.
|
||||
// Required for coupler mode as there are multiple router objects for example
|
||||
@ -128,27 +128,30 @@ void Bau07B0::loop()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Bau07B0::isAckRequired(uint16_t address, bool isGrpAddr)
|
||||
TPAckType Bau07B0::isAckRequired(uint16_t address, bool isGrpAddr)
|
||||
{
|
||||
if (isGrpAddr)
|
||||
{
|
||||
// ACK for broadcasts
|
||||
if (address == 0)
|
||||
return true;
|
||||
return TPAckType::AckReqAck;
|
||||
// is group address in group address table? ACK if yes.
|
||||
return _addrTable.contains(address);
|
||||
if(_addrTable.contains(address))
|
||||
return TPAckType::AckReqAck;
|
||||
else
|
||||
return TPAckType::AckReqNone;
|
||||
}
|
||||
|
||||
// Also ACK for our own individual address
|
||||
if (address == _deviceObj.individualAddress())
|
||||
return true;
|
||||
return TPAckType::AckReqAck;
|
||||
|
||||
if (address == 0)
|
||||
{
|
||||
println("Invalid broadcast detected: destination address is 0, but address type is \"individual\"");
|
||||
}
|
||||
|
||||
return false;
|
||||
return TPAckType::AckReqNone;
|
||||
}
|
||||
|
||||
TpUartDataLinkLayer* Bau07B0::getDataLinkLayer() {
|
||||
|
@ -19,10 +19,10 @@ class Bau07B0 : public BauSystemBDevice, public ITpUartCallBacks, public DataLin
|
||||
TpUartDataLinkLayer* getDataLinkLayer();
|
||||
protected:
|
||||
InterfaceObject* getInterfaceObject(uint8_t idx);
|
||||
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
|
||||
InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance);
|
||||
|
||||
// For TP1 only
|
||||
bool isAckRequired(uint16_t address, bool isGrpAddr) override;
|
||||
TPAckType isAckRequired(uint16_t address, bool isGrpAddr) override;
|
||||
|
||||
private:
|
||||
TpUartDataLinkLayer _dlLayer;
|
||||
|
@ -8,12 +8,17 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
/* ToDos
|
||||
Announce the line status of sec side 03_05_01 4.4.3
|
||||
implement PID_COUPLER_SERVICES_CONTROL 03_05_01 4.4.7
|
||||
*/
|
||||
|
||||
Bau091A::Bau091A(Platform& platform)
|
||||
: BauSystemBCoupler(platform),
|
||||
_routerObj(memory()),
|
||||
_routerObj(memory(), 0x200, 0x2000), // the Filtertable of 0x091A IP Routers is fixed at 0x200 and 0x2000 long
|
||||
_ipParameters(_deviceObj, platform),
|
||||
_dlLayerPrimary(_deviceObj, _ipParameters, _netLayer.getPrimaryInterface(), _platform, (DataLinkLayerCallbacks*) this),
|
||||
_dlLayerSecondary(_deviceObj, _netLayer.getSecondaryInterface(), platform, (ITpUartCallBacks&) *this, (DataLinkLayerCallbacks*) this),
|
||||
_dlLayerPrimary(_deviceObj, _ipParameters, _netLayer.getPrimaryInterface(), _platform, *this, (DataLinkLayerCallbacks*) this),
|
||||
_dlLayerSecondary(_deviceObj, _netLayer.getSecondaryInterface(), platform, *this, (ITpUartCallBacks&) *this, (DataLinkLayerCallbacks*) this),
|
||||
DataLinkLayerCallbacks()
|
||||
#ifdef USE_CEMI_SERVER
|
||||
,
|
||||
@ -33,9 +38,14 @@ Bau091A::Bau091A(Platform& platform)
|
||||
#ifdef USE_CEMI_SERVER
|
||||
_cemiServerObject.setMediumTypeAsSupported(DptMedium::KNX_IP);
|
||||
_cemiServerObject.setMediumTypeAsSupported(DptMedium::KNX_TP1);
|
||||
_cemiServer.dataLinkLayerPrimary(_dlLayerPrimary);
|
||||
_cemiServer.dataLinkLayer(_dlLayerSecondary); // Secondary I/F is the important one!
|
||||
_dlLayerPrimary.cemiServer(_cemiServer);
|
||||
_dlLayerSecondary.cemiServer(_cemiServer);
|
||||
_memory.addSaveRestore(&_cemiServerObject);
|
||||
uint8_t count = 1;
|
||||
uint16_t suppCommModes = 0x0100;
|
||||
_cemiServerObject.writeProperty(PID_COMM_MODES_SUPPORTED, 1, (uint8_t*)&suppCommModes, count); // set the properties Bit 0 to 1 meaning "LinkLayer supported"
|
||||
#endif
|
||||
|
||||
_memory.addSaveRestore(&_routerObj);
|
||||
@ -92,7 +102,7 @@ InterfaceObject* Bau091A::getInterfaceObject(uint8_t idx)
|
||||
}
|
||||
}
|
||||
|
||||
InterfaceObject* Bau091A::getInterfaceObject(ObjectType objectType, uint8_t objectInstance)
|
||||
InterfaceObject* Bau091A::getInterfaceObject(ObjectType objectType, uint16_t objectInstance)
|
||||
{
|
||||
// We do not use it right now.
|
||||
// Required for coupler mode as there are multiple router objects for example
|
||||
@ -139,6 +149,9 @@ void Bau091A::enabled(bool value)
|
||||
{
|
||||
_dlLayerPrimary.enabled(value);
|
||||
_dlLayerSecondary.enabled(value);
|
||||
|
||||
// ToDo change frame repitition in the TP layer - but default is ok.
|
||||
//_dlLayerSecondary.setFrameRepetition(3,3);
|
||||
}
|
||||
|
||||
void Bau091A::loop()
|
||||
@ -148,23 +161,71 @@ void Bau091A::loop()
|
||||
BauSystemBCoupler::loop();
|
||||
}
|
||||
|
||||
bool Bau091A::isAckRequired(uint16_t address, bool isGrpAddr)
|
||||
TPAckType Bau091A::isAckRequired(uint16_t address, bool isGrpAddr)
|
||||
{
|
||||
//only called from TpUartDataLinkLayer
|
||||
TPAckType ack = TPAckType::AckReqNone;
|
||||
|
||||
uint8_t lcconfig = LCCONFIG::PHYS_FRAME_ROUT | LCCONFIG::PHYS_REPEAT | LCCONFIG::BROADCAST_REPEAT | LCCONFIG::GROUP_IACK_ROUT | LCCONFIG::PHYS_IACK_NORMAL; // default value from spec. in case prop is not availible.
|
||||
Property* prop_lcconfig = _routerObj.property(PID_SUB_LCCONFIG);
|
||||
if(lcconfig)
|
||||
prop_lcconfig->read(lcconfig);
|
||||
|
||||
if (isGrpAddr)
|
||||
{
|
||||
// ACK for broadcasts
|
||||
if (address == 0)
|
||||
return true;
|
||||
ack = TPAckType::AckReqAck;
|
||||
|
||||
// is group address in filter table? ACK if yes.
|
||||
return _routerObj.isGroupAddressInFilterTable(address);
|
||||
if(lcconfig & LCCONFIG::GROUP_IACK_ROUT)
|
||||
// is group address in filter table? ACK if yes, No if not
|
||||
if(_netLayer.isRoutedGroupAddress(address, 1))
|
||||
ack = TPAckType::AckReqAck;
|
||||
else
|
||||
ack = TPAckType::AckReqNone;
|
||||
else
|
||||
// all are ACKED
|
||||
ack = TPAckType::AckReqAck;
|
||||
#ifdef KNX_TUNNELING
|
||||
if(_dlLayerPrimary.isSentToTunnel(address, isGrpAddr))
|
||||
ack = TPAckType::AckReqAck;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
return _netLayer.isRoutedIndividualAddress(address);
|
||||
if((lcconfig & LCCONFIG::PHYS_IACK) == LCCONFIG::PHYS_IACK_ALL)
|
||||
ack = TPAckType::AckReqAck;
|
||||
else if((lcconfig & LCCONFIG::PHYS_IACK) == LCCONFIG::PHYS_IACK_NACK)
|
||||
ack = TPAckType::AckReqNack;
|
||||
else
|
||||
if(_netLayer.isRoutedIndividualAddress(address, 1) || address == _deviceObj.individualAddress()) // Also ACK for our own individual address
|
||||
ack = TPAckType::AckReqAck;
|
||||
else
|
||||
ack = TPAckType::AckReqNone;
|
||||
|
||||
#ifdef KNX_TUNNELING
|
||||
if(_dlLayerPrimary.isSentToTunnel(address, isGrpAddr))
|
||||
ack = TPAckType::AckReqAck;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
return ack;
|
||||
}
|
||||
|
||||
bool Bau091A::configured()
|
||||
{
|
||||
// _configured is set to true initially, if the device was configured with ETS it will be set to true after restart
|
||||
|
||||
if (!_configured)
|
||||
return false;
|
||||
|
||||
_configured = _routerObj.loadState() == LS_LOADED;
|
||||
#ifdef USE_DATASECURE
|
||||
_configured &= _secIfObj.loadState() == LS_LOADED;
|
||||
#endif
|
||||
|
||||
return _configured;
|
||||
}
|
||||
|
||||
IpDataLinkLayer* Bau091A::getPrimaryDataLinkLayer() {
|
||||
|
@ -17,15 +17,16 @@ class Bau091A : public BauSystemBCoupler, public ITpUartCallBacks, public DataLi
|
||||
void loop() override;
|
||||
bool enabled() override;
|
||||
void enabled(bool value) override;
|
||||
bool configured() override;
|
||||
|
||||
IpDataLinkLayer* getPrimaryDataLinkLayer();
|
||||
TpUartDataLinkLayer* getSecondaryDataLinkLayer();
|
||||
protected:
|
||||
InterfaceObject* getInterfaceObject(uint8_t idx);
|
||||
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
|
||||
InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance);
|
||||
|
||||
// For TP1 only
|
||||
bool isAckRequired(uint16_t address, bool isGrpAddr) override;
|
||||
TPAckType isAckRequired(uint16_t address, bool isGrpAddr) override;
|
||||
|
||||
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
|
||||
private:
|
||||
|
@ -10,7 +10,7 @@ using namespace std;
|
||||
|
||||
Bau27B0::Bau27B0(Platform& platform)
|
||||
: BauSystemBDevice(platform),
|
||||
_dlLayer(_deviceObj, _rfMediumObj, _netLayer.getInterface(), _platform)
|
||||
_dlLayer(_deviceObj, _rfMediumObj, _netLayer.getInterface(), _platform, *this)
|
||||
#ifdef USE_CEMI_SERVER
|
||||
, _cemiServer(*this)
|
||||
#endif
|
||||
@ -90,7 +90,7 @@ InterfaceObject* Bau27B0::getInterfaceObject(uint8_t idx)
|
||||
}
|
||||
}
|
||||
|
||||
InterfaceObject* Bau27B0::getInterfaceObject(ObjectType objectType, uint8_t objectInstance)
|
||||
InterfaceObject* Bau27B0::getInterfaceObject(ObjectType objectType, uint16_t objectInstance)
|
||||
{
|
||||
// We do not use it right now.
|
||||
// Required for coupler mode as there are multiple router objects for example
|
||||
|
@ -25,7 +25,7 @@ class Bau27B0 : public BauSystemBDevice
|
||||
RfDataLinkLayer* getDataLinkLayer();
|
||||
protected:
|
||||
InterfaceObject* getInterfaceObject(uint8_t idx);
|
||||
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
|
||||
InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance);
|
||||
|
||||
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
|
||||
private:
|
||||
|
@ -14,8 +14,8 @@ Bau2920::Bau2920(Platform& platform)
|
||||
_rtObjPrimary(memory()),
|
||||
_rtObjSecondary(memory()),
|
||||
_rfMediumObject(),
|
||||
_dlLayerPrimary(_deviceObj, _netLayer.getPrimaryInterface(), _platform, (ITpUartCallBacks&) *this),
|
||||
_dlLayerSecondary(_deviceObj, _rfMediumObject, _netLayer.getSecondaryInterface(), platform)
|
||||
_dlLayerPrimary(_deviceObj, _netLayer.getPrimaryInterface(), _platform, *this, (ITpUartCallBacks&) *this),
|
||||
_dlLayerSecondary(_deviceObj, _rfMediumObject, _netLayer.getSecondaryInterface(), platform, *this)
|
||||
#ifdef USE_CEMI_SERVER
|
||||
,
|
||||
_cemiServer(*this)
|
||||
@ -97,7 +97,7 @@ InterfaceObject* Bau2920::getInterfaceObject(uint8_t idx)
|
||||
}
|
||||
}
|
||||
|
||||
InterfaceObject* Bau2920::getInterfaceObject(ObjectType objectType, uint8_t objectInstance)
|
||||
InterfaceObject* Bau2920::getInterfaceObject(ObjectType objectType, uint16_t objectInstance)
|
||||
{
|
||||
// We do not use it right now.
|
||||
// Required for coupler mode as there are multiple router objects for example
|
||||
|
@ -26,7 +26,7 @@ class Bau2920 : public BauSystemBCoupler
|
||||
RfDataLinkLayer* getSecondaryDataLinkLayer();
|
||||
protected:
|
||||
InterfaceObject* getInterfaceObject(uint8_t idx);
|
||||
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
|
||||
InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance);
|
||||
|
||||
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
|
||||
private:
|
||||
|
@ -11,7 +11,7 @@ using namespace std;
|
||||
Bau57B0::Bau57B0(Platform& platform)
|
||||
: BauSystemBDevice(platform),
|
||||
_ipParameters(_deviceObj, platform),
|
||||
_dlLayer(_deviceObj, _ipParameters, _netLayer.getInterface(), _platform, (DataLinkLayerCallbacks*) this),
|
||||
_dlLayer(_deviceObj, _ipParameters, _netLayer.getInterface(), _platform, *this, (DataLinkLayerCallbacks*) this),
|
||||
DataLinkLayerCallbacks()
|
||||
#ifdef USE_CEMI_SERVER
|
||||
,
|
||||
@ -85,7 +85,7 @@ InterfaceObject* Bau57B0::getInterfaceObject(uint8_t idx)
|
||||
}
|
||||
}
|
||||
|
||||
InterfaceObject* Bau57B0::getInterfaceObject(ObjectType objectType, uint8_t objectInstance)
|
||||
InterfaceObject* Bau57B0::getInterfaceObject(ObjectType objectType, uint16_t objectInstance)
|
||||
{
|
||||
// We do not use it right now.
|
||||
// Required for coupler mode as there are multiple router objects for example
|
||||
|
@ -19,7 +19,7 @@ class Bau57B0 : public BauSystemBDevice, public DataLinkLayerCallbacks
|
||||
IpDataLinkLayer* getDataLinkLayer();
|
||||
protected:
|
||||
InterfaceObject* getInterfaceObject(uint8_t idx);
|
||||
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
|
||||
InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance);
|
||||
|
||||
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
|
||||
private:
|
||||
|
@ -112,6 +112,50 @@ void BauSystemB::deviceDescriptorReadIndication(Priority priority, HopCountType
|
||||
pushWord(_deviceObj.maskVersion(), data);
|
||||
applicationLayer().deviceDescriptorReadResponse(AckRequested, priority, hopType, asap, secCtrl, descriptorType, data);
|
||||
}
|
||||
void BauSystemB::memoryRouterWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t *data)
|
||||
{
|
||||
print("Writing memory at: ");
|
||||
print(memoryAddress, HEX);
|
||||
print(" length: ");
|
||||
print(number);
|
||||
print(" data: ");
|
||||
printHex("=>", data, number);
|
||||
_memory.writeMemory(memoryAddress, number, data);
|
||||
if (_deviceObj.verifyMode())
|
||||
{
|
||||
print("Sending Read indication");
|
||||
memoryRouterReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress, data);
|
||||
}
|
||||
}
|
||||
|
||||
void BauSystemB::memoryRouterReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t *data)
|
||||
{
|
||||
applicationLayer().memoryRouterReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress, data);
|
||||
}
|
||||
|
||||
void BauSystemB::memoryRoutingTableReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t *data)
|
||||
{
|
||||
applicationLayer().memoryRoutingTableReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress, data);
|
||||
}
|
||||
void BauSystemB::memoryRoutingTableReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress)
|
||||
{
|
||||
memoryRoutingTableReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress, _memory.toAbsolute(memoryAddress));
|
||||
}
|
||||
|
||||
void BauSystemB::memoryRoutingTableWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t *data)
|
||||
{
|
||||
print("Writing memory at: ");
|
||||
print(memoryAddress, HEX);
|
||||
print(" length: ");
|
||||
print(number);
|
||||
print(" data: ");
|
||||
printHex("=>", data, number);
|
||||
_memory.writeMemory(memoryAddress, number, data);
|
||||
if (_deviceObj.verifyMode())
|
||||
memoryRoutingTableReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress, data);
|
||||
}
|
||||
|
||||
void BauSystemB::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t * data)
|
||||
@ -215,6 +259,33 @@ void BauSystemB::propertyDescriptionReadIndication(Priority priority, HopCountTy
|
||||
writeEnable, type, numberOfElements, access);
|
||||
}
|
||||
|
||||
void BauSystemB::propertyExtDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
|
||||
uint16_t objectType, uint16_t objectInstance, uint16_t propertyId, uint8_t descriptionType, uint16_t propertyIndex)
|
||||
{
|
||||
uint8_t pid = propertyId;
|
||||
uint8_t pidx = propertyIndex;
|
||||
if(propertyId > 0xFF || propertyIndex > 0xFF)
|
||||
{
|
||||
println("BauSystemB::propertyExtDescriptionReadIndication: propertyId or Idx > 256 are not supported");
|
||||
return;
|
||||
}
|
||||
if(descriptionType != 0)
|
||||
{
|
||||
println("BauSystemB::propertyExtDescriptionReadIndication: only descriptionType 0 supported");
|
||||
return;
|
||||
}
|
||||
bool writeEnable = false;
|
||||
uint8_t type = 0;
|
||||
uint16_t numberOfElements = 0;
|
||||
uint8_t access = 0;
|
||||
InterfaceObject* obj = getInterfaceObject((ObjectType)objectType, objectInstance);
|
||||
if (obj)
|
||||
obj->readPropertyDescription(pid, pidx, writeEnable, type, numberOfElements, access);
|
||||
|
||||
applicationLayer().propertyExtDescriptionReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, propertyIndex,
|
||||
descriptionType, writeEnable, type, numberOfElements, access);
|
||||
}
|
||||
|
||||
void BauSystemB::propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex,
|
||||
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length)
|
||||
{
|
||||
@ -246,6 +317,17 @@ void BauSystemB::propertyValueReadIndication(Priority priority, HopCountType hop
|
||||
{
|
||||
uint8_t size = 0;
|
||||
uint8_t elementCount = numberOfElements;
|
||||
#ifdef LOG_KNX_PROP
|
||||
print("propertyValueReadIndication: ObjIdx ");
|
||||
print(objectIndex);
|
||||
print(" propId ");
|
||||
print(propertyId);
|
||||
print(" num ");
|
||||
print(numberOfElements);
|
||||
print(" start ");
|
||||
print(startIndex);
|
||||
#endif
|
||||
|
||||
InterfaceObject* obj = getInterfaceObject(objectIndex);
|
||||
if (obj)
|
||||
{
|
||||
|
@ -51,7 +51,7 @@ class BauSystemB : protected BusAccessUnit
|
||||
protected:
|
||||
virtual ApplicationLayer& applicationLayer() = 0;
|
||||
virtual InterfaceObject* getInterfaceObject(uint8_t idx) = 0;
|
||||
virtual InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance) = 0;
|
||||
virtual InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance) = 0;
|
||||
|
||||
void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t* data) override;
|
||||
@ -59,6 +59,15 @@ class BauSystemB : protected BusAccessUnit
|
||||
uint16_t memoryAddress) override;
|
||||
void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t * data);
|
||||
void memoryRouterWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t *data);
|
||||
void memoryRouterReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t *data);
|
||||
void memoryRoutingTableWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
|
||||
uint16_t memoryAddress, uint8_t *data);
|
||||
void memoryRoutingTableReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t *data);
|
||||
void memoryRoutingTableReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress);
|
||||
//
|
||||
void memoryExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
|
||||
uint32_t memoryAddress, uint8_t* data) override;
|
||||
void memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
|
||||
@ -71,6 +80,8 @@ class BauSystemB : protected BusAccessUnit
|
||||
uint32_t memoryAddress, uint8_t* memoryData) override;
|
||||
void propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex,
|
||||
uint8_t propertyId, uint8_t propertyIndex) override;
|
||||
void propertyExtDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
|
||||
uint16_t objectType, uint16_t objectInstance, uint16_t propertyId, uint8_t descriptionType, uint16_t propertyIndex) override;
|
||||
void propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex,
|
||||
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) override;
|
||||
void propertyValueExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance,
|
||||
|
@ -17,7 +17,6 @@ BauSystemBCoupler::BauSystemBCoupler(Platform& platform) :
|
||||
_appLayer.transportLayer(_transLayer);
|
||||
_transLayer.networkLayer(_netLayer);
|
||||
_memory.addSaveRestore(&_deviceObj);
|
||||
_memory.addSaveRestore(&_appProgram);
|
||||
#ifdef USE_DATASECURE
|
||||
_memory.addSaveRestore(&_secIfObj);
|
||||
#endif
|
||||
|
@ -6,11 +6,17 @@
|
||||
/*
|
||||
cEMI Frame Format
|
||||
|
||||
+---------+--------+--------+--------+--------+---------+---------+--------+---------+
|
||||
| Header | Msg |Add.Info| Ctrl 1 | Ctrl 2 | Source | Dest. | Data | APDU |
|
||||
| | Code | Length | | | Address | Address | Length | |
|
||||
+--------+--------+--------+--------+---------+---------+--------+---------+
|
||||
| _data |
|
||||
+--------+--------+--------+--------+---------+---------+--------+---------+
|
||||
| LPDU |
|
||||
+--------+--------+--------+--------+---------+---------+--------+---------+
|
||||
| NPDU |
|
||||
+---------+--------+--------+--------+--------+---------+---------+--------+---------+
|
||||
6 bytes 1 byte 1 byte 1 byte 1 byte 2 bytes 2 bytes 1 byte 2 bytes
|
||||
| Header | Msg |Add.Info| Ctrl 1 | Ctrl 2 | Source | Dest. | Data | TPDU |
|
||||
| | Code | Length | | | Address | Address | Length | APDU |
|
||||
+---------+--------+--------+--------+--------+---------+---------+--------+---------+
|
||||
6 bytes 1 byte 1 byte 1 byte 1 byte 2 bytes 2 bytes 1 byte n 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
|
||||
@ -85,11 +91,11 @@ CemiFrame::CemiFrame(uint8_t apduLength)
|
||||
_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);
|
||||
_length = _npdu.length() + NPDU_LPDU_DIFF;
|
||||
}
|
||||
|
||||
CemiFrame::CemiFrame(const CemiFrame & other)
|
||||
@ -116,6 +122,7 @@ CemiFrame& CemiFrame::operator=(CemiFrame other)
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
MessageCode CemiFrame::messageCode() const
|
||||
{
|
||||
return (MessageCode)_data[0];
|
||||
@ -128,9 +135,7 @@ void CemiFrame::messageCode(MessageCode msgCode)
|
||||
|
||||
uint16_t CemiFrame::totalLenght() const
|
||||
{
|
||||
uint16_t tmp =
|
||||
_npdu.length() + NPDU_LPDU_DIFF;
|
||||
return tmp;
|
||||
return _length;
|
||||
}
|
||||
|
||||
uint16_t CemiFrame::telegramLengthtTP() const
|
||||
@ -369,14 +374,27 @@ bool CemiFrame::valid() const
|
||||
uint8_t apduLen = _data[_data[1] + NPDU_LPDU_DIFF];
|
||||
|
||||
if (_length != 0 && _length != (addInfoLen + apduLen + NPDU_LPDU_DIFF + 2))
|
||||
{
|
||||
print("length issue, length: ");
|
||||
print(_length);
|
||||
print(" addInfoLen: ");
|
||||
print(addInfoLen);
|
||||
print(" apduLen: ");
|
||||
print(apduLen);
|
||||
print(" expected length: ");
|
||||
println(addInfoLen + apduLen + NPDU_LPDU_DIFF + 2);
|
||||
printHex("Frame: ", _data, _length, true);
|
||||
|
||||
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)
|
||||
)
|
||||
){
|
||||
print("Other issue");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -11,10 +11,13 @@
|
||||
#include <stdio.h>
|
||||
|
||||
CemiServer::CemiServer(BauSystemB& bau)
|
||||
: _bau(bau),
|
||||
_usbTunnelInterface(*this,
|
||||
: _bau(bau)
|
||||
#ifdef USE_USB
|
||||
,
|
||||
_usbTunnelInterface(*this,
|
||||
_bau.deviceObject().maskVersion(),
|
||||
_bau.deviceObject().manufacturerId())
|
||||
#endif
|
||||
{
|
||||
// The cEMI server will hand out the device address + 1 to the cEMI client (e.g. ETS),
|
||||
// so that the device and the cEMI client/server connection(tunnel) can operate simultaneously.
|
||||
@ -26,6 +29,13 @@ void CemiServer::dataLinkLayer(DataLinkLayer& layer)
|
||||
_dataLinkLayer = &layer;
|
||||
}
|
||||
|
||||
#ifdef KNX_TUNNELING
|
||||
void CemiServer::dataLinkLayerPrimary(DataLinkLayer& layer)
|
||||
{
|
||||
_dataLinkLayerPrimary = &layer;
|
||||
}
|
||||
|
||||
#endif
|
||||
uint16_t CemiServer::clientAddress() const
|
||||
{
|
||||
return _clientAddress;
|
||||
@ -42,23 +52,34 @@ void CemiServer::dataConfirmationToTunnel(CemiFrame& frame)
|
||||
|
||||
frame.messageCode(L_data_con);
|
||||
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("L_data_con: src: ");
|
||||
print(frame.sourceAddress(), HEX);
|
||||
print(" dst: ");
|
||||
print(frame.destinationAddress(), HEX);
|
||||
|
||||
printHex(" frame: ", frame.data(), frame.dataLength());
|
||||
#endif
|
||||
|
||||
#ifdef USE_USB
|
||||
_usbTunnelInterface.sendCemiFrame(frame);
|
||||
#elif defined(KNX_TUNNELING)
|
||||
_dataLinkLayerPrimary->dataConfirmationToTunnel(frame);
|
||||
#endif
|
||||
|
||||
frame.messageCode(backupMsgCode);
|
||||
}
|
||||
|
||||
void CemiServer::dataIndicationToTunnel(CemiFrame& frame)
|
||||
{
|
||||
#ifdef USE_RF
|
||||
bool isRf = _dataLinkLayer->mediumType() == DptMedium::KNX_RF;
|
||||
uint8_t data[frame.dataLength() + (isRf ? 10 : 0)];
|
||||
#else
|
||||
uint8_t data[frame.dataLength()];
|
||||
#endif
|
||||
|
||||
#ifdef USE_RF
|
||||
if (isRf)
|
||||
{
|
||||
data[0] = L_data_ind; // Message Code
|
||||
@ -72,237 +93,51 @@ void CemiServer::dataIndicationToTunnel(CemiFrame& frame)
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
memcpy(&data[0], frame.data(), frame.dataLength());
|
||||
#ifdef USE_RF
|
||||
}
|
||||
#endif
|
||||
|
||||
CemiFrame tmpFrame(data, sizeof(data));
|
||||
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("ToTunnel ");
|
||||
print("L_data_ind: src: ");
|
||||
print(tmpFrame.sourceAddress(), HEX);
|
||||
print(" dst: ");
|
||||
print(tmpFrame.destinationAddress(), HEX);
|
||||
|
||||
printHex(" frame: ", tmpFrame.data(), tmpFrame.dataLength());
|
||||
#endif
|
||||
tmpFrame.apdu().type();
|
||||
|
||||
#ifdef USE_USB
|
||||
_usbTunnelInterface.sendCemiFrame(tmpFrame);
|
||||
#elif defined(KNX_TUNNELING)
|
||||
_dataLinkLayerPrimary->dataIndicationToTunnel(frame);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CemiServer::frameReceived(CemiFrame& frame)
|
||||
{
|
||||
bool isRf = _dataLinkLayer->mediumType() == DptMedium::KNX_RF;
|
||||
|
||||
switch(frame.messageCode())
|
||||
{
|
||||
case L_data_req:
|
||||
{
|
||||
// Fill in the cEMI client address if the client sets
|
||||
// source address to 0.
|
||||
if(frame.sourceAddress() == 0x0000)
|
||||
{
|
||||
frame.sourceAddress(_clientAddress);
|
||||
}
|
||||
|
||||
if (isRf)
|
||||
{
|
||||
// Check if we have additional info for RF
|
||||
if (((frame.data())[1] == 0x0A) && // Additional info total length: we only handle one additional info of type RF
|
||||
((frame.data())[2] == 0x02) && // Additional info type: RF
|
||||
((frame.data())[3] == 0x08) ) // Additional info length of type RF: 8 bytes (fixed)
|
||||
{
|
||||
frame.rfInfo((frame.data())[4]);
|
||||
// Use the values provided in the RF additonal info
|
||||
if ( ((frame.data())[5] != 0x00) || ((frame.data())[6] != 0x00) || ((frame.data())[7] != 0x00) ||
|
||||
((frame.data())[8] != 0x00) || ((frame.data())[9] != 0x00) || ((frame.data())[10] != 0x00) )
|
||||
{
|
||||
frame.rfSerialOrDoA(&((frame.data())[5]));
|
||||
} // else leave the nullptr as it is
|
||||
frame.rfLfn((frame.data())[11]);
|
||||
}
|
||||
|
||||
// If the cEMI client does not provide a link layer frame number (LFN),
|
||||
// we use our own counter.
|
||||
// Note: There is another link layer frame number counter inside the RF data link layer class!
|
||||
// That counter is solely for the local application!
|
||||
// If we set a LFN here, the data link layer counter is NOT used!
|
||||
if (frame.rfLfn() == 0xFF)
|
||||
{
|
||||
// Set Data Link Layer Frame Number
|
||||
frame.rfLfn(_frameNumber);
|
||||
// Link Layer frame number counts 0..7
|
||||
_frameNumber = (_frameNumber + 1) & 0x7;
|
||||
}
|
||||
}
|
||||
|
||||
print("L_data_req: src: ");
|
||||
print(frame.sourceAddress(), HEX);
|
||||
print(" dst: ");
|
||||
print(frame.destinationAddress(), HEX);
|
||||
|
||||
printHex(" frame: ", frame.data(), frame.dataLength());
|
||||
|
||||
_dataLinkLayer->dataRequestFromTunnel(frame);
|
||||
handleLData(frame);
|
||||
break;
|
||||
}
|
||||
|
||||
case M_PropRead_req:
|
||||
{
|
||||
print("M_PropRead_req: ");
|
||||
|
||||
uint16_t objectType;
|
||||
popWord(objectType, &frame.data()[1]);
|
||||
uint8_t objectInstance = frame.data()[3];
|
||||
uint8_t propertyId = frame.data()[4];
|
||||
uint8_t numberOfElements = frame.data()[5] >> 4;
|
||||
uint16_t startIndex = frame.data()[6] | ((frame.data()[5]&0x0F)<<8);
|
||||
uint8_t* data = nullptr;
|
||||
uint32_t dataSize = 0;
|
||||
|
||||
print("ObjType: ");
|
||||
print(objectType, DEC);
|
||||
print(" ObjInst: ");
|
||||
print(objectInstance, DEC);
|
||||
print(" PropId: ");
|
||||
print(propertyId, DEC);
|
||||
print(" NoE: ");
|
||||
print(numberOfElements, DEC);
|
||||
print(" startIdx: ");
|
||||
print(startIndex, DEC);
|
||||
|
||||
// propertyValueRead() allocates memory for the data! Needs to be deleted again!
|
||||
_bau.propertyValueRead((ObjectType)objectType, objectInstance, propertyId, numberOfElements, startIndex, &data, dataSize);
|
||||
|
||||
// Patch result for device address in device object
|
||||
// The cEMI server will hand out the device address + 1 to the cEMI client (e.g. ETS),
|
||||
// so that the device and the cEMI client/server connection(tunnel) can operate simultaneously.
|
||||
// KNX IP Interfaces which offer multiple simultaneous tunnel connections seem to operate the same way.
|
||||
// Each tunnel has its own cEMI client address which is based on the main device address.
|
||||
if (((ObjectType) objectType == OT_DEVICE) &&
|
||||
(propertyId == PID_DEVICE_ADDR) &&
|
||||
(numberOfElements == 1))
|
||||
{
|
||||
data[0] = (uint8_t) (_clientAddress & 0xFF);
|
||||
}
|
||||
else if (((ObjectType) objectType == OT_DEVICE) &&
|
||||
(propertyId == PID_SUBNET_ADDR) &&
|
||||
(numberOfElements == 1))
|
||||
{
|
||||
data[0] = (uint8_t) ((_clientAddress >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
if (data && dataSize && numberOfElements)
|
||||
{
|
||||
printHex(" <- data: ", data, dataSize);
|
||||
println("");
|
||||
|
||||
// Prepare positive response
|
||||
uint8_t responseData[7 + dataSize];
|
||||
memcpy(responseData, frame.data(), 7);
|
||||
memcpy(&responseData[7], data, dataSize);
|
||||
|
||||
CemiFrame responseFrame(responseData, sizeof(responseData));
|
||||
responseFrame.messageCode(M_PropRead_con);
|
||||
_usbTunnelInterface.sendCemiFrame(responseFrame);
|
||||
|
||||
delete[] data;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prepare negative response
|
||||
uint8_t responseData[7 + 1];
|
||||
memcpy(responseData, frame.data(), sizeof(responseData));
|
||||
responseData[7] = Void_DP; // Set cEMI error code
|
||||
responseData[5] = 0; // Set Number of elements to zero
|
||||
|
||||
printHex(" <- error: ", &responseData[7], 1);
|
||||
println("");
|
||||
|
||||
CemiFrame responseFrame(responseData, sizeof(responseData));
|
||||
responseFrame.messageCode(M_PropRead_con);
|
||||
_usbTunnelInterface.sendCemiFrame(responseFrame);
|
||||
}
|
||||
handleMPropRead(frame);
|
||||
break;
|
||||
}
|
||||
|
||||
case M_PropWrite_req:
|
||||
{
|
||||
print("M_PropWrite_req: ");
|
||||
|
||||
uint16_t objectType;
|
||||
popWord(objectType, &frame.data()[1]);
|
||||
uint8_t objectInstance = frame.data()[3];
|
||||
uint8_t propertyId = frame.data()[4];
|
||||
uint8_t numberOfElements = frame.data()[5] >> 4;
|
||||
uint16_t startIndex = frame.data()[6] | ((frame.data()[5]&0x0F)<<8);
|
||||
uint8_t* requestData = &frame.data()[7];
|
||||
uint32_t requestDataSize = frame.dataLength() - 7;
|
||||
|
||||
print("ObjType: ");
|
||||
print(objectType, DEC);
|
||||
print(" ObjInst: ");
|
||||
print(objectInstance, DEC);
|
||||
print(" PropId: ");
|
||||
print(propertyId, DEC);
|
||||
print(" NoE: ");
|
||||
print(numberOfElements, DEC);
|
||||
print(" startIdx: ");
|
||||
print(startIndex, DEC);
|
||||
|
||||
printHex(" -> data: ", requestData, requestDataSize);
|
||||
|
||||
// Patch request for device address in device object
|
||||
if (((ObjectType) objectType == OT_DEVICE) &&
|
||||
(propertyId == PID_DEVICE_ADDR) &&
|
||||
(numberOfElements == 1))
|
||||
{
|
||||
// Temporarily store new cEMI client address in memory
|
||||
// We also be sent back if the client requests it again
|
||||
_clientAddress = (_clientAddress & 0xFF00) | requestData[0];
|
||||
print("cEMI client address: ");
|
||||
println(_clientAddress, HEX);
|
||||
}
|
||||
else if (((ObjectType) objectType == OT_DEVICE) &&
|
||||
(propertyId == PID_SUBNET_ADDR) &&
|
||||
(numberOfElements == 1))
|
||||
{
|
||||
// Temporarily store new cEMI client address in memory
|
||||
// We also be sent back if the client requests it again
|
||||
_clientAddress = (_clientAddress & 0x00FF) | (requestData[0] << 8);
|
||||
print("cEMI client address: ");
|
||||
println(_clientAddress, HEX);
|
||||
}
|
||||
else
|
||||
{
|
||||
_bau.propertyValueWrite((ObjectType)objectType, objectInstance, propertyId, numberOfElements, startIndex, requestData, requestDataSize);
|
||||
}
|
||||
|
||||
if (numberOfElements)
|
||||
{
|
||||
// Prepare positive response
|
||||
uint8_t responseData[7];
|
||||
memcpy(responseData, frame.data(), sizeof(responseData));
|
||||
|
||||
println(" <- no error");
|
||||
|
||||
CemiFrame responseFrame(responseData, sizeof(responseData));
|
||||
responseFrame.messageCode(M_PropWrite_con);
|
||||
_usbTunnelInterface.sendCemiFrame(responseFrame);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prepare negative response
|
||||
uint8_t responseData[7 + 1];
|
||||
memcpy(responseData, frame.data(), sizeof(responseData));
|
||||
responseData[7] = Illegal_Command; // Set cEMI error code
|
||||
responseData[5] = 0; // Set Number of elements to zero
|
||||
|
||||
printHex(" <- error: ", &responseData[7], 1);
|
||||
println("");
|
||||
|
||||
CemiFrame responseFrame(responseData, sizeof(responseData));
|
||||
responseFrame.messageCode(M_PropWrite_con);
|
||||
_usbTunnelInterface.sendCemiFrame(responseFrame);
|
||||
}
|
||||
handleMPropWrite(frame);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -320,16 +155,7 @@ void CemiServer::frameReceived(CemiFrame& frame)
|
||||
|
||||
case M_Reset_req:
|
||||
{
|
||||
println("M_Reset_req: sending M_Reset_ind");
|
||||
// A real device reset does not work for USB or KNXNET/IP.
|
||||
// Thus, M_Reset_ind is NOT mandatory for USB and KNXNET/IP.
|
||||
// We just save all data to the EEPROM
|
||||
_bau.writeMemory();
|
||||
// Prepare response
|
||||
uint8_t responseData[1];
|
||||
CemiFrame responseFrame(responseData, sizeof(responseData));
|
||||
responseFrame.messageCode(M_Reset_ind);
|
||||
_usbTunnelInterface.sendCemiFrame(responseFrame);
|
||||
handleMReset(frame);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -347,9 +173,263 @@ void CemiServer::frameReceived(CemiFrame& frame)
|
||||
}
|
||||
}
|
||||
|
||||
void CemiServer::handleLData(CemiFrame& frame)
|
||||
{
|
||||
// Fill in the cEMI client address if the client sets
|
||||
// source address to 0.
|
||||
#ifndef KNX_TUNNELING
|
||||
//We already set the correct IA
|
||||
if(frame.sourceAddress() == 0x0000)
|
||||
{
|
||||
frame.sourceAddress(_clientAddress);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_RF
|
||||
if (_dataLinkLayer->mediumType() == DptMedium::KNX_RF)
|
||||
{
|
||||
// Check if we have additional info for RF
|
||||
if (((frame.data())[1] == 0x0A) && // Additional info total length: we only handle one additional info of type RF
|
||||
((frame.data())[2] == 0x02) && // Additional info type: RF
|
||||
((frame.data())[3] == 0x08) ) // Additional info length of type RF: 8 bytes (fixed)
|
||||
{
|
||||
frame.rfInfo((frame.data())[4]);
|
||||
// Use the values provided in the RF additonal info
|
||||
if ( ((frame.data())[5] != 0x00) || ((frame.data())[6] != 0x00) || ((frame.data())[7] != 0x00) ||
|
||||
((frame.data())[8] != 0x00) || ((frame.data())[9] != 0x00) || ((frame.data())[10] != 0x00) )
|
||||
{
|
||||
frame.rfSerialOrDoA(&((frame.data())[5]));
|
||||
} // else leave the nullptr as it is
|
||||
frame.rfLfn((frame.data())[11]);
|
||||
}
|
||||
|
||||
// If the cEMI client does not provide a link layer frame number (LFN),
|
||||
// we use our own counter.
|
||||
// Note: There is another link layer frame number counter inside the RF data link layer class!
|
||||
// That counter is solely for the local application!
|
||||
// If we set a LFN here, the data link layer counter is NOT used!
|
||||
if (frame.rfLfn() == 0xFF)
|
||||
{
|
||||
// Set Data Link Layer Frame Number
|
||||
frame.rfLfn(_frameNumber);
|
||||
// Link Layer frame number counts 0..7
|
||||
_frameNumber = (_frameNumber + 1) & 0x7;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("L_data_req: src: ");
|
||||
print(frame.sourceAddress(), HEX);
|
||||
print(" dst: ");
|
||||
print(frame.destinationAddress(), HEX);
|
||||
printHex(" frame: ", frame.data(), frame.dataLength());
|
||||
#endif
|
||||
_dataLinkLayer->dataRequestFromTunnel(frame);
|
||||
}
|
||||
|
||||
void CemiServer::handleMPropRead(CemiFrame& frame)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("M_PropRead_req: ");
|
||||
#endif
|
||||
|
||||
uint16_t objectType;
|
||||
popWord(objectType, &frame.data()[1]);
|
||||
uint8_t objectInstance = frame.data()[3];
|
||||
uint8_t propertyId = frame.data()[4];
|
||||
uint8_t numberOfElements = frame.data()[5] >> 4;
|
||||
uint16_t startIndex = frame.data()[6] | ((frame.data()[5]&0x0F)<<8);
|
||||
uint8_t* data = nullptr;
|
||||
uint32_t dataSize = 0;
|
||||
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("ObjType: ");
|
||||
print(objectType, DEC);
|
||||
print(" ObjInst: ");
|
||||
print(objectInstance, DEC);
|
||||
print(" PropId: ");
|
||||
print(propertyId, DEC);
|
||||
print(" NoE: ");
|
||||
print(numberOfElements, DEC);
|
||||
print(" startIdx: ");
|
||||
print(startIndex, DEC);
|
||||
#endif
|
||||
|
||||
// propertyValueRead() allocates memory for the data! Needs to be deleted again!
|
||||
_bau.propertyValueRead((ObjectType)objectType, objectInstance, propertyId, numberOfElements, startIndex, &data, dataSize);
|
||||
|
||||
// Patch result for device address in device object
|
||||
// The cEMI server will hand out the device address + 1 to the cEMI client (e.g. ETS),
|
||||
// so that the device and the cEMI client/server connection(tunnel) can operate simultaneously.
|
||||
// KNX IP Interfaces which offer multiple simultaneous tunnel connections seem to operate the same way.
|
||||
// Each tunnel has its own cEMI client address which is based on the main device address.
|
||||
if (((ObjectType) objectType == OT_DEVICE) &&
|
||||
(propertyId == PID_DEVICE_ADDR) &&
|
||||
(numberOfElements == 1))
|
||||
{
|
||||
data[0] = (uint8_t) (_clientAddress & 0xFF);
|
||||
}
|
||||
else if (((ObjectType) objectType == OT_DEVICE) &&
|
||||
(propertyId == PID_SUBNET_ADDR) &&
|
||||
(numberOfElements == 1))
|
||||
{
|
||||
data[0] = (uint8_t) ((_clientAddress >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
if (data && dataSize && numberOfElements)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
printHex(" <- data: ", data, dataSize);
|
||||
#endif
|
||||
|
||||
// Prepare positive response
|
||||
uint8_t responseData[7 + dataSize];
|
||||
memcpy(responseData, frame.data(), 7);
|
||||
memcpy(&responseData[7], data, dataSize);
|
||||
|
||||
CemiFrame responseFrame(responseData, sizeof(responseData));
|
||||
responseFrame.messageCode(M_PropRead_con);
|
||||
#ifdef USE_USB
|
||||
_usbTunnelInterface.sendCemiFrame(responseFrame);
|
||||
#elif defined(KNX_TUNNELING)
|
||||
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
|
||||
#endif
|
||||
delete[] data;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prepare negative response
|
||||
uint8_t responseData[7 + 1];
|
||||
memcpy(responseData, frame.data(), sizeof(responseData));
|
||||
responseData[7] = Void_DP; // Set cEMI error code
|
||||
responseData[5] = 0; // Set Number of elements to zero
|
||||
|
||||
printHex(" <- error: ", &responseData[7], 1);
|
||||
println("");
|
||||
|
||||
CemiFrame responseFrame(responseData, sizeof(responseData));
|
||||
responseFrame.messageCode(M_PropRead_con);
|
||||
#ifdef USE_USB
|
||||
_usbTunnelInterface.sendCemiFrame(responseFrame);
|
||||
#elif defined(KNX_TUNNELING)
|
||||
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void CemiServer::handleMPropWrite(CemiFrame& frame)
|
||||
{
|
||||
print("M_PropWrite_req: ");
|
||||
|
||||
uint16_t objectType;
|
||||
popWord(objectType, &frame.data()[1]);
|
||||
uint8_t objectInstance = frame.data()[3];
|
||||
uint8_t propertyId = frame.data()[4];
|
||||
uint8_t numberOfElements = frame.data()[5] >> 4;
|
||||
uint16_t startIndex = frame.data()[6] | ((frame.data()[5]&0x0F)<<8);
|
||||
uint8_t* requestData = &frame.data()[7];
|
||||
uint32_t requestDataSize = frame.dataLength() - 7;
|
||||
|
||||
print("ObjType: ");
|
||||
print(objectType, DEC);
|
||||
print(" ObjInst: ");
|
||||
print(objectInstance, DEC);
|
||||
print(" PropId: ");
|
||||
print(propertyId, DEC);
|
||||
print(" NoE: ");
|
||||
print(numberOfElements, DEC);
|
||||
print(" startIdx: ");
|
||||
print(startIndex, DEC);
|
||||
|
||||
printHex(" -> data: ", requestData, requestDataSize);
|
||||
|
||||
// Patch request for device address in device object
|
||||
if (((ObjectType) objectType == OT_DEVICE) &&
|
||||
(propertyId == PID_DEVICE_ADDR) &&
|
||||
(numberOfElements == 1))
|
||||
{
|
||||
// Temporarily store new cEMI client address in memory
|
||||
// We also be sent back if the client requests it again
|
||||
_clientAddress = (_clientAddress & 0xFF00) | requestData[0];
|
||||
print("cEMI client address: ");
|
||||
println(_clientAddress, HEX);
|
||||
}
|
||||
else if (((ObjectType) objectType == OT_DEVICE) &&
|
||||
(propertyId == PID_SUBNET_ADDR) &&
|
||||
(numberOfElements == 1))
|
||||
{
|
||||
// Temporarily store new cEMI client address in memory
|
||||
// We also be sent back if the client requests it again
|
||||
_clientAddress = (_clientAddress & 0x00FF) | (requestData[0] << 8);
|
||||
print("cEMI client address: ");
|
||||
println(_clientAddress, HEX);
|
||||
}
|
||||
else
|
||||
{
|
||||
_bau.propertyValueWrite((ObjectType)objectType, objectInstance, propertyId, numberOfElements, startIndex, requestData, requestDataSize);
|
||||
}
|
||||
|
||||
if (numberOfElements)
|
||||
{
|
||||
// Prepare positive response
|
||||
uint8_t responseData[7];
|
||||
memcpy(responseData, frame.data(), sizeof(responseData));
|
||||
|
||||
println(" <- no error");
|
||||
|
||||
CemiFrame responseFrame(responseData, sizeof(responseData));
|
||||
responseFrame.messageCode(M_PropWrite_con);
|
||||
#ifdef USE_USB
|
||||
_usbTunnelInterface.sendCemiFrame(responseFrame);
|
||||
#elif defined(KNX_TUNNELING)
|
||||
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prepare negative response
|
||||
uint8_t responseData[7 + 1];
|
||||
memcpy(responseData, frame.data(), sizeof(responseData));
|
||||
responseData[7] = Illegal_Command; // Set cEMI error code
|
||||
responseData[5] = 0; // Set Number of elements to zero
|
||||
|
||||
printHex(" <- error: ", &responseData[7], 1);
|
||||
println("");
|
||||
|
||||
CemiFrame responseFrame(responseData, sizeof(responseData));
|
||||
responseFrame.messageCode(M_PropWrite_con);
|
||||
#ifdef USE_USB
|
||||
_usbTunnelInterface.sendCemiFrame(responseFrame);
|
||||
#elif defined(KNX_TUNNELING)
|
||||
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void CemiServer::handleMReset(CemiFrame& frame)
|
||||
{
|
||||
println("M_Reset_req: sending M_Reset_ind");
|
||||
// A real device reset does not work for USB or KNXNET/IP.
|
||||
// Thus, M_Reset_ind is NOT mandatory for USB and KNXNET/IP.
|
||||
// We just save all data to the EEPROM
|
||||
_bau.writeMemory();
|
||||
// Prepare response
|
||||
uint8_t responseData[1];
|
||||
CemiFrame responseFrame(responseData, sizeof(responseData));
|
||||
responseFrame.messageCode(M_Reset_ind);
|
||||
#ifdef USE_USB
|
||||
_usbTunnelInterface.sendCemiFrame(responseFrame);
|
||||
#elif defined(KNX_TUNNELING)
|
||||
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CemiServer::loop()
|
||||
{
|
||||
#ifdef USE_USB
|
||||
_usbTunnelInterface.loop();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -29,6 +29,9 @@ class CemiServer
|
||||
CemiServer(BauSystemB& bau);
|
||||
|
||||
void dataLinkLayer(DataLinkLayer& layer);
|
||||
#ifdef KNX_TUNNELING
|
||||
void dataLinkLayerPrimary(DataLinkLayer& layer);
|
||||
#endif
|
||||
|
||||
// from data link layer
|
||||
// Only L_Data service
|
||||
@ -47,9 +50,19 @@ class CemiServer
|
||||
uint16_t _clientAddress = 0;
|
||||
uint8_t _frameNumber = 0;
|
||||
|
||||
void handleLData(CemiFrame& frame);
|
||||
void handleMPropRead(CemiFrame& frame);
|
||||
void handleMPropWrite(CemiFrame& frame);
|
||||
void handleMReset(CemiFrame& frame);
|
||||
|
||||
DataLinkLayer* _dataLinkLayer = nullptr;
|
||||
#ifdef KNX_TUNNELING
|
||||
DataLinkLayer* _dataLinkLayerPrimary = nullptr;
|
||||
#endif
|
||||
BauSystemB& _bau;
|
||||
#ifdef USE_USB
|
||||
UsbTunnelInterface _usbTunnelInterface;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
@ -52,7 +52,7 @@
|
||||
// cEMI options
|
||||
//#define USE_USB
|
||||
//#define USE_CEMI_SERVER
|
||||
#ifdef USE_USB
|
||||
#if defined(USE_USB) || defined(KNX_TUNNELING)
|
||||
#define USE_CEMI_SERVER
|
||||
#endif
|
||||
|
||||
|
@ -18,8 +18,8 @@ void DataLinkLayerCallbacks::setActivityCallback(ActivityCallback activityCallba
|
||||
_activityCallback = activityCallback;
|
||||
}
|
||||
|
||||
DataLinkLayer::DataLinkLayer(DeviceObject& devObj, NetworkLayerEntity& netLayerEntity, Platform& platform) :
|
||||
_deviceObject(devObj), _networkLayerEntity(netLayerEntity), _platform(platform)
|
||||
DataLinkLayer::DataLinkLayer(DeviceObject& devObj, NetworkLayerEntity& netLayerEntity, Platform& platform, BusAccessUnit& busAccessUnit) :
|
||||
_deviceObject(devObj), _networkLayerEntity(netLayerEntity), _platform(platform), _bau(busAccessUnit)
|
||||
{
|
||||
#ifdef KNX_ACTIVITYCALLBACK
|
||||
_netIndex = netLayerEntity.getEntityIndex();
|
||||
@ -33,15 +33,59 @@ void DataLinkLayer::cemiServer(CemiServer& cemiServer)
|
||||
_cemiServer = &cemiServer;
|
||||
}
|
||||
|
||||
#ifdef KNX_TUNNELING
|
||||
void DataLinkLayer::dataRequestToTunnel(CemiFrame& frame)
|
||||
{
|
||||
println("default dataRequestToTunnel");
|
||||
}
|
||||
|
||||
void DataLinkLayer::dataConfirmationToTunnel(CemiFrame& frame)
|
||||
{
|
||||
println("default dataConfirmationToTunnel");
|
||||
}
|
||||
|
||||
void DataLinkLayer::dataIndicationToTunnel(CemiFrame& frame)
|
||||
{
|
||||
println("default dataIndicationToTunnel");
|
||||
}
|
||||
|
||||
bool DataLinkLayer::isTunnelAddress(uint16_t addr)
|
||||
{
|
||||
println("default IsTunnelAddress");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void DataLinkLayer::dataRequestFromTunnel(CemiFrame& frame)
|
||||
{
|
||||
_cemiServer->dataConfirmationToTunnel(frame);
|
||||
|
||||
frame.messageCode(L_data_ind);
|
||||
|
||||
// Send to local stack
|
||||
// Send to local stack ( => cemiServer for potential other tunnel and network layer for routing)
|
||||
frameReceived(frame);
|
||||
|
||||
#ifdef KNX_TUNNELING
|
||||
// TunnelOpti
|
||||
// Optimize performance when receiving unicast data over tunnel wich is not meant to be used on the physical TP line
|
||||
// dont send to knx when
|
||||
// frame is individual adressed AND
|
||||
// destionation == PA of Tunnel-Server OR
|
||||
// destination == PA of a Tunnel OR (TODO)
|
||||
// destination is not the TP/secondary line/segment but IP/primary (TODO)
|
||||
|
||||
if(frame.addressType() == AddressType::IndividualAddress)
|
||||
{
|
||||
if(frame.destinationAddress() == _deviceObject.individualAddress())
|
||||
return;
|
||||
if(isRoutedPA(frame.destinationAddress()))
|
||||
return;
|
||||
if(isTunnelingPA(frame.destinationAddress()))
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Send to KNX medium
|
||||
sendFrame(frame);
|
||||
}
|
||||
@ -111,12 +155,24 @@ void DataLinkLayer::frameReceived(CemiFrame& frame)
|
||||
|
||||
#ifdef USE_CEMI_SERVER
|
||||
// Do not send our own message back to the tunnel
|
||||
#ifdef KNX_TUNNELING
|
||||
//we dont need to check it here
|
||||
// send inbound frames to the tunnel if we are the secondary (TP) interface
|
||||
if( _networkLayerEntity.getEntityIndex() == 1)
|
||||
_cemiServer->dataIndicationToTunnel(frame);
|
||||
#else
|
||||
if (frame.sourceAddress() != _cemiServer->clientAddress())
|
||||
{
|
||||
_cemiServer->dataIndicationToTunnel(frame);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// print("Frame received destination: ");
|
||||
// print(destination, 16);
|
||||
// println();
|
||||
// print("frameReceived: frame valid? :");
|
||||
// println(npdu.frame().valid() ? "true" : "false");
|
||||
if (source == ownAddr)
|
||||
_deviceObject.individualAddressDuplication(true);
|
||||
|
||||
@ -133,15 +189,17 @@ void DataLinkLayer::frameReceived(CemiFrame& frame)
|
||||
}
|
||||
}
|
||||
|
||||
bool DataLinkLayer::sendTelegram(NPDU & npdu, AckType ack, uint16_t destinationAddr, AddressType addrType, uint16_t sourceAddr, FrameFormat format, Priority priority, SystemBroadcast systemBroadcast)
|
||||
bool DataLinkLayer::sendTelegram(NPDU & npdu, AckType ack, uint16_t destinationAddr, AddressType addrType, uint16_t sourceAddr, FrameFormat format, Priority priority, SystemBroadcast systemBroadcast, bool doNotRepeat)
|
||||
{
|
||||
CemiFrame& frame = npdu.frame();
|
||||
// print("Send telegram frame valid ?: ");
|
||||
// println(frame.valid()?"true":"false");
|
||||
frame.messageCode(L_data_ind);
|
||||
frame.destinationAddress(destinationAddr);
|
||||
frame.sourceAddress(sourceAddr);
|
||||
frame.addressType(addrType);
|
||||
frame.priority(priority);
|
||||
frame.repetition(RepetitionAllowed);
|
||||
frame.repetition(doNotRepeat?NoRepitiion:RepetitionAllowed);
|
||||
frame.systemBroadcast(systemBroadcast);
|
||||
|
||||
if (npdu.octetCount() <= 15)
|
||||
@ -162,22 +220,45 @@ bool DataLinkLayer::sendTelegram(NPDU & npdu, AckType ack, uint16_t destinationA
|
||||
// frame.apdu().printPDU();
|
||||
// }
|
||||
|
||||
bool sendTheFrame = true;
|
||||
bool success = true;
|
||||
|
||||
#ifdef KNX_TUNNELING
|
||||
// TunnelOpti
|
||||
// Optimize performance when sending unicast data over tunnel wich is not meant to be used on the physical TP line
|
||||
// dont send to knx when
|
||||
// a) we are the secondary interface (e.g. TP) AND
|
||||
// b) destination == PA of a Tunnel (TODO)
|
||||
|
||||
if(_networkLayerEntity.getEntityIndex() == 1 && addrType == AddressType::IndividualAddress) // don't send to tp if we are the secondary (TP) interface AND the destination is a tunnel-PA
|
||||
{
|
||||
if(isTunnelingPA(destinationAddr))
|
||||
sendTheFrame = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// The data link layer might be an open media link layer
|
||||
// and will setup rfSerialOrDoA, rfInfo and rfLfn that we also
|
||||
// have to send through the cEMI server tunnel
|
||||
// Thus, reuse the modified cEMI frame as "frame" is only passed by reference here!
|
||||
bool success = sendFrame(frame);
|
||||
if(sendTheFrame)
|
||||
success = sendFrame(frame);
|
||||
|
||||
#ifdef USE_CEMI_SERVER
|
||||
CemiFrame tmpFrame(frame.data(), frame.totalLenght());
|
||||
// We can just copy the pointer for rfSerialOrDoA as sendFrame() sets
|
||||
// a pointer to const uint8_t data in either device object (serial) or
|
||||
// RF medium object (domain address)
|
||||
|
||||
#ifdef USE_RF
|
||||
tmpFrame.rfSerialOrDoA(frame.rfSerialOrDoA());
|
||||
tmpFrame.rfInfo(frame.rfInfo());
|
||||
tmpFrame.rfLfn(frame.rfLfn());
|
||||
#endif
|
||||
tmpFrame.confirm(ConfirmNoError);
|
||||
_cemiServer->dataIndicationToTunnel(tmpFrame);
|
||||
|
||||
if(_networkLayerEntity.getEntityIndex() == 1) // only send to tunnel if we are the secondary (TP) interface
|
||||
_cemiServer->dataIndicationToTunnel(tmpFrame);
|
||||
#endif
|
||||
|
||||
return success;
|
||||
@ -188,4 +269,48 @@ uint8_t* DataLinkLayer::frameData(CemiFrame& frame)
|
||||
return frame._data;
|
||||
}
|
||||
|
||||
#ifdef KNX_TUNNELING
|
||||
bool DataLinkLayer::isTunnelingPA(uint16_t pa)
|
||||
{
|
||||
uint8_t num = KNX_TUNNELING;
|
||||
uint32_t len = 0;
|
||||
uint8_t* data = nullptr;
|
||||
_bau.propertyValueRead(OT_IP_PARAMETER, 0, PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, num, 1, &data, len);
|
||||
//printHex("isTunnelingPA, PID_ADDITIONAL_INDIVIDUAL_ADDRESSES: ", *data, len);
|
||||
if(len != KNX_TUNNELING * 2)
|
||||
{
|
||||
println("Tunnel PAs unkwnown");
|
||||
if(data != nullptr)
|
||||
delete[] data;
|
||||
return false;
|
||||
}
|
||||
for(uint8_t i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
uint16_t tunnelpa;
|
||||
popWord(tunnelpa, (data)+i*2);
|
||||
if(pa == tunnelpa)
|
||||
{
|
||||
if(data != nullptr)
|
||||
delete[] data;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(data != nullptr)
|
||||
delete[] data;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DataLinkLayer::isRoutedPA(uint16_t pa)
|
||||
{
|
||||
uint16_t ownpa = _deviceObject.individualAddress();
|
||||
uint16_t own_sm;
|
||||
|
||||
if ((ownpa & 0x0F00) == 0x0)
|
||||
own_sm = 0xF000;
|
||||
else
|
||||
own_sm = 0xFF00;
|
||||
|
||||
return (pa & own_sm) != ownpa;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "knx_types.h"
|
||||
#include "network_layer_entity.h"
|
||||
#include "cemi_server.h"
|
||||
#include "bau.h"
|
||||
|
||||
class Platform;
|
||||
|
||||
@ -26,12 +27,18 @@ class DataLinkLayer
|
||||
{
|
||||
public:
|
||||
DataLinkLayer(DeviceObject& devObj, NetworkLayerEntity& netLayerEntity,
|
||||
Platform& platform);
|
||||
Platform& platform, BusAccessUnit& busAccessUnit);
|
||||
|
||||
#ifdef USE_CEMI_SERVER
|
||||
// from tunnel
|
||||
void cemiServer(CemiServer& cemiServer);
|
||||
void dataRequestFromTunnel(CemiFrame& frame);
|
||||
#ifdef KNX_TUNNELING
|
||||
virtual void dataRequestToTunnel(CemiFrame& frame);
|
||||
virtual void dataConfirmationToTunnel(CemiFrame& frame);
|
||||
virtual void dataIndicationToTunnel(CemiFrame& frame);
|
||||
virtual bool isTunnelAddress(uint16_t addr);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// from network layer
|
||||
@ -46,16 +53,21 @@ class DataLinkLayer
|
||||
protected:
|
||||
void frameReceived(CemiFrame& frame);
|
||||
void dataConReceived(CemiFrame& frame, bool success);
|
||||
bool sendTelegram(NPDU& npdu, AckType ack, uint16_t destinationAddr, AddressType addrType, uint16_t sourceAddr, FrameFormat format, Priority priority, SystemBroadcast systemBroadcast);
|
||||
bool sendTelegram(NPDU& npdu, AckType ack, uint16_t destinationAddr, AddressType addrType, uint16_t sourceAddr, FrameFormat format, Priority priority, SystemBroadcast systemBroadcast, bool doNotRepeat = false);
|
||||
virtual bool sendFrame(CemiFrame& frame) = 0;
|
||||
uint8_t* frameData(CemiFrame& frame);
|
||||
DeviceObject& _deviceObject;
|
||||
NetworkLayerEntity& _networkLayerEntity;
|
||||
Platform& _platform;
|
||||
BusAccessUnit& _bau;
|
||||
#ifdef USE_CEMI_SERVER
|
||||
CemiServer* _cemiServer;
|
||||
#endif
|
||||
#ifdef KNX_ACTIVITYCALLBACK
|
||||
uint8_t _netIndex = 0;
|
||||
#endif
|
||||
#ifdef KNX_TUNNELING
|
||||
bool isTunnelingPA(uint16_t pa);
|
||||
bool isRoutedPA(uint16_t pa);
|
||||
#endif
|
||||
};
|
||||
|
@ -43,5 +43,9 @@ public:
|
||||
uint8_t defaultHopCount();
|
||||
private:
|
||||
uint8_t _prgMode = 0;
|
||||
uint16_t _ownAddress = 65535; // 15.15.255;
|
||||
#if MASK_VERSION == 0x091A || MASK_VERSION == 0x2920
|
||||
uint16_t _ownAddress = 0xFF00; // 15.15.0; couplers have 15.15.0 as default PA
|
||||
#else
|
||||
uint16_t _ownAddress = 0xFFFF; // 15.15.255;
|
||||
#endif
|
||||
};
|
||||
|
@ -60,6 +60,20 @@ void InterfaceObject::masterReset(EraseCode eraseCode, uint8_t channel)
|
||||
// However, for the time being we provide an empty default implementation
|
||||
}
|
||||
|
||||
void InterfaceObject::readPropertyLength(PropertyID id, uint16_t &length)
|
||||
{
|
||||
uint8_t count = 1;
|
||||
uint16_t propval = 0;
|
||||
readProperty(id, 0, count, (uint8_t*)&propval);
|
||||
|
||||
if(count == 0)
|
||||
{
|
||||
length = 0;
|
||||
return;
|
||||
}
|
||||
length = ntohs(propval);
|
||||
}
|
||||
|
||||
void InterfaceObject::readProperty(PropertyID id, uint16_t start, uint8_t& count, uint8_t* data)
|
||||
{
|
||||
Property* prop = property(id);
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "property.h"
|
||||
#include "save_restore.h"
|
||||
#include "knx_types.h"
|
||||
#include "bits.h"
|
||||
|
||||
/** Enum for the type of an interface object. See Section 2.2 of knx:3/7/3 */
|
||||
enum ObjectType
|
||||
@ -54,7 +55,10 @@ enum ObjectType
|
||||
OT_SECURITY = 17,
|
||||
|
||||
/** RF Medium Object */
|
||||
OT_RF_MEDIUM = 19
|
||||
OT_RF_MEDIUM = 19,
|
||||
|
||||
/** Dummy so this enum is 16bit */
|
||||
OT_DUMMY = 0xFFFF
|
||||
};
|
||||
|
||||
/**
|
||||
@ -67,6 +71,14 @@ class InterfaceObject : public SaveRestore
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~InterfaceObject();
|
||||
/**
|
||||
* Read length of a property of the interface object. See section 4.8.4.2 of @cite knx:3/4/1.
|
||||
*
|
||||
* @param id id of the property to read
|
||||
*
|
||||
* @param[out] length length of the requested property
|
||||
*/
|
||||
virtual void readPropertyLength(PropertyID id, uint16_t &length);
|
||||
/**
|
||||
* Read a property of the interface object. See section 4.8.4.2 of @cite knx:3/4/1.
|
||||
*
|
||||
|
@ -9,6 +9,22 @@
|
||||
#include "knx_ip_routing_indication.h"
|
||||
#include "knx_ip_search_request.h"
|
||||
#include "knx_ip_search_response.h"
|
||||
#include "knx_ip_search_request_extended.h"
|
||||
#include "knx_ip_search_response_extended.h"
|
||||
#include "knx_facade.h"
|
||||
#ifdef KNX_TUNNELING
|
||||
#include "knx_ip_connect_request.h"
|
||||
#include "knx_ip_connect_response.h"
|
||||
#include "knx_ip_state_request.h"
|
||||
#include "knx_ip_state_response.h"
|
||||
#include "knx_ip_disconnect_request.h"
|
||||
#include "knx_ip_disconnect_response.h"
|
||||
#include "knx_ip_tunneling_request.h"
|
||||
#include "knx_ip_tunneling_ack.h"
|
||||
#include "knx_ip_description_request.h"
|
||||
#include "knx_ip_description_response.h"
|
||||
#include "knx_ip_config_request.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@ -19,7 +35,7 @@
|
||||
#define MIN_LEN_CEMI 10
|
||||
|
||||
IpDataLinkLayer::IpDataLinkLayer(DeviceObject& devObj, IpParameterObject& ipParam,
|
||||
NetworkLayerEntity &netLayerEntity, Platform& platform, DataLinkLayerCallbacks* dllcb) : DataLinkLayer(devObj, netLayerEntity, platform), _ipParameters(ipParam), _dllcb(dllcb)
|
||||
NetworkLayerEntity &netLayerEntity, Platform& platform, BusAccessUnit& busAccessUnit, DataLinkLayerCallbacks* dllcb) : DataLinkLayer(devObj, netLayerEntity, platform, busAccessUnit), _ipParameters(ipParam), _dllcb(dllcb)
|
||||
{
|
||||
}
|
||||
|
||||
@ -38,13 +54,241 @@ bool IpDataLinkLayer::sendFrame(CemiFrame& frame)
|
||||
return success;
|
||||
}
|
||||
|
||||
#ifdef KNX_TUNNELING
|
||||
void IpDataLinkLayer::dataRequestToTunnel(CemiFrame& frame)
|
||||
{
|
||||
if(frame.addressType() == AddressType::GroupAddress)
|
||||
{
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
if(tunnels[i].ChannelId != 0 && tunnels[i].IndividualAddress == frame.sourceAddress())
|
||||
sendFrameToTunnel(&tunnels[i], frame);
|
||||
//TODO check if source is from tunnel
|
||||
return;
|
||||
}
|
||||
|
||||
KnxIpTunnelConnection *tun = nullptr;
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
if(tunnels[i].IndividualAddress == frame.sourceAddress())
|
||||
continue;
|
||||
|
||||
if(tunnels[i].IndividualAddress == frame.destinationAddress())
|
||||
{
|
||||
tun = &tunnels[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(tun == nullptr)
|
||||
{
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
if(tunnels[i].IsConfig)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
println("Found config Channel");
|
||||
#endif
|
||||
tun = &tunnels[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(tun == nullptr)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("Found no Tunnel for IA: ");
|
||||
println(frame.destinationAddress(), 16);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
sendFrameToTunnel(tun, frame);
|
||||
}
|
||||
|
||||
void IpDataLinkLayer::dataConfirmationToTunnel(CemiFrame& frame)
|
||||
{
|
||||
if(frame.addressType() == AddressType::GroupAddress)
|
||||
{
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
if(tunnels[i].ChannelId != 0 && tunnels[i].IndividualAddress == frame.sourceAddress())
|
||||
sendFrameToTunnel(&tunnels[i], frame);
|
||||
//TODO check if source is from tunnel
|
||||
return;
|
||||
}
|
||||
|
||||
KnxIpTunnelConnection *tun = nullptr;
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
if(tunnels[i].IndividualAddress == frame.destinationAddress())
|
||||
continue;
|
||||
|
||||
if(tunnels[i].IndividualAddress == frame.sourceAddress())
|
||||
{
|
||||
tun = &tunnels[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(tun == nullptr)
|
||||
{
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
if(tunnels[i].IsConfig)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
println("Found config Channel");
|
||||
#endif
|
||||
tun = &tunnels[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(tun == nullptr)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("Found no Tunnel for IA: ");
|
||||
println(frame.destinationAddress(), 16);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
sendFrameToTunnel(tun, frame);
|
||||
}
|
||||
|
||||
void IpDataLinkLayer::dataIndicationToTunnel(CemiFrame& frame)
|
||||
{
|
||||
if(frame.addressType() == AddressType::GroupAddress)
|
||||
{
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
if(tunnels[i].ChannelId != 0 && tunnels[i].IndividualAddress != frame.sourceAddress())
|
||||
sendFrameToTunnel(&tunnels[i], frame);
|
||||
return;
|
||||
}
|
||||
|
||||
KnxIpTunnelConnection *tun = nullptr;
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
if(tunnels[i].ChannelId == 0 || tunnels[i].IndividualAddress == frame.sourceAddress())
|
||||
continue;
|
||||
|
||||
if(tunnels[i].IndividualAddress == frame.destinationAddress())
|
||||
{
|
||||
tun = &tunnels[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(tun == nullptr)
|
||||
{
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
if(tunnels[i].IsConfig)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
println("Found config Channel");
|
||||
#endif
|
||||
tun = &tunnels[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(tun == nullptr)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("Found no Tunnel for IA: ");
|
||||
println(frame.destinationAddress(), 16);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
sendFrameToTunnel(tun, frame);
|
||||
}
|
||||
|
||||
void IpDataLinkLayer::sendFrameToTunnel(KnxIpTunnelConnection *tunnel, CemiFrame& frame)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("Send to Channel: ");
|
||||
println(tunnel->ChannelId, 16);
|
||||
#endif
|
||||
KnxIpTunnelingRequest req(frame);
|
||||
req.connectionHeader().sequenceCounter(tunnel->SequenceCounter_S++);
|
||||
req.connectionHeader().length(LEN_CH);
|
||||
req.connectionHeader().channelId(tunnel->ChannelId);
|
||||
|
||||
if(frame.messageCode() != L_data_req && frame.messageCode() != L_data_con && frame.messageCode() != L_data_ind)
|
||||
req.serviceTypeIdentifier(DeviceConfigurationRequest);
|
||||
|
||||
_platform.sendBytesUniCast(tunnel->IpAddress, tunnel->PortData, req.data(), req.totalLength());
|
||||
}
|
||||
|
||||
bool IpDataLinkLayer::isTunnelAddress(uint16_t addr)
|
||||
{
|
||||
if(addr == 0)
|
||||
return false; // 0.0.0 is not a valid tunnel address and is used as default value
|
||||
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
if(tunnels[i].IndividualAddress == addr)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IpDataLinkLayer::isSentToTunnel(uint16_t address, bool isGrpAddr)
|
||||
{
|
||||
if(isGrpAddr)
|
||||
{
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
if(tunnels[i].ChannelId != 0)
|
||||
return true;
|
||||
return false;
|
||||
} else {
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
if(tunnels[i].ChannelId != 0 && tunnels[i].IndividualAddress == address)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void IpDataLinkLayer::loop()
|
||||
{
|
||||
if (!_enabled)
|
||||
return;
|
||||
|
||||
#ifdef KNX_TUNNELING
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
if(tunnels[i].ChannelId != 0)
|
||||
{
|
||||
if(millis() - tunnels[i].lastHeartbeat > 120000)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("Closed Tunnel 0x");
|
||||
print(tunnels[i].ChannelId, 16);
|
||||
println(" due to no heartbeat in 2 minutes");
|
||||
#endif
|
||||
KnxIpDisconnectRequest discReq;
|
||||
discReq.channelId(tunnels[i].ChannelId);
|
||||
discReq.hpaiCtrl().length(LEN_IPHPAI);
|
||||
discReq.hpaiCtrl().code(IPV4_UDP);
|
||||
discReq.hpaiCtrl().ipAddress(tunnels[i].IpAddress);
|
||||
discReq.hpaiCtrl().ipPortNumber(tunnels[i].PortCtrl);
|
||||
_platform.sendBytesUniCast(tunnels[i].IpAddress, tunnels[i].PortCtrl, discReq.data(), discReq.totalLength());
|
||||
tunnels[i].Reset();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
uint8_t buffer[512];
|
||||
int len = _platform.readBytesMultiCast(buffer, 512);
|
||||
uint16_t remotePort = 0;
|
||||
uint32_t remoteAddr = 0;
|
||||
int len = _platform.readBytesMultiCast(buffer, 512, remoteAddr, remotePort);
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
@ -70,6 +314,7 @@ void IpDataLinkLayer::loop()
|
||||
frameReceived(routingIndication.frame());
|
||||
break;
|
||||
}
|
||||
|
||||
case SearchRequest:
|
||||
{
|
||||
KnxIpSearchRequest searchRequest(buffer, len);
|
||||
@ -85,17 +330,698 @@ void IpDataLinkLayer::loop()
|
||||
}
|
||||
case SearchRequestExt:
|
||||
{
|
||||
// FIXME, implement (not needed atm)
|
||||
#if KNX_SERVICE_FAMILY_CORE >= 2
|
||||
loopHandleSearchRequestExtended(buffer, len);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
#ifdef KNX_TUNNELING
|
||||
case ConnectRequest:
|
||||
{
|
||||
// print("Unhandled service identifier: ");
|
||||
// println(code, HEX);
|
||||
loopHandleConnectRequest(buffer, len, remoteAddr, remotePort);
|
||||
break;
|
||||
}
|
||||
|
||||
case ConnectionStateRequest:
|
||||
{
|
||||
loopHandleConnectionStateRequest(buffer, len);
|
||||
break;
|
||||
}
|
||||
|
||||
case DisconnectRequest:
|
||||
{
|
||||
loopHandleDisconnectRequest(buffer, len);
|
||||
break;
|
||||
}
|
||||
|
||||
case DescriptionRequest:
|
||||
{
|
||||
loopHandleDescriptionRequest(buffer, len);
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceConfigurationRequest:
|
||||
{
|
||||
loopHandleDeviceConfigurationRequest(buffer, len);
|
||||
break;
|
||||
}
|
||||
|
||||
case TunnelingRequest:
|
||||
{
|
||||
loopHandleTunnelingRequest(buffer, len);
|
||||
return;
|
||||
}
|
||||
|
||||
case DeviceConfigurationAck:
|
||||
{
|
||||
//TOOD nothing to do now
|
||||
//println("got Ack");
|
||||
break;
|
||||
}
|
||||
|
||||
case TunnelingAck:
|
||||
{
|
||||
//TOOD nothing to do now
|
||||
//println("got Ack");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
print("Unhandled service identifier: ");
|
||||
println(code, HEX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if KNX_SERVICE_FAMILY_CORE >= 2
|
||||
void IpDataLinkLayer::loopHandleSearchRequestExtended(uint8_t* buffer, uint16_t length)
|
||||
{
|
||||
KnxIpSearchRequestExtended searchRequest(buffer, length);
|
||||
|
||||
if(searchRequest.srpByProgMode)
|
||||
{
|
||||
println("srpByProgMode");
|
||||
if(!knx.progMode()) return;
|
||||
}
|
||||
|
||||
if(searchRequest.srpByMacAddr)
|
||||
{
|
||||
println("srpByMacAddr");
|
||||
const uint8_t *x = _ipParameters.propertyData(PID_MAC_ADDRESS);
|
||||
for(int i = 0; i<6;i++)
|
||||
if(searchRequest.srpMacAddr[i] != x[i])
|
||||
return;
|
||||
}
|
||||
|
||||
#define LEN_SERVICE_FAMILIES 2
|
||||
#if MASK_VERSION == 0x091A
|
||||
#ifdef KNX_TUNNELING
|
||||
#define LEN_SERVICE_DIB (2 + 4 * LEN_SERVICE_FAMILIES)
|
||||
#else
|
||||
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
|
||||
#endif
|
||||
#else
|
||||
#ifdef KNX_TUNNELING
|
||||
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
|
||||
#else
|
||||
#define LEN_SERVICE_DIB (2 + 2 * LEN_SERVICE_FAMILIES)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//defaults: "Device Information DIB", "Extended Device Information DIB" and "Supported Services DIB".
|
||||
int dibLength = LEN_DEVICE_INFORMATION_DIB + LEN_SERVICE_DIB + LEN_EXTENDED_DEVICE_INFORMATION_DIB;
|
||||
|
||||
if(searchRequest.srpByService)
|
||||
{
|
||||
println("srpByService");
|
||||
uint8_t length = searchRequest.srpServiceFamilies[0];
|
||||
uint8_t *currentPos = searchRequest.srpServiceFamilies + 2;
|
||||
for(int i = 0; i < (length-2)/2; i++)
|
||||
{
|
||||
uint8_t serviceFamily = (currentPos + i*2)[0];
|
||||
uint8_t version = (currentPos + i*2)[1];
|
||||
switch(serviceFamily)
|
||||
{
|
||||
case Core:
|
||||
if(version > KNX_SERVICE_FAMILY_CORE) return;
|
||||
break;
|
||||
case DeviceManagement:
|
||||
if(version > KNX_SERVICE_FAMILY_DEVICE_MANAGEMENT) return;
|
||||
break;
|
||||
case Tunnelling:
|
||||
if(version > KNX_SERVICE_FAMILY_TUNNELING) return;
|
||||
break;
|
||||
case Routing:
|
||||
if(version > KNX_SERVICE_FAMILY_ROUTING) return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(searchRequest.srpRequestDIBs)
|
||||
{
|
||||
println("srpRequestDIBs");
|
||||
if(searchRequest.requestedDIB(IP_CONFIG))
|
||||
dibLength += LEN_IP_CONFIG_DIB; //16
|
||||
|
||||
if(searchRequest.requestedDIB(IP_CUR_CONFIG))
|
||||
dibLength += LEN_IP_CURRENT_CONFIG_DIB; //20
|
||||
|
||||
if(searchRequest.requestedDIB(KNX_ADDRESSES))
|
||||
{uint16_t length = 0;
|
||||
_ipParameters.readPropertyLength(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, length);
|
||||
dibLength += 4 + length*2;
|
||||
}
|
||||
|
||||
if(searchRequest.requestedDIB(MANUFACTURER_DATA))
|
||||
dibLength += 0; //4 + n
|
||||
|
||||
if(searchRequest.requestedDIB(TUNNELING_INFO))
|
||||
{
|
||||
uint16_t length = 0;
|
||||
_ipParameters.readPropertyLength(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, length);
|
||||
dibLength += 4 + length*4;
|
||||
}
|
||||
}
|
||||
|
||||
KnxIpSearchResponseExtended searchResponse(_ipParameters, _deviceObject, dibLength);
|
||||
|
||||
searchResponse.setDeviceInfo(_ipParameters, _deviceObject); //DescriptionTypeCode::DeviceInfo 1
|
||||
searchResponse.setSupportedServices(); //DescriptionTypeCode::SUPP_SVC_FAMILIES 2
|
||||
searchResponse.setExtendedDeviceInfo(); //DescriptionTypeCode::EXTENDED_DEVICE_INFO 8
|
||||
|
||||
if(searchRequest.srpRequestDIBs)
|
||||
{
|
||||
if(searchRequest.requestedDIB(IP_CONFIG))
|
||||
searchResponse.setIpConfig(_ipParameters);
|
||||
|
||||
if(searchRequest.requestedDIB(IP_CUR_CONFIG))
|
||||
searchResponse.setIpCurrentConfig(_ipParameters);
|
||||
|
||||
if(searchRequest.requestedDIB(KNX_ADDRESSES))
|
||||
searchResponse.setKnxAddresses(_ipParameters, _deviceObject);
|
||||
|
||||
if(searchRequest.requestedDIB(MANUFACTURER_DATA))
|
||||
{
|
||||
//println("requested MANUFACTURER_DATA but not implemented");
|
||||
}
|
||||
|
||||
if(searchRequest.requestedDIB(TUNNELING_INFO))
|
||||
searchResponse.setTunnelingInfo(_ipParameters, _deviceObject, tunnels);
|
||||
}
|
||||
|
||||
if(searchResponse.totalLength() > 150)
|
||||
{
|
||||
println("skipped response cause length is not plausible");
|
||||
return;
|
||||
}
|
||||
|
||||
_platform.sendBytesUniCast(searchRequest.hpai().ipAddress(), searchRequest.hpai().ipPortNumber(), searchResponse.data(), searchResponse.totalLength());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef KNX_TUNNELING
|
||||
void IpDataLinkLayer::loopHandleConnectRequest(uint8_t* buffer, uint16_t length, uint32_t& src_addr, uint16_t& src_port)
|
||||
{
|
||||
KnxIpConnectRequest connRequest(buffer, length);
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
println("Got Connect Request!");
|
||||
switch(connRequest.cri().type())
|
||||
{
|
||||
case DEVICE_MGMT_CONNECTION:
|
||||
println("Device Management Connection");
|
||||
break;
|
||||
case TUNNEL_CONNECTION:
|
||||
println("Tunnel Connection");
|
||||
break;
|
||||
case REMLOG_CONNECTION:
|
||||
println("RemLog Connection");
|
||||
break;
|
||||
case REMCONF_CONNECTION:
|
||||
println("RemConf Connection");
|
||||
break;
|
||||
case OBJSVR_CONNECTION:
|
||||
println("ObjectServer Connection");
|
||||
break;
|
||||
}
|
||||
|
||||
print("Data Endpoint: ");
|
||||
uint32_t ip = connRequest.hpaiData().ipAddress();
|
||||
print(ip >> 24);
|
||||
print(".");
|
||||
print((ip >> 16) & 0xFF);
|
||||
print(".");
|
||||
print((ip >> 8) & 0xFF);
|
||||
print(".");
|
||||
print(ip & 0xFF);
|
||||
print(":");
|
||||
println(connRequest.hpaiData().ipPortNumber());
|
||||
print("Ctrl Endpoint: ");
|
||||
ip = connRequest.hpaiCtrl().ipAddress();
|
||||
print(ip >> 24);
|
||||
print(".");
|
||||
print((ip >> 16) & 0xFF);
|
||||
print(".");
|
||||
print((ip >> 8) & 0xFF);
|
||||
print(".");
|
||||
print(ip & 0xFF);
|
||||
print(":");
|
||||
println(connRequest.hpaiCtrl().ipPortNumber());
|
||||
#endif
|
||||
|
||||
//We only support 0x03 and 0x04!
|
||||
if(connRequest.cri().type() != TUNNEL_CONNECTION && connRequest.cri().type() != DEVICE_MGMT_CONNECTION)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
println("Only Tunnel/DeviceMgmt Connection ist supported!");
|
||||
#endif
|
||||
KnxIpConnectResponse connRes(0x00, E_CONNECTION_TYPE);
|
||||
_platform.sendBytesUniCast(connRequest.hpaiCtrl().ipAddress(), connRequest.hpaiCtrl().ipPortNumber(), connRes.data(), connRes.totalLength());
|
||||
return;
|
||||
}
|
||||
|
||||
if(connRequest.cri().type() == TUNNEL_CONNECTION && connRequest.cri().layer() != 0x02) //LinkLayer
|
||||
{
|
||||
//We only support 0x02!
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
println("Only LinkLayer ist supported!");
|
||||
#endif
|
||||
KnxIpConnectResponse connRes(0x00, E_TUNNELING_LAYER);
|
||||
_platform.sendBytesUniCast(connRequest.hpaiCtrl().ipAddress(), connRequest.hpaiCtrl().ipPortNumber(), connRes.data(), connRes.totalLength());
|
||||
return;
|
||||
}
|
||||
|
||||
// data preparation
|
||||
|
||||
uint32_t srcIP = connRequest.hpaiCtrl().ipAddress()? connRequest.hpaiCtrl().ipAddress() : src_addr;
|
||||
uint16_t srcPort = connRequest.hpaiCtrl().ipPortNumber()? connRequest.hpaiCtrl().ipPortNumber() : src_port;
|
||||
|
||||
// read current elements in PID_ADDITIONAL_INDIVIDUAL_ADDRESSES
|
||||
uint16_t propCount = 0;
|
||||
_ipParameters.readPropertyLength(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, propCount);
|
||||
const uint8_t *addresses;
|
||||
if(propCount == KNX_TUNNELING)
|
||||
{
|
||||
addresses = _ipParameters.propertyData(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES);
|
||||
}
|
||||
else // no tunnel PA configured, that means device is unconfigured and has 15.15.0
|
||||
{
|
||||
uint8_t addrbuffer[KNX_TUNNELING*2];
|
||||
addresses = (uint8_t*)addrbuffer;
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
addrbuffer[i*2+1] = i+1;
|
||||
addrbuffer[i*2] = _deviceObject.individualAddress() / 0x0100;
|
||||
}
|
||||
uint8_t count = KNX_TUNNELING;
|
||||
_ipParameters.writeProperty(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, 1, addrbuffer, count);
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
println("no Tunnel-PAs configured, using own subnet");
|
||||
#endif
|
||||
}
|
||||
|
||||
_ipParameters.readPropertyLength(PID_CUSTOM_RESERVED_TUNNELS_CTRL, propCount);
|
||||
const uint8_t *tunCtrlBytes = nullptr;
|
||||
if(propCount == KNX_TUNNELING)
|
||||
tunCtrlBytes = _ipParameters.propertyData(PID_CUSTOM_RESERVED_TUNNELS_CTRL);
|
||||
|
||||
_ipParameters.readPropertyLength(PID_CUSTOM_RESERVED_TUNNELS_IP, propCount);
|
||||
const uint8_t *tunCtrlIp = nullptr;
|
||||
if(propCount == KNX_TUNNELING)
|
||||
tunCtrlIp = _ipParameters.propertyData(PID_CUSTOM_RESERVED_TUNNELS_IP);
|
||||
|
||||
bool resTunActive = (tunCtrlBytes && tunCtrlIp);
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
if(resTunActive) println("Reserved Tunnel Feature active");
|
||||
|
||||
if(tunCtrlBytes)
|
||||
printHex("tunCtrlBytes", tunCtrlBytes, KNX_TUNNELING);
|
||||
if(tunCtrlIp)
|
||||
printHex("tunCtrlIp", tunCtrlIp, KNX_TUNNELING*4);
|
||||
#endif
|
||||
|
||||
// check if there is a reserved tunnel for the source
|
||||
int firstFreeTunnel = -1;
|
||||
int firstResAndFreeTunnel = -1;
|
||||
int firstResAndOccTunnel = -1;
|
||||
bool tunnelResActive[KNX_TUNNELING];
|
||||
uint8_t tunnelResOptions[KNX_TUNNELING];
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
if(resTunActive)
|
||||
{
|
||||
tunnelResActive[i] = *(tunCtrlBytes+i) & 0x80;
|
||||
tunnelResOptions[i] = (*(tunCtrlBytes+i) & 0x60) >> 5;
|
||||
}
|
||||
|
||||
|
||||
if(tunnelResActive[i]) // tunnel reserve feature active for this tunnel
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("tunnel reserve feature active for this tunnel: ");
|
||||
print(tunnelResActive[i]);
|
||||
print(" options: ");
|
||||
println(tunnelResOptions[i]);
|
||||
#endif
|
||||
|
||||
uint32_t rIP = 0;
|
||||
popInt(rIP, tunCtrlIp+4*i);
|
||||
if(srcIP == rIP && connRequest.cri().type() == TUNNEL_CONNECTION)
|
||||
{
|
||||
// reserved tunnel for this ip found
|
||||
if(tunnels[i].ChannelId == 0) // check if it is free
|
||||
{
|
||||
if(firstResAndFreeTunnel < 0)
|
||||
firstResAndFreeTunnel = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(firstResAndOccTunnel < 0)
|
||||
firstResAndOccTunnel = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(tunnels[i].ChannelId == 0 && firstFreeTunnel < 0)
|
||||
firstFreeTunnel = i;
|
||||
}
|
||||
}
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("firstFreeTunnel: ");
|
||||
print(firstFreeTunnel);
|
||||
print(" firstResAndFreeTunnel: ");
|
||||
print(firstResAndFreeTunnel);
|
||||
print(" firstResAndOccTunnel: ");
|
||||
println(firstResAndOccTunnel);
|
||||
#endif
|
||||
|
||||
|
||||
uint8_t tunIdx = 0xff;
|
||||
if(resTunActive & (firstResAndFreeTunnel >= 0 || firstResAndOccTunnel >= 0)) // tunnel reserve feature active (for this src)
|
||||
{
|
||||
if(firstResAndFreeTunnel >= 0)
|
||||
{
|
||||
tunIdx = firstResAndFreeTunnel;
|
||||
}
|
||||
else if(firstResAndOccTunnel >= 0)
|
||||
{
|
||||
if(tunnelResOptions[firstResAndOccTunnel] == 1) // decline req
|
||||
{
|
||||
; // do nothing => decline
|
||||
}
|
||||
else if(tunnelResOptions[firstResAndOccTunnel] == 2) // close current tunnel connection on this tunnel and assign to this request
|
||||
{
|
||||
KnxIpDisconnectRequest discReq;
|
||||
discReq.channelId(tunnels[firstResAndOccTunnel].ChannelId);
|
||||
discReq.hpaiCtrl().length(LEN_IPHPAI);
|
||||
discReq.hpaiCtrl().code(IPV4_UDP);
|
||||
discReq.hpaiCtrl().ipAddress(tunnels[firstResAndOccTunnel].IpAddress);
|
||||
discReq.hpaiCtrl().ipPortNumber(tunnels[firstResAndOccTunnel].PortCtrl);
|
||||
_platform.sendBytesUniCast(tunnels[firstResAndOccTunnel].IpAddress, tunnels[firstResAndOccTunnel].PortCtrl, discReq.data(), discReq.totalLength());
|
||||
tunnels[firstResAndOccTunnel].Reset();
|
||||
|
||||
|
||||
tunIdx = firstResAndOccTunnel;
|
||||
}
|
||||
else if(tunnelResOptions[firstResAndOccTunnel] == 3) // use the first unreserved tunnel (if one)
|
||||
{
|
||||
if(firstFreeTunnel >= 0)
|
||||
tunIdx = firstFreeTunnel;
|
||||
else
|
||||
; // do nothing => decline
|
||||
}
|
||||
//else
|
||||
// should not happen
|
||||
// do nothing => decline
|
||||
}
|
||||
//else
|
||||
// should not happen
|
||||
// do nothing => decline
|
||||
}
|
||||
else
|
||||
{
|
||||
if(firstFreeTunnel >= 0)
|
||||
tunIdx = firstFreeTunnel;
|
||||
//else
|
||||
// do nothing => decline
|
||||
}
|
||||
|
||||
KnxIpTunnelConnection *tun = nullptr;
|
||||
if(tunIdx != 0xFF)
|
||||
{
|
||||
tun = &tunnels[tunIdx];
|
||||
|
||||
uint16_t tunPa = 0;
|
||||
popWord(tunPa, addresses + (tunIdx*2));
|
||||
|
||||
//check if this PA is in use (should not happen, only when there is one pa wrongly assigned to more then one tunnel)
|
||||
for(int x = 0; x < KNX_TUNNELING; x++)
|
||||
if(tunnels[x].IndividualAddress == tunPa)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
println("cannot use tunnel because PA is already in use");
|
||||
#endif
|
||||
tunIdx = 0xFF;
|
||||
tun = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
tun->IndividualAddress = tunPa;
|
||||
|
||||
}
|
||||
|
||||
if(tun == nullptr)
|
||||
{
|
||||
println("no free tunnel availible");
|
||||
KnxIpConnectResponse connRes(0x00, E_NO_MORE_CONNECTIONS);
|
||||
_platform.sendBytesUniCast(connRequest.hpaiCtrl().ipAddress(), connRequest.hpaiCtrl().ipPortNumber(), connRes.data(), connRes.totalLength());
|
||||
return;
|
||||
}
|
||||
|
||||
if(connRequest.cri().type() == DEVICE_MGMT_CONNECTION)
|
||||
tun->IsConfig = true;
|
||||
|
||||
// the channel ID shall be unique on this tunnel server. catch the rare case of a double channel ID
|
||||
bool channelIdInUse;
|
||||
do
|
||||
{
|
||||
_lastChannelId++;
|
||||
channelIdInUse = false;
|
||||
for(int x = 0; x < KNX_TUNNELING; x++)
|
||||
if(tunnels[x].ChannelId == _lastChannelId)
|
||||
channelIdInUse = true;
|
||||
}
|
||||
while(channelIdInUse);
|
||||
|
||||
tun->ChannelId = _lastChannelId;
|
||||
tun->lastHeartbeat = millis();
|
||||
if(_lastChannelId == 255)
|
||||
_lastChannelId = 0;
|
||||
|
||||
tun->IpAddress = srcIP;
|
||||
tun->PortData = srcPort;
|
||||
tun->PortCtrl = connRequest.hpaiCtrl().ipPortNumber()?connRequest.hpaiCtrl().ipPortNumber():srcPort;
|
||||
|
||||
print("New Tunnel-Connection[");
|
||||
print(tunIdx);
|
||||
print("], Channel: 0x");
|
||||
print(tun->ChannelId, 16);
|
||||
print(" PA: ");
|
||||
print(tun->IndividualAddress >> 12);
|
||||
print(".");
|
||||
print((tun->IndividualAddress >> 8) & 0xF);
|
||||
print(".");
|
||||
print(tun->IndividualAddress & 0xFF);
|
||||
|
||||
print(" with ");
|
||||
print(tun->IpAddress >> 24);
|
||||
print(".");
|
||||
print((tun->IpAddress >> 16) & 0xFF);
|
||||
print(".");
|
||||
print((tun->IpAddress >> 8) & 0xFF);
|
||||
print(".");
|
||||
print(tun->IpAddress & 0xFF);
|
||||
print(":");
|
||||
print(tun->PortData);
|
||||
if(tun->PortData != tun->PortCtrl)
|
||||
{
|
||||
print(" (Ctrlport: ");
|
||||
print(tun->PortCtrl);
|
||||
print(")");
|
||||
}
|
||||
if(tun->IsConfig)
|
||||
{
|
||||
print(" (Config-Channel)");
|
||||
}
|
||||
println();
|
||||
|
||||
|
||||
KnxIpConnectResponse connRes(_ipParameters, tun->IndividualAddress, 3671, tun->ChannelId, connRequest.cri().type());
|
||||
_platform.sendBytesUniCast(tun->IpAddress, tun->PortCtrl, connRes.data(), connRes.totalLength());
|
||||
}
|
||||
|
||||
void IpDataLinkLayer::loopHandleConnectionStateRequest(uint8_t* buffer, uint16_t length)
|
||||
{
|
||||
KnxIpStateRequest stateRequest(buffer, length);
|
||||
|
||||
KnxIpTunnelConnection *tun = nullptr;
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
if(tunnels[i].ChannelId == stateRequest.channelId())
|
||||
{
|
||||
tun = &tunnels[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(tun == nullptr)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("Channel ID nicht gefunden: ");
|
||||
println(stateRequest.channelId());
|
||||
#endif
|
||||
KnxIpStateResponse stateRes(0x00, E_CONNECTION_ID);
|
||||
_platform.sendBytesUniCast(stateRequest.hpaiCtrl().ipAddress(), stateRequest.hpaiCtrl().ipPortNumber(), stateRes.data(), stateRes.totalLength());
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO check knx connection!
|
||||
//if no connection return E_KNX_CONNECTION
|
||||
|
||||
//TODO check when to send E_DATA_CONNECTION
|
||||
|
||||
tun->lastHeartbeat = millis();
|
||||
KnxIpStateResponse stateRes(tun->ChannelId, E_NO_ERROR);
|
||||
_platform.sendBytesUniCast(stateRequest.hpaiCtrl().ipAddress(), stateRequest.hpaiCtrl().ipPortNumber(), stateRes.data(), stateRes.totalLength());
|
||||
}
|
||||
|
||||
void IpDataLinkLayer::loopHandleDisconnectRequest(uint8_t* buffer, uint16_t length)
|
||||
{
|
||||
KnxIpDisconnectRequest discReq(buffer, length);
|
||||
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print(">>> Disconnect Channel ID: ");
|
||||
println(discReq.channelId());
|
||||
#endif
|
||||
|
||||
KnxIpTunnelConnection *tun = nullptr;
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
if(tunnels[i].ChannelId == discReq.channelId())
|
||||
{
|
||||
tun = &tunnels[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(tun == nullptr)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("Channel ID nicht gefunden: ");
|
||||
println(discReq.channelId());
|
||||
#endif
|
||||
KnxIpDisconnectResponse discRes(0x00, E_CONNECTION_ID);
|
||||
_platform.sendBytesUniCast(discReq.hpaiCtrl().ipAddress(), discReq.hpaiCtrl().ipPortNumber(), discRes.data(), discRes.totalLength());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
KnxIpDisconnectResponse discRes(tun->ChannelId, E_NO_ERROR);
|
||||
_platform.sendBytesUniCast(discReq.hpaiCtrl().ipAddress(), discReq.hpaiCtrl().ipPortNumber(), discRes.data(), discRes.totalLength());
|
||||
tun->Reset();
|
||||
}
|
||||
|
||||
void IpDataLinkLayer::loopHandleDescriptionRequest(uint8_t* buffer, uint16_t length)
|
||||
{
|
||||
KnxIpDescriptionRequest descReq(buffer, length);
|
||||
KnxIpDescriptionResponse descRes(_ipParameters, _deviceObject);
|
||||
_platform.sendBytesUniCast(descReq.hpaiCtrl().ipAddress(), descReq.hpaiCtrl().ipPortNumber(), descRes.data(), descRes.totalLength());
|
||||
}
|
||||
|
||||
void IpDataLinkLayer::loopHandleDeviceConfigurationRequest(uint8_t* buffer, uint16_t length)
|
||||
{
|
||||
KnxIpConfigRequest confReq(buffer, length);
|
||||
|
||||
KnxIpTunnelConnection *tun = nullptr;
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
if(tunnels[i].ChannelId == confReq.connectionHeader().channelId())
|
||||
{
|
||||
tun = &tunnels[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(tun == nullptr)
|
||||
{
|
||||
print("Channel ID nicht gefunden: ");
|
||||
println(confReq.connectionHeader().channelId());
|
||||
KnxIpStateResponse stateRes(0x00, E_CONNECTION_ID);
|
||||
_platform.sendBytesUniCast(0, 0, stateRes.data(), stateRes.totalLength());
|
||||
return;
|
||||
}
|
||||
|
||||
KnxIpTunnelingAck tunnAck;
|
||||
tunnAck.serviceTypeIdentifier(DeviceConfigurationAck);
|
||||
tunnAck.connectionHeader().length(4);
|
||||
tunnAck.connectionHeader().channelId(tun->ChannelId);
|
||||
tunnAck.connectionHeader().sequenceCounter(confReq.connectionHeader().sequenceCounter());
|
||||
tunnAck.connectionHeader().status(E_NO_ERROR);
|
||||
_platform.sendBytesUniCast(tun->IpAddress, tun->PortData, tunnAck.data(), tunnAck.totalLength());
|
||||
|
||||
tun->lastHeartbeat = millis();
|
||||
_cemiServer->frameReceived(confReq.frame());
|
||||
}
|
||||
|
||||
void IpDataLinkLayer::loopHandleTunnelingRequest(uint8_t* buffer, uint16_t length)
|
||||
{
|
||||
KnxIpTunnelingRequest tunnReq(buffer, length);
|
||||
|
||||
KnxIpTunnelConnection *tun = nullptr;
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
if(tunnels[i].ChannelId == tunnReq.connectionHeader().channelId())
|
||||
{
|
||||
tun = &tunnels[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(tun == nullptr)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("Channel ID nicht gefunden: ");
|
||||
println(tunnReq.connectionHeader().channelId());
|
||||
#endif
|
||||
KnxIpStateResponse stateRes(0x00, E_CONNECTION_ID);
|
||||
_platform.sendBytesUniCast(0, 0, stateRes.data(), stateRes.totalLength());
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t sequence = tunnReq.connectionHeader().sequenceCounter();
|
||||
if(sequence == tun->SequenceCounter_R)
|
||||
{
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("Received SequenceCounter again: ");
|
||||
println(tunnReq.connectionHeader().sequenceCounter());
|
||||
#endif
|
||||
//we already got this one
|
||||
//so just ack it
|
||||
KnxIpTunnelingAck tunnAck;
|
||||
tunnAck.connectionHeader().length(4);
|
||||
tunnAck.connectionHeader().channelId(tun->ChannelId);
|
||||
tunnAck.connectionHeader().sequenceCounter(tunnReq.connectionHeader().sequenceCounter());
|
||||
tunnAck.connectionHeader().status(E_NO_ERROR);
|
||||
_platform.sendBytesUniCast(tun->IpAddress, tun->PortData, tunnAck.data(), tunnAck.totalLength());
|
||||
return;
|
||||
} else if((uint8_t)(sequence - 1) != tun->SequenceCounter_R) {
|
||||
#ifdef KNX_LOG_TUNNELING
|
||||
print("Wrong SequenceCounter: got ");
|
||||
print(tunnReq.connectionHeader().sequenceCounter());
|
||||
print(" expected ");
|
||||
println((uint8_t)(tun->SequenceCounter_R + 1));
|
||||
#endif
|
||||
//Dont handle it
|
||||
return;
|
||||
}
|
||||
|
||||
KnxIpTunnelingAck tunnAck;
|
||||
tunnAck.connectionHeader().length(4);
|
||||
tunnAck.connectionHeader().channelId(tun->ChannelId);
|
||||
tunnAck.connectionHeader().sequenceCounter(tunnReq.connectionHeader().sequenceCounter());
|
||||
tunnAck.connectionHeader().status(E_NO_ERROR);
|
||||
_platform.sendBytesUniCast(tun->IpAddress, tun->PortData, tunnAck.data(), tunnAck.totalLength());
|
||||
|
||||
tun->SequenceCounter_R = tunnReq.connectionHeader().sequenceCounter();
|
||||
|
||||
if(tunnReq.frame().sourceAddress() == 0)
|
||||
tunnReq.frame().sourceAddress(tun->IndividualAddress);
|
||||
|
||||
_cemiServer->frameReceived(tunnReq.frame());
|
||||
}
|
||||
#endif
|
||||
|
||||
void IpDataLinkLayer::enabled(bool value)
|
||||
{
|
||||
// _print("own address: ");
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <stdint.h>
|
||||
#include "data_link_layer.h"
|
||||
#include "ip_parameter_object.h"
|
||||
#include "knx_ip_tunnel_connection.h"
|
||||
#include "service_families.h"
|
||||
|
||||
class IpDataLinkLayer : public DataLinkLayer
|
||||
{
|
||||
@ -13,12 +15,19 @@ class IpDataLinkLayer : public DataLinkLayer
|
||||
|
||||
public:
|
||||
IpDataLinkLayer(DeviceObject& devObj, IpParameterObject& ipParam, NetworkLayerEntity& netLayerEntity,
|
||||
Platform& platform, DataLinkLayerCallbacks* dllcb = nullptr);
|
||||
Platform& platform, BusAccessUnit& busAccessUnit, DataLinkLayerCallbacks* dllcb = nullptr);
|
||||
|
||||
void loop();
|
||||
void enabled(bool value);
|
||||
bool enabled() const;
|
||||
DptMedium mediumType() const override;
|
||||
#ifdef KNX_TUNNELING
|
||||
void dataRequestToTunnel(CemiFrame& frame) override;
|
||||
void dataConfirmationToTunnel(CemiFrame& frame) override;
|
||||
void dataIndicationToTunnel(CemiFrame& frame) override;
|
||||
bool isTunnelAddress(uint16_t addr) override;
|
||||
bool isSentToTunnel(uint16_t address, bool isGrpAddr);
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool _enabled = false;
|
||||
@ -26,10 +35,26 @@ class IpDataLinkLayer : public DataLinkLayer
|
||||
uint8_t _frameCountBase = 0;
|
||||
uint32_t _frameCountTimeBase = 0;
|
||||
bool sendFrame(CemiFrame& frame);
|
||||
#ifdef KNX_TUNNELING
|
||||
void sendFrameToTunnel(KnxIpTunnelConnection *tunnel, CemiFrame& frame);
|
||||
void loopHandleConnectRequest(uint8_t* buffer, uint16_t length, uint32_t& src_addr, uint16_t& src_port);
|
||||
void loopHandleConnectionStateRequest(uint8_t* buffer, uint16_t length);
|
||||
void loopHandleDisconnectRequest(uint8_t* buffer, uint16_t length);
|
||||
void loopHandleDescriptionRequest(uint8_t* buffer, uint16_t length);
|
||||
void loopHandleDeviceConfigurationRequest(uint8_t* buffer, uint16_t length);
|
||||
void loopHandleTunnelingRequest(uint8_t* buffer, uint16_t length);
|
||||
#endif
|
||||
#if KNX_SERVICE_FAMILY_CORE >= 2
|
||||
void loopHandleSearchRequestExtended(uint8_t* buffer, uint16_t length);
|
||||
#endif
|
||||
bool sendBytes(uint8_t* buffer, uint16_t length);
|
||||
bool isSendLimitReached();
|
||||
|
||||
IpParameterObject& _ipParameters;
|
||||
DataLinkLayerCallbacks* _dllcb;
|
||||
};
|
||||
#ifdef KNX_TUNNELING
|
||||
KnxIpTunnelConnection tunnels[KNX_TUNNELING];
|
||||
uint8_t _lastChannelId = 1;
|
||||
#endif
|
||||
};
|
||||
#endif
|
@ -12,6 +12,7 @@ enum HostProtocolCode : uint8_t
|
||||
#ifdef USE_IP
|
||||
|
||||
#define LEN_IPHPAI 8
|
||||
#define LEN_CRD 4
|
||||
|
||||
class IpHostProtocolAddressInformation
|
||||
{
|
||||
|
@ -34,6 +34,11 @@ IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platf
|
||||
io->_deviceObject.individualAddress(getWord(data));
|
||||
return 1;
|
||||
}),
|
||||
#ifdef KNX_TUNNELING
|
||||
new DataProperty(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, true, PDT_UNSIGNED_INT, KNX_TUNNELING, ReadLv3 | WriteLv3),
|
||||
new DataProperty(PID_CUSTOM_RESERVED_TUNNELS_CTRL, true, PDT_UNSIGNED_CHAR, KNX_TUNNELING, ReadLv3 | WriteLv3), // custom propertiy to control the stacks behaviour for reserverd tunnels, not in Spec (PID >= 200)
|
||||
new DataProperty(PID_CUSTOM_RESERVED_TUNNELS_IP, true, PDT_UNSIGNED_LONG, KNX_TUNNELING, ReadLv3 | WriteLv3), // custom propertiy to control the stacks behaviour for reserverd tunnels, not in Spec (PID >= 200)
|
||||
#endif
|
||||
new DataProperty(PID_CURRENT_IP_ASSIGNMENT_METHOD, false, PDT_UNSIGNED_CHAR, 0, ReadLv3 | WriteLv3),
|
||||
new DataProperty(PID_IP_ASSIGNMENT_METHOD, true, PDT_UNSIGNED_CHAR, 1, ReadLv3 | WriteLv3),
|
||||
new DataProperty(PID_IP_CAPABILITIES, true, PDT_BITSET8, 0, ReadLv3 | WriteLv1), // must be set by application due to capabilities of the used ip stack
|
||||
|
48
src/knx/knx_ip_ch.cpp
Normal file
48
src/knx/knx_ip_ch.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include "knx_ip_ch.h"
|
||||
#ifdef USE_IP
|
||||
KnxIpCH::KnxIpCH(uint8_t* data) : _data(data)
|
||||
{}
|
||||
|
||||
KnxIpCH::~KnxIpCH()
|
||||
{}
|
||||
|
||||
uint8_t KnxIpCH::length() const
|
||||
{
|
||||
return *_data;
|
||||
}
|
||||
|
||||
void KnxIpCH::length(uint8_t value)
|
||||
{
|
||||
*_data = value;
|
||||
}
|
||||
|
||||
void KnxIpCH::channelId(uint8_t value)
|
||||
{
|
||||
_data[1] = value;
|
||||
}
|
||||
|
||||
uint8_t KnxIpCH::channelId() const
|
||||
{
|
||||
return _data[1];
|
||||
}
|
||||
|
||||
void KnxIpCH::sequenceCounter(uint8_t value)
|
||||
{
|
||||
_data[2] = value;
|
||||
}
|
||||
|
||||
uint8_t KnxIpCH::sequenceCounter() const
|
||||
{
|
||||
return _data[2];
|
||||
}
|
||||
|
||||
void KnxIpCH::status(uint8_t value)
|
||||
{
|
||||
_data[3] = value;
|
||||
}
|
||||
|
||||
uint8_t KnxIpCH::status() const
|
||||
{
|
||||
return _data[3];
|
||||
}
|
||||
#endif
|
28
src/knx/knx_ip_ch.h
Normal file
28
src/knx/knx_ip_ch.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "config.h"
|
||||
|
||||
#ifdef USE_IP
|
||||
|
||||
#define LEN_CH 4
|
||||
|
||||
// Connection Header
|
||||
class KnxIpCH
|
||||
{
|
||||
public:
|
||||
KnxIpCH(uint8_t* data);
|
||||
virtual ~KnxIpCH();
|
||||
void channelId(uint8_t channelId);
|
||||
uint8_t channelId() const;
|
||||
void sequenceCounter(uint8_t sequenceCounter);
|
||||
uint8_t sequenceCounter() const;
|
||||
void status(uint8_t status);
|
||||
uint8_t status() const;
|
||||
void length(uint8_t value);
|
||||
uint8_t length() const;
|
||||
|
||||
protected:
|
||||
uint8_t* _data = 0;
|
||||
};
|
||||
#endif
|
91
src/knx/knx_ip_config_dib.cpp
Normal file
91
src/knx/knx_ip_config_dib.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
#include "knx_ip_config_dib.h"
|
||||
|
||||
#ifdef USE_IP
|
||||
KnxIpConfigDIB::KnxIpConfigDIB(uint8_t* data, bool isCurrent) : KnxIpDIB(data)
|
||||
{
|
||||
_isCurrent = isCurrent;
|
||||
}
|
||||
|
||||
uint32_t KnxIpConfigDIB::address()
|
||||
{
|
||||
uint32_t addr = 0;
|
||||
popInt(addr, _data + 2);
|
||||
return addr;
|
||||
}
|
||||
|
||||
void KnxIpConfigDIB::address(uint32_t addr)
|
||||
{
|
||||
pushInt(addr, _data + 2);
|
||||
}
|
||||
|
||||
uint32_t KnxIpConfigDIB::subnet()
|
||||
{
|
||||
uint32_t addr = 0;
|
||||
popInt(addr, _data + 6);
|
||||
return addr;
|
||||
}
|
||||
|
||||
void KnxIpConfigDIB::subnet(uint32_t addr)
|
||||
{
|
||||
pushInt(addr, _data + 6);
|
||||
}
|
||||
|
||||
uint32_t KnxIpConfigDIB::gateway()
|
||||
{
|
||||
uint32_t addr = 0;
|
||||
popInt(addr, _data + 10);
|
||||
return addr;
|
||||
}
|
||||
|
||||
void KnxIpConfigDIB::gateway(uint32_t addr)
|
||||
{
|
||||
pushInt(addr, _data + 10);
|
||||
}
|
||||
|
||||
uint32_t KnxIpConfigDIB::dhcp()
|
||||
{
|
||||
if(!_isCurrent) return 0;
|
||||
uint32_t addr = 0;
|
||||
popInt(addr, _data + 14);
|
||||
return addr;
|
||||
}
|
||||
|
||||
void KnxIpConfigDIB::dhcp(uint32_t addr)
|
||||
{
|
||||
if(!_isCurrent) return;
|
||||
pushInt(addr, _data + 14);
|
||||
}
|
||||
|
||||
uint8_t KnxIpConfigDIB::info1()
|
||||
{
|
||||
if(_isCurrent)
|
||||
return _data[14];
|
||||
else
|
||||
return _data[18];
|
||||
}
|
||||
|
||||
void KnxIpConfigDIB::info1(uint8_t addr)
|
||||
{
|
||||
if(_isCurrent)
|
||||
_data[14] = addr;
|
||||
else
|
||||
_data[18] = addr;
|
||||
}
|
||||
|
||||
uint8_t KnxIpConfigDIB::info2()
|
||||
{
|
||||
if(_isCurrent)
|
||||
return _data[15];
|
||||
else
|
||||
return _data[19];
|
||||
}
|
||||
|
||||
void KnxIpConfigDIB::info2(uint8_t addr)
|
||||
{
|
||||
if(_isCurrent)
|
||||
_data[15] = addr;
|
||||
else
|
||||
_data[19] = addr;
|
||||
}
|
||||
|
||||
#endif
|
28
src/knx/knx_ip_config_dib.h
Normal file
28
src/knx/knx_ip_config_dib.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include "knx_ip_dib.h"
|
||||
#include "bits.h"
|
||||
|
||||
#ifdef USE_IP
|
||||
#define LEN_IP_CONFIG_DIB 16
|
||||
#define LEN_IP_CURRENT_CONFIG_DIB 20
|
||||
|
||||
class KnxIpConfigDIB : public KnxIpDIB
|
||||
{
|
||||
public:
|
||||
KnxIpConfigDIB(uint8_t* data, bool isCurrent = false);
|
||||
uint32_t address();
|
||||
void address(uint32_t addr);
|
||||
uint32_t subnet();
|
||||
void subnet(uint32_t addr);
|
||||
uint32_t gateway();
|
||||
void gateway(uint32_t addr);
|
||||
uint32_t dhcp();
|
||||
void dhcp(uint32_t addr);
|
||||
uint8_t info1();
|
||||
void info1(uint8_t addr);
|
||||
uint8_t info2();
|
||||
void info2(uint8_t addr);
|
||||
private:
|
||||
bool _isCurrent = false;
|
||||
};
|
||||
#endif
|
17
src/knx/knx_ip_config_request.cpp
Normal file
17
src/knx/knx_ip_config_request.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "knx_ip_config_request.h"
|
||||
#ifdef USE_IP
|
||||
KnxIpConfigRequest::KnxIpConfigRequest(uint8_t* data, uint16_t length)
|
||||
: KnxIpFrame(data, length), _ch(data + LEN_KNXIP_HEADER), _frame(data + LEN_KNXIP_HEADER + LEN_CH, length - LEN_KNXIP_HEADER - LEN_CH)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CemiFrame& KnxIpConfigRequest::frame()
|
||||
{
|
||||
return _frame;
|
||||
}
|
||||
KnxIpCH& KnxIpConfigRequest::connectionHeader()
|
||||
{
|
||||
return _ch;
|
||||
}
|
||||
#endif
|
17
src/knx/knx_ip_config_request.h
Normal file
17
src/knx/knx_ip_config_request.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "knx_ip_ch.h"
|
||||
#include "ip_host_protocol_address_information.h"
|
||||
#ifdef USE_IP
|
||||
class KnxIpConfigRequest : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpConfigRequest(uint8_t* data, uint16_t length);
|
||||
CemiFrame& frame();
|
||||
KnxIpCH& connectionHeader();
|
||||
private:
|
||||
CemiFrame _frame;
|
||||
KnxIpCH _ch;
|
||||
};
|
||||
#endif
|
21
src/knx/knx_ip_connect_request.cpp
Normal file
21
src/knx/knx_ip_connect_request.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include "knx_ip_connect_request.h"
|
||||
#ifdef USE_IP
|
||||
KnxIpConnectRequest::KnxIpConnectRequest(uint8_t* data, uint16_t length)
|
||||
: KnxIpFrame(data, length), _hpaiCtrl(data + LEN_KNXIP_HEADER), _hpaiData(data + LEN_KNXIP_HEADER + LEN_IPHPAI), _cri(data + LEN_KNXIP_HEADER + 2*LEN_IPHPAI)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
IpHostProtocolAddressInformation& KnxIpConnectRequest::hpaiCtrl()
|
||||
{
|
||||
return _hpaiCtrl;
|
||||
}
|
||||
IpHostProtocolAddressInformation& KnxIpConnectRequest::hpaiData()
|
||||
{
|
||||
return _hpaiData;
|
||||
}
|
||||
KnxIpCRI& KnxIpConnectRequest::cri()
|
||||
{
|
||||
return _cri;
|
||||
}
|
||||
#endif
|
19
src/knx/knx_ip_connect_request.h
Normal file
19
src/knx/knx_ip_connect_request.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "knx_ip_cri.h"
|
||||
#include "ip_host_protocol_address_information.h"
|
||||
#ifdef USE_IP
|
||||
class KnxIpConnectRequest : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpConnectRequest(uint8_t* data, uint16_t length);
|
||||
IpHostProtocolAddressInformation& hpaiCtrl();
|
||||
IpHostProtocolAddressInformation& hpaiData();
|
||||
KnxIpCRI& cri();
|
||||
private:
|
||||
IpHostProtocolAddressInformation _hpaiCtrl;
|
||||
IpHostProtocolAddressInformation _hpaiData;
|
||||
KnxIpCRI _cri;
|
||||
};
|
||||
#endif
|
45
src/knx/knx_ip_connect_response.cpp
Normal file
45
src/knx/knx_ip_connect_response.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include "knx_ip_connect_response.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
KnxIpConnectResponse::KnxIpConnectResponse(IpParameterObject& parameters, uint16_t address, uint16_t port, uint8_t channel, uint8_t type)
|
||||
: KnxIpFrame(LEN_KNXIP_HEADER + 1 /*Channel*/ + 1 /*Status*/ + LEN_IPHPAI + ((type == 4) ? 4 : 2)),
|
||||
_controlEndpoint(_data + LEN_KNXIP_HEADER + 1 /*Channel*/ + 1 /*Status*/),
|
||||
_crd(_data + LEN_KNXIP_HEADER + 1 /*Channel*/ + 1 /*Status*/ + LEN_IPHPAI)
|
||||
{
|
||||
serviceTypeIdentifier(ConnectResponse);
|
||||
|
||||
_data[LEN_KNXIP_HEADER] = channel;
|
||||
|
||||
_controlEndpoint.length(LEN_IPHPAI);
|
||||
_controlEndpoint.code(IPV4_UDP);
|
||||
_controlEndpoint.ipAddress(parameters.propertyValue<uint32_t>(PID_CURRENT_IP_ADDRESS));
|
||||
_controlEndpoint.ipPortNumber(KNXIP_MULTICAST_PORT);
|
||||
|
||||
_crd.length((type == 4) ? 4 : 2); //TunnelConnectionResponse length = 4; ConfigConnectionResponse length = 2;
|
||||
_crd.type(type);
|
||||
_crd.address(address);
|
||||
}
|
||||
|
||||
KnxIpConnectResponse::KnxIpConnectResponse(uint8_t channel, uint8_t errorCode)
|
||||
: KnxIpFrame(LEN_KNXIP_HEADER + 1 /*Channel*/ + 1 /*Status*/),
|
||||
_controlEndpoint(nullptr),
|
||||
_crd(nullptr)
|
||||
{
|
||||
serviceTypeIdentifier(ConnectResponse);
|
||||
|
||||
_data[LEN_KNXIP_HEADER] = channel;
|
||||
_data[LEN_KNXIP_HEADER + 1] = errorCode;
|
||||
}
|
||||
|
||||
|
||||
IpHostProtocolAddressInformation& KnxIpConnectResponse::controlEndpoint()
|
||||
{
|
||||
return _controlEndpoint;
|
||||
}
|
||||
|
||||
KnxIpCRD& KnxIpConnectResponse::crd()
|
||||
{
|
||||
return _crd;
|
||||
}
|
||||
|
||||
#endif
|
45
src/knx/knx_ip_connect_response.h
Normal file
45
src/knx/knx_ip_connect_response.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "knx_ip_crd.h"
|
||||
#include "ip_host_protocol_address_information.h"
|
||||
#include "knx_ip_device_information_dib.h"
|
||||
#include "knx_ip_supported_service_dib.h"
|
||||
#include "ip_parameter_object.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
enum KnxIpConnectionRequestErrorCodes
|
||||
{
|
||||
E_NO_ERROR = 0,
|
||||
|
||||
E_HOST_PROTOCOL_TYPE = 0x01,
|
||||
E_VERSION_NOT_SUPPORTED = 0x02,
|
||||
E_SEQUENCE_NUMBER = 0x04,
|
||||
|
||||
E_ERROR = 0x0F,
|
||||
|
||||
E_CONNECTION_ID = 0x21,
|
||||
E_CONNECTION_TYPE = 0x22,
|
||||
E_CONNECTION_OPTION = 0x23,
|
||||
E_NO_MORE_CONNECTIONS = 0x24,
|
||||
E_DATA_CONNECTION = 0x26,
|
||||
E_KNX_CONNECTION = 0x27,
|
||||
E_AUTHORISATION_ERROR = 0x28,
|
||||
E_TUNNELING_LAYER = 0x29,
|
||||
E_NO_TUNNELLING_ADDRESS = 0x2D,
|
||||
E_CONNECTION_IN_USE = 0x2E
|
||||
};
|
||||
|
||||
class KnxIpConnectResponse : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpConnectResponse(IpParameterObject& parameters, uint16_t address, uint16_t port, uint8_t channel, uint8_t type);
|
||||
KnxIpConnectResponse(uint8_t channel, uint8_t errorCode);
|
||||
IpHostProtocolAddressInformation& controlEndpoint();
|
||||
KnxIpCRD& crd();
|
||||
private:
|
||||
IpHostProtocolAddressInformation _controlEndpoint;
|
||||
KnxIpCRD _crd;
|
||||
};
|
||||
|
||||
#endif
|
41
src/knx/knx_ip_crd.cpp
Normal file
41
src/knx/knx_ip_crd.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
#include "knx_ip_crd.h"
|
||||
#ifdef USE_IP
|
||||
KnxIpCRD::KnxIpCRD(uint8_t* data) : _data(data)
|
||||
{}
|
||||
|
||||
KnxIpCRD::~KnxIpCRD()
|
||||
{}
|
||||
|
||||
uint8_t KnxIpCRD::length() const
|
||||
{
|
||||
return *_data;
|
||||
}
|
||||
|
||||
void KnxIpCRD::length(uint8_t value)
|
||||
{
|
||||
*_data = value;
|
||||
}
|
||||
|
||||
uint8_t KnxIpCRD::type() const
|
||||
{
|
||||
return _data[1];
|
||||
}
|
||||
|
||||
void KnxIpCRD::type(uint8_t value)
|
||||
{
|
||||
_data[1] = value;
|
||||
}
|
||||
|
||||
uint16_t KnxIpCRD::address() const
|
||||
{
|
||||
uint16_t addr = _data[3];
|
||||
addr |= _data[2] << 8;
|
||||
return addr;
|
||||
}
|
||||
|
||||
void KnxIpCRD::address(uint16_t value)
|
||||
{
|
||||
_data[2] = value >> 8;
|
||||
_data[3] = value & 0xFF;
|
||||
}
|
||||
#endif
|
23
src/knx/knx_ip_crd.h
Normal file
23
src/knx/knx_ip_crd.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "config.h"
|
||||
|
||||
#ifdef USE_IP
|
||||
|
||||
class KnxIpCRD
|
||||
{
|
||||
public:
|
||||
KnxIpCRD(uint8_t* data);
|
||||
virtual ~KnxIpCRD();
|
||||
void address(uint16_t addr);
|
||||
uint16_t address() const;
|
||||
void type(uint8_t addr);
|
||||
uint8_t type() const;
|
||||
uint8_t length() const;
|
||||
void length(uint8_t value);
|
||||
|
||||
protected:
|
||||
uint8_t* _data = 0;
|
||||
};
|
||||
#endif
|
38
src/knx/knx_ip_cri.cpp
Normal file
38
src/knx/knx_ip_cri.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include "knx_ip_cri.h"
|
||||
#ifdef USE_IP
|
||||
KnxIpCRI::KnxIpCRI(uint8_t* data) : _data(data)
|
||||
{}
|
||||
|
||||
KnxIpCRI::~KnxIpCRI()
|
||||
{}
|
||||
|
||||
uint8_t KnxIpCRI::length() const
|
||||
{
|
||||
return *_data;
|
||||
}
|
||||
|
||||
void KnxIpCRI::length(uint8_t value)
|
||||
{
|
||||
*_data = value;
|
||||
}
|
||||
|
||||
ConnectionType KnxIpCRI::type() const
|
||||
{
|
||||
return (ConnectionType)_data[1];
|
||||
}
|
||||
|
||||
void KnxIpCRI::type(ConnectionType value)
|
||||
{
|
||||
_data[1] = value;
|
||||
}
|
||||
|
||||
uint8_t KnxIpCRI::layer() const
|
||||
{
|
||||
return _data[2];
|
||||
}
|
||||
|
||||
void KnxIpCRI::layer(uint8_t value)
|
||||
{
|
||||
_data[2] = value;
|
||||
}
|
||||
#endif
|
36
src/knx/knx_ip_cri.h
Normal file
36
src/knx/knx_ip_cri.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "config.h"
|
||||
|
||||
#ifdef USE_IP
|
||||
|
||||
#define LEN_CRI 4
|
||||
|
||||
//TODO vervollständigen
|
||||
enum ConnectionType : uint8_t
|
||||
{
|
||||
DEVICE_MGMT_CONNECTION = 3,
|
||||
TUNNEL_CONNECTION = 4,
|
||||
REMLOG_CONNECTION = 6,
|
||||
REMCONF_CONNECTION = 7,
|
||||
OBJSVR_CONNECTION = 8
|
||||
};
|
||||
|
||||
// Connection Request Information
|
||||
class KnxIpCRI
|
||||
{
|
||||
public:
|
||||
KnxIpCRI(uint8_t* data);
|
||||
virtual ~KnxIpCRI();
|
||||
ConnectionType type() const;
|
||||
void type(ConnectionType value);
|
||||
void layer(uint8_t layer);
|
||||
uint8_t layer() const;
|
||||
uint8_t length() const;
|
||||
void length(uint8_t value);
|
||||
|
||||
protected:
|
||||
uint8_t* _data = 0;
|
||||
};
|
||||
#endif
|
13
src/knx/knx_ip_description_request.cpp
Normal file
13
src/knx/knx_ip_description_request.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
#include "knx_ip_description_request.h"
|
||||
#ifdef USE_IP
|
||||
KnxIpDescriptionRequest::KnxIpDescriptionRequest(uint8_t* data, uint16_t length)
|
||||
: KnxIpFrame(data, length), _hpaiCtrl(data + LEN_KNXIP_HEADER)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
IpHostProtocolAddressInformation& KnxIpDescriptionRequest::hpaiCtrl()
|
||||
{
|
||||
return _hpaiCtrl;
|
||||
}
|
||||
#endif
|
15
src/knx/knx_ip_description_request.h
Normal file
15
src/knx/knx_ip_description_request.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "knx_ip_cri.h"
|
||||
#include "ip_host_protocol_address_information.h"
|
||||
#ifdef USE_IP
|
||||
class KnxIpDescriptionRequest : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpDescriptionRequest(uint8_t* data, uint16_t length);
|
||||
IpHostProtocolAddressInformation& hpaiCtrl();
|
||||
private:
|
||||
IpHostProtocolAddressInformation _hpaiCtrl;
|
||||
};
|
||||
#endif
|
72
src/knx/knx_ip_description_response.cpp
Normal file
72
src/knx/knx_ip_description_response.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
#include "knx_ip_description_response.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
#define LEN_SERVICE_FAMILIES 2
|
||||
#if MASK_VERSION == 0x091A
|
||||
#ifdef KNX_TUNNELING
|
||||
#define LEN_SERVICE_DIB (2 + 4 * LEN_SERVICE_FAMILIES)
|
||||
#else
|
||||
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
|
||||
#endif
|
||||
#else
|
||||
#ifdef KNX_TUNNELING
|
||||
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
|
||||
#else
|
||||
#define LEN_SERVICE_DIB (2 + 2 * LEN_SERVICE_FAMILIES)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
KnxIpDescriptionResponse::KnxIpDescriptionResponse(IpParameterObject& parameters, DeviceObject& deviceObject)
|
||||
: KnxIpFrame(LEN_KNXIP_HEADER + LEN_DEVICE_INFORMATION_DIB + LEN_SERVICE_DIB),
|
||||
_deviceInfo(_data + LEN_KNXIP_HEADER),
|
||||
_supportedServices(_data + LEN_KNXIP_HEADER + LEN_DEVICE_INFORMATION_DIB)
|
||||
{
|
||||
serviceTypeIdentifier(DescriptionResponse);
|
||||
|
||||
_deviceInfo.length(LEN_DEVICE_INFORMATION_DIB);
|
||||
_deviceInfo.code(DEVICE_INFO);
|
||||
#if MASK_VERSION == 0x57B0
|
||||
_deviceInfo.medium(0x20); //MediumType is IP (for IP-Only Devices)
|
||||
#else
|
||||
_deviceInfo.medium(0x02); //MediumType is TP
|
||||
#endif
|
||||
_deviceInfo.status(deviceObject.progMode());
|
||||
_deviceInfo.individualAddress(parameters.propertyValue<uint16_t>(PID_KNX_INDIVIDUAL_ADDRESS));
|
||||
_deviceInfo.projectInstallationIdentifier(parameters.propertyValue<uint16_t>(PID_PROJECT_INSTALLATION_ID));
|
||||
_deviceInfo.serialNumber(deviceObject.propertyData(PID_SERIAL_NUMBER));
|
||||
_deviceInfo.routingMulticastAddress(parameters.propertyValue<uint32_t>(PID_ROUTING_MULTICAST_ADDRESS));
|
||||
//_deviceInfo.routingMulticastAddress(0);
|
||||
|
||||
uint8_t mac_address[LEN_MAC_ADDRESS] = {0};
|
||||
Property* prop = parameters.property(PID_MAC_ADDRESS);
|
||||
prop->read(mac_address);
|
||||
_deviceInfo.macAddress(mac_address);
|
||||
|
||||
uint8_t friendlyName[LEN_FRIENDLY_NAME] = {0};
|
||||
prop = parameters.property(PID_FRIENDLY_NAME);
|
||||
prop->read(1, LEN_FRIENDLY_NAME, friendlyName);
|
||||
_deviceInfo.friendlyName(friendlyName);
|
||||
|
||||
_supportedServices.length(LEN_SERVICE_DIB);
|
||||
_supportedServices.code(SUPP_SVC_FAMILIES);
|
||||
_supportedServices.serviceVersion(Core, 1);
|
||||
_supportedServices.serviceVersion(DeviceManagement, 1);
|
||||
#ifdef KNX_TUNNELING
|
||||
_supportedServices.serviceVersion(Tunnelling, 1);
|
||||
#endif
|
||||
#if MASK_VERSION == 0x091A
|
||||
_supportedServices.serviceVersion(Routing, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
KnxIpDeviceInformationDIB& KnxIpDescriptionResponse::deviceInfo()
|
||||
{
|
||||
return _deviceInfo;
|
||||
}
|
||||
|
||||
|
||||
KnxIpSupportedServiceDIB& KnxIpDescriptionResponse::supportedServices()
|
||||
{
|
||||
return _supportedServices;
|
||||
}
|
||||
#endif
|
21
src/knx/knx_ip_description_response.h
Normal file
21
src/knx/knx_ip_description_response.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "ip_host_protocol_address_information.h"
|
||||
#include "knx_ip_device_information_dib.h"
|
||||
#include "knx_ip_supported_service_dib.h"
|
||||
#include "ip_parameter_object.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
class KnxIpDescriptionResponse : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpDescriptionResponse(IpParameterObject& parameters, DeviceObject& deviceObj);
|
||||
KnxIpDeviceInformationDIB& deviceInfo();
|
||||
KnxIpSupportedServiceDIB& supportedServices();
|
||||
private:
|
||||
KnxIpDeviceInformationDIB _deviceInfo;
|
||||
KnxIpSupportedServiceDIB _supportedServices;
|
||||
};
|
||||
|
||||
#endif
|
@ -12,6 +12,9 @@ enum DescriptionTypeCode : uint8_t
|
||||
IP_CONFIG = 0x03,
|
||||
IP_CUR_CONFIG = 0x04,
|
||||
KNX_ADDRESSES = 0x05,
|
||||
MANUFACTURER_DATA = 0x06,
|
||||
TUNNELING_INFO = 0x07,
|
||||
EXTENDED_DEVICE_INFO = 0x08,
|
||||
MFR_DATA = 0xFE
|
||||
};
|
||||
|
||||
|
26
src/knx/knx_ip_disconnect_request.cpp
Normal file
26
src/knx/knx_ip_disconnect_request.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "knx_ip_disconnect_request.h"
|
||||
#ifdef USE_IP
|
||||
KnxIpDisconnectRequest::KnxIpDisconnectRequest(uint8_t* data, uint16_t length)
|
||||
: KnxIpFrame(data, length), _hpaiCtrl(data + LEN_KNXIP_HEADER + 1 /*ChannelId*/ + 1 /*Reserved*/)
|
||||
{
|
||||
}
|
||||
|
||||
KnxIpDisconnectRequest::KnxIpDisconnectRequest()
|
||||
: KnxIpFrame(1 /*ChannelId*/ + 1 /*Reserved*/ + LEN_KNXIP_HEADER + LEN_IPHPAI), _hpaiCtrl(_data + 1 /*ChannelId*/ + 1 /*Reserved*/ + LEN_KNXIP_HEADER)
|
||||
{
|
||||
serviceTypeIdentifier(DisconnectRequest);
|
||||
}
|
||||
|
||||
IpHostProtocolAddressInformation& KnxIpDisconnectRequest::hpaiCtrl()
|
||||
{
|
||||
return _hpaiCtrl;
|
||||
}
|
||||
uint8_t KnxIpDisconnectRequest::channelId()
|
||||
{
|
||||
return _data[LEN_KNXIP_HEADER];
|
||||
}
|
||||
void KnxIpDisconnectRequest::channelId(uint8_t channelId)
|
||||
{
|
||||
_data[LEN_KNXIP_HEADER] = channelId;
|
||||
}
|
||||
#endif
|
17
src/knx/knx_ip_disconnect_request.h
Normal file
17
src/knx/knx_ip_disconnect_request.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "ip_host_protocol_address_information.h"
|
||||
#ifdef USE_IP
|
||||
class KnxIpDisconnectRequest : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpDisconnectRequest(uint8_t* data, uint16_t length);
|
||||
KnxIpDisconnectRequest();
|
||||
IpHostProtocolAddressInformation& hpaiCtrl();
|
||||
uint8_t channelId();
|
||||
void channelId(uint8_t channelId);
|
||||
private:
|
||||
IpHostProtocolAddressInformation _hpaiCtrl;
|
||||
};
|
||||
#endif
|
12
src/knx/knx_ip_disconnect_response.cpp
Normal file
12
src/knx/knx_ip_disconnect_response.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "knx_ip_disconnect_response.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
KnxIpDisconnectResponse::KnxIpDisconnectResponse(uint8_t channel, uint8_t status)
|
||||
: KnxIpFrame(LEN_KNXIP_HEADER + 1 /*Channel*/ + 1 /*Status*/)
|
||||
{
|
||||
serviceTypeIdentifier(DisconnectResponse);
|
||||
|
||||
_data[LEN_KNXIP_HEADER] = channel;
|
||||
_data[LEN_KNXIP_HEADER+1] = status;
|
||||
}
|
||||
#endif
|
13
src/knx/knx_ip_disconnect_response.h
Normal file
13
src/knx/knx_ip_disconnect_response.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
class KnxIpDisconnectResponse : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpDisconnectResponse(uint8_t channel, uint8_t status);
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
42
src/knx/knx_ip_extended_device_information_dib.cpp
Normal file
42
src/knx/knx_ip_extended_device_information_dib.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include "knx_ip_extended_device_information_dib.h"
|
||||
#include "bits.h"
|
||||
|
||||
#ifdef USE_IP
|
||||
KnxIpExtendedDeviceInformationDIB::KnxIpExtendedDeviceInformationDIB(uint8_t* data) : KnxIpDIB(data)
|
||||
{}
|
||||
|
||||
uint8_t KnxIpExtendedDeviceInformationDIB::status() const
|
||||
{
|
||||
return _data[2];
|
||||
}
|
||||
|
||||
|
||||
void KnxIpExtendedDeviceInformationDIB::status(uint8_t value)
|
||||
{
|
||||
_data[2] = value;
|
||||
}
|
||||
|
||||
|
||||
uint16_t KnxIpExtendedDeviceInformationDIB::localMaxApdu() const
|
||||
{
|
||||
return getWord(_data + 4);
|
||||
}
|
||||
|
||||
|
||||
void KnxIpExtendedDeviceInformationDIB::localMaxApdu(uint16_t value)
|
||||
{
|
||||
pushWord(value, _data + 4);
|
||||
}
|
||||
|
||||
|
||||
uint16_t KnxIpExtendedDeviceInformationDIB::deviceDescriptor() const
|
||||
{
|
||||
return getWord(_data + 6);
|
||||
}
|
||||
|
||||
|
||||
void KnxIpExtendedDeviceInformationDIB::deviceDescriptor(uint16_t value)
|
||||
{
|
||||
pushWord(value, _data + 6);
|
||||
}
|
||||
#endif
|
19
src/knx/knx_ip_extended_device_information_dib.h
Normal file
19
src/knx/knx_ip_extended_device_information_dib.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "knx_ip_dib.h"
|
||||
|
||||
#ifdef USE_IP
|
||||
#define LEN_EXTENDED_DEVICE_INFORMATION_DIB 8
|
||||
|
||||
class KnxIpExtendedDeviceInformationDIB : public KnxIpDIB
|
||||
{
|
||||
public:
|
||||
KnxIpExtendedDeviceInformationDIB(uint8_t* data);
|
||||
uint8_t status() const;
|
||||
void status(uint8_t value);
|
||||
uint16_t localMaxApdu() const;
|
||||
void localMaxApdu(uint16_t value);
|
||||
uint16_t deviceDescriptor() const;
|
||||
void deviceDescriptor(uint16_t value);
|
||||
};
|
||||
|
||||
#endif
|
27
src/knx/knx_ip_knx_addresses_dib.cpp
Normal file
27
src/knx/knx_ip_knx_addresses_dib.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "knx_ip_knx_addresses_dib.h"
|
||||
|
||||
#ifdef USE_IP
|
||||
KnxIpKnxAddressesDIB::KnxIpKnxAddressesDIB(uint8_t* data) : KnxIpDIB(data)
|
||||
{
|
||||
currentPos = data + 4;
|
||||
}
|
||||
|
||||
uint16_t KnxIpKnxAddressesDIB::individualAddress()
|
||||
{
|
||||
uint16_t addr = 0;
|
||||
popWord(addr, _data + 2);
|
||||
return addr;
|
||||
}
|
||||
|
||||
void KnxIpKnxAddressesDIB::individualAddress(uint16_t addr)
|
||||
{
|
||||
pushInt(addr, _data + 2);
|
||||
}
|
||||
|
||||
void KnxIpKnxAddressesDIB::additional(uint16_t addr)
|
||||
{
|
||||
pushWord(addr, currentPos);
|
||||
currentPos += 2;
|
||||
length(currentPos - _data);
|
||||
}
|
||||
#endif
|
17
src/knx/knx_ip_knx_addresses_dib.h
Normal file
17
src/knx/knx_ip_knx_addresses_dib.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "knx_ip_dib.h"
|
||||
#include "bits.h"
|
||||
|
||||
#ifdef USE_IP
|
||||
|
||||
class KnxIpKnxAddressesDIB : public KnxIpDIB
|
||||
{
|
||||
public:
|
||||
KnxIpKnxAddressesDIB(uint8_t* data);
|
||||
uint16_t individualAddress();
|
||||
void individualAddress(uint16_t addr);
|
||||
void additional(uint16_t addr);
|
||||
private:
|
||||
uint8_t *currentPos = 0;
|
||||
};
|
||||
#endif
|
59
src/knx/knx_ip_search_request_extended.cpp
Normal file
59
src/knx/knx_ip_search_request_extended.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
#include "knx_ip_search_request_extended.h"
|
||||
#include "bits.h"
|
||||
#include "service_families.h"
|
||||
#if KNX_SERVICE_FAMILY_CORE >= 2
|
||||
#ifdef USE_IP
|
||||
KnxIpSearchRequestExtended::KnxIpSearchRequestExtended(uint8_t* data, uint16_t length)
|
||||
: KnxIpFrame(data, length), _hpai(data + LEN_KNXIP_HEADER)
|
||||
{
|
||||
if(length == LEN_KNXIP_HEADER + LEN_IPHPAI) return; //we dont have SRPs
|
||||
|
||||
int currentPos = LEN_KNXIP_HEADER + LEN_IPHPAI;
|
||||
while(currentPos < length)
|
||||
{
|
||||
switch(data[currentPos+1])
|
||||
{
|
||||
case 0x01:
|
||||
srpByProgMode = true;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
srpByMacAddr = true;
|
||||
srpMacAddr = data + currentPos + 2;
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
srpByService = true;
|
||||
srpServiceFamilies = data + currentPos;
|
||||
break;
|
||||
|
||||
case 0x04:
|
||||
srpRequestDIBs = true;
|
||||
for(int i = 0; i < data[currentPos]-2; i++)
|
||||
{
|
||||
if(data[currentPos+i+2] == 0) continue;
|
||||
if(data[currentPos+i+2] > REQUESTED_DIBS_MAX)
|
||||
{
|
||||
print("Requested DIBs too high ");
|
||||
continue;
|
||||
}
|
||||
requestedDIBs[data[currentPos+i+2]] = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
currentPos += data[currentPos];
|
||||
};
|
||||
}
|
||||
|
||||
IpHostProtocolAddressInformation& KnxIpSearchRequestExtended::hpai()
|
||||
{
|
||||
return _hpai;
|
||||
}
|
||||
|
||||
bool KnxIpSearchRequestExtended::requestedDIB(uint8_t code)
|
||||
{
|
||||
if(code > REQUESTED_DIBS_MAX) return false;
|
||||
return requestedDIBs[code];
|
||||
}
|
||||
#endif
|
||||
#endif
|
26
src/knx/knx_ip_search_request_extended.h
Normal file
26
src/knx/knx_ip_search_request_extended.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "service_families.h"
|
||||
#if KNX_SERVICE_FAMILY_CORE >= 2
|
||||
#include "knx_ip_frame.h"
|
||||
#include "ip_host_protocol_address_information.h"
|
||||
#ifdef USE_IP
|
||||
#define REQUESTED_DIBS_MAX 9
|
||||
class KnxIpSearchRequestExtended : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpSearchRequestExtended(uint8_t* data, uint16_t length);
|
||||
IpHostProtocolAddressInformation& hpai();
|
||||
bool requestedDIB(uint8_t code);
|
||||
bool srpByProgMode = false;
|
||||
bool srpByMacAddr = false;
|
||||
bool srpByService = false;
|
||||
bool srpRequestDIBs = false;
|
||||
uint8_t *srpMacAddr = nullptr;
|
||||
uint8_t *srpServiceFamilies = nullptr;
|
||||
private:
|
||||
IpHostProtocolAddressInformation _hpai;
|
||||
bool requestedDIBs[REQUESTED_DIBS_MAX]; //for now only 1 to 8
|
||||
};
|
||||
#endif
|
||||
#endif
|
@ -1,10 +1,23 @@
|
||||
#include "knx_ip_search_response.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
#define SERVICE_FAMILIES 2
|
||||
#define LEN_SERVICE_FAMILIES 2
|
||||
#if MASK_VERSION == 0x091A
|
||||
#ifdef KNX_TUNNELING
|
||||
#define LEN_SERVICE_DIB (2 + 4 * LEN_SERVICE_FAMILIES)
|
||||
#else
|
||||
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
|
||||
#endif
|
||||
#else
|
||||
#ifdef KNX_TUNNELING
|
||||
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
|
||||
#else
|
||||
#define LEN_SERVICE_DIB (2 + 2 * LEN_SERVICE_FAMILIES)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
KnxIpSearchResponse::KnxIpSearchResponse(IpParameterObject& parameters, DeviceObject& deviceObject)
|
||||
: KnxIpFrame(LEN_KNXIP_HEADER + LEN_IPHPAI + LEN_DEVICE_INFORMATION_DIB + 2 + 2 * SERVICE_FAMILIES),
|
||||
: KnxIpFrame(LEN_KNXIP_HEADER + LEN_IPHPAI + LEN_DEVICE_INFORMATION_DIB + LEN_SERVICE_DIB),
|
||||
_controlEndpoint(_data + LEN_KNXIP_HEADER), _deviceInfo(_data + LEN_KNXIP_HEADER + LEN_IPHPAI),
|
||||
_supportedServices(_data + LEN_KNXIP_HEADER + LEN_IPHPAI + LEN_DEVICE_INFORMATION_DIB)
|
||||
{
|
||||
@ -17,7 +30,11 @@ KnxIpSearchResponse::KnxIpSearchResponse(IpParameterObject& parameters, DeviceOb
|
||||
|
||||
_deviceInfo.length(LEN_DEVICE_INFORMATION_DIB);
|
||||
_deviceInfo.code(DEVICE_INFO);
|
||||
_deviceInfo.medium(0x20); //KNX-IP FIXME get this value from somewhere else
|
||||
#if MASK_VERSION == 0x57B0
|
||||
_deviceInfo.medium(0x20); //MediumType is IP (for IP-Only Devices)
|
||||
#else
|
||||
_deviceInfo.medium(0x02); //MediumType is TP
|
||||
#endif
|
||||
_deviceInfo.status(deviceObject.progMode());
|
||||
_deviceInfo.individualAddress(parameters.propertyValue<uint16_t>(PID_KNX_INDIVIDUAL_ADDRESS));
|
||||
_deviceInfo.projectInstallationIdentifier(parameters.propertyValue<uint16_t>(PID_PROJECT_INSTALLATION_ID));
|
||||
@ -35,11 +52,16 @@ KnxIpSearchResponse::KnxIpSearchResponse(IpParameterObject& parameters, DeviceOb
|
||||
prop->read(1, LEN_FRIENDLY_NAME, friendlyName);
|
||||
_deviceInfo.friendlyName(friendlyName);
|
||||
|
||||
_supportedServices.length(2 + 2 * SERVICE_FAMILIES);
|
||||
_supportedServices.length(LEN_SERVICE_DIB);
|
||||
_supportedServices.code(SUPP_SVC_FAMILIES);
|
||||
_supportedServices.serviceVersion(Core, 1);
|
||||
_supportedServices.serviceVersion(DeviceManagement, 1);
|
||||
// _supportedServices.serviceVersion(Routing, 1);
|
||||
_supportedServices.serviceVersion(Core, KNX_SERVICE_FAMILY_CORE);
|
||||
_supportedServices.serviceVersion(DeviceManagement, KNX_SERVICE_FAMILY_DEVICE_MANAGEMENT);
|
||||
#ifdef KNX_TUNNELING
|
||||
_supportedServices.serviceVersion(Tunnelling, KNX_SERVICE_FAMILY_TUNNELING);
|
||||
#endif
|
||||
#if MASK_VERSION == 0x091A
|
||||
_supportedServices.serviceVersion(Routing, KNX_SERVICE_FAMILY_ROUTING);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "knx_ip_device_information_dib.h"
|
||||
#include "knx_ip_supported_service_dib.h"
|
||||
#include "ip_parameter_object.h"
|
||||
#include "service_families.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
class KnxIpSearchResponse : public KnxIpFrame
|
||||
|
221
src/knx/knx_ip_search_response_extended.cpp
Normal file
221
src/knx/knx_ip_search_response_extended.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
#include "knx_ip_search_response_extended.h"
|
||||
#include "service_families.h"
|
||||
#if KNX_SERVICE_FAMILY_CORE >= 2
|
||||
#ifdef USE_IP
|
||||
|
||||
#define LEN_SERVICE_FAMILIES 2
|
||||
#if MASK_VERSION == 0x091A
|
||||
#ifdef KNX_TUNNELING
|
||||
#define LEN_SERVICE_DIB (2 + 4 * LEN_SERVICE_FAMILIES)
|
||||
#else
|
||||
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
|
||||
#endif
|
||||
#else
|
||||
#ifdef KNX_TUNNELING
|
||||
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
|
||||
#else
|
||||
#define LEN_SERVICE_DIB (2 + 2 * LEN_SERVICE_FAMILIES)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
KnxIpSearchResponseExtended::KnxIpSearchResponseExtended(IpParameterObject& parameters, DeviceObject& deviceObject, int dibLength)
|
||||
: KnxIpFrame(LEN_KNXIP_HEADER + LEN_IPHPAI + dibLength),
|
||||
_controlEndpoint(_data + LEN_KNXIP_HEADER)
|
||||
{
|
||||
serviceTypeIdentifier(SearchResponseExt);
|
||||
|
||||
_controlEndpoint.length(LEN_IPHPAI);
|
||||
_controlEndpoint.code(IPV4_UDP);
|
||||
_controlEndpoint.ipAddress(parameters.propertyValue<uint32_t>(PID_CURRENT_IP_ADDRESS));
|
||||
_controlEndpoint.ipPortNumber(KNXIP_MULTICAST_PORT);
|
||||
|
||||
currentPos = LEN_KNXIP_HEADER + LEN_IPHPAI;
|
||||
}
|
||||
|
||||
void KnxIpSearchResponseExtended::setDeviceInfo(IpParameterObject& parameters, DeviceObject& deviceObject)
|
||||
{
|
||||
println("setDeviceInfo");
|
||||
KnxIpDeviceInformationDIB _deviceInfo(_data + currentPos);
|
||||
_deviceInfo.length(LEN_DEVICE_INFORMATION_DIB);
|
||||
_deviceInfo.code(DEVICE_INFO);
|
||||
#if MASK_VERSION == 0x57B0
|
||||
_deviceInfo.medium(0x20); //MediumType is IP (for IP-Only Devices)
|
||||
#else
|
||||
_deviceInfo.medium(0x02); //MediumType is TP
|
||||
#endif
|
||||
_deviceInfo.status(deviceObject.progMode());
|
||||
_deviceInfo.individualAddress(parameters.propertyValue<uint16_t>(PID_KNX_INDIVIDUAL_ADDRESS));
|
||||
_deviceInfo.projectInstallationIdentifier(parameters.propertyValue<uint16_t>(PID_PROJECT_INSTALLATION_ID));
|
||||
_deviceInfo.serialNumber(deviceObject.propertyData(PID_SERIAL_NUMBER));
|
||||
_deviceInfo.routingMulticastAddress(parameters.propertyValue<uint32_t>(PID_ROUTING_MULTICAST_ADDRESS));
|
||||
//_deviceInfo.routingMulticastAddress(0);
|
||||
|
||||
uint8_t mac_address[LEN_MAC_ADDRESS] = {0};
|
||||
Property* prop = parameters.property(PID_MAC_ADDRESS);
|
||||
prop->read(mac_address);
|
||||
_deviceInfo.macAddress(mac_address);
|
||||
|
||||
uint8_t friendlyName[LEN_FRIENDLY_NAME] = {0};
|
||||
prop = parameters.property(PID_FRIENDLY_NAME);
|
||||
prop->read(1, LEN_FRIENDLY_NAME, friendlyName);
|
||||
_deviceInfo.friendlyName(friendlyName);
|
||||
|
||||
currentPos += LEN_DEVICE_INFORMATION_DIB;
|
||||
}
|
||||
|
||||
void KnxIpSearchResponseExtended::setSupportedServices()
|
||||
{
|
||||
println("setSupportedServices");
|
||||
KnxIpSupportedServiceDIB _supportedServices(_data + currentPos);
|
||||
_supportedServices.length(LEN_SERVICE_DIB);
|
||||
_supportedServices.code(SUPP_SVC_FAMILIES);
|
||||
_supportedServices.serviceVersion(Core, KNX_SERVICE_FAMILY_CORE);
|
||||
_supportedServices.serviceVersion(DeviceManagement, KNX_SERVICE_FAMILY_DEVICE_MANAGEMENT);
|
||||
#ifdef KNX_TUNNELING
|
||||
_supportedServices.serviceVersion(Tunnelling, KNX_SERVICE_FAMILY_TUNNELING);
|
||||
#endif
|
||||
#if MASK_VERSION == 0x091A
|
||||
_supportedServices.serviceVersion(Routing, KNX_SERVICE_FAMILY_ROUTING);
|
||||
#endif
|
||||
currentPos += LEN_SERVICE_DIB;
|
||||
}
|
||||
|
||||
void KnxIpSearchResponseExtended::setIpConfig(IpParameterObject& parameters)
|
||||
{
|
||||
println("setIpConfig");
|
||||
KnxIpConfigDIB _ipConfig(_data + currentPos);
|
||||
_ipConfig.length(LEN_IP_CONFIG_DIB);
|
||||
_ipConfig.code(IP_CONFIG);
|
||||
_ipConfig.address(parameters.propertyValue<uint32_t>(PID_IP_ADDRESS));
|
||||
_ipConfig.subnet(parameters.propertyValue<uint32_t>(PID_SUBNET_MASK));
|
||||
_ipConfig.gateway(parameters.propertyValue<uint32_t>(PID_DEFAULT_GATEWAY));
|
||||
_ipConfig.info1(parameters.propertyValue<uint8_t>(PID_IP_CAPABILITIES));
|
||||
_ipConfig.info2(parameters.propertyValue<uint8_t>(PID_IP_ASSIGNMENT_METHOD));
|
||||
|
||||
currentPos += LEN_IP_CONFIG_DIB;
|
||||
}
|
||||
|
||||
void KnxIpSearchResponseExtended::setIpCurrentConfig(IpParameterObject& parameters)
|
||||
{
|
||||
println("setIpCurrentConfig");
|
||||
KnxIpConfigDIB _ipCurConfig(_data + currentPos, true);
|
||||
_ipCurConfig.length(LEN_IP_CURRENT_CONFIG_DIB);
|
||||
_ipCurConfig.code(IP_CUR_CONFIG);
|
||||
_ipCurConfig.address(parameters.propertyValue<uint32_t>(PID_CURRENT_IP_ADDRESS));
|
||||
_ipCurConfig.subnet(parameters.propertyValue<uint32_t>(PID_CURRENT_SUBNET_MASK));
|
||||
_ipCurConfig.gateway(parameters.propertyValue<uint32_t>(PID_CURRENT_DEFAULT_GATEWAY));
|
||||
_ipCurConfig.dhcp(parameters.propertyValue<uint32_t>(PID_DHCP_BOOTP_SERVER));
|
||||
_ipCurConfig.info1(parameters.propertyValue<uint8_t>(PID_CURRENT_IP_ASSIGNMENT_METHOD));
|
||||
_ipCurConfig.info2(0x00); //Reserved
|
||||
|
||||
currentPos += LEN_IP_CURRENT_CONFIG_DIB;
|
||||
}
|
||||
|
||||
void KnxIpSearchResponseExtended::setKnxAddresses(IpParameterObject& parameters, DeviceObject& deviceObject)
|
||||
{
|
||||
println("setKnxAddresses");
|
||||
KnxIpKnxAddressesDIB _knxAddresses(_data + currentPos);
|
||||
_knxAddresses.length(4); //minimum
|
||||
_knxAddresses.code(KNX_ADDRESSES);
|
||||
_knxAddresses.individualAddress(deviceObject.individualAddress());
|
||||
|
||||
uint16_t length = 0;
|
||||
parameters.readPropertyLength(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, length);
|
||||
|
||||
const uint8_t *addresses = parameters.propertyData(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES);
|
||||
|
||||
for(int i = 0; i < length; i++)
|
||||
{
|
||||
uint16_t additional = 0;
|
||||
popWord(additional, addresses + i*2);
|
||||
_knxAddresses.additional(additional);
|
||||
}
|
||||
|
||||
currentPos += _knxAddresses.length();
|
||||
}
|
||||
|
||||
void KnxIpSearchResponseExtended::setTunnelingInfo(IpParameterObject& parameters, DeviceObject& deviceObject, KnxIpTunnelConnection tunnels[])
|
||||
{
|
||||
println("setTunnelingInfo");
|
||||
KnxIpTunnelingInfoDIB _tunnelInfo(_data + currentPos);
|
||||
_tunnelInfo.length(4); //minlength
|
||||
_tunnelInfo.code(TUNNELING_INFO);
|
||||
_tunnelInfo.apduLength(254); //FIXME where to get from
|
||||
|
||||
uint16_t length = 0;
|
||||
parameters.readPropertyLength(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, length);
|
||||
|
||||
const uint8_t *addresses;
|
||||
if(length == KNX_TUNNELING)
|
||||
{
|
||||
addresses = parameters.propertyData(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES);
|
||||
} else {
|
||||
uint8_t addrbuffer[KNX_TUNNELING*2];
|
||||
addresses = (uint8_t*)addrbuffer;
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
addrbuffer[i*2+1] = i+1;
|
||||
addrbuffer[i*2] = deviceObject.individualAddress() / 0x0100;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < length; i++)
|
||||
{
|
||||
uint16_t additional = 0;
|
||||
popWord(additional, addresses + i*2);
|
||||
uint16_t flags = 0;
|
||||
|
||||
uint8_t doubleCounter = 0;
|
||||
bool used = false;
|
||||
for(int i = 0; i < KNX_TUNNELING; i++)
|
||||
{
|
||||
if(tunnels[i].IndividualAddress == additional)
|
||||
{
|
||||
doubleCounter += 1;
|
||||
if(tunnels[i].ChannelId != 0)
|
||||
used = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(doubleCounter > 1 && used)
|
||||
flags |= 1 << 2; //Slot is not usable; double PA is already used
|
||||
|
||||
if(used)
|
||||
{
|
||||
flags |= 1 << 2; //Slot is not usable; PA is already used
|
||||
flags |= 1; //Slot is not free
|
||||
}
|
||||
|
||||
flags = ~flags;
|
||||
|
||||
_tunnelInfo.tunnelingSlot(additional, flags);
|
||||
}
|
||||
|
||||
currentPos += _tunnelInfo.length();
|
||||
}
|
||||
|
||||
void KnxIpSearchResponseExtended::setExtendedDeviceInfo()
|
||||
{
|
||||
println("setExtendedDeviceInfo");
|
||||
KnxIpExtendedDeviceInformationDIB _extended(_data + currentPos);
|
||||
_extended.length(LEN_EXTENDED_DEVICE_INFORMATION_DIB);
|
||||
_extended.code(EXTENDED_DEVICE_INFO);
|
||||
_extended.status(0x01); //FIXME dont know encoding PID_MEDIUM_STATUS=51 RouterObject
|
||||
_extended.localMaxApdu(254); //FIXME is this correct?
|
||||
_extended.deviceDescriptor(MASK_VERSION);
|
||||
|
||||
currentPos += LEN_EXTENDED_DEVICE_INFORMATION_DIB;
|
||||
}
|
||||
|
||||
IpHostProtocolAddressInformation& KnxIpSearchResponseExtended::controlEndpoint()
|
||||
{
|
||||
return _controlEndpoint;
|
||||
}
|
||||
|
||||
|
||||
uint8_t *KnxIpSearchResponseExtended::DIBs()
|
||||
{
|
||||
return _data + LEN_KNXIP_HEADER + LEN_IPHPAI;
|
||||
}
|
||||
#endif
|
||||
#endif
|
38
src/knx/knx_ip_search_response_extended.h
Normal file
38
src/knx/knx_ip_search_response_extended.h
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "service_families.h"
|
||||
#if KNX_SERVICE_FAMILY_CORE >= 2
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "ip_host_protocol_address_information.h"
|
||||
#include "knx_ip_device_information_dib.h"
|
||||
#include "knx_ip_extended_device_information_dib.h"
|
||||
#include "knx_ip_supported_service_dib.h"
|
||||
#include "knx_ip_config_dib.h"
|
||||
#include "knx_ip_knx_addresses_dib.h"
|
||||
#include "knx_ip_tunneling_info_dib.h"
|
||||
#include "ip_parameter_object.h"
|
||||
#include "knx_ip_tunnel_connection.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
class KnxIpSearchResponseExtended : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpSearchResponseExtended(IpParameterObject& parameters, DeviceObject& deviceObj, int dibLength);
|
||||
IpHostProtocolAddressInformation& controlEndpoint();
|
||||
void setDeviceInfo(IpParameterObject& parameters, DeviceObject& deviceObject);
|
||||
void setSupportedServices();
|
||||
void setIpConfig(IpParameterObject& parameters);
|
||||
void setIpCurrentConfig(IpParameterObject& parameters);
|
||||
void setKnxAddresses(IpParameterObject& parameters, DeviceObject& deviceObject);
|
||||
//setManuData
|
||||
void setTunnelingInfo(IpParameterObject& parameters, DeviceObject& deviceObject, KnxIpTunnelConnection tunnels[]);
|
||||
void setExtendedDeviceInfo();
|
||||
uint8_t *DIBs();
|
||||
private:
|
||||
IpHostProtocolAddressInformation _controlEndpoint;
|
||||
int currentPos = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
16
src/knx/knx_ip_state_request.cpp
Normal file
16
src/knx/knx_ip_state_request.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "knx_ip_state_request.h"
|
||||
#ifdef USE_IP
|
||||
KnxIpStateRequest::KnxIpStateRequest(uint8_t* data, uint16_t length)
|
||||
: KnxIpFrame(data, length), _hpaiCtrl(data + LEN_KNXIP_HEADER + 1 /*ChannelId*/ + 1 /*Reserved*/)
|
||||
{
|
||||
}
|
||||
|
||||
IpHostProtocolAddressInformation& KnxIpStateRequest::hpaiCtrl()
|
||||
{
|
||||
return _hpaiCtrl;
|
||||
}
|
||||
uint8_t KnxIpStateRequest::channelId()
|
||||
{
|
||||
return _data[LEN_KNXIP_HEADER];
|
||||
}
|
||||
#endif
|
17
src/knx/knx_ip_state_request.h
Normal file
17
src/knx/knx_ip_state_request.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "knx_ip_cri.h"
|
||||
#include "ip_host_protocol_address_information.h"
|
||||
#ifdef USE_IP
|
||||
class KnxIpStateRequest : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpStateRequest(uint8_t* data, uint16_t length);
|
||||
IpHostProtocolAddressInformation& hpaiCtrl();
|
||||
uint8_t channelId();
|
||||
private:
|
||||
IpHostProtocolAddressInformation _hpaiCtrl;
|
||||
|
||||
};
|
||||
#endif
|
26
src/knx/knx_ip_state_response.cpp
Normal file
26
src/knx/knx_ip_state_response.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "knx_ip_state_response.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
#define LEN_SERVICE_FAMILIES 2
|
||||
#if MASK_VERSION == 0x091A
|
||||
#ifdef KNX_TUNNELING
|
||||
#define LEN_SERVICE_DIB (2 + 4 * LEN_SERVICE_FAMILIES)
|
||||
#else
|
||||
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
|
||||
#endif
|
||||
#else
|
||||
#ifdef KNX_TUNNELING
|
||||
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
|
||||
#else
|
||||
#define LEN_SERVICE_DIB (2 + 2 * LEN_SERVICE_FAMILIES)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
KnxIpStateResponse::KnxIpStateResponse(uint8_t channelId, uint8_t errorCode)
|
||||
: KnxIpFrame(LEN_KNXIP_HEADER + 2)
|
||||
{
|
||||
serviceTypeIdentifier(ConnectionStateResponse);
|
||||
_data[LEN_KNXIP_HEADER] = channelId;
|
||||
_data[LEN_KNXIP_HEADER + 1] = errorCode;
|
||||
}
|
||||
#endif
|
13
src/knx/knx_ip_state_response.h
Normal file
13
src/knx/knx_ip_state_response.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
class KnxIpStateResponse : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpStateResponse(uint8_t channelId, uint8_t errorCode);
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
19
src/knx/knx_ip_tunnel_connection.cpp
Normal file
19
src/knx/knx_ip_tunnel_connection.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include "knx_ip_tunnel_connection.h"
|
||||
|
||||
KnxIpTunnelConnection::KnxIpTunnelConnection()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void KnxIpTunnelConnection::Reset()
|
||||
{
|
||||
ChannelId = 0;
|
||||
IpAddress = 0;
|
||||
PortData = 0;
|
||||
PortCtrl = 0;
|
||||
lastHeartbeat = 0;
|
||||
SequenceCounter_S = 0;
|
||||
SequenceCounter_R = 255;
|
||||
IndividualAddress = 0;
|
||||
IsConfig = false;
|
||||
}
|
24
src/knx/knx_ip_tunnel_connection.h
Normal file
24
src/knx/knx_ip_tunnel_connection.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "config.h"
|
||||
#include "platform.h"
|
||||
#include "bits.h"
|
||||
|
||||
class KnxIpTunnelConnection
|
||||
{
|
||||
public:
|
||||
KnxIpTunnelConnection();
|
||||
uint8_t ChannelId = 0;
|
||||
uint16_t IndividualAddress = 0;
|
||||
uint32_t IpAddress = 0;
|
||||
uint16_t PortData = 0;
|
||||
uint16_t PortCtrl = 0;
|
||||
uint8_t SequenceCounter_S = 0;
|
||||
uint8_t SequenceCounter_R = 255;
|
||||
unsigned long lastHeartbeat = 0;
|
||||
bool IsConfig = false;
|
||||
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
|
||||
};
|
20
src/knx/knx_ip_tunneling_ack.cpp
Normal file
20
src/knx/knx_ip_tunneling_ack.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include "knx_ip_tunneling_ack.h"
|
||||
#include <cstring>
|
||||
|
||||
#ifdef USE_IP
|
||||
KnxIpTunnelingAck::KnxIpTunnelingAck(uint8_t* data,
|
||||
uint16_t length) : KnxIpFrame(data, length), _ch(_data + LEN_KNXIP_HEADER)
|
||||
{
|
||||
}
|
||||
|
||||
KnxIpTunnelingAck::KnxIpTunnelingAck()
|
||||
: KnxIpFrame(LEN_KNXIP_HEADER + LEN_CH), _ch(_data + LEN_KNXIP_HEADER)
|
||||
{
|
||||
serviceTypeIdentifier(TunnelingAck);
|
||||
}
|
||||
|
||||
KnxIpCH& KnxIpTunnelingAck::connectionHeader()
|
||||
{
|
||||
return _ch;
|
||||
}
|
||||
#endif
|
17
src/knx/knx_ip_tunneling_ack.h
Normal file
17
src/knx/knx_ip_tunneling_ack.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "cemi_frame.h"
|
||||
#include "knx_ip_ch.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
class KnxIpTunnelingAck : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpTunnelingAck(uint8_t* data, uint16_t length);
|
||||
KnxIpTunnelingAck();
|
||||
KnxIpCH& connectionHeader();
|
||||
private:
|
||||
KnxIpCH _ch;
|
||||
};
|
||||
#endif
|
31
src/knx/knx_ip_tunneling_info_dib.cpp
Normal file
31
src/knx/knx_ip_tunneling_info_dib.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "knx_ip_tunneling_info_dib.h"
|
||||
#include "service_families.h"
|
||||
#if KNX_SERVICE_FAMILY_CORE >= 2
|
||||
|
||||
#ifdef USE_IP
|
||||
KnxIpTunnelingInfoDIB::KnxIpTunnelingInfoDIB(uint8_t* data) : KnxIpDIB(data)
|
||||
{
|
||||
currentPos = data + 4;
|
||||
}
|
||||
|
||||
uint16_t KnxIpTunnelingInfoDIB::apduLength()
|
||||
{
|
||||
uint16_t addr = 0;
|
||||
popWord(addr, _data+2);
|
||||
return addr;
|
||||
}
|
||||
|
||||
void KnxIpTunnelingInfoDIB::apduLength(uint16_t addr)
|
||||
{
|
||||
pushWord(addr, _data+2);
|
||||
}
|
||||
|
||||
void KnxIpTunnelingInfoDIB::tunnelingSlot(uint16_t addr, uint16_t state)
|
||||
{
|
||||
pushWord(addr, currentPos);
|
||||
pushWord(state, currentPos + 2);
|
||||
currentPos += 4;
|
||||
length(currentPos - _data);
|
||||
}
|
||||
#endif
|
||||
#endif
|
20
src/knx/knx_ip_tunneling_info_dib.h
Normal file
20
src/knx/knx_ip_tunneling_info_dib.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "knx_ip_dib.h"
|
||||
#include "bits.h"
|
||||
#include "service_families.h"
|
||||
#if KNX_SERVICE_FAMILY_CORE >= 2
|
||||
|
||||
#ifdef USE_IP
|
||||
|
||||
class KnxIpTunnelingInfoDIB : public KnxIpDIB
|
||||
{
|
||||
public:
|
||||
KnxIpTunnelingInfoDIB(uint8_t* data);
|
||||
uint16_t apduLength();
|
||||
void apduLength(uint16_t addr);
|
||||
void tunnelingSlot(uint16_t addr, uint16_t state);
|
||||
private:
|
||||
uint8_t *currentPos = 0;
|
||||
};
|
||||
#endif
|
||||
#endif
|
26
src/knx/knx_ip_tunneling_request.cpp
Normal file
26
src/knx/knx_ip_tunneling_request.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "knx_ip_tunneling_request.h"
|
||||
#include <cstring>
|
||||
|
||||
#ifdef USE_IP
|
||||
KnxIpTunnelingRequest::KnxIpTunnelingRequest(uint8_t* data,
|
||||
uint16_t length) : KnxIpFrame(data, length), _ch(_data + headerLength()), _frame(data + LEN_CH + headerLength(), length - LEN_CH - headerLength())
|
||||
{
|
||||
}
|
||||
|
||||
KnxIpTunnelingRequest::KnxIpTunnelingRequest(CemiFrame frame)
|
||||
: KnxIpFrame(frame.totalLenght() + LEN_CH + LEN_KNXIP_HEADER), _ch(_data + LEN_KNXIP_HEADER), _frame(_data + LEN_CH + LEN_KNXIP_HEADER, frame.totalLenght())
|
||||
{
|
||||
serviceTypeIdentifier(TunnelingRequest);
|
||||
memcpy(_data + LEN_KNXIP_HEADER + LEN_CH, frame.data(), frame.totalLenght());
|
||||
}
|
||||
|
||||
CemiFrame& KnxIpTunnelingRequest::frame()
|
||||
{
|
||||
return _frame;
|
||||
}
|
||||
|
||||
KnxIpCH& KnxIpTunnelingRequest::connectionHeader()
|
||||
{
|
||||
return _ch;
|
||||
}
|
||||
#endif
|
19
src/knx/knx_ip_tunneling_request.h
Normal file
19
src/knx/knx_ip_tunneling_request.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "cemi_frame.h"
|
||||
#include "knx_ip_ch.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
class KnxIpTunnelingRequest : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpTunnelingRequest(uint8_t* data, uint16_t length);
|
||||
KnxIpTunnelingRequest(CemiFrame frame);
|
||||
CemiFrame& frame();
|
||||
KnxIpCH& connectionHeader();
|
||||
private:
|
||||
CemiFrame _frame;
|
||||
KnxIpCH _ch;
|
||||
};
|
||||
#endif
|
@ -20,6 +20,15 @@ enum AckType
|
||||
AckRequested = 0x2, //!< We want a DataLinkLayer acknowledgement.
|
||||
};
|
||||
|
||||
enum TPAckType
|
||||
{
|
||||
// see U_ACK_REQ defines in tpuart_data_link_layer.cpp
|
||||
AckReqNack = 0x04,
|
||||
AckReqBusy = 0x02,
|
||||
AckReqAck = 0x01,
|
||||
AckReqNone = 0x0,
|
||||
};
|
||||
|
||||
enum AddressType
|
||||
{
|
||||
IndividualAddress = 0,
|
||||
@ -190,6 +199,12 @@ enum ApduType
|
||||
DeviceDescriptorResponse = 0x340,
|
||||
Restart = 0x380,
|
||||
RestartMasterReset = 0x381,
|
||||
RoutingTableOpen = 0x3C0,
|
||||
RoutingTableRead = 0x3C1,
|
||||
RoutingTableReadResponse = 0x3C2,
|
||||
RoutingTableWrite = 0x3C3,
|
||||
MemoryRouterWrite = 0x3CA,
|
||||
MemoryRouterReadResponse = 0x3C9,
|
||||
AuthorizeRequest = 0x3d1,
|
||||
AuthorizeResponse = 0x3d2,
|
||||
KeyWrite = 0x3d3,
|
||||
@ -244,3 +259,32 @@ enum DptMedium
|
||||
KNX_RF = 0x02,
|
||||
KNX_IP = 0x05
|
||||
};
|
||||
|
||||
enum LCGRPCONFIG
|
||||
{
|
||||
GROUP_6FFF = 0b00000011,
|
||||
GROUP_7000 = 0b00001100,
|
||||
GROUP_REPEAT = 0b00010000,
|
||||
GROUP_6FFFUNLOCK = 0b00000001,
|
||||
GROUP_6FFFLOCK = 0b00000010,
|
||||
GROUP_6FFFROUTE = 0b00000011,
|
||||
GROUP_7000UNLOCK = 0b00000100,
|
||||
GROUP_7000LOCK = 0b00001000,
|
||||
GROUP_7000ROUTE = 0b00001100
|
||||
};
|
||||
|
||||
enum LCCONFIG
|
||||
{
|
||||
PHYS_FRAME = 0b00000011,
|
||||
PHYS_FRAME_UNLOCK = 0b00000001,
|
||||
PHYS_FRAME_LOCK = 0b00000010,
|
||||
PHYS_FRAME_ROUT = 0b00000011,
|
||||
PHYS_REPEAT = 0b00000100,
|
||||
BROADCAST_LOCK = 0b00001000,
|
||||
BROADCAST_REPEAT = 0b00010000,
|
||||
GROUP_IACK_ROUT = 0b00100000,
|
||||
PHYS_IACK = 0b11000000,
|
||||
PHYS_IACK_NORMAL = 0b01000000,
|
||||
PHYS_IACK_ALL = 0b10000000,
|
||||
PHYS_IACK_NACK = 0b11000000
|
||||
};
|
@ -114,7 +114,7 @@ void Memory::readMemory()
|
||||
buffer = _tableObjects[i]->restore(buffer);
|
||||
uint16_t memorySize = 0;
|
||||
buffer = popWord(memorySize, buffer);
|
||||
|
||||
println(memorySize);
|
||||
if (memorySize == 0)
|
||||
continue;
|
||||
|
||||
@ -279,6 +279,11 @@ void Memory::writeMemory(uint32_t relativeAddress, size_t size, uint8_t* data)
|
||||
_platform.writeNonVolatileMemory(relativeAddress, data, size);
|
||||
}
|
||||
|
||||
void Memory::readMemory(uint32_t relativeAddress, size_t size, uint8_t* data)
|
||||
{
|
||||
_platform.readNonVolatileMemory(relativeAddress, data, size);
|
||||
}
|
||||
|
||||
|
||||
uint8_t* Memory::toAbsolute(uint32_t relativeAddress)
|
||||
{
|
||||
|
@ -35,7 +35,9 @@ typedef VersionCheckResult (*VersionCheckCallback)(uint16_t manufacturerId, uint
|
||||
|
||||
class Memory
|
||||
{
|
||||
public:
|
||||
friend class TableObject;
|
||||
|
||||
public:
|
||||
Memory(Platform& platform, DeviceObject& deviceObject);
|
||||
virtual ~Memory();
|
||||
void readMemory();
|
||||
@ -47,6 +49,7 @@ public:
|
||||
uint8_t* allocMemory(size_t size);
|
||||
void freeMemory(uint8_t* ptr);
|
||||
void writeMemory(uint32_t relativeAddress, size_t size, uint8_t* data);
|
||||
void readMemory(uint32_t relativeAddress, size_t size, uint8_t* data);
|
||||
uint8_t* toAbsolute(uint32_t relativeAddress);
|
||||
uint32_t toRelative(uint8_t* absoluteAddress);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "network_layer_coupler.h"
|
||||
#include "data_link_layer.h"
|
||||
#include "device_object.h"
|
||||
#include "router_object.h"
|
||||
#include "tpdu.h"
|
||||
@ -86,29 +87,202 @@ bool NetworkLayerCoupler::isGroupAddressInFilterTable(uint16_t groupAddress)
|
||||
}
|
||||
}
|
||||
|
||||
bool NetworkLayerCoupler::isRoutedIndividualAddress(uint16_t individualAddress)
|
||||
bool NetworkLayerCoupler::isRoutedGroupAddress(uint16_t groupAddress, uint8_t sourceInterfaceIndex)
|
||||
{
|
||||
// TODO: ACKs for frames with individual addresses of the sub line (secondary I/F)
|
||||
// Check spec. about his
|
||||
// See PID_MAIN_LCCONFIG/PID_SUB_LCCONFIG: PHYS_IACK
|
||||
// 0 = not used
|
||||
// 1 = normal mode (all frames that will be routed or that are addressed to the Coupler itself will be acknowledged)
|
||||
// 2 = all frames will be acknowledged (useful only to avoid the repetitions of misrouted frames)
|
||||
// 3 = all frames on point-to-point connectionless – or connection-oriented communication mode shall be negatively acknowledge (NACK).
|
||||
// This shall serve for protection purposes. (It is useful to prevent all parameterisation in one Subnetwork; the Coupler shall be protected
|
||||
// too. A typical use case is the protection of a Subnetwork that is located outside a building)
|
||||
uint8_t lcconfig = LCCONFIG::PHYS_FRAME_ROUT | LCCONFIG::PHYS_REPEAT | LCCONFIG::BROADCAST_REPEAT | LCCONFIG::GROUP_IACK_ROUT | LCCONFIG::PHYS_IACK_NORMAL; // default value from spec. in case prop is not availible.
|
||||
uint8_t lcgrpconfig = LCGRPCONFIG::GROUP_6FFFROUTE | LCGRPCONFIG::GROUP_7000UNLOCK | LCGRPCONFIG::GROUP_REPEAT; // default value from spec. in case prop is not availible.
|
||||
Property* prop_lcgrpconfig;
|
||||
Property* prop_lcconfig;
|
||||
|
||||
// Also ACK for our own individual address
|
||||
if (individualAddress == _deviceObj.individualAddress())
|
||||
return true;
|
||||
if(sourceInterfaceIndex == kPrimaryIfIndex) // direction Prim -> Sec ( e.g. IP -> TP)
|
||||
{
|
||||
prop_lcgrpconfig = _rtObjPrimary->property(PID_MAIN_LCGRPCONFIG);
|
||||
prop_lcconfig = _rtObjPrimary->property(PID_MAIN_LCCONFIG);
|
||||
}
|
||||
else // direction Sec -> Prim ( e.g. TP -> IP)
|
||||
{
|
||||
prop_lcgrpconfig = _rtObjPrimary->property(PID_SUB_LCGRPCONFIG);
|
||||
prop_lcconfig = _rtObjPrimary->property(PID_SUB_LCCONFIG);
|
||||
}
|
||||
if(prop_lcgrpconfig)
|
||||
prop_lcgrpconfig->read(lcgrpconfig);
|
||||
|
||||
if(prop_lcconfig)
|
||||
prop_lcconfig->read(lcconfig);
|
||||
|
||||
|
||||
if(groupAddress < 0x7000) // Main group 0-13
|
||||
{
|
||||
// PID_SUB_LCGRPCONFIG Bit 0-1
|
||||
switch(lcgrpconfig & LCGRPCONFIG::GROUP_6FFF)
|
||||
{
|
||||
case LCGRPCONFIG::GROUP_6FFFLOCK:
|
||||
//printHex("1drop frame to 0x", (uint8_t*)destination, 2);
|
||||
return false;//drop
|
||||
break;
|
||||
case LCGRPCONFIG::GROUP_6FFFROUTE:
|
||||
if(isGroupAddressInFilterTable(groupAddress))
|
||||
;//send
|
||||
else
|
||||
{
|
||||
//printHex("2drop frame to 0x", (uint8_t*)destination, 2);
|
||||
return false;//drop
|
||||
}
|
||||
break;
|
||||
default: // LCGRPCONFIG::GROUP_6FFFUNLOCK
|
||||
;//send
|
||||
}
|
||||
}
|
||||
else // Main group 14-31
|
||||
{
|
||||
// PID_SUB_LCGRPCONFIG Bit 2-3 LCGRPCONFIG::GROUP_7000
|
||||
switch(lcgrpconfig & LCGRPCONFIG::GROUP_7000)
|
||||
{
|
||||
case LCGRPCONFIG::GROUP_7000LOCK:
|
||||
//printHex("3drop frame to 0x", (uint8_t*)destination, 2);
|
||||
return false;//drop
|
||||
break;
|
||||
case LCGRPCONFIG::GROUP_7000ROUTE:
|
||||
if(isGroupAddressInFilterTable(groupAddress))
|
||||
;//send
|
||||
else
|
||||
{
|
||||
//printHex("4drop frame to 0x", (uint8_t*)destination, 2);
|
||||
return false;//drop
|
||||
}
|
||||
break;
|
||||
default: // LCGRPCONFIG::GROUP_7000UNLOCK
|
||||
;//send
|
||||
}
|
||||
}
|
||||
|
||||
// use 2 for now
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NetworkLayerCoupler::isRoutedIndividualAddress(uint16_t individualAddress, uint8_t srcIfIndex)
|
||||
{
|
||||
// TODO: improve: we have to be notified about anything that might affect routing decision
|
||||
// Ugly: we could ALWAYS evaluate coupler type for every received frame
|
||||
if (_currentAddress != _deviceObj.individualAddress())
|
||||
{
|
||||
evaluateCouplerType();
|
||||
}
|
||||
|
||||
// See KNX spec.: Network Layer (03/03/03) and AN161 (Coupler model 2.0)
|
||||
/*
|
||||
* C hop count value contained in the N-protocol header
|
||||
* D low order octet of the Destination Address, i.e. Device Address part
|
||||
* G Group Address
|
||||
* SD low nibble of high order octet plus low order octet, i.e. Line Address + Device Address
|
||||
* Z high nibble of high order octet of the Destination Address, i.e. Area Address
|
||||
* ZS high order octet of the Destination Address, i.e. hierarchy information part: Area Address + Line Address
|
||||
*/
|
||||
uint16_t ownSNA = _deviceObj.individualAddress() & 0xFF00; // Own subnetwork address (area + line)
|
||||
uint16_t ownAA = _deviceObj.individualAddress() & 0xF000; // Own area address
|
||||
uint16_t ZS = individualAddress & 0xFF00; // destination subnetwork address (area + line)
|
||||
uint16_t Z = individualAddress & 0xF000; // destination area address
|
||||
|
||||
|
||||
if (_couplerType == LineCoupler)
|
||||
{
|
||||
// Main line to sub line routing
|
||||
if (srcIfIndex == kPrimaryIfIndex)
|
||||
{
|
||||
if (ZS != ownSNA)
|
||||
{
|
||||
// IGNORE_TOTALLY
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (srcIfIndex == kSecondaryIfIndex) // Sub line to main line routing
|
||||
{
|
||||
if (ZS != ownSNA)
|
||||
{
|
||||
// ROUTE_XXX
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//not from primiary not from sec if, should not happen
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (_couplerType == BackboneCoupler)
|
||||
{
|
||||
// Backbone line to main line routing
|
||||
if (srcIfIndex == kPrimaryIfIndex)
|
||||
{
|
||||
if (Z != ownAA)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (srcIfIndex == kSecondaryIfIndex) // Main line to backbone line routing
|
||||
{
|
||||
if (Z != ownAA)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//not from primiary not from sec if, should not happen
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//unknown coupler type, should not happen
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkLayerCoupler::sendMsgHopCount(AckType ack, AddressType addrType, uint16_t destination, NPDU& npdu, Priority priority,
|
||||
SystemBroadcast broadcastType, uint8_t sourceInterfaceIndex, uint16_t source)
|
||||
{
|
||||
uint8_t interfaceIndex = (sourceInterfaceIndex == kSecondaryIfIndex) ? kPrimaryIfIndex : kSecondaryIfIndex;
|
||||
|
||||
uint8_t lcconfig = LCCONFIG::PHYS_FRAME_ROUT | LCCONFIG::PHYS_REPEAT | LCCONFIG::BROADCAST_REPEAT | LCCONFIG::GROUP_IACK_ROUT | LCCONFIG::PHYS_IACK_NORMAL; // default value from spec. in case prop is not availible.
|
||||
uint8_t lcgrpconfig = LCGRPCONFIG::GROUP_6FFFROUTE | LCGRPCONFIG::GROUP_7000UNLOCK | LCGRPCONFIG::GROUP_REPEAT; // default value from spec. in case prop is not availible.
|
||||
Property* prop_lcgrpconfig;
|
||||
Property* prop_lcconfig;
|
||||
|
||||
if(sourceInterfaceIndex == kPrimaryIfIndex) // direction Prim -> Sec ( e.g. IP -> TP)
|
||||
{
|
||||
prop_lcgrpconfig = _rtObjPrimary->property(PID_MAIN_LCGRPCONFIG);
|
||||
prop_lcconfig = _rtObjPrimary->property(PID_MAIN_LCCONFIG);
|
||||
}
|
||||
else // direction Sec -> Prim ( e.g. TP -> IP)
|
||||
{
|
||||
prop_lcgrpconfig = _rtObjPrimary->property(PID_SUB_LCGRPCONFIG);
|
||||
prop_lcconfig = _rtObjPrimary->property(PID_SUB_LCCONFIG);
|
||||
}
|
||||
if(prop_lcgrpconfig)
|
||||
prop_lcgrpconfig->read(lcgrpconfig);
|
||||
|
||||
if(prop_lcconfig)
|
||||
prop_lcconfig->read(lcconfig);
|
||||
|
||||
|
||||
if(addrType == AddressType::GroupAddress && destination != 0) // destination == 0 means broadcast and must not be filtered with the GroupAddresses
|
||||
{
|
||||
if(!isRoutedGroupAddress(destination, sourceInterfaceIndex))
|
||||
return; // drop;
|
||||
}
|
||||
|
||||
|
||||
// If we have a frame from open medium on secondary side (e.g. RF) to primary side, then shall use the hop count of the primary router object
|
||||
if ((_rtObjPrimary != nullptr) && (_rtObjSecondary != nullptr) && (sourceInterfaceIndex == kSecondaryIfIndex))
|
||||
{
|
||||
@ -138,10 +312,10 @@ void NetworkLayerCoupler::sendMsgHopCount(AckType ack, AddressType addrType, uin
|
||||
{
|
||||
// ROUTE_UNMODIFIED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use other interface
|
||||
uint8_t interfaceIndex = (sourceInterfaceIndex == kSecondaryIfIndex) ? kPrimaryIfIndex : kSecondaryIfIndex;
|
||||
#ifdef KNX_LOG_COUPLER
|
||||
if (sourceInterfaceIndex == 0)
|
||||
print("Routing from P->S: ");
|
||||
else
|
||||
@ -149,144 +323,104 @@ void NetworkLayerCoupler::sendMsgHopCount(AckType ack, AddressType addrType, uin
|
||||
print(source, HEX); print(" -> "); print(destination, HEX);
|
||||
print(" - ");
|
||||
npdu.frame().apdu().printPDU();
|
||||
_netLayerEntities[interfaceIndex].sendDataRequest(npdu, ack, destination, source, priority, addrType, broadcastType);
|
||||
#endif
|
||||
|
||||
//evaluiate PHYS_REPEAT, BROADCAST_REPEAT and GROUP_REPEAT
|
||||
bool doNotRepeat = false;
|
||||
if((addrType == AddressType::GroupAddress && !(lcgrpconfig & LCGRPCONFIG::GROUP_REPEAT)) ||
|
||||
(addrType == AddressType::IndividualAddress && !(lcconfig & LCCONFIG::PHYS_REPEAT)) ||
|
||||
(addrType == AddressType::GroupAddress && destination == 0 && !(lcconfig & LCCONFIG::BROADCAST_REPEAT)))
|
||||
doNotRepeat = true;
|
||||
|
||||
_netLayerEntities[interfaceIndex].sendDataRequest(npdu, ack, destination, source, priority, addrType, broadcastType, doNotRepeat);
|
||||
}
|
||||
|
||||
// TODO: for later: improve by putting routing algorithms in its own class/functions and only instantiate required algorithm (line vs. coupler)
|
||||
// TODO: we could also do the sanity checks here, i.e. check if sourceAddress is really coming in from correct srcIfIdx, etc. (see PID_COUPL_SERV_CONTROL: EN_SNA_INCONSISTENCY_CHECK)
|
||||
void NetworkLayerCoupler::routeDataIndividual(AckType ack, uint16_t destination, NPDU& npdu, Priority priority, uint16_t source, uint8_t srcIfIndex)
|
||||
{
|
||||
// TODO: improve: we have to be notified about anything that might affect routing decision
|
||||
// Ugly: we could ALWAYS evaluate coupler type for every received frame
|
||||
if (_currentAddress != _deviceObj.individualAddress())
|
||||
//print("NetworkLayerCoupler::routeDataIndividual dest 0x");
|
||||
//print(destination, HEX);
|
||||
//print(" own addr 0x");
|
||||
//println(_deviceObj.individualAddress(), HEX);
|
||||
|
||||
if(destination == _deviceObj.individualAddress())
|
||||
{
|
||||
evaluateCouplerType();
|
||||
// FORWARD_LOCALLY
|
||||
//println("NetworkLayerCoupler::routeDataIndividual locally");
|
||||
HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter;
|
||||
_transportLayer.dataIndividualIndication(destination, hopType, priority, source, npdu.tpdu());
|
||||
return;
|
||||
}
|
||||
|
||||
// See KNX spec.: Network Layer (03/03/03) and AN161 (Coupler model 2.0)
|
||||
/*
|
||||
* C hop count value contained in the N-protocol header
|
||||
* D low order octet of the Destination Address, i.e. Device Address part
|
||||
* G Group Address
|
||||
* SD low nibble of high order octet plus low order octet, i.e. Line Address + Device Address
|
||||
* Z high nibble of high order octet of the Destination Address, i.e. Area Address
|
||||
* ZS high order octet of the Destination Address, i.e. hierarchy information part: Area Address + Line Address
|
||||
*/
|
||||
uint16_t ownSNA = _deviceObj.individualAddress() & 0xFF00; // Own subnetwork address (area + line)
|
||||
uint16_t ownAA = _deviceObj.individualAddress() & 0xF000; // Own area address
|
||||
uint16_t ZS = destination & 0xFF00; // destination subnetwork address (area + line)
|
||||
uint16_t Z = destination & 0xF000; // destination area address
|
||||
uint16_t D = destination & 0x00FF; // destination device address (without subnetwork part)
|
||||
uint16_t SD = destination & 0x0FFF; // destination device address (with line part, but without area part)
|
||||
|
||||
if (_couplerType == LineCoupler)
|
||||
// Local to main or sub line
|
||||
if (srcIfIndex == kLocalIfIndex)
|
||||
{
|
||||
// Main line to sub line routing
|
||||
if (srcIfIndex == kPrimaryIfIndex)
|
||||
uint16_t netaddr;
|
||||
uint16_t Z;
|
||||
if(_couplerType == CouplerType::BackboneCoupler)
|
||||
{
|
||||
if (ZS != ownSNA)
|
||||
{
|
||||
// IGNORE_TOTALLY
|
||||
return;
|
||||
}
|
||||
|
||||
if (D == 0)
|
||||
{
|
||||
// FORWARD_LOCALLY
|
||||
HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter;
|
||||
_transportLayer.dataIndividualIndication(destination, hopType, priority, source, npdu.tpdu());
|
||||
}
|
||||
else
|
||||
{ // ROUTE_XXX
|
||||
sendMsgHopCount(ack, AddressType::IndividualAddress, destination, npdu, priority, Broadcast, srcIfIndex, source);
|
||||
}
|
||||
return;
|
||||
netaddr = _deviceObj.individualAddress() & 0xF000;
|
||||
Z = destination & 0xF000;
|
||||
}
|
||||
|
||||
// Sub line to main line routing
|
||||
if (srcIfIndex == kSecondaryIfIndex)
|
||||
else if(_couplerType == CouplerType::LineCoupler)
|
||||
{
|
||||
if (ZS != ownSNA)
|
||||
{
|
||||
// ROUTE_XXX
|
||||
sendMsgHopCount(ack, AddressType::IndividualAddress, destination, npdu, priority, Broadcast, srcIfIndex, source);
|
||||
}
|
||||
else if (D == 0)
|
||||
{
|
||||
// FORWARD_LOCALLY
|
||||
HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter;
|
||||
_transportLayer.dataIndividualIndication(destination, hopType, priority, source, npdu.tpdu());
|
||||
}
|
||||
else
|
||||
{
|
||||
// IGNORE_TOTALLY
|
||||
}
|
||||
return;
|
||||
netaddr = _deviceObj.individualAddress() & 0xFF00;
|
||||
Z = destination & 0xFF00;
|
||||
}
|
||||
|
||||
// Local to main or sub line
|
||||
if (srcIfIndex == kLocalIfIndex)
|
||||
else
|
||||
{
|
||||
// if destination is not within our subnet then send via primary interface, else via secondary interface
|
||||
uint8_t destIfidx = (ZS != ownSNA) ? kPrimaryIfIndex : kSecondaryIfIndex;
|
||||
_netLayerEntities[destIfidx].sendDataRequest(npdu, ack, destination, source, priority, AddressType::IndividualAddress, Broadcast);
|
||||
return;
|
||||
//unknown coupler type, should not happen
|
||||
return ;
|
||||
}
|
||||
|
||||
|
||||
// if destination is not within our scope then send via primary interface, else via secondary interface
|
||||
uint8_t destIfidx = (Z != netaddr) ? kPrimaryIfIndex : kSecondaryIfIndex;
|
||||
#ifdef KNX_TUNNELING
|
||||
if(destIfidx == kPrimaryIfIndex)
|
||||
if(isTunnelAddress(destination))
|
||||
destIfidx = kSecondaryIfIndex;
|
||||
#endif
|
||||
//print("NetworkLayerCoupler::routeDataIndividual local to s or p: ");
|
||||
//println(destIfidx);
|
||||
_netLayerEntities[destIfidx].sendDataRequest(npdu, ack, destination, source, priority, AddressType::IndividualAddress, Broadcast);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_couplerType == BackboneCoupler)
|
||||
uint8_t lcconfig = LCCONFIG::PHYS_FRAME_ROUT | LCCONFIG::PHYS_REPEAT | LCCONFIG::BROADCAST_REPEAT | LCCONFIG::GROUP_IACK_ROUT | LCCONFIG::PHYS_IACK_NORMAL; // default value from spec. in case prop is not availible.
|
||||
Property* prop_lcconfig;
|
||||
if(srcIfIndex == kPrimaryIfIndex) // direction Prim -> Sec ( e.g. IP -> TP)
|
||||
prop_lcconfig = _rtObjPrimary->property(PID_MAIN_LCCONFIG);
|
||||
else // direction Sec -> Prim ( e.g. TP -> IP)
|
||||
prop_lcconfig = _rtObjPrimary->property(PID_SUB_LCCONFIG);
|
||||
if(prop_lcconfig)
|
||||
prop_lcconfig->read(lcconfig);
|
||||
|
||||
if((lcconfig & LCCONFIG::PHYS_FRAME) == LCCONFIG::PHYS_FRAME_LOCK)
|
||||
{
|
||||
// Backbone line to main line routing
|
||||
if (srcIfIndex == kPrimaryIfIndex)
|
||||
// IGNORE_TOTALLY
|
||||
//println("NetworkLayerCoupler::routeDataIndividual locked");
|
||||
return;
|
||||
}
|
||||
else if((lcconfig & LCCONFIG::PHYS_FRAME) == LCCONFIG::PHYS_FRAME_UNLOCK)
|
||||
{
|
||||
// ROUTE_XXX
|
||||
//println("NetworkLayerCoupler::routeDataIndividual unlocked");
|
||||
sendMsgHopCount(ack, AddressType::IndividualAddress, destination, npdu, priority, Broadcast, srcIfIndex, source);
|
||||
return;
|
||||
}
|
||||
else // LCCONFIG::PHYS_FRAME_ROUTE or 0
|
||||
{
|
||||
if(isRoutedIndividualAddress(destination, srcIfIndex))
|
||||
{
|
||||
if (Z != ownAA)
|
||||
{
|
||||
// IGNORE_TOTALLY
|
||||
return;
|
||||
}
|
||||
|
||||
if (SD == 0)
|
||||
{
|
||||
// FORWARD_LOCALLY
|
||||
HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter;
|
||||
_transportLayer.dataIndividualIndication(destination, hopType, priority, source, npdu.tpdu());
|
||||
}
|
||||
else
|
||||
{
|
||||
// ROUTE_XXX
|
||||
sendMsgHopCount(ack, AddressType::IndividualAddress, destination, npdu, priority, Broadcast, srcIfIndex, source);
|
||||
}
|
||||
return;
|
||||
//println("NetworkLayerCoupler::routeDataIndividual routed");
|
||||
sendMsgHopCount(ack, AddressType::IndividualAddress, destination, npdu, priority, Broadcast, srcIfIndex, source); // ROUTE_XXX
|
||||
}
|
||||
|
||||
// Main line to backbone line routing
|
||||
if (srcIfIndex == kSecondaryIfIndex)
|
||||
else
|
||||
{
|
||||
if (Z != ownAA)
|
||||
{
|
||||
// ROUTE_XXX
|
||||
sendMsgHopCount(ack, AddressType::IndividualAddress, destination, npdu, priority, Broadcast, srcIfIndex, source);
|
||||
}
|
||||
else if(SD == 0)
|
||||
{
|
||||
// FORWARD_LOCALLY
|
||||
HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter;
|
||||
_transportLayer.dataIndividualIndication(destination, hopType, priority, source, npdu.tpdu());
|
||||
}
|
||||
else
|
||||
{
|
||||
// IGNORE_TOTALLY
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Local to main or sub line
|
||||
if (srcIfIndex == kLocalIfIndex)
|
||||
{
|
||||
// if destination is not within our area then send via primary interface, else via secondary interface
|
||||
uint8_t destIfidx = (Z != ownAA) ? kPrimaryIfIndex : kSecondaryIfIndex;
|
||||
_netLayerEntities[destIfidx].sendDataRequest(npdu, ack, destination, source, priority, AddressType::IndividualAddress, Broadcast);
|
||||
return;
|
||||
//println("NetworkLayerCoupler::routeDataIndividual not routed");
|
||||
; // IGNORE_TOTALLY
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -296,31 +430,25 @@ void NetworkLayerCoupler::dataIndication(AckType ack, AddressType addrType, uint
|
||||
// routing for individual addresses
|
||||
if (addrType == IndividualAddress)
|
||||
{
|
||||
//printHex("NetworkLayerCoupler::dataIndication to IA ", (uint8_t*)&destination, 2);
|
||||
//npdu.frame().valid();
|
||||
routeDataIndividual(ack, destination, npdu, priority, source, srcIfIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
//printHex("NetworkLayerCoupler::dataIndication to GA ", (uint8_t*)&destination, 2);
|
||||
// routing for group addresses
|
||||
// TODO: check new AN189
|
||||
// "AN189 only makes that group messages with hop count 7 cannot bypass the Filter Table unfiltered,
|
||||
// what made the Security Proxy(AN192) useless; now, hc 7 Telegrams are filtered as any other and the value is decremented.
|
||||
if (isGroupAddressInFilterTable(destination))
|
||||
{
|
||||
// ROUTE_XXX
|
||||
sendMsgHopCount(ack, addrType, destination, npdu, priority, Broadcast, srcIfIdx, source);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// IGNORE_TOTALLY
|
||||
return;
|
||||
}
|
||||
|
||||
println("Unhandled routing case! Should not happen!");
|
||||
// ROUTE_XXX
|
||||
sendMsgHopCount(ack, addrType, destination, npdu, priority, Broadcast, srcIfIdx, source);
|
||||
return;
|
||||
}
|
||||
|
||||
void NetworkLayerCoupler::dataConfirm(AckType ack, AddressType addrType, uint16_t destination, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx)
|
||||
{
|
||||
//println("NetworkLayerCoupler::dataConfirm");
|
||||
HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter;
|
||||
|
||||
// Check if received frame is an echo from our sent frame, we are a normal device in this case
|
||||
@ -356,8 +484,18 @@ void NetworkLayerCoupler::broadcastIndication(AckType ack, FrameFormat format, N
|
||||
_transportLayer.dataBroadcastIndication(hopType, priority, source, npdu.tpdu());
|
||||
}
|
||||
|
||||
uint8_t lcconfig = LCCONFIG::PHYS_FRAME_ROUT | LCCONFIG::PHYS_REPEAT | LCCONFIG::BROADCAST_REPEAT | LCCONFIG::GROUP_IACK_ROUT | LCCONFIG::PHYS_IACK_NORMAL; // default value from spec. in case prop is not availible.
|
||||
Property* prop_lcconfig;
|
||||
if(srcIfIdx == kPrimaryIfIndex) // direction Prim -> Sec ( e.g. IP -> TP)
|
||||
prop_lcconfig = _rtObjPrimary->property(PID_MAIN_LCCONFIG);
|
||||
else // direction Sec -> Prim ( e.g. TP -> IP)
|
||||
prop_lcconfig = _rtObjPrimary->property(PID_SUB_LCCONFIG);
|
||||
if(prop_lcconfig)
|
||||
prop_lcconfig->read(lcconfig);
|
||||
|
||||
// Route to other interface
|
||||
sendMsgHopCount(ack, GroupAddress, 0, npdu, priority, Broadcast, srcIfIdx, source);
|
||||
if(!(lcconfig & LCCONFIG::BROADCAST_LOCK))
|
||||
sendMsgHopCount(ack, GroupAddress, 0, npdu, priority, Broadcast, srcIfIdx, source);
|
||||
}
|
||||
|
||||
void NetworkLayerCoupler::broadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx)
|
||||
@ -379,8 +517,19 @@ void NetworkLayerCoupler::systemBroadcastIndication(AckType ack, FrameFormat for
|
||||
HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter;
|
||||
_transportLayer.dataSystemBroadcastIndication(hopType, priority, source, npdu.tpdu());
|
||||
}
|
||||
|
||||
uint8_t lcconfig = LCCONFIG::PHYS_FRAME_ROUT | LCCONFIG::PHYS_REPEAT | LCCONFIG::BROADCAST_REPEAT | LCCONFIG::GROUP_IACK_ROUT | LCCONFIG::PHYS_IACK_NORMAL; // default value from spec. in case prop is not availible.
|
||||
Property* prop_lcconfig;
|
||||
if(srcIfIdx == kPrimaryIfIndex) // direction Prim -> Sec ( e.g. IP -> TP)
|
||||
prop_lcconfig = _rtObjPrimary->property(PID_MAIN_LCCONFIG);
|
||||
else // direction Sec -> Prim ( e.g. TP -> IP)
|
||||
prop_lcconfig = _rtObjPrimary->property(PID_SUB_LCCONFIG);
|
||||
if(prop_lcconfig)
|
||||
prop_lcconfig->read(lcconfig);
|
||||
|
||||
// Route to other interface
|
||||
sendMsgHopCount(ack, GroupAddress, 0, npdu, priority, SysBroadcast, srcIfIdx, source);
|
||||
if(!(lcconfig & LCCONFIG::BROADCAST_LOCK))
|
||||
sendMsgHopCount(ack, GroupAddress, 0, npdu, priority, SysBroadcast, srcIfIdx, source);
|
||||
}
|
||||
|
||||
void NetworkLayerCoupler::systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx)
|
||||
@ -438,8 +587,10 @@ void NetworkLayerCoupler::dataBroadcastRequest(AckType ack, HopCountType hopType
|
||||
else
|
||||
npdu.hopCount(hopCount());
|
||||
|
||||
CemiFrame tmpFrame(tpdu.frame());
|
||||
|
||||
_netLayerEntities[kPrimaryIfIndex].sendDataRequest(npdu, ack, 0, _deviceObj.individualAddress(), priority, GroupAddress, Broadcast);
|
||||
_netLayerEntities[kSecondaryIfIndex].sendDataRequest(npdu, ack, 0, _deviceObj.individualAddress(), priority, GroupAddress, Broadcast);
|
||||
_netLayerEntities[kSecondaryIfIndex].sendDataRequest(tmpFrame.npdu(), ack, 0, _deviceObj.individualAddress(), priority, GroupAddress, Broadcast);
|
||||
}
|
||||
|
||||
void NetworkLayerCoupler::dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu)
|
||||
@ -451,6 +602,24 @@ void NetworkLayerCoupler::dataSystemBroadcastRequest(AckType ack, HopCountType h
|
||||
else
|
||||
npdu.hopCount(hopCount());
|
||||
|
||||
_netLayerEntities[kPrimaryIfIndex].sendDataRequest(npdu, ack, 0, _deviceObj.individualAddress(), priority, GroupAddress, SysBroadcast);
|
||||
_netLayerEntities[kSecondaryIfIndex].sendDataRequest(npdu, ack, 0, _deviceObj.individualAddress(), priority, GroupAddress, SysBroadcast);
|
||||
|
||||
CemiFrame tmpFrame(tpdu.frame());
|
||||
|
||||
// for closed media like TP1 and IP
|
||||
bool isClosedMedium = (_netLayerEntities[kPrimaryIfIndex].mediumType() == DptMedium::KNX_TP1) || (_netLayerEntities[kPrimaryIfIndex].mediumType() == DptMedium::KNX_IP);
|
||||
SystemBroadcast broadcastType = (isClosedMedium && isApciSystemBroadcast(tpdu.apdu()) ? Broadcast : SysBroadcast);
|
||||
_netLayerEntities[kPrimaryIfIndex].sendDataRequest(npdu, ack, 0, _deviceObj.individualAddress(), priority, GroupAddress, broadcastType);
|
||||
|
||||
isClosedMedium = (_netLayerEntities[kSecondaryIfIndex].mediumType() == DptMedium::KNX_TP1) || (_netLayerEntities[kSecondaryIfIndex].mediumType() == DptMedium::KNX_IP);
|
||||
broadcastType = (isClosedMedium && isApciSystemBroadcast(tmpFrame.apdu()) ? Broadcast : SysBroadcast);
|
||||
println(broadcastType);
|
||||
_netLayerEntities[kSecondaryIfIndex].sendDataRequest(tmpFrame.npdu(), ack, 0, _deviceObj.individualAddress(), priority, GroupAddress, broadcastType);
|
||||
}
|
||||
|
||||
#ifdef KNX_TUNNELING
|
||||
bool NetworkLayerCoupler::isTunnelAddress(uint16_t destination)
|
||||
{
|
||||
// tunnels are managed within the IpDataLinkLayer - kPrimaryIfIndex
|
||||
return _netLayerEntities[kPrimaryIfIndex].dataLinkLayer().isTunnelAddress(destination);
|
||||
}
|
||||
#endif
|
@ -20,7 +20,9 @@ class NetworkLayerCoupler : public NetworkLayer
|
||||
NetworkLayerEntity& getPrimaryInterface();
|
||||
NetworkLayerEntity& getSecondaryInterface();
|
||||
|
||||
bool isRoutedIndividualAddress(uint16_t individualAddress);
|
||||
bool isRoutedIndividualAddress(uint16_t individualAddress, uint8_t srcIfIndex);
|
||||
|
||||
bool isRoutedGroupAddress(uint16_t groupAddress, uint8_t sourceInterfaceIndex);
|
||||
|
||||
void rtObjPrimary(RouterObject& rtObjPrimary); // Coupler model 2.0
|
||||
void rtObjSecondary(RouterObject& rtObjSecondary); // Coupler model 2.0
|
||||
@ -63,6 +65,9 @@ class NetworkLayerCoupler : public NetworkLayer
|
||||
|
||||
void evaluateCouplerType();
|
||||
bool isGroupAddressInFilterTable(uint16_t groupAddress);
|
||||
#ifdef KNX_TUNNELING
|
||||
bool isTunnelAddress(uint16_t destination);
|
||||
#endif
|
||||
|
||||
// Support a maximum of two physical interfaces for couplers
|
||||
NetworkLayerEntity _netLayerEntities[2];
|
||||
|
@ -18,6 +18,11 @@ DataLinkLayer& NetworkLayerEntity::dataLinkLayer()
|
||||
return *_dataLinkLayer;
|
||||
}
|
||||
|
||||
NetworkLayer& NetworkLayerEntity::networkLayer()
|
||||
{
|
||||
return _netLayer;
|
||||
}
|
||||
|
||||
DptMedium NetworkLayerEntity::mediumType() const
|
||||
{
|
||||
return _dataLinkLayer->mediumType();
|
||||
@ -58,7 +63,7 @@ void NetworkLayerEntity::systemBroadcastConfirm(AckType ack, FrameFormat format,
|
||||
_netLayer.systemBroadcastConfirm(ack, format, priority, source, npdu, status, _entityIndex);
|
||||
}
|
||||
|
||||
void NetworkLayerEntity::sendDataRequest(NPDU &npdu, AckType ack, uint16_t destination, uint16_t source, Priority priority, AddressType addrType, SystemBroadcast systemBroadcast)
|
||||
void NetworkLayerEntity::sendDataRequest(NPDU &npdu, AckType ack, uint16_t destination, uint16_t source, Priority priority, AddressType addrType, SystemBroadcast systemBroadcast, bool doNotRepeat)
|
||||
{
|
||||
FrameFormat frameFormat = npdu.octetCount() > 15 ? ExtendedFrame : StandardFrame;
|
||||
|
||||
|
@ -17,6 +17,7 @@ class NetworkLayerEntity
|
||||
|
||||
void dataLinkLayer(DataLinkLayer& layer);
|
||||
DataLinkLayer& dataLinkLayer();
|
||||
NetworkLayer& networkLayer();
|
||||
|
||||
DptMedium mediumType() const;
|
||||
uint8_t getEntityIndex();
|
||||
@ -35,7 +36,7 @@ class NetworkLayerEntity
|
||||
|
||||
private:
|
||||
// From network layer
|
||||
void sendDataRequest(NPDU& npdu, AckType ack, uint16_t destination, uint16_t source, Priority priority, AddressType addrType, SystemBroadcast systemBroadcast);
|
||||
void sendDataRequest(NPDU& npdu, AckType ack, uint16_t destination, uint16_t source, Priority priority, AddressType addrType, SystemBroadcast systemBroadcast, bool doNotRepeat = false);
|
||||
|
||||
DataLinkLayer* _dataLinkLayer = 0;
|
||||
NetworkLayer& _netLayer;
|
||||
|
@ -109,6 +109,11 @@ int Platform::readBytesMultiCast(uint8_t *buffer, uint16_t maxLen)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Platform::readBytesMultiCast(uint8_t* buffer, uint16_t maxLen, uint32_t& src_addr, uint16_t& src_port)
|
||||
{
|
||||
return readBytesMultiCast(buffer, maxLen);
|
||||
}
|
||||
|
||||
size_t Platform::flashEraseBlockSize()
|
||||
{
|
||||
return 0;
|
||||
@ -193,6 +198,13 @@ void Platform::commitNonVolatileMemory()
|
||||
|
||||
uint32_t Platform::writeNonVolatileMemory(uint32_t relativeAddress, uint8_t* buffer, size_t size)
|
||||
{
|
||||
#ifdef KNX_LOG_MEM
|
||||
print("Platform::writeNonVolatileMemory relativeAddress ");
|
||||
print(relativeAddress);
|
||||
print(" size ");
|
||||
println(size);
|
||||
#endif
|
||||
|
||||
if(_memoryType == Flash)
|
||||
{
|
||||
while (size > 0)
|
||||
@ -225,6 +237,79 @@ uint32_t Platform::writeNonVolatileMemory(uint32_t relativeAddress, uint8_t* buf
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Platform::readNonVolatileMemory(uint32_t relativeAddress, uint8_t* buffer, size_t size)
|
||||
{
|
||||
#ifdef KNX_LOG_MEM
|
||||
print("Platform::readNonVolatileMemory relativeAddress ");
|
||||
print(relativeAddress);
|
||||
print(" size ");
|
||||
println(size);
|
||||
#endif
|
||||
|
||||
if(_memoryType == Flash)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
while (size > 0)
|
||||
{
|
||||
// bufferd block is "left" of requested memory, read until the end and return
|
||||
if(_bufferedEraseblockNumber < getEraseBlockNumberOf(relativeAddress))
|
||||
{
|
||||
memcpy(buffer+offset, userFlashStart()+relativeAddress, size);
|
||||
return relativeAddress + size;
|
||||
}
|
||||
// bufferd block is "right" of requested memory, and may interfere
|
||||
else if(_bufferedEraseblockNumber > getEraseBlockNumberOf(relativeAddress))
|
||||
{
|
||||
// if the end of the requested memory is before the buffered block, read until the end and return
|
||||
int32_t eraseblockNumberEnd = getEraseBlockNumberOf(relativeAddress+size-1);
|
||||
if(_bufferedEraseblockNumber > eraseblockNumberEnd)
|
||||
{
|
||||
memcpy(buffer+offset, userFlashStart()+relativeAddress, size);
|
||||
return relativeAddress + size;
|
||||
}
|
||||
// if not, read until the buffered block starts and loop through while again
|
||||
else
|
||||
{
|
||||
uint32_t sizeToRead = (eraseblockNumberEnd * flashEraseBlockSize()) - relativeAddress;
|
||||
memcpy(buffer+offset, userFlashStart()+relativeAddress, sizeToRead);
|
||||
relativeAddress += sizeToRead;
|
||||
size -= sizeToRead;
|
||||
offset += sizeToRead;
|
||||
}
|
||||
}
|
||||
// start of requested memory is within the buffered erase block
|
||||
else
|
||||
{
|
||||
// if the end of the requested memory is also in the buffered block, read until the end and return
|
||||
int32_t eraseblockNumberEnd = getEraseBlockNumberOf(relativeAddress+size-1);
|
||||
if(_bufferedEraseblockNumber == eraseblockNumberEnd)
|
||||
{
|
||||
uint8_t* start = _eraseblockBuffer + (relativeAddress - _bufferedEraseblockNumber * flashEraseBlockSize());
|
||||
memcpy(buffer+offset, start, size);
|
||||
return relativeAddress + size;
|
||||
}
|
||||
// if not, read until the end of the buffered block and loop through while again
|
||||
else
|
||||
{
|
||||
uint32_t offsetInBufferedBlock = relativeAddress - _bufferedEraseblockNumber * flashEraseBlockSize();
|
||||
uint8_t* start = _eraseblockBuffer + offsetInBufferedBlock;
|
||||
uint32_t sizeToRead = flashEraseBlockSize() - offsetInBufferedBlock;
|
||||
memcpy(buffer+offset, start, sizeToRead);
|
||||
relativeAddress += sizeToRead;
|
||||
size -= sizeToRead;
|
||||
offset += sizeToRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
return relativeAddress;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(buffer, getEepromBuffer(KNX_FLASH_SIZE)+relativeAddress, size);
|
||||
return relativeAddress+size;
|
||||
}
|
||||
}
|
||||
|
||||
// writes value repeat times into flash starting at relativeAddress
|
||||
// returns next free relativeAddress
|
||||
uint32_t Platform::writeNonVolatileMemory(uint32_t relativeAddress, uint8_t value, size_t repeat)
|
||||
|
@ -50,6 +50,7 @@ class Platform
|
||||
virtual void closeMultiCast();
|
||||
virtual bool sendBytesMultiCast(uint8_t* buffer, uint16_t len);
|
||||
virtual int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen);
|
||||
virtual int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen, uint32_t& src_addr, uint16_t& src_port);
|
||||
|
||||
//unicast socket
|
||||
virtual bool sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len);
|
||||
@ -85,6 +86,7 @@ class Platform
|
||||
virtual void commitNonVolatileMemory();
|
||||
// address is relative to start of nonvolatile memory
|
||||
virtual uint32_t writeNonVolatileMemory(uint32_t relativeAddress, uint8_t* buffer, size_t size);
|
||||
virtual uint32_t readNonVolatileMemory(uint32_t relativeAddress, uint8_t* buffer, size_t size);
|
||||
virtual uint32_t writeNonVolatileMemory(uint32_t relativeAddress, uint8_t value, size_t repeat);
|
||||
|
||||
NvMemoryType NonVolatileMemoryType();
|
||||
|
@ -139,6 +139,8 @@ enum PropertyID
|
||||
PID_MSG_TRANSMIT_TO_KNX = 75,
|
||||
PID_FRIENDLY_NAME = 76,
|
||||
PID_ROUTING_BUSY_WAIT_TIME = 78,
|
||||
PID_CUSTOM_RESERVED_TUNNELS_CTRL = 201, // custom propertiy to control the stacks behaviour for reserverd tunnels, not in Spec (PID >= 200)
|
||||
PID_CUSTOM_RESERVED_TUNNELS_IP = 202, // custom propertiy to control the stacks behaviour for reserverd tunnels, not in Spec (PID >= 200)
|
||||
|
||||
/** cEMI Server Object */
|
||||
PID_MEDIUM_TYPE = 51,
|
||||
|
@ -78,8 +78,8 @@ bool RfDataLinkLayer::sendFrame(CemiFrame& frame)
|
||||
}
|
||||
|
||||
RfDataLinkLayer::RfDataLinkLayer(DeviceObject& devObj, RfMediumObject& rfMediumObj,
|
||||
NetworkLayerEntity &netLayerEntity, Platform& platform)
|
||||
: DataLinkLayer(devObj, netLayerEntity, platform),
|
||||
NetworkLayerEntity &netLayerEntity, Platform& platform, BusAccessUnit& busAccessUnit)
|
||||
: DataLinkLayer(devObj, netLayerEntity, platform, busAccessUnit),
|
||||
_rfMediumObj(rfMediumObj),
|
||||
_rfPhy(*this, platform)
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ class RfDataLinkLayer : public DataLinkLayer
|
||||
|
||||
public:
|
||||
RfDataLinkLayer(DeviceObject& devObj, RfMediumObject& rfMediumObj, NetworkLayerEntity& netLayerEntity,
|
||||
Platform& platform);
|
||||
Platform& platform, BusAccessUnit& busAccessUnit);
|
||||
|
||||
void loop();
|
||||
void enabled(bool value);
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "callback_property.h"
|
||||
#include "function_property.h"
|
||||
|
||||
|
||||
// Filter Table Realization Type 3
|
||||
// The Filter Table Realisation Type 3 shall be organised as a memory mapped bit-field of
|
||||
// 65536 bits and thus 8 192 octets. Each bit shall uniquely correspond to one Group Address.
|
||||
@ -25,8 +26,8 @@ enum RouteTableServices
|
||||
SetGroupAddress = 0x04, // 4 bytes: start address and end address
|
||||
};
|
||||
|
||||
RouterObject::RouterObject(Memory& memory)
|
||||
: TableObject(memory)
|
||||
RouterObject::RouterObject(Memory& memory, uint32_t staticTableAdr, uint32_t staticTableSize)
|
||||
: TableObject(memory, staticTableAdr, staticTableSize)
|
||||
{
|
||||
}
|
||||
|
||||
@ -45,6 +46,7 @@ void RouterObject::initialize(CouplerModel model, uint8_t objIndex, DptMedium me
|
||||
{
|
||||
bool useHopCount = false;
|
||||
bool useTable = true;
|
||||
_model = model;
|
||||
|
||||
if (model == CouplerModel::Model_20)
|
||||
{
|
||||
@ -64,11 +66,11 @@ void RouterObject::initialize(CouplerModel model, uint8_t objIndex, DptMedium me
|
||||
// Only present if coupler model is 1.x
|
||||
Property* model1xProperties[] =
|
||||
{
|
||||
// TODO: implement filtering based on this config here
|
||||
new DataProperty( PID_MAIN_LCCONFIG, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, (uint8_t) 0 ), // Primary: data individual (connless and connorient) + broadcast
|
||||
new DataProperty( PID_SUB_LCCONFIG, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, (uint8_t) 0 ), // Secondary: data individual (connless and connorient) + broadcast
|
||||
new DataProperty( PID_MAIN_LCGRPCONFIG, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, (uint8_t) 0 ), // Primary: data group
|
||||
new DataProperty( PID_SUB_LCGRPCONFIG, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, (uint8_t) 0 ), // Secondary: data group
|
||||
// default values from Spec, see 03_05_01 4.4.4 and 4.4.5
|
||||
new DataProperty( PID_MAIN_LCCONFIG, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, (uint8_t) (LCCONFIG::PHYS_FRAME_ROUT | LCCONFIG::PHYS_REPEAT | LCCONFIG::BROADCAST_REPEAT | LCCONFIG::GROUP_IACK_ROUT | LCCONFIG::PHYS_IACK_NORMAL) ), // Primary: data individual (connless and connorient) + broadcast
|
||||
new DataProperty( PID_SUB_LCCONFIG, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, (uint8_t) (LCCONFIG::PHYS_FRAME_ROUT | LCCONFIG::PHYS_REPEAT | LCCONFIG::BROADCAST_REPEAT | LCCONFIG::GROUP_IACK_ROUT | LCCONFIG::PHYS_IACK_NORMAL) ), // Secondary: data individual (connless and connorient) + broadcast
|
||||
new DataProperty( PID_MAIN_LCGRPCONFIG, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, (uint8_t) (LCGRPCONFIG::GROUP_6FFFROUTE | LCGRPCONFIG::GROUP_7000UNLOCK | LCGRPCONFIG::GROUP_REPEAT)) , // Primary: data group
|
||||
new DataProperty( PID_SUB_LCGRPCONFIG, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, (uint8_t) (LCGRPCONFIG::GROUP_6FFFROUTE | LCGRPCONFIG::GROUP_7000UNLOCK | LCGRPCONFIG::GROUP_REPEAT)), // Secondary: data group
|
||||
};
|
||||
uint8_t model1xPropertiesCount = sizeof(model1xProperties) / sizeof(Property*);
|
||||
|
||||
@ -83,8 +85,6 @@ void RouterObject::initialize(CouplerModel model, uint8_t objIndex, DptMedium me
|
||||
|
||||
Property* tableProperties[] =
|
||||
{
|
||||
new DataProperty( PID_COUPLER_SERVICES_CONTROL, true, PDT_GENERIC_01, 1, ReadLv3 | WriteLv0, (uint8_t) 0), // written by ETS TODO: implement
|
||||
new DataProperty( PID_FILTER_TABLE_USE, true, PDT_BINARY_INFORMATION, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ), // default: invalid filter table, do not use, written by ETS
|
||||
new FunctionProperty<RouterObject>(this, PID_ROUTETABLE_CONTROL,
|
||||
// Command Callback of PID_ROUTETABLE_CONTROL
|
||||
[](RouterObject* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void {
|
||||
@ -95,12 +95,21 @@ void RouterObject::initialize(CouplerModel model, uint8_t objIndex, DptMedium me
|
||||
obj->functionRouteTableControl(false, data, length, resultData, resultLength);
|
||||
})
|
||||
};
|
||||
|
||||
Property* tableProperties20[] =
|
||||
{
|
||||
new DataProperty( PID_COUPLER_SERVICES_CONTROL, true, PDT_GENERIC_01, 1, ReadLv3 | WriteLv0, (uint8_t) 0), // written by ETS TODO: implement
|
||||
new DataProperty( PID_FILTER_TABLE_USE, true, PDT_BINARY_INFORMATION, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ) // default: invalid filter table, do not use, written by ETS
|
||||
};
|
||||
|
||||
uint8_t tablePropertiesCount = sizeof(tableProperties) / sizeof(Property*);
|
||||
uint8_t tableProperties20Count = sizeof(tableProperties20) / sizeof(Property*);
|
||||
|
||||
size_t allPropertiesCount = fixedPropertiesCount;
|
||||
allPropertiesCount += (model == CouplerModel::Model_1x) ? model1xPropertiesCount : model20PropertiesCount;
|
||||
allPropertiesCount += useHopCount ? 1 : 0;
|
||||
allPropertiesCount += useTable ? tablePropertiesCount : 0;
|
||||
allPropertiesCount += useTable && (model == CouplerModel::Model_20) ? tableProperties20Count : 0;
|
||||
allPropertiesCount += ((mediumType == DptMedium::KNX_RF) || (mediumType == DptMedium::KNX_IP)) ? 1 : 0; // PID_RF_ENABLE_SBC and PID_IP_ENABLE_SBC
|
||||
|
||||
Property* allProperties[allPropertiesCount];
|
||||
@ -131,6 +140,11 @@ void RouterObject::initialize(CouplerModel model, uint8_t objIndex, DptMedium me
|
||||
{
|
||||
memcpy(&allProperties[i], tableProperties, sizeof(tableProperties));
|
||||
i += tablePropertiesCount;
|
||||
if((model == CouplerModel::Model_20))
|
||||
{
|
||||
memcpy(&allProperties[i], tableProperties20, sizeof(tableProperties20));
|
||||
i += tableProperties20Count;
|
||||
}
|
||||
}
|
||||
|
||||
if (mediumType == DptMedium::KNX_RF)
|
||||
@ -166,23 +180,32 @@ void RouterObject::initialize(CouplerModel model, uint8_t objIndex, DptMedium me
|
||||
|
||||
const uint8_t* RouterObject::restore(const uint8_t* buffer)
|
||||
{
|
||||
buffer = TableObject::restore(buffer);
|
||||
|
||||
_filterTableGroupAddresses = (uint16_t*)data();
|
||||
|
||||
return buffer;
|
||||
return TableObject::restore(buffer);
|
||||
}
|
||||
|
||||
void RouterObject::commandClearSetRoutingTable(bool bitIsSet)
|
||||
{
|
||||
uint8_t fillbyte = bitIsSet ? 0xFF : 0x00;
|
||||
uint32_t relptr = _memory.toRelative(data());
|
||||
#ifdef KNX_LOG_COUPLER
|
||||
print("RouterObject::commandClearSetRoutingTable ");
|
||||
println(bitIsSet);
|
||||
println(relptr);
|
||||
println((uint32_t)data());
|
||||
#endif
|
||||
|
||||
for (uint16_t i = 0; i < kFilterTableSize; i++)
|
||||
{
|
||||
data()[i] = bitIsSet ? 0xFF : 0x00;
|
||||
_memory.writeMemory(relptr+i, 1, &fillbyte);
|
||||
}
|
||||
}
|
||||
|
||||
bool RouterObject::statusClearSetRoutingTable(bool bitIsSet)
|
||||
{
|
||||
#ifdef KNX_LOG_COUPLER
|
||||
print("RouterObject::statusClearSetRoutingTable ");
|
||||
println(bitIsSet);
|
||||
#endif
|
||||
for (uint16_t i = 0; i < kFilterTableSize; i++)
|
||||
{
|
||||
if (data()[i] != (bitIsSet ? 0xFF : 0x00))
|
||||
@ -193,6 +216,15 @@ bool RouterObject::statusClearSetRoutingTable(bool bitIsSet)
|
||||
|
||||
void RouterObject::commandClearSetGroupAddress(uint16_t startAddress, uint16_t endAddress, bool bitIsSet)
|
||||
{
|
||||
#ifdef KNX_LOG_COUPLER
|
||||
print("RouterObject::commandClearSetGroupAddress ");
|
||||
print(startAddress);
|
||||
print(" ");
|
||||
print(endAddress);
|
||||
print(" ");
|
||||
println(bitIsSet);
|
||||
#endif
|
||||
|
||||
uint16_t startOctet = startAddress / 8;
|
||||
uint8_t startBitPosition = startAddress % 8;
|
||||
uint16_t endOctet = endAddress / 8;
|
||||
@ -200,26 +232,34 @@ void RouterObject::commandClearSetGroupAddress(uint16_t startAddress, uint16_t e
|
||||
|
||||
if (startOctet == endOctet)
|
||||
{
|
||||
uint32_t relptr = _memory.toRelative(data()) + startOctet;
|
||||
uint8_t octetData = 0; // = data()[startOctet];
|
||||
_memory.readMemory(relptr, 1, &octetData);
|
||||
|
||||
for (uint8_t bitPos = startBitPosition; bitPos <= endBitPosition; bitPos++)
|
||||
{
|
||||
if (bitIsSet)
|
||||
data()[startOctet] |= 1 << bitPos;
|
||||
octetData |= 1 << bitPos;
|
||||
else
|
||||
data()[startOctet] &= ~(1 << bitPos);
|
||||
octetData &= ~(1 << bitPos);
|
||||
}
|
||||
_memory.writeMemory(relptr, 1, &octetData);
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint16_t i = startOctet; i <= endOctet; i++)
|
||||
{
|
||||
uint32_t relptr = _memory.toRelative(data()) + i;
|
||||
uint8_t octetData = 0;
|
||||
_memory.readMemory(relptr, 1, &octetData);
|
||||
if (i == startOctet)
|
||||
{
|
||||
for (uint8_t bitPos = startBitPosition; bitPos <= 7; bitPos++)
|
||||
{
|
||||
if (bitIsSet)
|
||||
data()[i] |= 1 << bitPos;
|
||||
octetData |= 1 << bitPos;
|
||||
else
|
||||
data()[i] &= ~(1 << bitPos);
|
||||
octetData &= ~(1 << bitPos);
|
||||
}
|
||||
}
|
||||
else if (i == endOctet)
|
||||
@ -227,23 +267,33 @@ void RouterObject::commandClearSetGroupAddress(uint16_t startAddress, uint16_t e
|
||||
for (uint8_t bitPos = 0; bitPos <= endBitPosition; bitPos++)
|
||||
{
|
||||
if (bitIsSet)
|
||||
data()[i] |= 1 << bitPos;
|
||||
octetData |= 1 << bitPos;
|
||||
else
|
||||
data()[i] &= ~(1 << bitPos);
|
||||
octetData &= ~(1 << bitPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bitIsSet)
|
||||
data()[i] = 0xFF;
|
||||
octetData = 0xFF;
|
||||
else
|
||||
data()[i] = 0x00;
|
||||
octetData = 0x00;
|
||||
}
|
||||
_memory.writeMemory(relptr, 1, &octetData);
|
||||
}
|
||||
}
|
||||
|
||||
bool RouterObject::statusClearSetGroupAddress(uint16_t startAddress, uint16_t endAddress, bool bitIsSet)
|
||||
{
|
||||
#ifdef KNX_LOG_COUPLER
|
||||
print("RouterObject::statusClearSetGroupAddress ");
|
||||
print(startAddress);
|
||||
print(" ");
|
||||
print(endAddress);
|
||||
print(" ");
|
||||
println(bitIsSet);
|
||||
#endif
|
||||
|
||||
uint16_t startOctet = startAddress / 8;
|
||||
uint8_t startBitPosition = startAddress % 8;
|
||||
uint16_t endOctet = endAddress / 8;
|
||||
@ -313,10 +363,25 @@ bool RouterObject::statusClearSetGroupAddress(uint16_t startAddress, uint16_t en
|
||||
|
||||
void RouterObject::functionRouteTableControl(bool isCommand, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength)
|
||||
{
|
||||
#ifdef KNX_LOG_COUPLER
|
||||
print("RouterObject::functionRouteTableControl ");
|
||||
print(isCommand);
|
||||
print(" ");
|
||||
printHex("", data, length);
|
||||
#endif
|
||||
|
||||
RouteTableServices srvId = (RouteTableServices) data[1];
|
||||
|
||||
if (isCommand)
|
||||
{
|
||||
if (loadState() != LS_LOADING)
|
||||
{
|
||||
println("access violation. filter table can only be modified in LS_LOADING");
|
||||
resultData[0] = ReturnCodes::AccessReadOnly;
|
||||
resultData[1] = srvId;
|
||||
resultLength = 2;
|
||||
return;
|
||||
}
|
||||
switch(srvId)
|
||||
{
|
||||
case ClearRoutingTable:
|
||||
@ -424,12 +489,22 @@ void RouterObject::functionRfEnableSbc(bool isCommand, uint8_t* data, uint8_t le
|
||||
|
||||
bool RouterObject::isRfSbcRoutingEnabled()
|
||||
{
|
||||
#ifdef KNX_LOG_COUPLER
|
||||
print("RouterObject::isRfSbcRoutingEnabled ");
|
||||
println(_rfSbcRoutingEnabled);
|
||||
#endif
|
||||
return _rfSbcRoutingEnabled;
|
||||
}
|
||||
|
||||
// TODO: check if IP SBC works the same way, just copied from RF
|
||||
void RouterObject::functionIpEnableSbc(bool isCommand, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength)
|
||||
{
|
||||
#ifdef KNX_LOG_COUPLER
|
||||
print("RouterObject::functionIpEnableSbc ");
|
||||
print(isCommand);
|
||||
printHex(" ", data, length);
|
||||
#endif
|
||||
|
||||
if (isCommand)
|
||||
{
|
||||
_ipSbcRoutingEnabled = (data[0] == 1) ? true : false;
|
||||
@ -443,19 +518,31 @@ void RouterObject::functionIpEnableSbc(bool isCommand, uint8_t* data, uint8_t le
|
||||
// TODO: check if IP SBC works the same way, just copied from RF
|
||||
bool RouterObject::isIpSbcRoutingEnabled()
|
||||
{
|
||||
#ifdef KNX_LOG_COUPLER
|
||||
print("RouterObject::isIpSbcRoutingEnabled ");
|
||||
println(_ipSbcRoutingEnabled);
|
||||
#endif
|
||||
return _ipSbcRoutingEnabled;
|
||||
}
|
||||
|
||||
void RouterObject::beforeStateChange(LoadState& newState)
|
||||
{
|
||||
#ifdef KNX_LOG_COUPLER
|
||||
println("RouterObject::beforeStateChange");
|
||||
#endif
|
||||
if (newState != LS_LOADED)
|
||||
return;
|
||||
|
||||
_filterTableGroupAddresses = (uint16_t*)data();
|
||||
}
|
||||
|
||||
void RouterObject::masterReset(EraseCode eraseCode, uint8_t channel)
|
||||
{
|
||||
#ifdef KNX_LOG_COUPLER
|
||||
print("RouterObject::masterReset ");
|
||||
print(eraseCode);
|
||||
print(" ");
|
||||
println(channel);
|
||||
#endif
|
||||
|
||||
if (eraseCode == FactoryReset)
|
||||
{
|
||||
// TODO: handle different erase codes
|
||||
@ -468,18 +555,28 @@ bool RouterObject::isGroupAddressInFilterTable(uint16_t groupAddress)
|
||||
if (loadState() != LS_LOADED)
|
||||
return false;
|
||||
|
||||
uint8_t filterTableUse = 0x00;
|
||||
if (property(PID_FILTER_TABLE_USE)->read(filterTableUse) == 0)
|
||||
return false;
|
||||
uint8_t filterTableUse = 0x01;
|
||||
Property* propFilterTableUse = property(PID_FILTER_TABLE_USE);
|
||||
if(propFilterTableUse) // check if property PID_FILTER_TABLE_USE exists (only coupler 20), if not, ignore this
|
||||
if (propFilterTableUse->read(filterTableUse) == 0) // check if property PID_FILTER_TABLE_USE is empty, if so, return false
|
||||
return false;
|
||||
|
||||
if ((filterTableUse&0x01) == 1)
|
||||
{
|
||||
uint8_t* filterTable = data();
|
||||
// octet_address = GA_value div 8
|
||||
// bit_position = GA_value mod 8
|
||||
uint16_t octetAddress = groupAddress / 8;
|
||||
uint8_t bitPosition = groupAddress % 8;
|
||||
|
||||
|
||||
return (data()[octetAddress] & (1 << bitPosition)) == (1 << bitPosition);
|
||||
if(filterTable)
|
||||
return (filterTable[octetAddress] & (1 << bitPosition)) == (1 << bitPosition);
|
||||
else
|
||||
{
|
||||
println("RouterObject::isGroupAddressInFilterTable filterTable is NULL");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -23,7 +23,7 @@ enum RouterObjectType
|
||||
class RouterObject : public TableObject
|
||||
{
|
||||
public:
|
||||
RouterObject(Memory& memory);
|
||||
RouterObject(Memory& memory, uint32_t staticTableAdr = 0, uint32_t staticTableSize = 0);
|
||||
|
||||
void initialize1x(DptMedium mediumType, uint16_t maxApduSize);
|
||||
void initialize20(uint8_t objIndex, DptMedium mediumType, RouterObjectType rtType, uint16_t maxApduSize);
|
||||
@ -54,5 +54,5 @@ private:
|
||||
|
||||
bool _rfSbcRoutingEnabled = false;
|
||||
bool _ipSbcRoutingEnabled = false;
|
||||
uint16_t* _filterTableGroupAddresses = 0;
|
||||
CouplerModel _model = CouplerModel::Model_20;
|
||||
};
|
||||
|
15
src/knx/service_families.h
Normal file
15
src/knx/service_families.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef KNX_SERVICE_FAMILY_CORE
|
||||
#define KNX_SERVICE_FAMILY_CORE 1
|
||||
#endif
|
||||
|
||||
#ifndef KNX_SERVICE_FAMILY_DEVICE_MANAGEMENT
|
||||
#define KNX_SERVICE_FAMILY_DEVICE_MANAGEMENT 1
|
||||
#endif
|
||||
|
||||
#ifndef KNX_SERVICE_FAMILY_TUNNELING
|
||||
#define KNX_SERVICE_FAMILY_TUNNELING 1
|
||||
#endif
|
||||
|
||||
#ifndef KNX_SERVICE_FAMILY_ROUTING
|
||||
#define KNX_SERVICE_FAMILY_ROUTING 1
|
||||
#endif
|
@ -19,9 +19,12 @@ BeforeTablesUnloadCallback TableObject::beforeTablesUnloadCallback()
|
||||
return _beforeTablesUnload;
|
||||
}
|
||||
|
||||
TableObject::TableObject(Memory& memory)
|
||||
TableObject::TableObject(Memory& memory, uint32_t staticTableAdr , uint32_t staticTableSize)
|
||||
: _memory(memory)
|
||||
{}
|
||||
{
|
||||
_staticTableAdr = staticTableAdr;
|
||||
_staticTableSize = staticTableSize;
|
||||
}
|
||||
|
||||
TableObject::~TableObject()
|
||||
{}
|
||||
@ -55,6 +58,9 @@ void TableObject::loadState(LoadState newState)
|
||||
|
||||
uint8_t* TableObject::save(uint8_t* buffer)
|
||||
{
|
||||
//println("TableObject::save");
|
||||
allocTableStatic();
|
||||
|
||||
buffer = pushByte(_state, buffer);
|
||||
|
||||
buffer = pushInt(_size, buffer);
|
||||
@ -64,12 +70,14 @@ uint8_t* TableObject::save(uint8_t* buffer)
|
||||
else
|
||||
buffer = pushInt(0, buffer);
|
||||
|
||||
return buffer;
|
||||
return InterfaceObject::save(buffer);
|
||||
}
|
||||
|
||||
|
||||
const uint8_t* TableObject::restore(const uint8_t* buffer)
|
||||
{
|
||||
//println("TableObject::restore");
|
||||
|
||||
uint8_t state = 0;
|
||||
buffer = popByte(state, buffer);
|
||||
_state = (LoadState)state;
|
||||
@ -78,13 +86,14 @@ const uint8_t* TableObject::restore(const uint8_t* buffer)
|
||||
|
||||
uint32_t relativeAddress = 0;
|
||||
buffer = popInt(relativeAddress, buffer);
|
||||
//println(relativeAddress);
|
||||
|
||||
if (relativeAddress != 0)
|
||||
_data = _memory.toAbsolute(relativeAddress);
|
||||
else
|
||||
_data = 0;
|
||||
|
||||
return buffer;
|
||||
//println((uint32_t)_data);
|
||||
return InterfaceObject::restore(buffer);
|
||||
}
|
||||
|
||||
uint32_t TableObject::tableReference()
|
||||
@ -94,6 +103,9 @@ uint32_t TableObject::tableReference()
|
||||
|
||||
bool TableObject::allocTable(uint32_t size, bool doFill, uint8_t fillByte)
|
||||
{
|
||||
if(_staticTableAdr)
|
||||
return false;
|
||||
|
||||
if (_data)
|
||||
{
|
||||
_memory.freeMemory(_data);
|
||||
@ -119,8 +131,20 @@ bool TableObject::allocTable(uint32_t size, bool doFill, uint8_t fillByte)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void TableObject::allocTableStatic()
|
||||
{
|
||||
if(_staticTableAdr && !_data)
|
||||
{
|
||||
_data = _memory.toAbsolute(_staticTableAdr);
|
||||
_size = _staticTableSize;
|
||||
_memory.addNewUsedBlock(_data, _size);
|
||||
}
|
||||
}
|
||||
|
||||
void TableObject::loadEvent(const uint8_t* data)
|
||||
{
|
||||
//printHex("TableObject::loadEvent 0x", data, 10);
|
||||
switch (_state)
|
||||
{
|
||||
case LS_UNLOADED:
|
||||
@ -200,8 +224,11 @@ void TableObject::loadEventLoaded(const uint8_t* data)
|
||||
//free nv memory
|
||||
if (_data)
|
||||
{
|
||||
_memory.freeMemory(_data);
|
||||
_data = 0;
|
||||
if(!_staticTableAdr)
|
||||
{
|
||||
_memory.freeMemory(_data);
|
||||
_data = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LE_ADDITIONAL_LOAD_CONTROLS:
|
||||
@ -288,7 +315,28 @@ void TableObject::initializeProperties(size_t propertiesSize, Property** propert
|
||||
[](TableObject* obj, uint16_t start, uint8_t count, const uint8_t* data) -> uint8_t {
|
||||
obj->loadEvent(data);
|
||||
return 1;
|
||||
}),
|
||||
})
|
||||
};
|
||||
|
||||
uint8_t ownPropertiesCount = sizeof(ownProperties) / sizeof(Property*);
|
||||
|
||||
uint8_t propertyCount = propertiesSize / sizeof(Property*);
|
||||
uint8_t allPropertiesCount = propertyCount + ownPropertiesCount;
|
||||
|
||||
Property* allProperties[allPropertiesCount];
|
||||
memcpy(allProperties, properties, propertiesSize);
|
||||
memcpy(allProperties + propertyCount, ownProperties, sizeof(ownProperties));
|
||||
|
||||
if(_staticTableAdr)
|
||||
InterfaceObject::initializeProperties(sizeof(allProperties), allProperties);
|
||||
else
|
||||
initializeDynTableProperties(sizeof(allProperties), allProperties);
|
||||
}
|
||||
|
||||
void TableObject::initializeDynTableProperties(size_t propertiesSize, Property** properties)
|
||||
{
|
||||
Property* ownProperties[] =
|
||||
{
|
||||
new CallbackProperty<TableObject>(this, PID_TABLE_REFERENCE, false, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv0,
|
||||
[](TableObject* obj, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t {
|
||||
if(start == 0)
|
||||
@ -321,9 +369,6 @@ void TableObject::initializeProperties(size_t propertiesSize, Property** propert
|
||||
}),
|
||||
new DataProperty(PID_ERROR_CODE, false, PDT_ENUM8, 1, ReadLv3 | WriteLv0, (uint8_t)E_NO_FAULT)
|
||||
};
|
||||
//TODO: missing
|
||||
|
||||
// 23 PID_TABLE 3 / (3)
|
||||
|
||||
uint8_t ownPropertiesCount = sizeof(ownProperties) / sizeof(Property*);
|
||||
|
||||
|
@ -18,7 +18,7 @@ class TableObject: public InterfaceObject
|
||||
* The constuctor.
|
||||
* @param memory The instance of the memory management class to use.
|
||||
*/
|
||||
TableObject(Memory& memory);
|
||||
TableObject(Memory& memory, uint32_t staticTableAdr = 0, uint32_t staticTableSize = 0);
|
||||
|
||||
/**
|
||||
* The destructor.
|
||||
@ -45,7 +45,7 @@ class TableObject: public InterfaceObject
|
||||
|
||||
/**
|
||||
* returns the internal data of the interface object. This pointer belongs to the TableObject class and
|
||||
* must not be freed.
|
||||
* must not be written at nor freed.
|
||||
*/
|
||||
uint8_t* data();
|
||||
/**
|
||||
@ -57,15 +57,20 @@ class TableObject: public InterfaceObject
|
||||
|
||||
static BeforeTablesUnloadCallback _beforeTablesUnload;
|
||||
|
||||
Memory& _memory;
|
||||
|
||||
private:
|
||||
uint32_t tableReference();
|
||||
bool allocTable(uint32_t size, bool doFill, uint8_t fillByte);
|
||||
void allocTableStatic();
|
||||
void initializeDynTableProperties(size_t propertiesSize, Property** properties);
|
||||
void loadEvent(const uint8_t* data);
|
||||
void loadEventUnloaded(const uint8_t* data);
|
||||
void loadEventLoading(const uint8_t* data);
|
||||
void loadEventLoaded(const uint8_t* data);
|
||||
void loadEventError(const uint8_t* data);
|
||||
void additionalLoadControls(const uint8_t* data);
|
||||
|
||||
/**
|
||||
* set the ::LoadState of the interface object.
|
||||
*
|
||||
@ -75,9 +80,10 @@ class TableObject: public InterfaceObject
|
||||
*/
|
||||
void loadState(LoadState newState);
|
||||
LoadState _state = LS_UNLOADED;
|
||||
Memory& _memory;
|
||||
uint8_t *_data = 0;
|
||||
static uint8_t _tableUnloadCount;
|
||||
uint32_t _staticTableAdr;
|
||||
uint32_t _staticTableSize;
|
||||
|
||||
/**
|
||||
* used to store size of data() in allocTable(), needed for calculation of crc in PID_MCB_TABLE.
|
||||
|
@ -245,7 +245,7 @@ class TpFrame
|
||||
*/
|
||||
uint16_t cemiSize()
|
||||
{
|
||||
return fullSize() + (isExtended() ? 2 : 3);
|
||||
return fullSize() + (isExtended() ? 2 : 3) - 1; // -1 without CRC
|
||||
}
|
||||
|
||||
/**
|
||||
@ -262,14 +262,14 @@ class TpFrame
|
||||
cemiBuffer[2] = _data[0];
|
||||
if (isExtended())
|
||||
{
|
||||
memcpy(cemiBuffer + 2, _data, fullSize());
|
||||
memcpy(cemiBuffer + 2, _data, fullSize() - 1); // -1 without CRC
|
||||
}
|
||||
else
|
||||
{
|
||||
cemiBuffer[3] = _data[5] & 0xF0;
|
||||
memcpy(cemiBuffer + 4, _data + 1, 4);
|
||||
cemiBuffer[8] = _data[5] & 0x0F;
|
||||
memcpy(cemiBuffer + 9, _data + 6, cemiBuffer[8] + 2);
|
||||
memcpy(cemiBuffer + 9, _data + 6, cemiBuffer[8] + 2 - 1); // -1 without CRC
|
||||
}
|
||||
|
||||
return cemiBuffer;
|
||||
|
@ -408,7 +408,8 @@ void TpUartDataLinkLayer::processRxFrameByte(uint8_t byte)
|
||||
if (_rxFrame->size() == 7)
|
||||
{
|
||||
// Prüfe ob ich für das Frame zuständig bin
|
||||
if (_forceAck || _cb.isAckRequired(_rxFrame->destination(), _rxFrame->isGroupAddress()))
|
||||
TPAckType ack = _cb.isAckRequired(_rxFrame->destination(), _rxFrame->isGroupAddress());
|
||||
if (_forceAck || ack)
|
||||
{
|
||||
/*
|
||||
* Speichere die Zuständigkeit dass dieses Frame weiterverarbeitet werden soll.
|
||||
@ -425,7 +426,7 @@ void TpUartDataLinkLayer::processRxFrameByte(uint8_t byte)
|
||||
_rxFrame->addFlags(TP_FRAME_FLAG_ACK);
|
||||
|
||||
// und im TPUart damit dieser das ACK schicken kann
|
||||
_platform.writeUart(U_ACK_REQ | U_ACK_REQ_ADRESSED);
|
||||
_platform.writeUart(U_ACK_REQ | ack);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1022,9 +1023,10 @@ bool TpUartDataLinkLayer::processTxFrameBytes()
|
||||
TpUartDataLinkLayer::TpUartDataLinkLayer(DeviceObject &devObj,
|
||||
NetworkLayerEntity &netLayerEntity,
|
||||
Platform &platform,
|
||||
BusAccessUnit& busAccessUnit,
|
||||
ITpUartCallBacks &cb,
|
||||
DataLinkLayerCallbacks *dllcb)
|
||||
: DataLinkLayer(devObj, netLayerEntity, platform),
|
||||
: DataLinkLayer(devObj, netLayerEntity, platform, busAccessUnit),
|
||||
_cb(cb),
|
||||
_dllcb(dllcb)
|
||||
{
|
||||
|
@ -29,7 +29,7 @@ class ITpUartCallBacks
|
||||
{
|
||||
public:
|
||||
virtual ~ITpUartCallBacks() = default;
|
||||
virtual bool isAckRequired(uint16_t address, bool isGrpAddr) = 0;
|
||||
virtual TPAckType isAckRequired(uint16_t address, bool isGrpAddr) = 0;
|
||||
};
|
||||
|
||||
class TpUartDataLinkLayer : public DataLinkLayer
|
||||
@ -39,7 +39,7 @@ class TpUartDataLinkLayer : public DataLinkLayer
|
||||
|
||||
public:
|
||||
TpUartDataLinkLayer(DeviceObject& devObj, NetworkLayerEntity& netLayerEntity,
|
||||
Platform& platform, ITpUartCallBacks& cb, DataLinkLayerCallbacks* dllcb = nullptr);
|
||||
Platform& platform, BusAccessUnit& busAccessUnit, ITpUartCallBacks& cb, DataLinkLayerCallbacks* dllcb = nullptr);
|
||||
|
||||
void loop();
|
||||
void enabled(bool value);
|
||||
|
@ -189,22 +189,6 @@ template <class P, class B> class KnxFacade : private SaveRestore
|
||||
{
|
||||
_progLedOnCallback = progLedOnCallback;
|
||||
}
|
||||
|
||||
#ifdef KNX_ACTIVITYCALLBACK
|
||||
/// @brief sets the Callback Function indicating sent or received telegrams
|
||||
/// @param activityCallback
|
||||
/// @details the info parameter
|
||||
void setActivityCallback(ActivityCallback activityCallback)
|
||||
{
|
||||
_activityCallback = activityCallback;
|
||||
}
|
||||
|
||||
void Activity(uint8_t info)
|
||||
{
|
||||
if(_activityCallback)
|
||||
_activityCallback(info);
|
||||
}
|
||||
#endif
|
||||
|
||||
int32_t buttonPin()
|
||||
{
|
||||
|
@ -506,7 +506,7 @@ bool RP2040ArduinoPlatform::sendBytesMultiCast(uint8_t* buffer, uint16_t len)
|
||||
return true;
|
||||
}
|
||||
|
||||
int RP2040ArduinoPlatform::readBytesMultiCast(uint8_t* buffer, uint16_t maxLen)
|
||||
int RP2040ArduinoPlatform::readBytesMultiCast(uint8_t* buffer, uint16_t maxLen, uint32_t& src_addr, uint16_t& src_port)
|
||||
{
|
||||
int len = _udp.parsePacket();
|
||||
if (len == 0)
|
||||
@ -521,6 +521,10 @@ int RP2040ArduinoPlatform::readBytesMultiCast(uint8_t* buffer, uint16_t maxLen)
|
||||
}
|
||||
|
||||
_udp.read(buffer, len);
|
||||
_remoteIP = _udp.remoteIP();
|
||||
_remotePort = _udp.remotePort();
|
||||
src_addr = htonl(_remoteIP);
|
||||
src_port = _remotePort;
|
||||
|
||||
// print("Remote IP: ");
|
||||
// print(_udp.remoteIP().toString().c_str());
|
||||
@ -534,6 +538,11 @@ bool RP2040ArduinoPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8
|
||||
{
|
||||
IPAddress ucastaddr(htonl(addr));
|
||||
|
||||
if(!addr)
|
||||
ucastaddr = _remoteIP;
|
||||
|
||||
if(!port)
|
||||
port = _remotePort;
|
||||
// print("sendBytesUniCast to:");
|
||||
// println(ucastaddr.toString().c_str());
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user