diff --git a/src/esp32_platform.cpp b/src/esp32_platform.cpp index 9367f4e..b0d5b03 100644 --- a/src/esp32_platform.cpp +++ b/src/esp32_platform.cpp @@ -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; } diff --git a/src/esp32_platform.h b/src/esp32_platform.h index 318291c..4318fa4 100644 --- a/src/esp32_platform.h +++ b/src/esp32_platform.h @@ -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; diff --git a/src/knx/application_layer.cpp b/src/knx/application_layer.cpp index 1e8ccec..21346a6 100644 --- a/src/knx/application_layer.cpp +++ b/src/knx/application_layer.cpp @@ -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); diff --git a/src/knx/application_layer.h b/src/knx/application_layer.h index c7593c8..e7f80d1 100644 --- a/src/knx/application_layer.h +++ b/src/knx/application_layer.h @@ -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); diff --git a/src/knx/application_program_object.cpp b/src/knx/application_program_object.cpp index 598d0fe..d58061f 100644 --- a/src/knx/application_program_object.cpp +++ b/src/knx/application_program_object.cpp @@ -6,7 +6,11 @@ #include ApplicationProgramObject::ApplicationProgramObject(Memory& memory) +#if MASK_VERSION == 0x091A + : TableObject(memory, 0x0100, 0x0100) +#else : TableObject(memory) +#endif { Property* properties[] = { diff --git a/src/knx/bau.cpp b/src/knx/bau.cpp index d54602f..a4958ff 100644 --- a/src/knx/bau.cpp +++ b/src/knx/bau.cpp @@ -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) { } diff --git a/src/knx/bau.h b/src/knx/bau.h index ad26183..44a0833 100644 --- a/src/knx/bau.h +++ b/src/knx/bau.h @@ -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); diff --git a/src/knx/bau07B0.cpp b/src/knx/bau07B0.cpp index c30906b..cfbc1bb 100644 --- a/src/knx/bau07B0.cpp +++ b/src/knx/bau07B0.cpp @@ -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() { diff --git a/src/knx/bau07B0.h b/src/knx/bau07B0.h index b87ca6d..44c51ca 100644 --- a/src/knx/bau07B0.h +++ b/src/knx/bau07B0.h @@ -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; diff --git a/src/knx/bau091A.cpp b/src/knx/bau091A.cpp index 202315f..430f2e6 100644 --- a/src/knx/bau091A.cpp +++ b/src/knx/bau091A.cpp @@ -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() { diff --git a/src/knx/bau091A.h b/src/knx/bau091A.h index 59b8151..494d2da 100644 --- a/src/knx/bau091A.h +++ b/src/knx/bau091A.h @@ -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: diff --git a/src/knx/bau27B0.cpp b/src/knx/bau27B0.cpp index e11cde7..3573202 100644 --- a/src/knx/bau27B0.cpp +++ b/src/knx/bau27B0.cpp @@ -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 diff --git a/src/knx/bau27B0.h b/src/knx/bau27B0.h index b5d5e81..d002040 100644 --- a/src/knx/bau27B0.h +++ b/src/knx/bau27B0.h @@ -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: diff --git a/src/knx/bau2920.cpp b/src/knx/bau2920.cpp index 3ed7827..b0ed9b4 100644 --- a/src/knx/bau2920.cpp +++ b/src/knx/bau2920.cpp @@ -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 diff --git a/src/knx/bau2920.h b/src/knx/bau2920.h index 6c0bbcb..98552fc 100644 --- a/src/knx/bau2920.h +++ b/src/knx/bau2920.h @@ -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: diff --git a/src/knx/bau57B0.cpp b/src/knx/bau57B0.cpp index 2e3b7d2..170bf7e 100644 --- a/src/knx/bau57B0.cpp +++ b/src/knx/bau57B0.cpp @@ -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 diff --git a/src/knx/bau57B0.h b/src/knx/bau57B0.h index fb437a1..e8e6835 100644 --- a/src/knx/bau57B0.h +++ b/src/knx/bau57B0.h @@ -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: diff --git a/src/knx/bau_systemB.cpp b/src/knx/bau_systemB.cpp index fc1fdf8..61e8b82 100644 --- a/src/knx/bau_systemB.cpp +++ b/src/knx/bau_systemB.cpp @@ -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) { diff --git a/src/knx/bau_systemB.h b/src/knx/bau_systemB.h index ad57e66..c652c94 100644 --- a/src/knx/bau_systemB.h +++ b/src/knx/bau_systemB.h @@ -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, diff --git a/src/knx/bau_systemB_coupler.cpp b/src/knx/bau_systemB_coupler.cpp index d9e92ce..5a7f12b 100644 --- a/src/knx/bau_systemB_coupler.cpp +++ b/src/knx/bau_systemB_coupler.cpp @@ -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 diff --git a/src/knx/cemi_frame.cpp b/src/knx/cemi_frame.cpp index b58b1f2..50354b8 100644 --- a/src/knx/cemi_frame.cpp +++ b/src/knx/cemi_frame.cpp @@ -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; } diff --git a/src/knx/cemi_server.cpp b/src/knx/cemi_server.cpp index 983d6dc..3961c61 100644 --- a/src/knx/cemi_server.cpp +++ b/src/knx/cemi_server.cpp @@ -11,10 +11,13 @@ #include 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 diff --git a/src/knx/cemi_server.h b/src/knx/cemi_server.h index b44e1cf..f97ead8 100644 --- a/src/knx/cemi_server.h +++ b/src/knx/cemi_server.h @@ -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 \ No newline at end of file diff --git a/src/knx/config.h b/src/knx/config.h index f937510..a7a0ada 100644 --- a/src/knx/config.h +++ b/src/knx/config.h @@ -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 diff --git a/src/knx/data_link_layer.cpp b/src/knx/data_link_layer.cpp index be9d6fa..e111566 100644 --- a/src/knx/data_link_layer.cpp +++ b/src/knx/data_link_layer.cpp @@ -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 diff --git a/src/knx/data_link_layer.h b/src/knx/data_link_layer.h index 817078e..b544fb0 100644 --- a/src/knx/data_link_layer.h +++ b/src/knx/data_link_layer.h @@ -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 }; diff --git a/src/knx/device_object.h b/src/knx/device_object.h index 10f171d..744965a 100644 --- a/src/knx/device_object.h +++ b/src/knx/device_object.h @@ -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 }; diff --git a/src/knx/interface_object.cpp b/src/knx/interface_object.cpp index 0789992..19c58b6 100644 --- a/src/knx/interface_object.cpp +++ b/src/knx/interface_object.cpp @@ -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); diff --git a/src/knx/interface_object.h b/src/knx/interface_object.h index 47b1124..0cfa5b1 100644 --- a/src/knx/interface_object.h +++ b/src/knx/interface_object.h @@ -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. * diff --git a/src/knx/ip_data_link_layer.cpp b/src/knx/ip_data_link_layer.cpp index 6219b09..3fe45b9 100644 --- a/src/knx/ip_data_link_layer.cpp +++ b/src/knx/ip_data_link_layer.cpp @@ -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 #include @@ -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: "); diff --git a/src/knx/ip_data_link_layer.h b/src/knx/ip_data_link_layer.h index 5402a93..128f59f 100644 --- a/src/knx/ip_data_link_layer.h +++ b/src/knx/ip_data_link_layer.h @@ -6,6 +6,8 @@ #include #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 \ No newline at end of file diff --git a/src/knx/ip_host_protocol_address_information.h b/src/knx/ip_host_protocol_address_information.h index f2c9ae7..bcf0494 100644 --- a/src/knx/ip_host_protocol_address_information.h +++ b/src/knx/ip_host_protocol_address_information.h @@ -12,6 +12,7 @@ enum HostProtocolCode : uint8_t #ifdef USE_IP #define LEN_IPHPAI 8 +#define LEN_CRD 4 class IpHostProtocolAddressInformation { diff --git a/src/knx/ip_parameter_object.cpp b/src/knx/ip_parameter_object.cpp index 3de44d3..5980783 100644 --- a/src/knx/ip_parameter_object.cpp +++ b/src/knx/ip_parameter_object.cpp @@ -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 diff --git a/src/knx/knx_ip_ch.cpp b/src/knx/knx_ip_ch.cpp new file mode 100644 index 0000000..aa473fb --- /dev/null +++ b/src/knx/knx_ip_ch.cpp @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_ch.h b/src/knx/knx_ip_ch.h new file mode 100644 index 0000000..5277928 --- /dev/null +++ b/src/knx/knx_ip_ch.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#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 diff --git a/src/knx/knx_ip_config_dib.cpp b/src/knx/knx_ip_config_dib.cpp new file mode 100644 index 0000000..e603de0 --- /dev/null +++ b/src/knx/knx_ip_config_dib.cpp @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_config_dib.h b/src/knx/knx_ip_config_dib.h new file mode 100644 index 0000000..717bab5 --- /dev/null +++ b/src/knx/knx_ip_config_dib.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_config_request.cpp b/src/knx/knx_ip_config_request.cpp new file mode 100644 index 0000000..962d1ea --- /dev/null +++ b/src/knx/knx_ip_config_request.cpp @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_config_request.h b/src/knx/knx_ip_config_request.h new file mode 100644 index 0000000..d7d6d1c --- /dev/null +++ b/src/knx/knx_ip_config_request.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_connect_request.cpp b/src/knx/knx_ip_connect_request.cpp new file mode 100644 index 0000000..a1f64c0 --- /dev/null +++ b/src/knx/knx_ip_connect_request.cpp @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_connect_request.h b/src/knx/knx_ip_connect_request.h new file mode 100644 index 0000000..23e398f --- /dev/null +++ b/src/knx/knx_ip_connect_request.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_connect_response.cpp b/src/knx/knx_ip_connect_response.cpp new file mode 100644 index 0000000..11eb705 --- /dev/null +++ b/src/knx/knx_ip_connect_response.cpp @@ -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(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 diff --git a/src/knx/knx_ip_connect_response.h b/src/knx/knx_ip_connect_response.h new file mode 100644 index 0000000..ecf1d8f --- /dev/null +++ b/src/knx/knx_ip_connect_response.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_crd.cpp b/src/knx/knx_ip_crd.cpp new file mode 100644 index 0000000..b9f96b0 --- /dev/null +++ b/src/knx/knx_ip_crd.cpp @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_crd.h b/src/knx/knx_ip_crd.h new file mode 100644 index 0000000..c7e613b --- /dev/null +++ b/src/knx/knx_ip_crd.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#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 diff --git a/src/knx/knx_ip_cri.cpp b/src/knx/knx_ip_cri.cpp new file mode 100644 index 0000000..8bb2107 --- /dev/null +++ b/src/knx/knx_ip_cri.cpp @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_cri.h b/src/knx/knx_ip_cri.h new file mode 100644 index 0000000..6c1377a --- /dev/null +++ b/src/knx/knx_ip_cri.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#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 diff --git a/src/knx/knx_ip_description_request.cpp b/src/knx/knx_ip_description_request.cpp new file mode 100644 index 0000000..2e463f8 --- /dev/null +++ b/src/knx/knx_ip_description_request.cpp @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_description_request.h b/src/knx/knx_ip_description_request.h new file mode 100644 index 0000000..39327e2 --- /dev/null +++ b/src/knx/knx_ip_description_request.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_description_response.cpp b/src/knx/knx_ip_description_response.cpp new file mode 100644 index 0000000..6df1c13 --- /dev/null +++ b/src/knx/knx_ip_description_response.cpp @@ -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(PID_KNX_INDIVIDUAL_ADDRESS)); + _deviceInfo.projectInstallationIdentifier(parameters.propertyValue(PID_PROJECT_INSTALLATION_ID)); + _deviceInfo.serialNumber(deviceObject.propertyData(PID_SERIAL_NUMBER)); + _deviceInfo.routingMulticastAddress(parameters.propertyValue(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 diff --git a/src/knx/knx_ip_description_response.h b/src/knx/knx_ip_description_response.h new file mode 100644 index 0000000..f9d1055 --- /dev/null +++ b/src/knx/knx_ip_description_response.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_dib.h b/src/knx/knx_ip_dib.h index 236d25c..cb848cf 100644 --- a/src/knx/knx_ip_dib.h +++ b/src/knx/knx_ip_dib.h @@ -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 }; diff --git a/src/knx/knx_ip_disconnect_request.cpp b/src/knx/knx_ip_disconnect_request.cpp new file mode 100644 index 0000000..f5f041f --- /dev/null +++ b/src/knx/knx_ip_disconnect_request.cpp @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_disconnect_request.h b/src/knx/knx_ip_disconnect_request.h new file mode 100644 index 0000000..560441d --- /dev/null +++ b/src/knx/knx_ip_disconnect_request.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_disconnect_response.cpp b/src/knx/knx_ip_disconnect_response.cpp new file mode 100644 index 0000000..d232539 --- /dev/null +++ b/src/knx/knx_ip_disconnect_response.cpp @@ -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 diff --git a/src/knx/knx_ip_disconnect_response.h b/src/knx/knx_ip_disconnect_response.h new file mode 100644 index 0000000..26ef875 --- /dev/null +++ b/src/knx/knx_ip_disconnect_response.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_extended_device_information_dib.cpp b/src/knx/knx_ip_extended_device_information_dib.cpp new file mode 100644 index 0000000..574f9a2 --- /dev/null +++ b/src/knx/knx_ip_extended_device_information_dib.cpp @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_extended_device_information_dib.h b/src/knx/knx_ip_extended_device_information_dib.h new file mode 100644 index 0000000..df46f5e --- /dev/null +++ b/src/knx/knx_ip_extended_device_information_dib.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_knx_addresses_dib.cpp b/src/knx/knx_ip_knx_addresses_dib.cpp new file mode 100644 index 0000000..63b5f7b --- /dev/null +++ b/src/knx/knx_ip_knx_addresses_dib.cpp @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_knx_addresses_dib.h b/src/knx/knx_ip_knx_addresses_dib.h new file mode 100644 index 0000000..c090552 --- /dev/null +++ b/src/knx/knx_ip_knx_addresses_dib.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_search_request_extended.cpp b/src/knx/knx_ip_search_request_extended.cpp new file mode 100644 index 0000000..c838716 --- /dev/null +++ b/src/knx/knx_ip_search_request_extended.cpp @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_search_request_extended.h b/src/knx/knx_ip_search_request_extended.h new file mode 100644 index 0000000..6d019d4 --- /dev/null +++ b/src/knx/knx_ip_search_request_extended.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_search_response.cpp b/src/knx/knx_ip_search_response.cpp index c4d8830..508617d 100644 --- a/src/knx/knx_ip_search_response.cpp +++ b/src/knx/knx_ip_search_response.cpp @@ -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(PID_KNX_INDIVIDUAL_ADDRESS)); _deviceInfo.projectInstallationIdentifier(parameters.propertyValue(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 } diff --git a/src/knx/knx_ip_search_response.h b/src/knx/knx_ip_search_response.h index 5cc84cd..056f975 100644 --- a/src/knx/knx_ip_search_response.h +++ b/src/knx/knx_ip_search_response.h @@ -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 diff --git a/src/knx/knx_ip_search_response_extended.cpp b/src/knx/knx_ip_search_response_extended.cpp new file mode 100644 index 0000000..eae2182 --- /dev/null +++ b/src/knx/knx_ip_search_response_extended.cpp @@ -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(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(PID_KNX_INDIVIDUAL_ADDRESS)); + _deviceInfo.projectInstallationIdentifier(parameters.propertyValue(PID_PROJECT_INSTALLATION_ID)); + _deviceInfo.serialNumber(deviceObject.propertyData(PID_SERIAL_NUMBER)); + _deviceInfo.routingMulticastAddress(parameters.propertyValue(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(PID_IP_ADDRESS)); + _ipConfig.subnet(parameters.propertyValue(PID_SUBNET_MASK)); + _ipConfig.gateway(parameters.propertyValue(PID_DEFAULT_GATEWAY)); + _ipConfig.info1(parameters.propertyValue(PID_IP_CAPABILITIES)); + _ipConfig.info2(parameters.propertyValue(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(PID_CURRENT_IP_ADDRESS)); + _ipCurConfig.subnet(parameters.propertyValue(PID_CURRENT_SUBNET_MASK)); + _ipCurConfig.gateway(parameters.propertyValue(PID_CURRENT_DEFAULT_GATEWAY)); + _ipCurConfig.dhcp(parameters.propertyValue(PID_DHCP_BOOTP_SERVER)); + _ipCurConfig.info1(parameters.propertyValue(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 \ No newline at end of file diff --git a/src/knx/knx_ip_search_response_extended.h b/src/knx/knx_ip_search_response_extended.h new file mode 100644 index 0000000..c7d3793 --- /dev/null +++ b/src/knx/knx_ip_search_response_extended.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_state_request.cpp b/src/knx/knx_ip_state_request.cpp new file mode 100644 index 0000000..a1828d1 --- /dev/null +++ b/src/knx/knx_ip_state_request.cpp @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_state_request.h b/src/knx/knx_ip_state_request.h new file mode 100644 index 0000000..26cab9b --- /dev/null +++ b/src/knx/knx_ip_state_request.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_state_response.cpp b/src/knx/knx_ip_state_response.cpp new file mode 100644 index 0000000..bfb8871 --- /dev/null +++ b/src/knx/knx_ip_state_response.cpp @@ -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 diff --git a/src/knx/knx_ip_state_response.h b/src/knx/knx_ip_state_response.h new file mode 100644 index 0000000..253de4d --- /dev/null +++ b/src/knx/knx_ip_state_response.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_tunnel_connection.cpp b/src/knx/knx_ip_tunnel_connection.cpp new file mode 100644 index 0000000..9d4fe2a --- /dev/null +++ b/src/knx/knx_ip_tunnel_connection.cpp @@ -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; +} diff --git a/src/knx/knx_ip_tunnel_connection.h b/src/knx/knx_ip_tunnel_connection.h new file mode 100644 index 0000000..e1323ad --- /dev/null +++ b/src/knx/knx_ip_tunnel_connection.h @@ -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: + +}; \ No newline at end of file diff --git a/src/knx/knx_ip_tunneling_ack.cpp b/src/knx/knx_ip_tunneling_ack.cpp new file mode 100644 index 0000000..b17ae35 --- /dev/null +++ b/src/knx/knx_ip_tunneling_ack.cpp @@ -0,0 +1,20 @@ +#include "knx_ip_tunneling_ack.h" +#include + +#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 \ No newline at end of file diff --git a/src/knx/knx_ip_tunneling_ack.h b/src/knx/knx_ip_tunneling_ack.h new file mode 100644 index 0000000..8ef0983 --- /dev/null +++ b/src/knx/knx_ip_tunneling_ack.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_tunneling_info_dib.cpp b/src/knx/knx_ip_tunneling_info_dib.cpp new file mode 100644 index 0000000..ef5b4ef --- /dev/null +++ b/src/knx/knx_ip_tunneling_info_dib.cpp @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_tunneling_info_dib.h b/src/knx/knx_ip_tunneling_info_dib.h new file mode 100644 index 0000000..77d63fd --- /dev/null +++ b/src/knx/knx_ip_tunneling_info_dib.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_ip_tunneling_request.cpp b/src/knx/knx_ip_tunneling_request.cpp new file mode 100644 index 0000000..452fbfc --- /dev/null +++ b/src/knx/knx_ip_tunneling_request.cpp @@ -0,0 +1,26 @@ +#include "knx_ip_tunneling_request.h" +#include + +#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 \ No newline at end of file diff --git a/src/knx/knx_ip_tunneling_request.h b/src/knx/knx_ip_tunneling_request.h new file mode 100644 index 0000000..1373eb7 --- /dev/null +++ b/src/knx/knx_ip_tunneling_request.h @@ -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 \ No newline at end of file diff --git a/src/knx/knx_types.h b/src/knx/knx_types.h index b0a2a95..ecbbd97 100644 --- a/src/knx/knx_types.h +++ b/src/knx/knx_types.h @@ -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 +}; \ No newline at end of file diff --git a/src/knx/memory.cpp b/src/knx/memory.cpp index c7bdd00..2cedf47 100644 --- a/src/knx/memory.cpp +++ b/src/knx/memory.cpp @@ -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) { diff --git a/src/knx/memory.h b/src/knx/memory.h index 58a10ef..f0c2298 100644 --- a/src/knx/memory.h +++ b/src/knx/memory.h @@ -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); diff --git a/src/knx/network_layer_coupler.cpp b/src/knx/network_layer_coupler.cpp index 038d455..bcfe68d 100644 --- a/src/knx/network_layer_coupler.cpp +++ b/src/knx/network_layer_coupler.cpp @@ -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 \ No newline at end of file diff --git a/src/knx/network_layer_coupler.h b/src/knx/network_layer_coupler.h index b57f30a..23f714b 100644 --- a/src/knx/network_layer_coupler.h +++ b/src/knx/network_layer_coupler.h @@ -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]; diff --git a/src/knx/network_layer_entity.cpp b/src/knx/network_layer_entity.cpp index 6463d83..389f765 100644 --- a/src/knx/network_layer_entity.cpp +++ b/src/knx/network_layer_entity.cpp @@ -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; diff --git a/src/knx/network_layer_entity.h b/src/knx/network_layer_entity.h index 82db48c..22599e4 100644 --- a/src/knx/network_layer_entity.h +++ b/src/knx/network_layer_entity.h @@ -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; diff --git a/src/knx/platform.cpp b/src/knx/platform.cpp index 2b398ae..51b2d03 100644 --- a/src/knx/platform.cpp +++ b/src/knx/platform.cpp @@ -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) diff --git a/src/knx/platform.h b/src/knx/platform.h index ddb1a67..29c46f1 100644 --- a/src/knx/platform.h +++ b/src/knx/platform.h @@ -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(); diff --git a/src/knx/property.h b/src/knx/property.h index 56c6b24..09a964c 100644 --- a/src/knx/property.h +++ b/src/knx/property.h @@ -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, diff --git a/src/knx/rf_data_link_layer.cpp b/src/knx/rf_data_link_layer.cpp index 7765736..3e0baab 100644 --- a/src/knx/rf_data_link_layer.cpp +++ b/src/knx/rf_data_link_layer.cpp @@ -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) { diff --git a/src/knx/rf_data_link_layer.h b/src/knx/rf_data_link_layer.h index 79dd28b..93ed1e5 100644 --- a/src/knx/rf_data_link_layer.h +++ b/src/knx/rf_data_link_layer.h @@ -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); diff --git a/src/knx/router_object.cpp b/src/knx/router_object.cpp index b7c7b62..70fab47 100644 --- a/src/knx/router_object.cpp +++ b/src/knx/router_object.cpp @@ -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(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; diff --git a/src/knx/router_object.h b/src/knx/router_object.h index 0064a06..467835b 100644 --- a/src/knx/router_object.h +++ b/src/knx/router_object.h @@ -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; }; diff --git a/src/knx/service_families.h b/src/knx/service_families.h new file mode 100644 index 0000000..5120e4f --- /dev/null +++ b/src/knx/service_families.h @@ -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 \ No newline at end of file diff --git a/src/knx/table_object.cpp b/src/knx/table_object.cpp index 90e6624..993277c 100644 --- a/src/knx/table_object.cpp +++ b/src/knx/table_object.cpp @@ -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(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*); diff --git a/src/knx/table_object.h b/src/knx/table_object.h index 66768cd..61520c6 100644 --- a/src/knx/table_object.h +++ b/src/knx/table_object.h @@ -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. diff --git a/src/knx/tp_frame.h b/src/knx/tp_frame.h index cd900a6..f1bbf76 100644 --- a/src/knx/tp_frame.h +++ b/src/knx/tp_frame.h @@ -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; diff --git a/src/knx/tpuart_data_link_layer.cpp b/src/knx/tpuart_data_link_layer.cpp index 627daf0..4649b68 100644 --- a/src/knx/tpuart_data_link_layer.cpp +++ b/src/knx/tpuart_data_link_layer.cpp @@ -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) { diff --git a/src/knx/tpuart_data_link_layer.h b/src/knx/tpuart_data_link_layer.h index 7dd0d55..4fe8fe8 100644 --- a/src/knx/tpuart_data_link_layer.h +++ b/src/knx/tpuart_data_link_layer.h @@ -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); diff --git a/src/knx_facade.h b/src/knx_facade.h index 3f1c60b..0e55667 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -189,22 +189,6 @@ template 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() { diff --git a/src/rp2040_arduino_platform.cpp b/src/rp2040_arduino_platform.cpp index cbc745e..6ac1ea4 100644 --- a/src/rp2040_arduino_platform.cpp +++ b/src/rp2040_arduino_platform.cpp @@ -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()); diff --git a/src/rp2040_arduino_platform.h b/src/rp2040_arduino_platform.h index bb756d0..aa6e746 100644 --- a/src/rp2040_arduino_platform.h +++ b/src/rp2040_arduino_platform.h @@ -119,7 +119,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; @@ -131,6 +131,9 @@ public: #endif protected: pin_size_t _rxPin = UART_PIN_NOT_DEFINED; protected: pin_size_t _txPin = UART_PIN_NOT_DEFINED; + + protected: IPAddress _remoteIP = 0; + protected: uint16_t _remotePort = 0; }; #endif