diff --git a/src/knx/bau091A.cpp b/src/knx/bau091A.cpp index 9e99538..b36ac9c 100644 --- a/src/knx/bau091A.cpp +++ b/src/knx/bau091A.cpp @@ -18,13 +18,21 @@ Bau091A::Bau091A(Platform& platform) _cemiServer(*this) #endif { + _rtObjPrimary.property(PID_MEDIUM)->write((uint8_t) DptMedium::KNX_IP); + _rtObjSecondary.property(PID_MEDIUM)->write((uint8_t) DptMedium::KNX_TP1); + + _rtObjPrimary.property(PID_OBJECT_INDEX)->write((uint8_t) 1); + _rtObjSecondary.property(PID_OBJECT_INDEX)->write((uint8_t) 2); + _netLayer.getEntity(0).dataLinkLayer(_dlLayerPrimary); _netLayer.getEntity(1).dataLinkLayer(_dlLayerSecondary); + #ifdef USE_CEMI_SERVER _cemiServer.dataLinkLayer(_dlLayerSecondary); // Secondary I/F is the important one! _dlLayer.cemiServer(_cemiServer); _memory.addSaveRestore(&_cemiServerObject); #endif + _memory.addSaveRestore(&_ipParameters); // Set Mask Version in Device Object depending on the BAU @@ -35,18 +43,17 @@ Bau091A::Bau091A(Platform& platform) // See PID_IO_LIST Property* prop = _deviceObj.property(PID_IO_LIST); prop->write(1, (uint16_t) OT_DEVICE); - prop->write(2, (uint16_t) OT_ADDR_TABLE); - prop->write(3, (uint16_t) OT_ASSOC_TABLE); - prop->write(4, (uint16_t) OT_GRP_OBJ_TABLE); - prop->write(5, (uint16_t) OT_APPLICATION_PROG); - prop->write(6, (uint16_t) OT_IP_PARAMETER); + prop->write(2, (uint16_t) OT_ROUTER); + prop->write(3, (uint16_t) OT_ROUTER); + prop->write(3, (uint16_t) OT_APPLICATION_PROG); + prop->write(4, (uint16_t) OT_IP_PARAMETER); #if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER) - prop->write(7, (uint16_t) OT_SECURITY); - prop->write(8, (uint16_t) OT_CEMI_SERVER); + prop->write(5, (uint16_t) OT_SECURITY); + prop->write(6, (uint16_t) OT_CEMI_SERVER); #elif defined(USE_DATASECURE) - prop->write(7, (uint16_t) OT_SECURITY); + prop->write(5, (uint16_t) OT_SECURITY); #elif defined(USE_CEMI_SERVER) - prop->write(7, (uint16_t) OT_CEMI_SERVER); + prop->write(5, (uint16_t) OT_CEMI_SERVER); #endif } @@ -57,9 +64,9 @@ InterfaceObject* Bau091A::getInterfaceObject(uint8_t idx) case 0: return &_deviceObj; case 1: - return nullptr; // TODO: Router Object Primary IF + return &_rtObjPrimary; case 2: - return nullptr; // TODO: Router Object Secondary IF + return &_rtObjSecondary; case 3: return &_appProgram; case 4: @@ -91,6 +98,8 @@ InterfaceObject* Bau091A::getInterfaceObject(ObjectType objectType, uint8_t obje { case OT_DEVICE: return &_deviceObj; + case OT_ROUTER: + return objectInstance == 0 ? &_rtObjPrimary : &_rtObjSecondary; case OT_APPLICATION_PROG: return &_appProgram; case OT_IP_PARAMETER: diff --git a/src/knx/bau_systemB_coupler.cpp b/src/knx/bau_systemB_coupler.cpp index adf96d2..0aa005c 100644 --- a/src/knx/bau_systemB_coupler.cpp +++ b/src/knx/bau_systemB_coupler.cpp @@ -6,6 +6,8 @@ BauSystemBCoupler::BauSystemBCoupler(Platform& platform) : BauSystemB(platform), _platform(platform), + _rtObjPrimary(), + _rtObjSecondary(memory()), #ifdef USE_DATASECURE _appLayer(_deviceObj, _secIfObj, *this), #else diff --git a/src/knx/bau_systemB_coupler.h b/src/knx/bau_systemB_coupler.h index 36d2deb..59a2278 100644 --- a/src/knx/bau_systemB_coupler.h +++ b/src/knx/bau_systemB_coupler.h @@ -8,6 +8,8 @@ #include "group_object_table_object.h" #include "security_interface_object.h" #include "application_program_object.h" +#include "router_object.h" +#include "router_object_filtertable.h" #include "application_layer.h" #include "secure_application_layer.h" #include "transport_layer.h" @@ -29,6 +31,9 @@ class BauSystemBCoupler : public BauSystemB virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override; Platform& _platform; + RouterObject _rtObjPrimary; + RouterObjectFilterTable _rtObjSecondary; + #ifdef USE_DATASECURE SecureApplicationLayer _appLayer; SecurityInterfaceObject _secIfObj; diff --git a/src/knx/knx_types.h b/src/knx/knx_types.h index f92b60c..7a740cc 100644 --- a/src/knx/knx_types.h +++ b/src/knx/knx_types.h @@ -228,3 +228,13 @@ enum EraseCode ResetLinks = 0x06, FactoryResetWithoutIA = 0x07 }; + +enum DptMedium +{ + // DPT_Medium (20.1004), range 0-255 + // All other values are reserved. + KNX_TP1 = 0x00, + KNX_PL110 = 0x01, + KNX_RF = 0x02, + KNX_IP = 0x05 +}; diff --git a/src/knx/router_object.cpp b/src/knx/router_object.cpp index b7b1412..6b35ec9 100644 --- a/src/knx/router_object.cpp +++ b/src/knx/router_object.cpp @@ -15,11 +15,11 @@ void RouterObject::initializeProperties(size_t propertiesSize, Property** proper Property* ownProperties[] = { new DataProperty( PID_OBJECT_TYPE, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t) OT_ROUTER ), - new DataProperty( PID_OBJECT_INDEX, false, PDT_UNSIGNED_CHAR, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ), // TODO - new DataProperty( PID_MEDIUM_STATUS, false, PDT_GENERIC_01, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ), // TODO - new DataProperty( PID_MAX_APDU_LENGTH_ROUTER, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t) 254 ), - new DataProperty( PID_HOP_COUNT, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t) 5), - new DataProperty( PID_MEDIUM, false, PDT_ENUM8, 1, ReadLv3 | WriteLv0, (uint16_t) 0) , // TODO + new DataProperty( PID_OBJECT_INDEX, false, PDT_UNSIGNED_CHAR, 1, ReadLv3 | WriteLv0 ), // Must be set by concrete BAUxxxx + new DataProperty( PID_MEDIUM_STATUS, false, PDT_GENERIC_01, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ), // For now: communication on medium is always possible + new DataProperty( PID_MAX_APDU_LENGTH_ROUTER, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t) 254 ), // For now: fixed size + new DataProperty( PID_HOP_COUNT, true, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t) 5), // TODO: Primary side: 5 for line coupler, 4 for backbone coupler, only exists if secondary is open medium without hop count + new DataProperty( PID_MEDIUM, false, PDT_ENUM8, 1, ReadLv3 | WriteLv0 ), // Must be set by concrete BAUxxxx }; uint8_t ownPropertiesCount = sizeof(ownProperties) / sizeof(Property*); diff --git a/src/knx/router_object_filtertable.cpp b/src/knx/router_object_filtertable.cpp index 60039d8..5b61c4e 100644 --- a/src/knx/router_object_filtertable.cpp +++ b/src/knx/router_object_filtertable.cpp @@ -8,6 +8,14 @@ #include "callback_property.h" #include "function_property.h" +enum RouteTableServices +{ + ClearRoutingTable = 0x01, // no info bytes + SetRoutingTable = 0x02, // no info bytes + ClearGroupAddress = 0x03, // 4 bytes: start address and end address + SetGroupAddress = 0x04, // 4 bytes: start address and end address +}; + RouterObjectFilterTable::RouterObjectFilterTable(Memory& memory) : _memory(memory) { @@ -48,121 +56,24 @@ RouterObjectFilterTable::RouterObjectFilterTable(Memory& memory) new FunctionProperty(this, PID_ROUTETABLE_CONTROL, // Command Callback of PID_ROUTETABLE_CONTROL [](RouterObjectFilterTable* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { - if (length != 3) - { - resultData[0] = ReturnCodes::DataVoid; - resultLength = 1; - return; - } - uint8_t id = data[1]; - uint8_t info = data[2]; - if (id == 0 && info == 0) - { - //obj->clearFailureLog(); - resultData[0] = ReturnCodes::Success; - resultData[1] = id; - resultLength = 2; - return; - } - resultData[0] = ReturnCodes::GenericError; - resultLength = 1; + obj->functionRouteTableControl(true, data, length, resultData, resultLength); }, // State Callback of PID_ROUTETABLE_CONTROL [](RouterObjectFilterTable* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { - if (length != 3) - { - resultData[0] = ReturnCodes::DataVoid; - resultLength = 1; - return; - } - uint8_t id = data[1]; - uint8_t info = data[2]; - - // failure counters - if (id == 0 && info == 0) - { - resultData[0] = ReturnCodes::Success; - resultData[1] = id; - resultData[2] = info; - //obj->getFailureCounters(&resultData[3]); // Put 8 bytes in the buffer - resultLength = 3 + 8; - return; - } - // query latest failure by index - else if(id == 1) - { - uint8_t maxBufferSize = resultLength; // Remember the maximum buffer size of the buffer that is provided to us - uint8_t index = info; - uint8_t numBytes = 0;//obj->getFromFailureLogByIndex(index, &resultData[2], maxBufferSize); - if ( numBytes > 0) - { - resultData[0] = ReturnCodes::Success; - resultData[1] = id; - resultData[2] = index; - resultLength += numBytes; - resultLength = 3 + numBytes; - return; - } - resultData[0] = ReturnCodes::DataVoid; - resultData[1] = id; - resultLength = 2; - return; - } - resultData[0] = ReturnCodes::GenericError; - resultLength = 1; + obj->functionRouteTableControl(false, data, length, resultData, resultLength); }), new DataProperty( PID_FILTER_TABLE_USE, false, PDT_BINARY_INFORMATION, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ), // TODO -/* + new FunctionProperty(this, PID_RF_ENABLE_SBC, // Command Callback of PID_RF_ENABLE_SBC [](RouterObjectFilterTable* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { - uint8_t serviceId = data[1] & 0xff; - if (serviceId != 0) - { - resultData[0] = ReturnCodes::InvalidCommand; - resultLength = 1; - return; - } - if (length == 3) - { - uint8_t mode = data[2]; - if (mode > 1) - { - resultData[0] = ReturnCodes::DataVoid; - resultLength = 1; - return; - } - //obj->setSecurityMode(mode == 1); - resultData[0] = ReturnCodes::Success; - resultData[1] = serviceId; - resultLength = 2; - return; - } - resultData[0] = ReturnCodes::GenericError; - resultLength = 1; + obj->functionRfEnableSbc(true, data, length, resultData, resultLength); }, // State Callback of PID_RF_ENABLE_SBC [](RouterObjectFilterTable* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { - uint8_t serviceId = data[1] & 0xff; - if (serviceId != 0) - { - resultData[0] = ReturnCodes::InvalidCommand; - resultLength = 1; - return; - } - if (length == 2) - { - resultData[0] = ReturnCodes::Success; - resultData[1] = serviceId; - resultData[2] = 0;//obj->isSecurityModeEnabled() ? 1 : 0; - resultLength = 3; - return; - } - resultData[0] = ReturnCodes::GenericError; - resultLength = 1; + obj->functionRfEnableSbc(false, data, length, resultData, resultLength); }), -*/ }; RouterObject::initializeProperties(sizeof(properties), properties); @@ -202,11 +113,103 @@ uint16_t RouterObjectFilterTable::saveSize() return 1 + 4 + RouterObject::saveSize(); } +void RouterObjectFilterTable::functionRouteTableControl(bool isCommand, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) +{ + bool isError = false; + RouteTableServices srvId = (RouteTableServices) data[1]; + + // 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. + // The full 16 bit KNX GA encoding range shall be supported. + // + // octet_address = GA_value div 8 + // bit_position = GA_value mod 8 + + // The CRC of the Memory Control Block Table Property is a CRC16-CCITT with the following + // parameters: + // Width = 16 bit + // Truncated polynomial = 1021h + // Initial value = FFFFh + // Input date is NOT reflected. + // Output CRC is NOT reflected. + // No XOR is performed on the output CRC. + // EXAMPLE The correct CRC16-CCITT of the string ‘123456789’ is E5CCh. + + if (isCommand) + { + switch(srvId) + { + case ClearRoutingTable: + case SetRoutingTable: + case ClearGroupAddress: + case SetGroupAddress: break; + default: isError = true; + } + } + else + { + switch(srvId) + { + case ClearRoutingTable: + case SetRoutingTable: + case ClearGroupAddress: + case SetGroupAddress: break; + default: isError = true; + } + } + + if (isError) + { + resultData[0] = ReturnCodes::GenericError; + resultData[1] = srvId; + resultLength = 2; + } +} + +void RouterObjectFilterTable::functionRfEnableSbc(bool isCommand, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) +{ + if (isCommand) + { + _rfSbcRoutingEnabled = (data[0] == 1) ? true : false; + } + + resultData[0] = ReturnCodes::Success; + resultData[1] = _rfSbcRoutingEnabled ? 1 : 0; + resultLength = 2; +} + +bool RouterObjectFilterTable::isRfSbcRoutingEnabled() +{ + return _rfSbcRoutingEnabled; +} + uint32_t RouterObjectFilterTable::tableReference() { return (uint32_t)_memory.toRelative(_data); } +bool RouterObjectFilterTable::allocTable(uint32_t size, bool doFill, uint8_t fillByte) +{ + if (_data) + { + _memory.freeMemory(_data); + _data = 0; + } + + if (size == 0) + return true; + + _data = _memory.allocMemory(size); + if (!_data) + return false; + + if (doFill) + memset(_data, fillByte, size); + + return true; +} + bool RouterObjectFilterTable::isLoaded() { return _state == LS_LOADED; @@ -272,7 +275,9 @@ void RouterObjectFilterTable::loadEventLoading(const uint8_t* data) case LE_UNLOAD: loadState(LS_UNLOADED); break; - case LE_ADDITIONAL_LOAD_CONTROLS: // Not supported here + case LE_ADDITIONAL_LOAD_CONTROLS: + additionalLoadControls(data); + break; default: loadState(LS_ERROR); errorCode(E_GOT_UNDEF_LOAD_CMD); @@ -292,6 +297,12 @@ void RouterObjectFilterTable::loadEventLoaded(const uint8_t* data) break; case LE_UNLOAD: loadState(LS_UNLOADED); + //free nv memory + if (_data) + { + _memory.freeMemory(_data); + _data = 0; + } break; case LE_ADDITIONAL_LOAD_CONTROLS: loadState(LS_ERROR); @@ -322,6 +333,25 @@ void RouterObjectFilterTable::loadEventError(const uint8_t* data) } } +void RouterObjectFilterTable::additionalLoadControls(const uint8_t* data) +{ + if (data[1] != 0x0B) // Data Relative Allocation + { + loadState(LS_ERROR); + errorCode(E_INVALID_OPCODE); + return; + } + + size_t size = ((data[2] << 24) | (data[3] << 16) | (data[4] << 8) | data[5]); + bool doFill = data[6] == 0x1; + uint8_t fillByte = data[7]; + if (!allocTable(size, doFill, fillByte)) + { + loadState(LS_ERROR); + errorCode(E_MAX_TABLE_LENGTH_EXEEDED); + } +} + void RouterObjectFilterTable::loadState(LoadState newState) { if (newState == _state) diff --git a/src/knx/router_object_filtertable.h b/src/knx/router_object_filtertable.h index 15c3e72..29d3b07 100644 --- a/src/knx/router_object_filtertable.h +++ b/src/knx/router_object_filtertable.h @@ -12,6 +12,8 @@ class RouterObjectFilterTable: public RouterObject public: RouterObjectFilterTable(Memory& memory); + bool isRfSbcRoutingEnabled(); + virtual void masterReset(EraseCode eraseCode, uint8_t channel) override; bool isLoaded(); @@ -22,7 +24,12 @@ public: uint16_t saveSize() override; private: + // Function properties + void functionRouteTableControl(bool isCommand, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength); + void functionRfEnableSbc(bool isCommand, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength); + uint32_t tableReference(); + bool allocTable(uint32_t size, bool doFill, uint8_t fillByte); void errorCode(ErrorCode errorCode); void loadEvent(const uint8_t* data); @@ -30,10 +37,12 @@ private: void loadEventLoading(const uint8_t* data); void loadEventLoaded(const uint8_t* data); void loadEventError(const uint8_t* data); + void additionalLoadControls(const uint8_t* data); void loadState(LoadState newState); LoadState _state = LS_UNLOADED; Memory& _memory; uint8_t *_data = 0; + bool _rfSbcRoutingEnabled = false; };