knx/src/knx/bau_systemB.cpp
2020-07-09 21:08:38 +02:00

636 lines
24 KiB
C++

#include "bau_systemB.h"
#include "bits.h"
#include <string.h>
#include <stdio.h>
enum NmReadSerialNumberType
{
NM_Read_SerialNumber_By_ProgrammingMode = 0x01,
NM_Read_SerialNumber_By_ExFactoryState = 0x02,
NM_Read_SerialNumber_By_PowerReset = 0x03,
NM_Read_SerialNumber_By_ManufacturerSpecific = 0xFE,
};
static constexpr auto kFunctionPropertyResultBufferMaxSize = 64;
static constexpr auto kRestartProcessTime = 3;
BauSystemB::BauSystemB(Platform& platform): _memory(platform, _deviceObj),
_appProgram(_memory),
_platform(platform)
{
_memory.addSaveRestore(&_appProgram);
}
void BauSystemB::loop()
{
dataLinkLayer().loop();
nextRestartState();
}
bool BauSystemB::enabled()
{
return dataLinkLayer().enabled();
}
void BauSystemB::enabled(bool value)
{
dataLinkLayer().enabled(value);
}
void BauSystemB::readMemory()
{
_memory.readMemory();
}
void BauSystemB::writeMemory()
{
_memory.writeMemory();
}
ApplicationProgramObject& BauSystemB::parameters()
{
return _appProgram;
}
DeviceObject& BauSystemB::deviceObject()
{
return _deviceObj;
}
bool BauSystemB::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 = _appProgram.loadState() == LS_LOADED;
return _configured;
}
uint8_t BauSystemB::checkmasterResetValidity(EraseCode eraseCode, uint8_t channel)
{
static constexpr uint8_t successCode = 0x00; // Where does this come from? It is the code for "success".
static constexpr uint8_t invalidEraseCode = 0x02; // Where does this come from? It is the error code for "unspported erase code".
switch (eraseCode)
{
case EraseCode::ConfirmedRestart:
{
println("Confirmed restart requested.");
return successCode;
}
case EraseCode::ResetAP:
{
// TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER)
println("ResetAP requested. Not implemented yet.");
return successCode;
}
case EraseCode::ResetIA:
{
// TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER)
println("ResetAP requested. Not implemented yet.");
return successCode;
}
case EraseCode::ResetLinks:
{
// TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER)
println("ResetLinks requested. Not implemented yet.");
return successCode;
}
case EraseCode::ResetParam:
{
// TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER)
println("ResetParam requested. Not implemented yet.");
return successCode;
}
case EraseCode::FactoryReset:
{
// TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER)
println("Factory reset requested. type: with IA");
return successCode;
}
case EraseCode::FactoryResetWithoutIA:
{
// TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER)
println("Factory reset requested. type: without IA");
return successCode;
}
default:
{
print("Unhandled erase code: ");
println(eraseCode, HEX);
return invalidEraseCode;
}
}
}
void BauSystemB::deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t descriptorType)
{
if (descriptorType != 0)
descriptorType = 0x3f;
uint8_t data[2];
pushWord(_deviceObj.maskVersion(), data);
applicationLayer().deviceDescriptorReadResponse(AckRequested, priority, hopType, asap, secCtrl, descriptorType, data);
}
void BauSystemB::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t * data)
{
_memory.writeMemory(memoryAddress, number, data);
if (_deviceObj.verifyMode())
memoryReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress);
}
void BauSystemB::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress)
{
applicationLayer().memoryReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress,
_memory.toAbsolute(memoryAddress));
}
void BauSystemB::memoryExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t * data)
{
_memory.writeMemory(memoryAddress, number, data);
applicationLayer().memoryExtWriteResponse(AckRequested, priority, hopType, asap, secCtrl, ReturnCodes::Success, number, memoryAddress, _memory.toAbsolute(memoryAddress));
}
void BauSystemB::memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress)
{
applicationLayer().memoryExtReadResponse(AckRequested, priority, hopType, asap, secCtrl, ReturnCodes::Success, number, memoryAddress, _memory.toAbsolute(memoryAddress));
}
void BauSystemB::doMasterReset(EraseCode eraseCode, uint8_t channel)
{
_appProgram.masterReset(eraseCode, channel);
}
void BauSystemB::restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, RestartType restartType, EraseCode eraseCode, uint8_t channel)
{
if (restartType == RestartType::BasicRestart)
{
println("Basic restart requested");
}
else if (restartType == RestartType::MasterReset)
{
uint8_t errorCode = checkmasterResetValidity(eraseCode, channel);
// We send the restart response now before actually applying the reset values
// Processing time is kRestartProcessTime (example 3 seconds) that we require for the applying the master reset with restart
applicationLayer().restartResponse(AckRequested, priority, hopType, secCtrl, errorCode, (errorCode == 0) ? kRestartProcessTime : 0);
doMasterReset(eraseCode, channel);
}
else
{
// Cannot happen as restartType is just one bit
println("Unhandled restart type.");
_platform.fatalError();
}
// Flush the EEPROM before resetting
_memory.writeMemory();
_platform.restart();
}
void BauSystemB::authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint32_t key)
{
applicationLayer().authorizeResponse(AckRequested, priority, hopType, asap, secCtrl, 0);
}
void BauSystemB::userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress)
{
applicationLayer().userMemoryReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress,
_memory.toAbsolute(memoryAddress));
}
void BauSystemB::userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data)
{
_memory.writeMemory(memoryAddress, number, data);
if (_deviceObj.verifyMode())
userMemoryReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress);
}
void BauSystemB::propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t propertyIndex)
{
uint8_t pid = propertyId;
bool writeEnable = false;
uint8_t type = 0;
uint16_t numberOfElements = 0;
uint8_t access = 0;
InterfaceObject* obj = getInterfaceObject(objectIndex);
if (obj)
obj->readPropertyDescription(pid, propertyIndex, writeEnable, type, numberOfElements, access);
applicationLayer().propertyDescriptionReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, pid, propertyIndex,
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)
{
InterfaceObject* obj = getInterfaceObject(objectIndex);
if(obj)
obj->writeProperty((PropertyID)propertyId, startIndex, data, numberOfElements);
propertyValueReadIndication(priority, hopType, asap, secCtrl, objectIndex, propertyId, numberOfElements, startIndex);
}
void BauSystemB::propertyValueExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool confirmed)
{
uint8_t returnCode = ReturnCodes::Success;
InterfaceObject* obj = getInterfaceObject(objectType, objectInstance);
if(obj)
obj->writeProperty((PropertyID)propertyId, startIndex, data, numberOfElements);
else
returnCode = ReturnCodes::AddressVoid;
if (confirmed)
{
applicationLayer().propertyValueExtWriteConResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, numberOfElements, startIndex, returnCode);
}
}
void BauSystemB::propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex)
{
uint8_t size = 0;
uint8_t elementCount = numberOfElements;
InterfaceObject* obj = getInterfaceObject(objectIndex);
if (obj)
{
uint8_t elementSize = obj->propertySize((PropertyID)propertyId);
if (startIndex > 0)
size = elementSize * numberOfElements;
else
size = sizeof(uint16_t); // size of property array entry 0 which contains the current number of elements
}
else
elementCount = 0;
uint8_t data[size];
if(obj)
obj->readProperty((PropertyID)propertyId, startIndex, elementCount, data);
if (elementCount == 0)
size = 0;
applicationLayer().propertyValueReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, elementCount,
startIndex, data, size);
}
void BauSystemB::propertyValueExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex)
{
uint8_t size = 0;
uint8_t elementCount = numberOfElements;
InterfaceObject* obj = getInterfaceObject(objectType, objectInstance);
if (obj)
{
uint8_t elementSize = obj->propertySize((PropertyID)propertyId);
if (startIndex > 0)
size = elementSize * numberOfElements;
else
size = sizeof(uint16_t); // size of propert array entry 0 which is the size
}
else
elementCount = 0;
uint8_t data[size];
if(obj)
obj->readProperty((PropertyID)propertyId, startIndex, elementCount, data);
if (elementCount == 0)
size = 0;
applicationLayer().propertyValueExtReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, elementCount,
startIndex, data, size);
}
void BauSystemB::functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t* data, uint8_t length)
{
uint8_t resultData[kFunctionPropertyResultBufferMaxSize];
uint8_t resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer
InterfaceObject* obj = getInterfaceObject(objectIndex);
if(obj)
{
if (obj->property((PropertyID)propertyId)->Type() == PDT_FUNCTION)
{
obj->command((PropertyID)propertyId, data, length, resultData, resultLength);
}
else
{
resultLength = 0; // We must not send a return code or any data fields
}
}
applicationLayer().functionPropertyStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, resultData, resultLength);
}
void BauSystemB::functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t* data, uint8_t length)
{
uint8_t resultData[kFunctionPropertyResultBufferMaxSize];
uint8_t resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer
InterfaceObject* obj = getInterfaceObject(objectIndex);
if(obj)
{
if (obj->property((PropertyID)propertyId)->Type() == PDT_FUNCTION)
{
obj->state((PropertyID)propertyId, data, length, resultData, resultLength);
}
else
{
resultLength = 0; // We must not send a return code or any data fields
}
}
applicationLayer().functionPropertyStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, resultData, resultLength);
}
void BauSystemB::functionPropertyExtCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t* data, uint8_t length)
{
uint8_t resultData[kFunctionPropertyResultBufferMaxSize];
uint8_t resultLength = 1; // we always have to include the return code at least
InterfaceObject* obj = getInterfaceObject(objectType, objectInstance);
if(obj)
{
PropertyDataType propType = obj->property((PropertyID)propertyId)->Type();
if (propType == PDT_FUNCTION)
{
// The first byte is reserved and 0 for PDT_FUNCTION
uint8_t reservedByte = data[0];
if (reservedByte != 0x00)
{
resultData[0] = ReturnCodes::DataVoid;
}
else
{
resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer
obj->command((PropertyID)propertyId, data, length, resultData, resultLength);
// resultLength was modified by the callee
}
}
else if (propType == PDT_CONTROL)
{
uint8_t count = 1;
// write the event
obj->writeProperty((PropertyID)propertyId, 1, data, count);
if (count == 1)
{
// Read the current state (one byte only) for the response
obj->readProperty((PropertyID)propertyId, 1, count, &resultData[1]);
resultLength = count ? 2 : 1;
resultData[0] = count ? ReturnCodes::Success : ReturnCodes::DataVoid;
}
else
{
resultData[0] = ReturnCodes::AddressVoid;
}
}
else
{
resultData[0] = ReturnCodes::DataTypeConflict;
}
}
else
{
resultData[0] = ReturnCodes::GenericError;
}
applicationLayer().functionPropertyExtStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, resultData, resultLength);
}
void BauSystemB::functionPropertyExtStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t* data, uint8_t length)
{
uint8_t resultData[kFunctionPropertyResultBufferMaxSize];
uint8_t resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer
InterfaceObject* obj = getInterfaceObject(objectType, objectInstance);
if(obj)
{
PropertyDataType propType = obj->property((PropertyID)propertyId)->Type();
if (propType == PDT_FUNCTION)
{
// The first byte is reserved and 0 for PDT_FUNCTION
uint8_t reservedByte = data[0];
if (reservedByte != 0x00)
{
resultData[0] = ReturnCodes::DataVoid;
}
else
{
resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer
obj->state((PropertyID)propertyId, data, length, resultData, resultLength);
// resultLength was modified by the callee
}
}
else if (propType == PDT_CONTROL)
{
uint8_t count = 1;
// Read the current state (one byte only) for the response
obj->readProperty((PropertyID)propertyId, 1, count, &resultData[1]);
resultLength = count ? 2 : 1;
resultData[0] = count ? ReturnCodes::Success : ReturnCodes::DataVoid;
}
else
{
resultData[0] = ReturnCodes::DataTypeConflict;
}
}
else
{
resultData[0] = ReturnCodes::GenericError;
}
applicationLayer().functionPropertyExtStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, resultData, resultLength);
}
void BauSystemB::individualAddressReadIndication(HopCountType hopType, const SecurityControl &secCtrl)
{
if (_deviceObj.progMode())
applicationLayer().individualAddressReadResponse(AckRequested, hopType, secCtrl);
}
void BauSystemB::individualAddressWriteIndication(HopCountType hopType, const SecurityControl &secCtrl, uint16_t newaddress)
{
if (_deviceObj.progMode())
_deviceObj.induvidualAddress(newaddress);
}
void BauSystemB::individualAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t newIndividualAddress,
uint8_t* knxSerialNumber)
{
// If the received serial number matches our serial number
// then store the received new individual address in the device object
if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6))
_deviceObj.induvidualAddress(newIndividualAddress);
}
void BauSystemB::individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* knxSerialNumber)
{
// If the received serial number matches our serial number
// then send a response with the serial number. The domain address is set to 0 for closed media.
// An open medium BAU has to override this method and provide a proper domain address.
if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6))
{
uint8_t emptyDomainAddress[6] = {0x00};
applicationLayer().IndividualAddressSerialNumberReadResponse(priority, hopType, secCtrl, emptyDomainAddress, knxSerialNumber);
}
}
void BauSystemB::addSaveRestore(SaveRestore* obj)
{
_memory.addSaveRestore(obj);
}
bool BauSystemB::restartRequest(uint16_t asap, const SecurityControl secCtrl)
{
if (applicationLayer().isConnected())
return false;
_restartState = Connecting; // order important, has to be set BEFORE connectRequest
_restartSecurity = secCtrl;
applicationLayer().connectRequest(asap, SystemPriority);
applicationLayer().deviceDescriptorReadRequest(AckRequested, SystemPriority, NetworkLayerParameter, asap, secCtrl, 0);
return true;
}
void BauSystemB::connectConfirm(uint16_t tsap)
{
if (_restartState == Connecting && tsap >= 0)
{
/* restart connection is confirmed, go to the next state */
_restartState = Connected;
_restartDelay = millis();
}
else
{
_restartState = Idle;
}
}
void BauSystemB::nextRestartState()
{
switch (_restartState)
{
case Idle:
/* inactive state, do nothing */
break;
case Connecting:
/* wait for connection, we do nothing here */
break;
case Connected:
/* connection confirmed, we send restartRequest, but we wait a moment (sending ACK etc)... */
if (millis() - _restartDelay > 30)
{
applicationLayer().restartRequest(AckRequested, SystemPriority, NetworkLayerParameter, _restartSecurity);
_restartState = Restarted;
_restartDelay = millis();
}
break;
case Restarted:
/* restart is finished, we send a discommect */
if (millis() - _restartDelay > 30)
{
applicationLayer().disconnectRequest(SystemPriority);
_restartState = Idle;
}
default:
break;
}
}
void BauSystemB::systemNetworkParameterReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t objectType,
uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength)
{
uint8_t operand;
popByte(operand, testInfo + 1); // First byte (+ 0) contains only 4 reserved bits (0)
// See KNX spec. 3.5.2 p.33 (Management Procedures: Procedures with A_SystemNetworkParameter_Read)
switch((NmReadSerialNumberType)operand)
{
case NM_Read_SerialNumber_By_ProgrammingMode: // NM_Read_SerialNumber_By_ProgrammingMode
// Only send a reply if programming mode is on
if (_deviceObj.progMode() && (objectType == OT_DEVICE) && (propertyId == PID_SERIAL_NUMBER))
{
// Send reply. testResult data is KNX serial number
applicationLayer().systemNetworkParameterReadResponse(priority, hopType, secCtrl, objectType, propertyId,
testInfo, testInfoLength, (uint8_t*)_deviceObj.propertyData(PID_SERIAL_NUMBER), 6);
}
break;
case NM_Read_SerialNumber_By_ExFactoryState: // NM_Read_SerialNumber_By_ExFactoryState
break;
case NM_Read_SerialNumber_By_PowerReset: // NM_Read_SerialNumber_By_PowerReset
break;
case NM_Read_SerialNumber_By_ManufacturerSpecific: // Manufacturer specific use of A_SystemNetworkParameter_Read
break;
}
}
void BauSystemB::systemNetworkParameterReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t objectType,
uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength, bool status)
{
}
void BauSystemB::propertyValueRead(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId,
uint8_t &numberOfElements, uint16_t startIndex,
uint8_t **data, uint32_t &length)
{
uint32_t size = 0;
uint8_t elementCount = numberOfElements;
InterfaceObject* obj = getInterfaceObject(objectType, objectInstance);
if (obj)
{
uint8_t elementSize = obj->propertySize((PropertyID)propertyId);
if (startIndex > 0)
size = elementSize * numberOfElements;
else
size = sizeof(uint16_t); // size of property array entry 0 which contains the current number of elements
*data = new uint8_t [size];
obj->readProperty((PropertyID)propertyId, startIndex, elementCount, *data);
}
else
{
elementCount = 0;
*data = nullptr;
}
numberOfElements = elementCount;
length = size;
}
void BauSystemB::propertyValueWrite(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId,
uint8_t &numberOfElements, uint16_t startIndex,
uint8_t* data, uint32_t length)
{
InterfaceObject* obj = getInterfaceObject(objectType, objectInstance);
if(obj)
obj->writeProperty((PropertyID)propertyId, startIndex, data, numberOfElements);
else
numberOfElements = 0;
}
Memory& BauSystemB::memory()
{
return _memory;
}