initial commit

This commit is contained in:
Thomas Kunze 2018-04-09 22:30:23 +02:00
parent 96eea4ffe8
commit 2a169ee66b
49 changed files with 6025 additions and 0 deletions

63
.gitattributes vendored Normal file
View File

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

261
.gitignore vendored Normal file
View File

@ -0,0 +1,261 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

85
address_table_object.cpp Normal file
View File

@ -0,0 +1,85 @@
#include <cstring>
#include "address_table_object.h"
#include "bits.h"
using namespace std;
AddressTableObject::AddressTableObject(uint8_t* memoryReference): TableObject(memoryReference)
{
}
void AddressTableObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data)
{
switch (id)
{
case PID_OBJECT_TYPE:
pushWord(OT_ADDR_TABLE, data);
break;
default:
TableObject::readProperty(id, start, count, data);
}
}
uint16_t AddressTableObject::entryCount()
{
if (loadState() != LS_LOADED)
return 0;
return ntohs(_groupAddresses[0]);
}
uint16_t AddressTableObject::getGa(uint16_t tsap)
{
if (loadState() != LS_LOADED || tsap > entryCount() )
return 0;
return ntohs(_groupAddresses[tsap]);
}
uint16_t AddressTableObject::getTsap(uint16_t addr)
{
uint16_t size = entryCount();
for (uint16_t i = 1; i <= size; i++)
if (ntohs(_groupAddresses[i]) == addr)
return i;
return 1;
}
#pragma region SaveRestore
uint8_t* AddressTableObject::save(uint8_t* buffer)
{
return TableObject::save(buffer);
}
uint8_t* AddressTableObject::restore(uint8_t* buffer)
{
buffer = TableObject::restore(buffer);
_groupAddresses = (uint16_t*)_data;
return buffer;
}
#pragma endregion
bool AddressTableObject::contains(uint16_t addr)
{
uint16_t size = entryCount();
for (uint16_t i = 1; i <= size; i++)
if (ntohs(_groupAddresses[i]) == addr)
return true;
return false;
}
void AddressTableObject::beforeStateChange(LoadState& newState)
{
if (newState != LS_LOADED)
return;
_groupAddresses = (uint16_t*)_data;
}

20
address_table_object.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include "table_object.h"
class AddressTableObject: public TableObject
{
public:
AddressTableObject(uint8_t* memoryReference);
void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data);
uint16_t entryCount();
uint16_t getGa(uint16_t tsap);
uint16_t getTsap(uint16_t ga);
uint8_t* save(uint8_t* buffer);
uint8_t* restore(uint8_t* buffer);
bool contains(uint16_t addr);
protected:
virtual void beforeStateChange(LoadState& newState);
private:
uint16_t* _groupAddresses;
};

53
apdu.cpp Normal file
View File

@ -0,0 +1,53 @@
#include "apdu.h"
#include "cemi_frame.h"
#include "bits.h"
APDU::APDU(uint8_t* data, CemiFrame& frame): _data(data), _frame(frame)
{
}
ApduType APDU::type()
{
uint16_t apci;
apci = getWord(_data);
popWord(apci, _data);
apci &= 0x3ff;
if ((apci >> 6) < 11) //short apci
apci &= 0x3c0;
return (ApduType)apci;
}
void APDU::type(ApduType atype)
{
// ApduType is in big endian so convert to host first, pushWord converts back
pushWord((uint16_t)atype, _data);
}
uint8_t* APDU::data()
{
return _data + 1;
}
CemiFrame& APDU::frame()
{
return _frame;
}
uint8_t APDU::length() const
{
return _frame.npdu().octetCount();
}
void APDU::printPDU()
{
//Print.print("APDU: ");
//print.print(type(), HEX, 4);
//print.print(" ");
//print.print(_data[0] & 0x3, HEX, 2);
//for (uint8_t i = 1; i < length() + 1; ++i)
//{
// if (i) print.print(" ");
// print.print(_data[i], HEX, 2);
//}
//print.println();
}

22
apdu.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <stdint.h>
#include "knx_types.h"
class CemiFrame;
class APDU
{
friend class CemiFrame;
public:
APDU(uint8_t* data, CemiFrame& frame);
ApduType type();
void type(ApduType atype);
uint8_t* data();
CemiFrame& frame();
uint8_t length() const;
void printPDU();
private:
uint8_t* _data;
CemiFrame& _frame;
};

792
application_layer.cpp Normal file
View File

@ -0,0 +1,792 @@
#include "application_layer.h"
#include "transport_layer.h"
#include "cemi_frame.h"
#include "association_table_object.h"
#include "apdu.h"
#include "bau.h"
#include "string.h"
#include "bits.h"
#include <stdio.h>
ApplicationLayer::ApplicationLayer(AssociationTableObject& assocTable, BusAccessUnit& bau):
_assocTable(assocTable), _bau(bau)
{
}
void ApplicationLayer::transportLayer(TransportLayer& layer)
{
_transportLayer = &layer;
}
#pragma region TL Callbacks
void ApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu)
{
uint16_t entries = _assocTable.entryCount();
uint8_t len = apdu.length();
uint8_t dataArray[len];
uint8_t* data = dataArray;
memcpy(data, apdu.data(), len);
if (len == 1)
{
//less than six bit are encoded in first byte
*data &= 0x3f;
}
else
{
data += 1;
len -= 1;
}
for (uint16_t i = 0; i < entries; i++)
{
uint16_t entry = _assocTable[i];
if (highByte(entry) == tsap)
{
uint16_t asap = lowByte(entry);
switch (apdu.type())
{
case GroupValueRead:
_bau.groupValueReadIndication(asap, priority, hopType);
break;
case GroupValueResponse:
_bau.groupValueReadAppLayerConfirm(asap, priority, hopType, data, len);
break;
case GroupValueWrite:
_bau.groupValueWriteIndication(asap, priority, hopType, data, len);
}
}
}
}
void ApplicationLayer::dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status)
{
switch (apdu.type())
{
case GroupValueRead:
_bau.groupValueReadLocalConfirm(ack, _savedAsapReadRequest, priority, hopType, status);
break;
case GroupValueResponse:
_bau.groupValueReadResponseConfirm(ack, _savedAsapResponse, priority, hopType, apdu.data(), apdu.length() - 1, status);
break;
case GroupValueWrite:
_bau.groupValueWriteLocalConfirm(ack, _savedAsapWriteRequest, priority, hopType, apdu.data(), apdu.length() - 1, status);
break;
}
}
void ApplicationLayer::dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu)
{
uint8_t* data = apdu.data();
switch (apdu.type())
{
case IndividualAddressWrite:
{
uint16_t newAddress;
popWord(newAddress, data + 1);
_bau.individualAddressWriteIndication(hopType, newAddress);
break;
}
case IndividualAddressRead:
_bau.individualAddressReadIndication(hopType);
break;
case IndividualAddressResponse:
_bau.individualAddressReadAppLayerConfirm(hopType, apdu.frame().sourceAddress());
break;
case IndividualAddressSerialNumberRead:
_bau.individualAddressSerialNumberReadIndication(hopType, data + 1);
break;
case IndividualAddressSerialNumberResponse:
{
uint16_t domainAddress;
popWord(domainAddress, data + 7);
_bau.individualAddressSerialNumberReadAppLayerConfirm(hopType, data + 1, apdu.frame().sourceAddress(),
domainAddress);
break;
}
case IndividualAddressSerialNumberWrite:
{
uint16_t newAddress;
popWord(newAddress, data + 7);
_bau.individualAddressSerialNumberWriteIndication(hopType, data + 1, newAddress);
break;
}
}
}
void ApplicationLayer::dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status)
{
uint8_t* data = apdu.data();
switch (apdu.type())
{
case IndividualAddressWrite:
{
uint16_t newAddress;
popWord(newAddress, data + 1);
_bau.individualAddressWriteLocalConfirm(ack, hopType, newAddress, status);
break;
}
case IndividualAddressRead:
_bau.individualAddressReadLocalConfirm(ack, hopType, status);
break;
case IndividualAddressResponse:
_bau.individualAddressReadResponseConfirm(ack, hopType, status);
break;
case IndividualAddressSerialNumberRead:
_bau.individualAddressSerialNumberReadLocalConfirm(ack, hopType, data + 1, status);
break;
case IndividualAddressSerialNumberResponse:
{
uint16_t domainAddress;
popWord(domainAddress, data + 7);
_bau.individualAddressSerialNumberReadResponseConfirm(ack, hopType, data + 1, domainAddress, status);
break;
}
case IndividualAddressSerialNumberWrite:
{
uint16_t newAddress;
popWord(newAddress, data + 7);
_bau.individualAddressSerialNumberWriteLocalConfirm(ack, hopType, data + 1, newAddress, status);
break;
}
}
}
void ApplicationLayer::dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu)
{
}
void ApplicationLayer::dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status)
{
}
void ApplicationLayer::dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu)
{
individualIndication(hopType, priority, tsap, apdu);
}
void ApplicationLayer::dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status)
{
individualConfirm(ack, hopType, priority, tsap, apdu, status);
}
void ApplicationLayer::connectIndication(uint16_t tsap)
{
_connectedTsap = tsap;
}
void ApplicationLayer::connectConfirm(uint16_t destination, uint16_t tsap, bool status)
{
}
void ApplicationLayer::disconnectIndication(uint16_t tsap)
{
_connectedTsap = -1;
}
void ApplicationLayer::disconnectConfirm(Priority priority, uint16_t tsap, bool status)
{
}
void ApplicationLayer::dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu)
{
individualIndication(NetworkLayerParameter, priority, tsap, apdu);
}
void ApplicationLayer::dataConnectedConfirm(uint16_t tsap)
{
}
#pragma endregion
void ApplicationLayer::groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType)
{
_savedAsapReadRequest = asap;
CemiFrame frame(1);
APDU& apdu = frame.apdu();
apdu.type(GroupValueRead);
int32_t value = _assocTable.translateAsap(asap);
if (value < 0)
return; // there is no tsap in association table for this asap
uint16_t tsap = (uint16_t)value;
// first to bus then to itself
_transportLayer->dataGroupRequest(ack, hopType, priority, tsap, apdu);
dataGroupIndication(hopType, priority, tsap, apdu);
}
void ApplicationLayer::groupValueReadResponse(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength)
{
_savedAsapResponse = asap;
groupValueSend(GroupValueResponse, ack, asap, priority, hopType, data, dataLength);
}
void ApplicationLayer::groupValueWriteRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength)
{
_savedAsapWriteRequest = asap;
groupValueSend(GroupValueWrite, ack, asap, priority, hopType, data, dataLength);
}
void ApplicationLayer::individualAddressWriteRequest(AckType ack, HopCountType hopType, uint16_t newaddress)
{
CemiFrame frame(3);
APDU& apdu = frame.apdu();
apdu.type(IndividualAddressWrite);
uint8_t* apduData = apdu.data();
pushWord(newaddress, apduData + 1);
_transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu);
}
void ApplicationLayer::individualAddressReadRequest(AckType ack, HopCountType hopType)
{
CemiFrame frame(1);
APDU& apdu = frame.apdu();
apdu.type(IndividualAddressRead);
_transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu);
}
void ApplicationLayer::individualAddressReadResponse(AckType ack, HopCountType hopType)
{
CemiFrame frame(1);
APDU& apdu = frame.apdu();
apdu.type(IndividualAddressResponse);
_transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu);
}
void ApplicationLayer::individualAddressSerialNumberReadRequest(AckType ack, HopCountType hopType, uint8_t * serialNumber)
{
CemiFrame frame(7);
APDU& apdu = frame.apdu();
apdu.type(IndividualAddressSerialNumberRead);
uint8_t* data = apdu.data() + 1;
memcpy(data, serialNumber, 6);
_transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu);
}
void ApplicationLayer::individualAddressSerialNumberReadResponse(AckType ack, HopCountType hopType,
uint8_t * serialNumber, uint16_t domainAddress)
{
CemiFrame frame(7);
APDU& apdu = frame.apdu();
apdu.type(IndividualAddressSerialNumberResponse);
uint8_t* data = apdu.data() + 1;
memcpy(data, serialNumber, 6);
data += 6;
pushWord(domainAddress, data);
_transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu);
}
void ApplicationLayer::individualAddressSerialNumberWriteRequest(AckType ack, HopCountType hopType, uint8_t * serialNumber,
uint16_t newaddress)
{
CemiFrame frame(13);
APDU& apdu = frame.apdu();
apdu.type(IndividualAddressSerialNumberWrite);
uint8_t* data = apdu.data() + 1;
memcpy(data, serialNumber, 6);
data += 6;
pushWord(newaddress, data);
_transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu);
}
void ApplicationLayer::deviceDescriptorReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t descriptorType)
{
CemiFrame frame(1);
APDU& apdu = frame.apdu();
apdu.type(DeviceDescriptorRead);
uint8_t* data = apdu.data();
*data |= (descriptorType & 0x3f);
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::deviceDescriptorReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t descriptorType, uint8_t* deviceDescriptor)
{
uint8_t length = 0;
switch (descriptorType)
{
case 0:
length = 3;
break;
case 2:
length = 14;
break;
default:
length = 1;
descriptorType = 0x3f;
break;
}
CemiFrame frame(length);
APDU& apdu = frame.apdu();
apdu.type(DeviceDescriptorResponse);
uint8_t* data = apdu.data();
*data |= (descriptorType & 0x3f);
if (length > 1)
memcpy(data + 1, deviceDescriptor, length - 1);
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::restartRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap)
{
CemiFrame frame(1);
APDU& apdu = frame.apdu();
apdu.type(Restart);
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::propertyValueReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex)
{
CemiFrame frame(5);
APDU& apdu = frame.apdu();
apdu.type(PropertyValueRead);
uint8_t* data = apdu.data();
data += 1;
data = pushByte(objectIndex, data);
data = pushByte(propertyId, data);
pushWord(startIndex & 0xfff, data);
*data &= ((numberOfElements & 0xf) << 4);
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::propertyValueReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length)
{
propertyDataSend(PropertyValueResponse, ack, priority, hopType, asap, objectIndex, propertyId, numberOfElements,
startIndex, data, length);
}
void ApplicationLayer::propertyValueWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t * data, uint8_t length)
{
propertyDataSend(PropertyValueWrite, ack, priority, hopType, asap, objectIndex, propertyId, numberOfElements,
startIndex, data, length);
}
void ApplicationLayer::propertyDescriptionReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex)
{
CemiFrame frame(4);
APDU& apdu = frame.apdu();
apdu.type(PropertyDescriptionRead);
uint8_t* data = apdu.data();
data[1] = objectIndex;
data[2] = propertyId;
data[3] = propertyIndex;
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type,
uint16_t maxNumberOfElements, uint8_t access)
{
CemiFrame frame(8);
APDU& apdu = frame.apdu();
apdu.type(PropertyDescriptionRead);
uint8_t* data = apdu.data();
data[1] = objectIndex;
data[2] = propertyId;
data[3] = propertyIndex;
if (writeEnable)
data[4] |= 0x80;
data[4] |= (type & 0x3f);
pushWord(maxNumberOfElements & 0xfff, data + 5);
data[7] = access;
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::memoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress)
{
CemiFrame frame(3);
APDU& apdu = frame.apdu();
apdu.type(MemoryRead);
uint8_t* data = apdu.data();
*data |= (number & 0x3f);
pushWord(memoryAddress, data + 1);
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::memoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress, uint8_t * memoryData)
{
memorySend(MemoryResponse, ack, priority, hopType, asap, number, memoryAddress, memoryData);
}
void ApplicationLayer::memoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t number, uint16_t memoryAddress, uint8_t * data)
{
memorySend(MemoryWrite, ack, priority, hopType, asap, number, memoryAddress, data);
}
void ApplicationLayer::userMemoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t number, uint32_t memoryAddress)
{
CemiFrame frame(4);
APDU& apdu = frame.apdu();
apdu.type(UserMemoryRead);
uint8_t* data = apdu.data();
data[1] |= (number & 0xf);
data[1] |= ((memoryAddress >> 12) & 0xf0);
pushWord(memoryAddress & 0xff, data + 2);
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::userMemoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t number, uint32_t memoryAddress, uint8_t * memoryData)
{
userMemorySend(UserMemoryResponse, ack, priority, hopType, asap, number, memoryAddress, memoryData);
}
void ApplicationLayer::userMemoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t number, uint32_t memoryAddress, uint8_t * memoryData)
{
userMemorySend(UserMemoryWrite, ack, priority, hopType, asap, number, memoryAddress, memoryData);
}
void ApplicationLayer::userManufacturerInfoReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap)
{
CemiFrame frame(1);
APDU& apdu = frame.apdu();
apdu.type(UserManufacturerInfoRead);
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::userManufacturerInfoReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t* info)
{
CemiFrame frame(4);
APDU& apdu = frame.apdu();
apdu.type(UserMemoryRead);
uint8_t* data = apdu.data();
memcpy(data + 1, info, 3);
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::authorizeRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key)
{
CemiFrame frame(6);
APDU& apdu = frame.apdu();
apdu.type(AuthorizeRequest);
uint8_t* data = apdu.data();
pushInt(key, data + 2);
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::authorizeResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level)
{
CemiFrame frame(2);
APDU& apdu = frame.apdu();
apdu.type(AuthorizeResponse);
uint8_t* data = apdu.data();
data[1] = level;
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::keyWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key)
{
CemiFrame frame(6);
APDU& apdu = frame.apdu();
apdu.type(KeyWrite);
uint8_t* data = apdu.data();
data[1] = level;
pushInt(key, data + 2);
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::keyWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level)
{
CemiFrame frame(6);
APDU& apdu = frame.apdu();
apdu.type(KeyResponse);
uint8_t* data = apdu.data();
data[1] = level;
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::propertyDataSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length)
{
CemiFrame frame(5 + length);
APDU& apdu = frame.apdu();
apdu.type(type);
uint8_t* apduData = apdu.data();
apduData += 1;
apduData = pushByte(objectIndex, apduData);
apduData = pushByte(propertyId, apduData);
pushWord(startIndex & 0xfff, apduData);
*apduData |= ((numberOfElements & 0xf) << 4);
apduData += 2;
if (length > 0)
memcpy(apduData, data, length);
if (asap == _connectedTsap)
_transportLayer->dataConnectedRequest(asap, priority, apdu);
else
_transportLayer->dataIndividualRequest(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::groupValueSend(ApduType type, AckType ack, uint16_t asap, Priority priority, HopCountType hopType,
uint8_t* data, uint8_t& dataLength)
{
CemiFrame frame(dataLength + 1);
APDU& apdu = frame.apdu();
apdu.type(type);
uint8_t* apdudata = apdu.data();
if (dataLength == 0)
{
// data size is six bit or less. So store int first byte
*apdudata &= ~0x3f;
*apdudata |= (*data & 0x3f);
}
else
{
memcpy(apdudata + 1, data, dataLength);
}
// no need to check if there is a tsap. This is a response, so the read got trough
uint16_t tsap = (uint16_t)_assocTable.translateAsap(asap);
_transportLayer->dataGroupRequest(ack, hopType, priority, tsap, apdu);
dataGroupIndication(hopType, priority, tsap, apdu);
}
void ApplicationLayer::memorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress, uint8_t * memoryData)
{
CemiFrame frame(3 + number);
APDU& apdu = frame.apdu();
apdu.type(type);
uint8_t* data = apdu.data();
*data |= (number & 0x3f);
pushWord(memoryAddress, data + 1);
if (number > 0)
memcpy(data + 3, memoryData, number);
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::userMemorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint32_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);
data[1] |= ((memoryAddress >> 12) & 0xf0);
pushWord(memoryAddress & 0xffff, data + 2);
if (number > 0)
memcpy(data + 4, memoryData, number);
individualSend(ack, hopType, priority, asap, apdu);
}
void ApplicationLayer::individualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU & apdu)
{
uint8_t* data = apdu.data();
switch (apdu.type())
{
case DeviceDescriptorRead:
_bau.deviceDescriptorReadIndication(priority, hopType, tsap, *data & 0x3f);
break;
case DeviceDescriptorResponse:
_bau.deviceDescriptorReadAppLayerConfirm(priority, hopType, tsap, *data & 0x3f, data + 1);
break;
case Restart:
if ((*data & 0x3f) == 0)
_bau.restartRequestIndication(priority, hopType, tsap);
break;
case PropertyValueRead:
{
uint16_t startIndex;
popWord(startIndex, data + 3);
startIndex &= 0xfff;
_bau.propertyValueReadIndication(priority, hopType, tsap, data[1], data[2], data[3] >> 4, startIndex);
break;
}
case PropertyValueResponse:
{
uint16_t startIndex;
popWord(startIndex, data + 3);
startIndex &= 0xfff;
_bau.propertyValueReadAppLayerConfirm(priority, hopType, tsap, data[1], data[2], data[3] >> 4,
startIndex, data + 5, apdu.length() - 5);
break;
}
case PropertyValueWrite:
{
uint16_t startIndex;
popWord(startIndex, data + 3);
startIndex &= 0xfff;
_bau.propertyValueWriteIndication(priority, hopType, tsap, data[1], data[2], data[3] >> 4,
startIndex, data + 5, apdu.length() - 5);
break;
}
case PropertyDescriptionRead:
_bau.propertyDescriptionReadIndication(priority, hopType, tsap, data[1], data[2], data[3]);
break;
case PropertyDescriptionResponse:
_bau.propertyDescriptionReadAppLayerConfirm(priority, hopType, tsap, data[1], data[2], data[3],
(data[4] & 0x80) > 0, data[4] & 0x3f, getWord(data + 5) & 0xfff, data[7]);
break;
case MemoryRead:
_bau.memoryReadIndication(priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1));
break;
case MemoryResponse:
_bau.memoryReadAppLayerConfirm(priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3);
break;
case MemoryWrite:
_bau.memoryWriteIndication(priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3);
break;
case UserMemoryRead:
{
uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3];
_bau.userMemoryReadIndication(priority, hopType, tsap, data[1] & 0xf, address);
break;
}
case UserMemoryResponse:
{
uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3];
_bau.userMemoryReadAppLayerConfirm(priority, hopType, tsap, data[1] & 0xf, address, data + 4);
break;
}
case UserMemoryWrite:
{
uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3];
_bau.userMemoryWriteIndication(priority, hopType, tsap, data[1] & 0xf, address, data + 4);
break;
}
case UserManufacturerInfoRead:
_bau.userManufacturerInfoIndication(priority, hopType, tsap);
break;
case UserManufacturerInfoResponse:
_bau.userManufacturerInfoAppLayerConfirm(priority, hopType, tsap, data + 1);
break;
case AuthorizeRequest:
_bau.authorizeIndication(priority, hopType, tsap, getInt(data + 2));
break;
case AuthorizeResponse:
_bau.authorizeAppLayerConfirm(priority, hopType, tsap, data[1]);
break;
case KeyWrite:
_bau.keyWriteIndication(priority, hopType, tsap, data[1], getInt(data + 2));
break;
case KeyResponse:
_bau.keyWriteAppLayerConfirm(priority, hopType, tsap, data[1]);
break;
}
}
void ApplicationLayer::individualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU & apdu, bool status)
{
uint8_t* data = apdu.data();
switch (apdu.type())
{
case DeviceDescriptorRead:
_bau.deviceDescriptorReadLocalConfirm(ack, priority, hopType, tsap, *data & 0x3f, status);
break;
case DeviceDescriptorResponse:
_bau.deviceDescriptorReadResponseConfirm(ack, priority, hopType, tsap, *data & 0x3f, data + 1, status);
break;
case Restart:
_bau.restartRequestLocalConfirm(ack, priority, hopType, tsap, status);
break;
case PropertyValueRead:
{
uint16_t startIndex;
popWord(startIndex, data + 3);
startIndex &= 0xfff;
_bau.propertyValueReadLocalConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3] >> 4,
startIndex, status);
break;
}
case PropertyValueResponse:
{
uint16_t startIndex;
popWord(startIndex, data + 3);
startIndex &= 0xfff;
_bau.propertyValueReadResponseConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3] >> 4,
startIndex, data + 5, apdu.length() - 5, status);
break;
}
case PropertyValueWrite:
{
uint16_t startIndex;
popWord(startIndex, data + 3);
startIndex &= 0xfff;
_bau.propertyValueWriteLocalConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3] >> 4,
startIndex, data + 5, apdu.length() - 5, status);
break;
}
case PropertyDescriptionRead:
_bau.propertyDescriptionReadLocalConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3], status);
break;
case PropertyDescriptionResponse:
_bau.propertyDescriptionReadResponseConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3],
(data[4] & 0x80) > 0, data[4] & 0x3f, getWord(data + 5) & 0xfff, data[7], status);
break;
case MemoryRead:
_bau.memoryReadLocalConfirm(ack, priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), status);
break;
case MemoryResponse:
_bau.memoryReadResponseConfirm(ack, priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3, status);
break;
case MemoryWrite:
_bau.memoryWriteLocalConfirm(ack, priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3, status);
break;
case UserMemoryRead:
{
uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3];
_bau.memoryReadLocalConfirm(ack, priority, hopType, tsap, data[1] & 0xf, address, status);
break;
}
case UserMemoryResponse:
{
uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3];
_bau.memoryReadResponseConfirm(ack, priority, hopType, tsap, data[1] & 0xf, address, data + 4, status);
break;
}
case UserMemoryWrite:
{
uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3];
_bau.memoryWriteLocalConfirm(ack, priority, hopType, tsap, data[1] & 0xf, address, data + 4, status);
break;
}
case UserManufacturerInfoRead:
_bau.userManufacturerInfoLocalConfirm(ack, priority, hopType, tsap, status);
break;
case UserManufacturerInfoResponse:
_bau.userManufacturerInfoResponseConfirm(ack, priority, hopType, tsap, data + 1, status);
break;
case AuthorizeRequest:
_bau.authorizeLocalConfirm(ack, priority, hopType, tsap, getInt(data + 2), status);
break;
case AuthorizeResponse:
_bau.authorizeResponseConfirm(ack, priority, hopType, tsap, data[1], status);
break;
case KeyWrite:
_bau.keyWriteLocalConfirm(ack, priority, hopType, tsap, data[1], getInt(data + 2), status);
break;
case KeyResponse:
_bau.keyWriteResponseConfirm(ack, priority, hopType, tsap, data[1], status);
break;
}
}
void ApplicationLayer::individualSend(AckType ack, HopCountType hopType, Priority priority, uint16_t asap, APDU& apdu)
{
if (asap == _connectedTsap)
_transportLayer->dataConnectedRequest(asap, priority, apdu);
else
_transportLayer->dataIndividualRequest(ack, hopType, priority, asap, apdu);
}

102
application_layer.h Normal file
View File

@ -0,0 +1,102 @@
#pragma once
#include <stdint.h>
#include "knx_types.h"
#include "apdu.h"
class AssociationTableObject;
class BusAccessUnit;
class TransportLayer;
class ApplicationLayer
{
public:
ApplicationLayer(AssociationTableObject& assocTable, BusAccessUnit& bau);
void transportLayer(TransportLayer& layer);
// from transport layer
#pragma region Transport-Layer-Callbacks
void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu);
void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap,
APDU& apdu, bool status);
void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu);
void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status);
void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu);
void dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status);
void dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu);
void dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status);
void connectIndication(uint16_t tsap);
void connectConfirm(uint16_t destination, uint16_t tsap, bool status);
void disconnectIndication(uint16_t tsap);
void disconnectConfirm(Priority priority, uint16_t tsap, bool status);
void dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu);
void dataConnectedConfirm(uint16_t tsap);
#pragma endregion
void groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType);
void groupValueReadResponse(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t dataLength);
void groupValueWriteRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t dataLength);
void individualAddressWriteRequest(AckType ack, HopCountType hopType, uint16_t newaddress);
void individualAddressReadRequest(AckType ack, HopCountType hopType);
void individualAddressReadResponse(AckType ack, HopCountType hopType);
void individualAddressSerialNumberReadRequest(AckType ack, HopCountType hopType, uint8_t* serialNumber);
void individualAddressSerialNumberReadResponse(AckType ack, HopCountType hopType, uint8_t* serialNumber,
uint16_t domainAddress);
void individualAddressSerialNumberWriteRequest(AckType ack, HopCountType hopType, uint8_t* serialNumber,
uint16_t newaddress);
void deviceDescriptorReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t descriptorType);
void deviceDescriptorReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t descriptorType, uint8_t* deviceDescriptor);
void restartRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap);
void propertyValueReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex);
void propertyValueReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length);
void propertyValueWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length);
void propertyDescriptionReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex);
void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type,
uint16_t maxNumberOfElements, uint8_t access);
void memoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress);
void memoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress, uint8_t* data);
void memoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress, uint8_t* data);
void userMemoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint32_t memoryAddress);
void userMemoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint32_t memoryAddress, uint8_t* memoryData);
void userMemoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint32_t memoryAddress, uint8_t* memoryData);
void userManufacturerInfoReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap);
void userManufacturerInfoReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t* info);
void authorizeRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key);
void authorizeResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level);
void keyWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key);
void keyWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level);
private:
void propertyDataSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, 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, uint8_t number,
uint16_t memoryAddress, uint8_t * memoryData);
void userMemorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t number, uint32_t memoryAddress, uint8_t* memoryData);
void groupValueSend(ApduType type, AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t& dataLength);
void individualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu);
void individualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status);
void individualSend(AckType ack, HopCountType hopType, Priority priority, uint16_t asap, APDU& apdu);
uint16_t _savedAsapReadRequest;
uint16_t _savedAsapWriteRequest;
uint16_t _savedAsapResponse;
AssociationTableObject& _assocTable;
BusAccessUnit& _bau;
TransportLayer* _transportLayer;
int32_t _connectedTsap;
};

View File

@ -0,0 +1,87 @@
#include "application_program_object.h"
#include "bits.h"
ApplicationProgramObject::ApplicationProgramObject(uint8_t* memoryReference): TableObject(memoryReference)
{
}
void ApplicationProgramObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data)
{
switch (id)
{
case PID_OBJECT_TYPE:
pushWord(OT_APPLICATION_PROG, data);
break;
case PID_PROG_VERSION:
pushByteArray(_programVersion, 5, data);
break;
case PID_PEI_TYPE:
pushByte(0x0, data);
break;
default:
TableObject::readProperty(id, start, count, data);
}
}
void ApplicationProgramObject::writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count)
{
switch (id)
{
case PID_PROG_VERSION:
for (uint32_t i = 0; i < 5; i++)
{
_programVersion[i] = data[i];
}
break;
default:
TableObject::writeProperty(id, start, data, count);
}
}
uint8_t ApplicationProgramObject::propertySize(PropertyID id)
{
switch (id)
{
case PID_OBJECT_TYPE:
case PID_PEI_TYPE:
return 1;
case PID_PROG_VERSION:
return 5;
}
return TableObject::propertySize(id);
}
uint8_t * ApplicationProgramObject::data(uint32_t addr)
{
return _data + addr;
}
uint8_t ApplicationProgramObject::getByte(uint32_t addr)
{
return *(_data + addr);
}
uint16_t ApplicationProgramObject::getWord(uint32_t addr)
{
return ::getWord(_data + addr);
}
uint32_t ApplicationProgramObject::getInt(uint32_t addr)
{
return ::getInt(_data + addr);
}
uint8_t* ApplicationProgramObject::save(uint8_t* buffer)
{
buffer = pushByteArray(_programVersion, 5, buffer);
return TableObject::save(buffer);
}
uint8_t* ApplicationProgramObject::restore(uint8_t* buffer)
{
buffer = popByteArray(_programVersion, 5, buffer);
return TableObject::restore(buffer);
}

View File

@ -0,0 +1,20 @@
#pragma once
#include "table_object.h"
class ApplicationProgramObject: public TableObject
{
public:
ApplicationProgramObject(uint8_t* memoryReference);
void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data);
void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count);
uint8_t propertySize(PropertyID id);
uint8_t* data(uint32_t addr);
uint8_t getByte(uint32_t addr);
uint16_t getWord(uint32_t addr);
uint32_t getInt(uint32_t addr);
uint8_t* save(uint8_t* buffer);
uint8_t* restore(uint8_t* buffer);
private:
uint8_t _programVersion[5];
};

View File

@ -0,0 +1,69 @@
#include <cstring>
#include "association_table_object.h"
#include "bits.h"
using namespace std;
AssociationTableObject::AssociationTableObject(uint8_t* memoryReference): TableObject(memoryReference)
{
}
void AssociationTableObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data)
{
switch (id)
{
case PID_OBJECT_TYPE:
pushWord(OT_ASSOC_TABLE, data);
break;
default:
TableObject::readProperty(id, start, count, data);
}
}
uint16_t AssociationTableObject::entryCount()
{
return ntohs(_tableData[0]);
}
uint16_t AssociationTableObject::operator[](uint16_t idx)
{
if (idx < 0 || idx >= entryCount())
return 0;
return ntohs(_tableData[idx + 1]);
}
uint8_t* AssociationTableObject::save(uint8_t* buffer)
{
return TableObject::save(buffer);
}
uint8_t* AssociationTableObject::restore(uint8_t* buffer)
{
buffer = TableObject::restore(buffer);
_tableData = (uint16_t*)_data;
return buffer;
}
int32_t AssociationTableObject::translateAsap(uint16_t asap)
{
uint16_t entries = entryCount();
for (uint16_t i = 0; i < entries; i++)
{
uint16_t entry = ntohs(operator[](i));
if (lowByte(entry) == asap)
return highByte(entry);
}
return -1;
}
void AssociationTableObject::beforeStateChange(LoadState& newState)
{
if (newState != LS_LOADED)
return;
_tableData = (uint16_t*)_data;
}

View File

@ -0,0 +1,20 @@
#pragma once
#include "table_object.h"
class AssociationTableObject: public TableObject
{
public:
AssociationTableObject(uint8_t* memoryReference);
void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data);
uint16_t entryCount();
uint16_t operator[](uint16_t idx);
uint8_t* save(uint8_t* buffer);
uint8_t* restore(uint8_t* buffer);
int32_t translateAsap(uint16_t asap);
protected:
void beforeStateChange(LoadState& newState);
private:
uint16_t* _tableData;
};

238
bau.cpp Normal file
View File

@ -0,0 +1,238 @@
#include "bau.h"
void BusAccessUnit::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status)
{
}
void BusAccessUnit::groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType)
{
}
void BusAccessUnit::groupValueReadResponseConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopTtype, uint8_t * data, uint8_t dataLength, bool status)
{
}
void BusAccessUnit::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength)
{
}
void BusAccessUnit::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength, bool status)
{
}
void BusAccessUnit::groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength)
{
}
void BusAccessUnit::individualAddressWriteLocalConfirm(AckType ack, HopCountType hopType, uint16_t newaddress, bool status)
{
}
void BusAccessUnit::individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress)
{
}
void BusAccessUnit::individualAddressReadLocalConfirm(AckType ack, HopCountType hopType, bool status)
{
}
void BusAccessUnit::individualAddressReadIndication(HopCountType hopType)
{
}
void BusAccessUnit::individualAddressReadResponseConfirm(AckType ack, HopCountType hopType, bool status)
{
}
void BusAccessUnit::individualAddressReadAppLayerConfirm(HopCountType hopType, uint16_t individualAddress)
{
}
void BusAccessUnit::individualAddressSerialNumberReadLocalConfirm(AckType ack, HopCountType hopType, uint8_t * serialNumber, bool status)
{
}
void BusAccessUnit::individualAddressSerialNumberReadIndication(HopCountType hopType, uint8_t * serialNumber)
{
}
void BusAccessUnit::individualAddressSerialNumberReadResponseConfirm(AckType ack, HopCountType hopType, uint8_t * serialNumber, uint16_t domainAddress, bool status)
{
}
void BusAccessUnit::individualAddressSerialNumberReadAppLayerConfirm(HopCountType hopType, uint8_t * serialNumber, uint16_t individualAddress, uint16_t domainAddress)
{
}
void BusAccessUnit::individualAddressSerialNumberWriteLocalConfirm(AckType ack, HopCountType hopType, uint8_t* serialNumber, uint16_t newaddress, bool status)
{
}
void BusAccessUnit::individualAddressSerialNumberWriteIndication(HopCountType hopType, uint8_t * serialNumber, uint16_t newaddress)
{
}
void BusAccessUnit::deviceDescriptorReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType, bool status)
{
}
void BusAccessUnit::deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType)
{
}
void BusAccessUnit::deviceDescriptorReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptor_type,
uint8_t * device_descriptor, bool status)
{
}
void BusAccessUnit::deviceDescriptorReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptortype, uint8_t * deviceDescriptor)
{
}
void BusAccessUnit::restartRequestLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status)
{
}
void BusAccessUnit::restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap)
{
}
void BusAccessUnit::propertyValueReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, bool status)
{
}
void BusAccessUnit::propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex)
{
}
void BusAccessUnit::propertyValueReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t * data, uint8_t length, bool status)
{
}
void BusAccessUnit::propertyValueReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t * data, uint8_t length)
{
}
void BusAccessUnit::propertyValueWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t * data, uint8_t length, bool status)
{
}
void BusAccessUnit::propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t * data, uint8_t length)
{
}
void BusAccessUnit::propertyDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool status)
{
}
void BusAccessUnit::propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex)
{
}
void BusAccessUnit::propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access)
{
}
void BusAccessUnit::propertyDescriptionReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access, bool status)
{
}
void BusAccessUnit::propertyDescriptionReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access)
{
}
void BusAccessUnit::memoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, bool status)
{
}
void BusAccessUnit::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress)
{
}
void BusAccessUnit::memoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t * data, bool status)
{
}
void BusAccessUnit::memoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t * data)
{
}
void BusAccessUnit::memoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t * data, bool status)
{
}
void BusAccessUnit::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t * data)
{
}
void BusAccessUnit::userMemoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, bool status)
{
}
void BusAccessUnit::userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress)
{
}
void BusAccessUnit::userMemoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData, bool status)
{
}
void BusAccessUnit::userMemoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData)
{
}
void BusAccessUnit::userMemoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData, bool status)
{
}
void BusAccessUnit::userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData)
{
}
void BusAccessUnit::userManufacturerInfoLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status)
{
}
void BusAccessUnit::userManufacturerInfoIndication(Priority priority, HopCountType hopType, uint16_t asap)
{
}
void BusAccessUnit::userManufacturerInfoResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t * info, bool status)
{
}
void BusAccessUnit::userManufacturerInfoAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t * info)
{
}
void BusAccessUnit::authorizeLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key, bool status)
{
}
void BusAccessUnit::authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key)
{
}
void BusAccessUnit::authorizeResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, bool status)
{
}
void BusAccessUnit::authorizeAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level)
{
}
void BusAccessUnit::keyWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key, bool status)
{
}
void BusAccessUnit::keyWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key)
{
}
void BusAccessUnit::keyWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, bool status)
{
}
void BusAccessUnit::keyWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level)
{
}

110
bau.h Normal file
View File

@ -0,0 +1,110 @@
#pragma once
#include <stdint.h>
#include "knx_types.h"
class BusAccessUnit
{
public:
virtual void groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status);
virtual void groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType);
virtual void groupValueReadResponseConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopTtype,
uint8_t* data, uint8_t dataLength, bool status);
virtual void groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType,
uint8_t* data, uint8_t dataLength);
virtual void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType,
uint8_t* data, uint8_t dataLength, bool status);
virtual void groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType,
uint8_t* data, uint8_t dataLength);
virtual void individualAddressWriteLocalConfirm(AckType ack, HopCountType hopType,
uint16_t newaddress, bool status);
virtual void individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress);
virtual void individualAddressReadLocalConfirm(AckType ack, HopCountType hopType, bool status);
virtual void individualAddressReadIndication(HopCountType hopType);
virtual void individualAddressReadResponseConfirm(AckType ack, HopCountType hopType, bool status);
virtual void individualAddressReadAppLayerConfirm(HopCountType hopType, uint16_t individualAddress);
virtual void individualAddressSerialNumberReadLocalConfirm(AckType ack, HopCountType hopType,
uint8_t* serialNumber, bool status);
virtual void individualAddressSerialNumberReadIndication(HopCountType hopType, uint8_t* serialNumber);
virtual void individualAddressSerialNumberReadResponseConfirm(AckType ack, HopCountType hopType,
uint8_t* serialNumber, uint16_t domainAddress, bool status);
virtual void individualAddressSerialNumberReadAppLayerConfirm(HopCountType hopType, uint8_t* serialNumber,
uint16_t individualAddress, uint16_t domainAddress);
virtual void individualAddressSerialNumberWriteLocalConfirm(AckType ack, HopCountType hopType, uint8_t* serialNumber,
uint16_t newaddress, bool status);
virtual void individualAddressSerialNumberWriteIndication(HopCountType hopType, uint8_t* serialNumber, uint16_t newaddress);
virtual void deviceDescriptorReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t descriptorType, bool status);
virtual void deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType);
virtual void deviceDescriptorReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t descriptor_type, uint8_t* device_descriptor, bool status);
virtual void deviceDescriptorReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap,
uint8_t descriptortype, uint8_t* deviceDescriptor);
virtual void restartRequestLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status);
virtual void restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap);
virtual void propertyValueReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, bool status);
virtual void propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex);
virtual void propertyValueReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool status);
virtual void propertyValueReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length);
virtual void propertyValueWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool status);
virtual void propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length);
virtual void propertyDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool status);
virtual void propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex);
virtual void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type,
uint16_t maxNumberOfElements, uint8_t access);
virtual void propertyDescriptionReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type,
uint16_t maxNumberOfElements, uint8_t access, bool status);
virtual void propertyDescriptionReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap,
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type,
uint16_t maxNumberOfElements, uint8_t access);
virtual void memoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress, bool status);
virtual void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress);
virtual void memoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress, uint8_t* data, bool status);
virtual void memoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress, uint8_t* data);
virtual void memoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress, uint8_t* data, bool status);
virtual void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress, uint8_t* data);
virtual void userMemoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint32_t memoryAddress, bool status);
virtual void userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint32_t memoryAddress);
virtual void userMemoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint32_t memoryAddress, uint8_t* memoryData, bool status);
virtual void userMemoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint32_t memoryAddress, uint8_t* memoryData);
virtual void userMemoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint32_t memoryAddress, uint8_t* memoryData, bool status);
virtual void userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint32_t memoryAddress, uint8_t* memoryData);
virtual void userManufacturerInfoLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status);
virtual void userManufacturerInfoIndication(Priority priority, HopCountType hopType, uint16_t asap);
virtual void userManufacturerInfoResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap,
uint8_t* info, bool status);
virtual void userManufacturerInfoAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap,
uint8_t* info);
virtual void authorizeLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key, bool status);
virtual void authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key);
virtual void authorizeResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level,
bool status);
virtual void authorizeAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level);
virtual void keyWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level,
uint32_t key, bool status);
virtual void keyWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level,
uint32_t key);
virtual void keyWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level,
bool status);
virtual void keyWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level);
};

286
bau57B0.cpp Normal file
View File

@ -0,0 +1,286 @@
#include "bau57B0.h"
#include <string.h>
#include <stdio.h>
using namespace std;
Bau57B0::Bau57B0(Platform& platform): _memoryReference((uint8_t*)&_deviceObj), _memory(platform), _addrTable(_memoryReference),
_assocTable(_memoryReference), _groupObjTable(_memoryReference), _appProgram(_memoryReference),
_ipParameters(_deviceObj, _platform), _platform(platform), _appLayer(_assocTable, *this),
_transLayer(_appLayer, _addrTable, _platform), _netLayer(_transLayer),
_dlLayer(_deviceObj, _addrTable, _ipParameters, _netLayer, _platform)
{
_appLayer.transportLayer(_transLayer);
_transLayer.networkLayer(_netLayer);
_netLayer.dataLinkLayer(_dlLayer);
_memory.addSaveRestore(&_deviceObj);
_memory.addSaveRestore(&_ipParameters);
_memory.addSaveRestore(&_appProgram);
_memory.addSaveRestore(&_addrTable);
_memory.addSaveRestore(&_assocTable);
_memory.addSaveRestore(&_groupObjTable);
}
void Bau57B0::loop()
{
_dlLayer.loop();
_transLayer.loop();
sendNextGroupTelegram();
}
void Bau57B0::sendNextGroupTelegram()
{
static uint16_t startIdx = 1;
GroupObjectTableObject& table = _groupObjTable;
uint16_t objCount = table.entryCount();
for (uint16_t asap = startIdx; asap < objCount; asap++)
{
GroupObject& go = table.get(asap);
ComFlag flag = go.commFlag();
if (flag != ReadRequest && flag != WriteRequest)
continue;
if(!go.communicationEnable() || ! go.transmitEnable())
continue;
if (flag == WriteRequest)
{
uint8_t* data = go.valueRef();
_appLayer.groupValueWriteRequest(AckRequested, asap, go.priority(), NetworkLayerParameter, data,
go.sizeInTelegram());
}
else
{
_appLayer.groupValueReadRequest(AckRequested, asap, go.priority(), NetworkLayerParameter);
}
go.commFlag(Transmitting);
startIdx = asap + 1;
return;
}
startIdx = 1;
}
void Bau57B0::updateGroupObject(GroupObject & go, uint8_t * data, uint8_t length)
{
uint8_t* goData = go.valueRef();
if (length != go.valueSize())
{
go.commFlag(Error);
return;
}
memcpy(goData, data, length);
go.commFlag(cfUpdate);
if (go.updateHandler)
go.updateHandler(go);
}
void Bau57B0::readMemory()
{
_memory.readMemory();
}
DeviceObject& Bau57B0::deviceObject()
{
return _deviceObj;
}
GroupObjectTableObject& Bau57B0::groupObjectTable()
{
return _groupObjTable;
}
ApplicationProgramObject& Bau57B0::parameters()
{
return _appProgram;
}
bool Bau57B0::configured()
{
return _groupObjTable.loadState() == LS_LOADED
&& _addrTable.loadState() == LS_LOADED
&& _assocTable.loadState() == LS_LOADED
&& _appProgram.loadState() == LS_LOADED;
}
bool Bau57B0::enabled()
{
return _dlLayer.enabled();
}
void Bau57B0::enabled(bool value)
{
_dlLayer.enabled(value);
}
void Bau57B0::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress, uint8_t * data)
{
memcpy(_memoryReference + memoryAddress, data, number);
_memory.memoryModified();
if (_deviceObj.verifyMode())
memoryReadIndication(priority, hopType, asap, number, memoryAddress);
}
void Bau57B0::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress)
{
_appLayer.memoryReadResponse(AckRequested, priority, hopType, asap, number, memoryAddress,
_memoryReference + memoryAddress);
}
void Bau57B0::deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType)
{
if (descriptorType != 0)
descriptorType = 0x3f;
uint8_t descriptor[] = { 0x57, 0xb0 };
_appLayer.deviceDescriptorReadResponse(AckRequested, priority, hopType, asap, descriptorType, descriptor);
}
void Bau57B0::restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap)
{
// for platforms that don't really restart
_deviceObj.progMode(false);
// Flush the EEPROM before resetting
_memory.writeMemory();
_platform.restart();
}
void Bau57B0::authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key)
{
_appLayer.authorizeResponse(AckRequested, priority, hopType, asap, 0);
}
void Bau57B0::userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress)
{
_appLayer.userMemoryReadResponse(AckRequested, priority, hopType, asap, number, memoryAddress,
_memoryReference + memoryAddress);
}
void Bau57B0::userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t* data)
{
memcpy(_memoryReference + memoryAddress, data, number);
_memory.memoryModified();
if (_deviceObj.verifyMode())
userMemoryReadIndication(priority, hopType, asap, number, memoryAddress);
}
void Bau57B0::propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex,
uint8_t propertyId, uint8_t propertyIndex)
{
// TODO: reply correctly
_appLayer.propertyDescriptionReadResponse(AckRequested, priority, hopType, asap, objectIndex, propertyId, propertyIndex,
false, 0, 0, 0);
}
void Bau57B0::propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length)
{
InterfaceObject& obj = getInterfaceObject(objectIndex);
obj.writeProperty((PropertyID)propertyId, startIndex, data, numberOfElements);
propertyValueReadIndication(priority, hopType, asap, objectIndex, propertyId, numberOfElements, startIndex);
}
void Bau57B0::propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex)
{
InterfaceObject& obj = getInterfaceObject(objectIndex);
uint8_t elementSize = obj.propertySize((PropertyID)propertyId);
uint8_t size = elementSize * numberOfElements;
uint8_t data[size];
obj.readProperty((PropertyID)propertyId, startIndex, numberOfElements, data);
_appLayer.propertyValueReadResponse(AckRequested, priority, hopType, asap, objectIndex, propertyId, numberOfElements,
startIndex, data, size);
}
void Bau57B0::individualAddressReadIndication(HopCountType hopType)
{
if (_deviceObj.progMode())
_appLayer.individualAddressReadResponse(AckRequested, hopType);
}
void Bau57B0::individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress)
{
if (_deviceObj.progMode())
_deviceObj.induvidualAddress(newaddress);
}
void Bau57B0::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength, bool status)
{
GroupObject& go = _groupObjTable.get(asap);
if (status)
go.commFlag(Ok);
else
go.commFlag(Error);
}
void Bau57B0::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status)
{
GroupObject& go = _groupObjTable.get(asap);
if (status)
go.commFlag(Ok);
else
go.commFlag(Error);
}
void Bau57B0::groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType)
{
GroupObject& go = _groupObjTable.get(asap);
uint8_t* data = go.valueRef();
_appLayer.groupValueReadResponse(AckRequested, asap, priority, hopType, data, go.sizeInTelegram());
}
void Bau57B0::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data,
uint8_t dataLength)
{
GroupObject& go = _groupObjTable.get(asap);
if (!go.communicationEnable() || !go.responseUpdateEnable())
return;
updateGroupObject(go, data, dataLength);
}
void Bau57B0::groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength)
{
GroupObject& go = _groupObjTable.get(asap);
if (!go.communicationEnable() || !go.writeEnable())
return;
updateGroupObject(go, data, dataLength);
}
InterfaceObject& Bau57B0::getInterfaceObject(uint8_t idx)
{
switch (idx)
{
case 0:
return _deviceObj;
case 1:
return _addrTable;
case 2:
return _assocTable;
case 3:
return _groupObjTable;
case 4:
return _appProgram;
case 5:
return _ipParameters;
default:
return _deviceObj;
}
}

77
bau57B0.h Normal file
View File

@ -0,0 +1,77 @@
#pragma once
#include "bau.h"
#include "device_object.h"
#include "address_table_object.h"
#include "association_table_object.h"
#include "group_object_table_object.h"
#include "application_program_object.h"
#include "ip_parameter_object.h"
#include "application_layer.h"
#include "transport_layer.h"
#include "network_layer.h"
#include "data_link_layer.h"
#include "platform.h"
#include "memory.h"
class Bau57B0: protected BusAccessUnit
{
using BusAccessUnit::memoryReadIndication;
public:
Bau57B0(Platform& platform);
void loop();
DeviceObject& deviceObject();
GroupObjectTableObject& groupObjectTable();
ApplicationProgramObject& parameters();
bool configured();
bool enabled();
void enabled(bool value);
void readMemory();
protected:
void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress, uint8_t* data) override;
void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint16_t memoryAddress) override;
void deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType);
void restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap);
void authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key);
void userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress);
void userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number,
uint32_t memoryAddress, uint8_t* memoryData);
void propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex,
uint8_t propertyId, uint8_t propertyIndex);
void propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length);
void propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex);
void individualAddressReadIndication(HopCountType hopType);
void individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress);
void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType,
uint8_t* data, uint8_t dataLength, bool status);
void groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status);
void groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType);
void groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType,
uint8_t* data, uint8_t dataLength);
void groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType,
uint8_t* data, uint8_t dataLength);
InterfaceObject& getInterfaceObject(uint8_t idx);
void sendNextGroupTelegram();
void updateGroupObject(GroupObject& go, uint8_t* data, uint8_t length);
private:
DeviceObject _deviceObj;
// pointer to first private variable as reference to memory read/write commands
uint8_t* _memoryReference;
Memory _memory;
AddressTableObject _addrTable;
AssociationTableObject _assocTable;
GroupObjectTableObject _groupObjTable;
ApplicationProgramObject _appProgram;
IpParameterObject _ipParameters;
Platform& _platform;
ApplicationLayer _appLayer;
TransportLayer _transLayer;
NetworkLayer _netLayer;
DataLinkLayer _dlLayer;
};

75
bits.cpp Normal file
View File

@ -0,0 +1,75 @@
#include "bits.h"
uint8_t* popByte(uint8_t& b, uint8_t* data)
{
b = *data;
data += 1;
return data;
}
uint8_t* popWord(uint16_t& w, uint8_t* data)
{
w = getWord(data);
data += 2;
return data;
}
uint8_t* popInt(uint32_t& i, uint8_t* data)
{
i = getInt(data);
data += 4;
return data;
}
uint8_t* popByteArray(uint8_t* dst, uint32_t size, uint8_t* data)
{
for (uint32_t i = 0; i < size; i++)
dst[i] = data[i];
data += size;
return data;
}
uint8_t* pushByte(uint8_t b, uint8_t* data)
{
data[0] = b;
data += 1;
return data;
}
uint8_t* pushWord(uint16_t w, uint8_t* data)
{
data[0] = ((w >> 8) & 0xff);
data[1] = (w & 0xff);
data += 2;
return data;
}
uint8_t* pushInt(uint32_t i, uint8_t* data)
{
data[0] = ((i >> 24) & 0xff);
data[1] = ((i >> 16) & 0xff);
data[2] = ((i >> 8) & 0xff);
data[3] = (i & 0xff);
data += 4;
return data;
}
uint8_t* pushByteArray(const uint8_t* src, uint32_t size, uint8_t* data)
{
for (uint32_t i = 0; i < size; i++)
data[i] = src[i];
data += size;
return data;
}
uint16_t getWord(uint8_t* data)
{
return (data[0] << 8) + data[1];
}
uint32_t getInt(uint8_t * data)
{
return (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3];
}

27
bits.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include <stdint.h>
#ifdef __linux__
#include <arpa/inet.h>
#define lowByte(val) ((val) & 255)
#define highByte(val) (((val) >> ((sizeof(val) - 1) << 3)) & 255)
#define bitRead(val, bitno) (((val) >> (bitno)) & 1)
#else
#include <Arduino.h>
#include <user_interface.h>
#define printf Serial.printf
#endif
uint8_t* popByte(uint8_t& b, uint8_t* data);
uint8_t* popWord(uint16_t& w, uint8_t* data);
uint8_t* popInt(uint32_t& i, uint8_t* data);
uint8_t* popByteArray(uint8_t* dst, uint32_t size, uint8_t* data);
uint8_t* pushByte(uint8_t b, uint8_t* data);
uint8_t* pushWord(uint16_t w, uint8_t* data);
uint8_t* pushInt(uint32_t i, uint8_t* data);
uint8_t* pushByteArray(const uint8_t* src, uint32_t size, uint8_t* data);
uint16_t getWord(uint8_t* data);
uint32_t getInt(uint8_t* data);

195
cemi_frame.cpp Normal file
View File

@ -0,0 +1,195 @@
#include "cemi_frame.h"
#include "bits.h"
#include "string.h"
#include <stdio.h>
CemiFrame::CemiFrame(uint8_t* data, uint16_t length): _npdu(data + NPDU_LPDU_DIFF, *this),
_tpdu(data + TPDU_LPDU_DIFF, *this), _apdu(data + APDU_LPDU_DIFF, *this)
{
_data = data;
_ctrl1 = data + data[1] + 2;
_length = length;
}
CemiFrame::CemiFrame(uint8_t apduLength): _data(buffer),
_npdu(_data + NPDU_LPDU_DIFF, *this), _tpdu(_data + TPDU_LPDU_DIFF, *this), _apdu(_data + APDU_LPDU_DIFF, *this)
{
_ctrl1 = _data + 2;
_length = 0;
memset(_data, 0, apduLength + APDU_LPDU_DIFF);
_ctrl1[0] |= Broadcast;
_npdu.octetCount(apduLength);
}
CemiFrame::CemiFrame(const CemiFrame & other): _data(buffer),
_npdu(_data + NPDU_LPDU_DIFF, *this), _tpdu(_data + TPDU_LPDU_DIFF, *this), _apdu(_data + APDU_LPDU_DIFF, *this)
{
_ctrl1 = _data + 2;
_length = other._length;
memcpy(_data, other._data, other.totalLenght());
}
CemiFrame& CemiFrame::operator=(CemiFrame other)
{
_length = other._length;
_data = buffer;
_ctrl1 = _data + 2;
memcpy(_data, other._data, other.totalLenght());
_npdu._data = _data + NPDU_LPDU_DIFF;
_tpdu._data = _data + TPDU_LPDU_DIFF;
_apdu._data = _data + APDU_LPDU_DIFF;
return *this;
}
MessageCode CemiFrame::messageCode() const
{
return (MessageCode)_data[0];
}
void CemiFrame::messageCode(MessageCode msgCode)
{
_data[0] = msgCode;
}
uint16_t CemiFrame::totalLenght() const
{
uint16_t tmp =
_npdu.length() + NPDU_LPDU_DIFF;
return tmp;
}
FrameFormat CemiFrame::frameType() const
{
return (FrameFormat)(_ctrl1[0] & StandardFrame);
}
void CemiFrame::frameType(FrameFormat type)
{
_ctrl1[0] &= ~StandardFrame;
_ctrl1[0] |= type;
}
Repetition CemiFrame::repetition() const
{
return (Repetition)(_ctrl1[0] & RepititionAllowed);
}
void CemiFrame::repetition(Repetition rep)
{
_ctrl1[0] &= ~RepititionAllowed;
_ctrl1[0] |= rep;
}
SystemBroadcast CemiFrame::systemBroadcast() const
{
return (SystemBroadcast)(_ctrl1[0] & Broadcast);
}
void CemiFrame::systemBroadcast(SystemBroadcast value)
{
_ctrl1[0] &= ~Broadcast;
_ctrl1[0] |= value;
}
Priority CemiFrame::priority() const
{
return (Priority)(_ctrl1[0] & LowPriority);
}
void CemiFrame::priority(Priority value)
{
_ctrl1[0] &= ~LowPriority;
_ctrl1[0] |= value;
}
AckType CemiFrame::ack() const
{
return (AckType)(_ctrl1[0] & AckRequested);
}
void CemiFrame::ack(AckType value)
{
_ctrl1[0] &= ~AckRequested;
_ctrl1[0] |= value;
}
AddressType CemiFrame::addressType() const
{
return (AddressType)(_ctrl1[1] & GroupAddress);
}
void CemiFrame::addressType(AddressType value)
{
_ctrl1[1] &= ~GroupAddress;
_ctrl1[1] |= value;
}
uint8_t CemiFrame::hopCount() const
{
return ((_ctrl1[1] >> 4) & 0x7);
}
void CemiFrame::hopCount(uint8_t value)
{
_ctrl1[1] &= ~(0x7 << 4);
_ctrl1[1] |= ((value & 0x7) << 4);
}
uint16_t CemiFrame::sourceAddress() const
{
uint16_t addr;
popWord(addr, _ctrl1 + 2);
return addr;
}
void CemiFrame::sourceAddress(uint16_t value)
{
pushWord(value, _ctrl1 + 2);
}
uint16_t CemiFrame::destinationAddress() const
{
uint16_t addr;
popWord(addr, _ctrl1 + 4);
return addr;
}
void CemiFrame::destinationAddress(uint16_t value)
{
pushWord(value, _ctrl1 + 4);
}
NPDU& CemiFrame::npdu()
{
return _npdu;
}
TPDU& CemiFrame::tpdu()
{
return _tpdu;
}
APDU& CemiFrame::apdu()
{
return _apdu;
}
bool CemiFrame::valid() const
{
uint8_t addInfoLen = _data[1];
uint8_t apduLen = _data[1 + _data[1] + NPDU_LPDU_DIFF];
if (_length != 0 && _length != (addInfoLen + apduLen + NPDU_LPDU_DIFF + 2))
return false;
if ((_ctrl1[0] & 0x40) > 0 // Bit 6 has do be 0
|| (_ctrl1[1] & 0xF) > 0 // only standard or extended frames
|| _npdu.octetCount() == 0xFF // not allowed
|| (_npdu.octetCount() > 15 && frameType() == StandardFrame)
)
return false;
return true;
}

60
cemi_frame.h Normal file
View File

@ -0,0 +1,60 @@
#pragma once
#include "knx_types.h"
#include "stdint.h"
#include "npdu.h"
#include "tpdu.h"
#include "apdu.h"
#define NPDU_LPDU_DIFF 8
#define TPDU_NPDU_DIFF 1
#define APDU_TPDU_DIFF 0
#define TPDU_LPDU_DIFF (TPDU_NPDU_DIFF + NPDU_LPDU_DIFF)
#define APDU_LPDU_DIFF (APDU_TPDU_DIFF + TPDU_NPDU_DIFF + NPDU_LPDU_DIFF)
class CemiFrame
{
friend class DataLinkLayer;
public:
CemiFrame(uint8_t* data, uint16_t length);
CemiFrame(uint8_t apduLength);
CemiFrame(const CemiFrame& other);
CemiFrame& operator= (CemiFrame other);
MessageCode messageCode() const;
void messageCode(MessageCode value);
uint16_t totalLenght() const;
FrameFormat frameType() const;
void frameType(FrameFormat value);
Repetition repetition() const;
void repetition(Repetition value);
SystemBroadcast systemBroadcast() const;
void systemBroadcast(SystemBroadcast value);
Priority priority() const;
void priority(Priority value);
AckType ack() const;
void ack(AckType value);
AddressType addressType() const;
void addressType(AddressType value);
uint8_t hopCount() const;
void hopCount(uint8_t value);
uint16_t sourceAddress() const;
void sourceAddress(uint16_t value);
uint16_t destinationAddress() const;
void destinationAddress(uint16_t value);
NPDU& npdu();
TPDU& tpdu();
APDU& apdu();
bool valid() const;
private:
uint8_t buffer[0xff + NPDU_LPDU_DIFF]; //only valid of add info is zero
uint8_t* _data;
uint8_t* _ctrl1;
NPDU _npdu;
TPDU _tpdu;
APDU _apdu;
uint16_t _length; // only set if created from byte array
};

199
data_link_layer.cpp Normal file
View File

@ -0,0 +1,199 @@
/*
* bus.cpp - Low level EIB bus access.
*
* Copyright (c) 2014 Stefan Taferner <stefan.taferner@gmx.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*/
#include "data_link_layer.h"
#include "bits.h"
#include "platform.h"
#include "device_object.h"
#include "address_table_object.h"
#include "cemi_frame.h"
#include <stdio.h>
#include <string.h>
#define KNXIP_HEADER_LEN 0x6
#define KNXIP_PROTOCOL_VERSION 0x10
#define ROUTING_INDICATION 0x0530
#define KNXIP_MULTICAST_PORT 3671
#define MIN_LEN_CEMI 10
#ifdef DUMP_TELEGRAMS
unsigned char telBuffer[32];
uint32_t telLength = 0;
#endif
DataLinkLayer::DataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab, IpParameterObject& ipParam,
NetworkLayer& layer, Platform& platform) :
_deviceObject(devObj), _groupAddressTable(addrTab), _ipParameters(ipParam), _networkLayer(layer), _platform(platform)
{
}
void DataLinkLayer::dataRequest(AckType ack, AddressType addrType, uint16_t destinationAddr, FrameFormat format, Priority priority, NPDU& npdu)
{
bool success = sendPacket(npdu, ack, destinationAddr, addrType, format, priority);
_networkLayer.dataConfirm(ack, addrType, destinationAddr, format, priority, npdu.frame().sourceAddress(), npdu, success);
}
void DataLinkLayer::systemBroadcastRequest(AckType ack, FrameFormat format, Priority priority, NPDU& npdu)
{
bool success = sendPacket(npdu, ack, 0, GroupAddress, format, priority);
_networkLayer.systemBroadcastConfirm(ack, format, priority, npdu.frame().sourceAddress(), npdu, success);
}
bool DataLinkLayer::sendPacket(NPDU &npdu, AckType ack, uint16_t destinationAddr, AddressType addrType, FrameFormat format, Priority priority)
{
CemiFrame& frame = npdu.frame();
frame.messageCode(L_data_ind);
frame.destinationAddress(destinationAddr);
frame.sourceAddress(_deviceObject.induvidualAddress());
frame.addressType(addrType);
frame.priority(priority);
frame.repetition(RepititionAllowed);
if (npdu.octetCount() <= 15)
frame.frameType(StandardFrame);
else
frame.frameType(format);
if (!frame.valid())
{
printf("invalid frame\n");
return false;
}
//if (frame.npdu().octetCount() > 0)
//{
// print.print("-> DLL ");
// frame.apdu().printPDU();
//}
uint16_t length = frame.totalLenght() + KNXIP_HEADER_LEN;
uint8_t* buffer = new uint8_t[length];
buffer[0] = KNXIP_HEADER_LEN;
buffer[1] = KNXIP_PROTOCOL_VERSION;
pushWord(ROUTING_INDICATION, buffer + 2);
pushWord(length, buffer + 4);
memcpy(buffer + KNXIP_HEADER_LEN, frame._data, frame.totalLenght());
bool success = sendBytes(buffer, length);
// only send 50 packet per second: see KNX 3.2.6 p.6
_platform.mdelay(20);
delete[] buffer;
return success;
}
void DataLinkLayer::loop()
{
if (!_enabled)
return;
uint8_t buffer[512];
int len = _platform.readBytes(buffer, 512);
if (len <= 0)
return;
if (len < KNXIP_HEADER_LEN)
return;
if (buffer[0] != KNXIP_HEADER_LEN
|| buffer[1] != KNXIP_PROTOCOL_VERSION)
return;
uint16_t code;
popWord(code, buffer + 2);
if (code != ROUTING_INDICATION) // only routing indication for now
return;
if (len < MIN_LEN_CEMI)
return;
//TODO: Check correct length (additions Info + apdu length)
CemiFrame frame(buffer + KNXIP_HEADER_LEN, len - KNXIP_HEADER_LEN);
AckType ack = frame.ack();
AddressType addrType = frame.addressType();
uint16_t destination = frame.destinationAddress();
uint16_t source = frame.sourceAddress();
FrameFormat type = frame.frameType();
Priority priority = frame.priority();
NPDU& npdu = frame.npdu();
uint16_t ownAddr = _deviceObject.induvidualAddress();
if (source == ownAddr)
_deviceObject.induvidualAddressDuplication(true);
if (addrType == GroupAddress && destination == 0)
_networkLayer.systemBroadcastIndication(ack, type, npdu, priority, source);
else
{
if (addrType == InduvidualAddress && destination != _deviceObject.induvidualAddress())
return;
if (addrType == GroupAddress && !_groupAddressTable.contains(destination))
return;
//if (frame.npdu().octetCount() > 0)
//{
// print.print("<- DLL ");
// frame.apdu().printPDU();
//}
_networkLayer.dataIndication(ack, addrType, destination, type, npdu, priority, source);
}
}
void DataLinkLayer::enabled(bool value)
{
if (value && !_enabled)
{
_platform.setupMultiCast(_ipParameters.multicastAddress(), KNXIP_MULTICAST_PORT);
_enabled = true;
return;
}
if(!value && _enabled)
{
_platform.closeMultiCast();
_enabled = false;
return;
}
}
bool DataLinkLayer::enabled() const
{
return _enabled;
}
bool DataLinkLayer::sendBytes(uint8_t* bytes, uint16_t length)
{
if (!_enabled)
return false;
#ifdef DUMP_TELEGRAMS_
{
print.print("QSD: ");
for (uint32_t i = 0; i <= length; ++i)
{
if (i) print.print(" ");
print.print(bytes[i], HEX, 2);
}
print.println();
}
#endif
return _platform.sendBytes(bytes, length);
}

33
data_link_layer.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include <stdint.h>
#include "device_object.h"
#include "address_table_object.h"
#include "ip_parameter_object.h"
#include "knx_types.h"
#include "network_layer.h"
class DataLinkLayer
{
public:
DataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab, IpParameterObject& ipParam, NetworkLayer& layer,
Platform& platform);
// from network layer
void dataRequest(AckType ack, AddressType addrType, uint16_t destinationAddr, FrameFormat format,
Priority priority, NPDU& npdu);
void systemBroadcastRequest(AckType ack, FrameFormat format, Priority priority, NPDU& npdu);
void loop();
void enabled(bool value);
bool enabled() const;
private:
bool _enabled = false;
bool sendPacket(NPDU &npdu, AckType ack, uint16_t destinationAddr, AddressType addrType, FrameFormat format, Priority priority);
bool sendBytes(uint8_t* buffer, uint16_t length);
DeviceObject& _deviceObject;
AddressTableObject& _groupAddressTable;
IpParameterObject& _ipParameters;
NetworkLayer& _networkLayer;
Platform& _platform;
};

63
datapoint_types.cpp Normal file
View File

@ -0,0 +1,63 @@
/*
* datapoint_types.h - Conversion functions for datapoint types.
*
* Copyright (c) 2014 Stefan Taferner <stefan.taferner@gmx.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*/
#include "datapoint_types.h"
#include <stdint.h>
// Sign for a negative DPT9 float value
#define DPT_FLOAT_NEG_SIGN 0x8000
uint16_t dptToFloat(int32_t value)
{
uint16_t exp = 0;
if (value < -67108864 || value > 67076096)
return 0x7fff;
if (value < 0)
{
while (value < -2048)
{
value >>= 1;
++exp;
}
return DPT_FLOAT_NEG_SIGN | (((int32_t) value) & 2047) | (exp << 11);
}
else
{
while (value > 2047)
{
value >>= 1;
++exp;
}
return value | (exp << 11);
}
}
int32_t dptFromFloat(uint16_t dptValue)
{
uint16_t exp = (dptValue >> 11) & 15;
int32_t value;
if (dptValue == 0x7fff)
return INVALID_DPT_FLOAT;
if (dptValue >= 0x8000)
value = dptValue | (-1L & ~2047);
else value = dptValue & 2047;
for (; exp; --exp)
value <<= 1;
return value;
}

37
datapoint_types.h Normal file
View File

@ -0,0 +1,37 @@
/*
* datapoint_types.h - Conversion functions for datapoint types.
*
* Copyright (c) 2014 Stefan Taferner <stefan.taferner@gmx.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*/
#pragma once
#include <stdint.h>
/**
* An invalid 2 uint8_t float (DPT9/EIS5).
* To be used for dptToFloat() and dptFromFloat().
*/
#define INVALID_DPT_FLOAT 2147483647U
/**
* Convert a value from uint32_t to 2 uint8_t float (DPT9/EIS5). The possible range
* of the values is -67108864 to 67076096.
*
* @param value - the value to convert.
* Use INVALID_DPT_FLOAT for the DPT9 "invalid data" value.
* @return The 2 uint8_t float (DPT9/EIS5).
*/
uint16_t dptToFloat(int32_t value);
/**
* Convert a value from 2 uint8_t float (DPT9/EIS5) to integer.
*
* @param dptValue - the 2 uint8_t float (DPT9/EIS5) to convert
* @return The value as integer, or INVALID_DPT_FLOAT for the
* DPT9 "invalid data" value.
*/
int32_t dptFromFloat(uint16_t dptValue);

253
device_object.cpp Normal file
View File

@ -0,0 +1,253 @@
#include <cstring>
#include "device_object.h"
#include "bits.h"
void DeviceObject::readProperty(PropertyID propertyId, uint32_t start, uint32_t count, uint8_t* data)
{
switch (propertyId)
{
case PID_OBJECT_TYPE:
pushWord(OT_DEVICE, data);
break;
case PID_SERIAL_NUMBER:
pushWord(_manufacturerId, data);
pushInt(_bauNumber, data);
break;
case PID_MANUFACTURER_ID:
pushWord(_manufacturerId, data);
break;
case PID_DEVICE_CONTROL:
*data = _deviceControl;
break;
case PID_ORDER_INFO:
pushByteArray((uint8_t*)_orderNumber, 10, data);
break;
case PID_HARDWARE_TYPE:
pushByteArray((uint8_t*)_hardwareType, 6, data);
break;
case PID_VERSION:
pushWord(_version, data);
break;
case PID_ROUTING_COUNT:
*data = _routingCount;
break;
case PID_PROG_MODE:
*data = _prgMode;
break;
case PID_MAX_APDU_LENGTH:
*data = 15;
break;
case PID_SUBNET_ADDR:
*data = ((_ownAddress >> 8) & 0xff);
break;
case PID_DEVICE_ADDR:
*data = (_ownAddress & 0xff);
break;
case PID_IO_LIST:
{
uint32_t ifObjs[] = {
6, // length
OT_DEVICE, OT_ADDR_TABLE, OT_ASSOC_TABLE, OT_GRP_OBJ_TABLE, OT_APPLICATION_PROG, OT_IP_PARAMETER};
for (uint32_t i = start; i < (ifObjs[0] + 1) && i < count; i++)
pushInt(ifObjs[i], data);
break;
}
case PID_DEVICE_DESCRIPTOR:
data[0] = 0x57;
data[1] = 0xB0;
break;
}
}
void DeviceObject::writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count)
{
switch (id)
{
case PID_DEVICE_CONTROL:
_deviceControl = data[0];
break;
case PID_ROUTING_COUNT:
_routingCount = data[0];
break;
case PID_PROG_MODE:
_prgMode = data[0];
break;
}
}
uint8_t DeviceObject::propertySize(PropertyID id)
{
switch (id)
{
case PID_OBJECT_TYPE:
case PID_DEVICE_CONTROL:
case PID_ROUTING_COUNT:
case PID_PROG_MODE:
case PID_MAX_APDU_LENGTH:
case PID_SUBNET_ADDR:
case PID_DEVICE_ADDR:
return 1;
case PID_MANUFACTURER_ID:
case PID_VERSION:
case PID_DEVICE_DESCRIPTOR:
return 2;
case PID_IO_LIST:
return 4;
case PID_SERIAL_NUMBER:
case PID_HARDWARE_TYPE:
return 6;
case PID_ORDER_INFO:
return 10;
}
return 0;
}
uint8_t* DeviceObject::save(uint8_t* buffer)
{
buffer = pushByte(_deviceControl, buffer);
buffer = pushByte(_routingCount, buffer);
buffer = pushWord(_ownAddress, buffer);
return buffer;
}
uint8_t* DeviceObject::restore(uint8_t* buffer)
{
buffer = popByte(_deviceControl, buffer);
buffer = popByte(_routingCount, buffer);
buffer = popWord(_ownAddress, buffer);
_prgMode = 0;
return buffer;
}
uint16_t DeviceObject::induvidualAddress()
{
return _ownAddress;
}
void DeviceObject::induvidualAddress(uint16_t value)
{
_ownAddress = value;
}
#define USER_STOPPED 0x1
#define OWN_ADDR_DUPL 0x2
#define VERIFY_MODE 0x4
#define SAFE_STATE 0x8
bool DeviceObject::userStopped()
{
return (_deviceControl & USER_STOPPED) > 0;
}
void DeviceObject::userStopped(bool value)
{
if (value)
_deviceControl |= USER_STOPPED;
else
_deviceControl &= ~USER_STOPPED;
}
bool DeviceObject::induvidualAddressDuplication()
{
return (_deviceControl & OWN_ADDR_DUPL) > 0;
}
void DeviceObject::induvidualAddressDuplication(bool value)
{
if (value)
_deviceControl |= OWN_ADDR_DUPL;
else
_deviceControl &= ~OWN_ADDR_DUPL;
}
bool DeviceObject::verifyMode()
{
return (_deviceControl & VERIFY_MODE) > 0;
}
void DeviceObject::verifyMode(bool value)
{
if (value)
_deviceControl |= VERIFY_MODE;
else
_deviceControl &= ~VERIFY_MODE;
}
bool DeviceObject::safeState()
{
return (_deviceControl & SAFE_STATE) > 0;
}
void DeviceObject::safeState(bool value)
{
if (value)
_deviceControl |= SAFE_STATE;
else
_deviceControl &= ~SAFE_STATE;
}
bool DeviceObject::progMode()
{
return _prgMode == 1;
}
void DeviceObject::progMode(bool value)
{
if (value)
_prgMode = 1;
else
_prgMode = 0;
}
uint16_t DeviceObject::manufacturerId()
{
return _manufacturerId;
}
void DeviceObject::manufacturerId(uint16_t value)
{
_manufacturerId = value;
}
uint32_t DeviceObject::bauNumber()
{
return _bauNumber;
}
void DeviceObject::bauNumber(uint32_t value)
{
_bauNumber = value;
}
const char* DeviceObject::orderNumber()
{
return _orderNumber;
}
void DeviceObject::orderNumber(const char* value)
{
strncpy(_orderNumber, value, 10);
}
const uint8_t* DeviceObject::hardwareType()
{
return _hardwareType;
}
void DeviceObject::hardwareType(const uint8_t* value)
{
pushByteArray(value, 6, _hardwareType);
}
uint16_t DeviceObject::version()
{
return _version;
}
void DeviceObject::version(uint16_t value)
{
_version = value;
}

46
device_object.h Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include "interface_object.h"
class DeviceObject: public InterfaceObject
{
public:
void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data);
void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count);
uint8_t propertySize(PropertyID id);
uint8_t* save(uint8_t* buffer);
uint8_t* restore(uint8_t* buffer);
uint16_t induvidualAddress();
void induvidualAddress(uint16_t value);
bool userStopped();
void userStopped(bool value);
bool induvidualAddressDuplication();
void induvidualAddressDuplication(bool value);
bool verifyMode();
void verifyMode(bool value);
bool safeState();
void safeState(bool value);
bool progMode();
void progMode(bool value);
uint16_t manufacturerId();
void manufacturerId(uint16_t value);
uint32_t bauNumber();
void bauNumber(uint32_t value);
const char* orderNumber();
void orderNumber(const char* value);
const uint8_t* hardwareType();
void hardwareType(const uint8_t* value);
uint16_t version();
void version(uint16_t value);
private:
uint8_t _deviceControl;
uint8_t _routingCount;
uint8_t _prgMode;
uint16_t _ownAddress;
uint16_t _manufacturerId;
uint32_t _bauNumber;
char _orderNumber[10];
uint8_t _hardwareType[6];
uint16_t _version;
};

191
group_object.cpp Normal file
View File

@ -0,0 +1,191 @@
#include "group_object.h"
#include "bits.h"
#include "string.h"
#include "datapoint_types.h"
#include "group_object_table_object.h"
GroupObject::GroupObject(uint8_t size)
{
_data = new uint8_t[size];
_commFlag = Ok;
_table = 0;
_dataLength = size;
updateHandler = 0;
}
GroupObject::~GroupObject()
{
delete[] _data;
}
bool GroupObject::responseUpdateEnable()
{
if (!_table)
return false;
return bitRead(ntohs(_table->_tableData[_asap]), 15) > 0;
}
bool GroupObject::transmitEnable()
{
if (!_table)
return false;
return bitRead(ntohs(_table->_tableData[_asap]), 14) > 0 ;
}
bool GroupObject::valueReadOnInit()
{
if (!_table)
return false;
return bitRead(ntohs(_table->_tableData[_asap]), 13) > 0;
}
bool GroupObject::writeEnable()
{
if (!_table)
return false;
return bitRead(ntohs(_table->_tableData[_asap]), 12) > 0 ;
}
bool GroupObject::readEnable()
{
if (!_table)
return false;
return bitRead(ntohs(_table->_tableData[_asap]), 11) > 0;
}
bool GroupObject::communicationEnable()
{
if (!_table)
return false;
return bitRead(ntohs(_table->_tableData[_asap]), 10) > 0;
}
Priority GroupObject::priority()
{
if (!_table)
return LowPriority;
return (Priority)((ntohs(_table->_tableData[_asap]) >> 6) & (3 << 2)) ;
}
uint8_t* GroupObject::valueRef()
{
return _data;
}
uint16_t GroupObject::asap()
{
return _asap;
}
size_t GroupObject::goSize()
{
size_t size = sizeInTelegram();
if (size == 0)
return 1;
return size;
}
// see knxspec 3.5.1 p. 178
size_t GroupObject::asapValueSize(uint8_t code)
{
if (code < 7)
return 0;
if (code < 8)
return 1;
if (code < 11 || (code > 20 && code < 255))
return code - 6;
switch (code)
{
case 11:
return 6;
case 12:
return 8;
case 13:
return 10;
case 14:
return 14;
case 15:
return 5;
case 16:
return 7;
case 17:
return 9;
case 18:
return 11;
case 19:
return 12;
case 20:
return 13;
case 255:
return 252;
}
return -1;
}
ComFlag GroupObject::commFlag()
{
return _commFlag;
}
void GroupObject::commFlag(ComFlag value)
{
_commFlag = value;
}
int32_t GroupObject::objectReadFloat()
{
uint16_t dptValue = getWord(_data);
return dptFromFloat(dptValue);
}
bool GroupObject::objectReadBool()
{
return _data[0] > 0;
}
void GroupObject::requestObjectRead()
{
_commFlag = ReadRequest;
}
void GroupObject::objectWritten()
{
_commFlag = WriteRequest;
}
void GroupObject::objectWriteFloat(int32_t value)
{
uint16_t dptValue = dptToFloat(value);
pushWord(dptValue, _data);
objectWritten();
}
void GroupObject::objectUpdateFloat(int32_t value)
{
uint16_t dptValue = dptToFloat(value);
pushWord(dptValue, _data);
_commFlag = cfUpdate;
}
size_t GroupObject::valueSize()
{
return _dataLength;
}
size_t GroupObject::sizeInTelegram()
{
uint8_t code = lowByte(ntohs(_table->_tableData[_asap]));
return asapValueSize(code);
}

111
group_object.h Normal file
View File

@ -0,0 +1,111 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "knx_types.h"
class GroupObjectTableObject;
enum ComFlag
{
cfUpdate = 0,
ReadRequest = 1,
WriteRequest = 2,
Transmitting = 3,
Ok = 4,
Error = 5
};
class GroupObject;
typedef void (*GroupObjectUpdatedHandler)(GroupObject& go);
class GroupObject
{
friend class GroupObjectTableObject;
public:
GroupObject(uint8_t size);
virtual ~GroupObject();
// config flags from ETS
bool responseUpdateEnable();
bool transmitEnable();
bool valueReadOnInit();
bool writeEnable();
bool readEnable();
bool communicationEnable();
Priority priority();
ComFlag commFlag();
void commFlag(ComFlag value);
/**
* Get the float value from a communication object. Can be used for
* communication objects of type 2 uint8_t float (EIS5 / DPT9). The value is in
* 1/100 - a DPT9 value of 21.01 is returned as 2101.
*
* @return The value of the com-object in 1/100. INVALID_DPT_FLOAT is returned
* for the DPT9 "invalid data" value.
*/
int32_t objectReadFloat();
bool objectReadBool();
/**
* Request the read of a communication object. Calling this function triggers the
* sending of a read-group-value telegram, to read the value of the communication
* object from the bus.
*
* When the answer is received, the communication object's value will be updated.
* You can cycle through all updated communication objects with nextUpdatedObject().
*
*
* @see objectWritten()
*/
void requestObjectRead();
/**
* Mark a communication object as written. Use this function if you directly change
* the value of a communication object without using objectWrite(). Calling this
* function triggers the sending of a write-group-value telegram.
*
* @see requestObjectRead()
*/
void objectWritten();
/**
* Set the value of a communication object. Calling this function triggers the
* sending of a write-group-value telegram.
*
* The communication object is a 2 uint8_t float (EIS5 / DPT9) object. The value is
* in 1/100, so a value of 2101 would set a DPT9 float value of 21.01. The valid
* range of the values is -671088.64 to 670760.96.
*
* @param value - the new value of the communication object in 1/100.
* Use INVALID_DPT_FLOAT for the DPT9 "invalid data" value.
*/
void objectWriteFloat(int32_t value);
/**
* Set the value of a communication object and mark the communication object
* as updated. This does not trigger a write-group-value telegram.
*
* The communication object is a 2 uint8_t float (EIS5 / DPT9) object. The value
* is in 1/100, so a value of 2101 would set a DPT9 float value of 21.01.
* The possible range of the values is -671088.64 to 670760.96.
*
* @param objno - the ID of the communication object.
* @param value - the new value of the communication object in 1/100.
* Use INVALID_DPT_FLOAT for the DPT9 "invalid data" value.
*/
void objectUpdateFloat(int32_t value);
size_t valueSize();
size_t asapValueSize(uint8_t code);
size_t sizeInTelegram();
uint8_t* valueRef();
uint16_t asap();
GroupObjectUpdatedHandler updateHandler;
private:
size_t goSize();
uint16_t _asap;
ComFlag _commFlag;
uint8_t* _data;
uint8_t _dataLength;
GroupObjectTableObject* _table;
};

View File

@ -0,0 +1,121 @@
#include <cstring>
#include "group_object_table_object.h"
#include "group_object.h"
#include "bits.h"
GroupObjectTableObject::GroupObjectTableObject(uint8_t* memoryReference): TableObject(memoryReference)
{
_groupObjects = 0;
_groupObjectCount = 0;
}
void GroupObjectTableObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data)
{
switch (id)
{
case PID_OBJECT_TYPE:
pushWord(OT_GRP_OBJ_TABLE, data);
break;
default:
TableObject::readProperty(id, start, count, data);
}
}
uint16_t GroupObjectTableObject::entryCount()
{
if (loadState() != LS_LOADED)
return 0;
return ntohs(_tableData[0]);
}
GroupObject& GroupObjectTableObject::get(uint16_t asap)
{
return _groupObjects[asap - 1];
}
uint8_t* GroupObjectTableObject::save(uint8_t* buffer)
{
return TableObject::save(buffer);
}
uint8_t* GroupObjectTableObject::restore(uint8_t* buffer)
{
buffer = TableObject::restore(buffer);
_tableData = (uint16_t*)_data;
initGroupObjects();
return buffer;
}
GroupObject& GroupObjectTableObject::nextUpdatedObject(bool& valid)
{
static uint16_t startIdx = 1;
uint16_t objCount = entryCount();
for (uint16_t asap = startIdx; asap <= objCount; asap++)
{
GroupObject& go = get(asap);
if (go.commFlag() == cfUpdate)
{
go.commFlag(Ok);
startIdx = asap + 1;
valid = true;
return go;
}
}
startIdx = 1;
valid = false;
return get(1);
}
void GroupObjectTableObject::groupObjects(GroupObject * objs, uint16_t size)
{
_groupObjects = objs;
_groupObjectCount = size;
initGroupObjects();
}
void GroupObjectTableObject::beforeStateChange(LoadState& newState)
{
if (newState != LS_LOADED)
return;
_tableData = (uint16_t*)_data;
if (!initGroupObjects())
{
newState = LS_ERROR;
TableObject::_errorCode = E_SOFTWARE_FAULT;
}
}
bool GroupObjectTableObject::initGroupObjects()
{
if (!_tableData)
return false;
uint16_t goCount = ntohs(_tableData[0]);
if (goCount != _groupObjectCount)
return false;
for (uint16_t asap = 1; asap <= goCount; asap++)
{
GroupObject& go = _groupObjects[asap - 1];
go._asap = asap;
go._table = this;
if (go._dataLength != go.goSize())
return false;
}
return true;
}

View File

@ -0,0 +1,27 @@
#pragma once
#include "table_object.h"
#include "group_object.h"
class GroupObjectTableObject: public TableObject
{
friend class GroupObject;
public:
GroupObjectTableObject(uint8_t* memoryReference);
void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data);
uint16_t entryCount();
GroupObject& get(uint16_t asap);
GroupObject& nextUpdatedObject(bool& valid);
void groupObjects(GroupObject* objs, uint16_t size);
virtual uint8_t* save(uint8_t* buffer);
virtual uint8_t* restore(uint8_t* buffer);
protected:
virtual void beforeStateChange(LoadState& newState);
private:
bool initGroupObjects();
uint16_t* _tableData = 0;
GroupObject* _groupObjects;
uint16_t _groupObjectCount;
};

62
interface_object.h Normal file
View File

@ -0,0 +1,62 @@
#pragma once
#include <stddef.h>
#include "property_types.h"
#include "save_restore.h"
enum ObjectType
{
/** Device object. */
OT_DEVICE = 0,
/** Address table object. */
OT_ADDR_TABLE = 1,
/** Association table object. */
OT_ASSOC_TABLE = 2,
/** Application program object. */
OT_APPLICATION_PROG = 3,
/** Interface program object. */
OT_INTERFACE_PROG = 4,
/** KNX - Object Associationtable. */
OT_OJB_ASSOC_TABLE = 5,
/** Router Object */
OT_ROUTER = 6,
/** LTE Address Routing Table Object */
OT_LTE_ADDR_ROUTING_TABLE = 7,
/** cEMI Server Object */
OT_CEMI_SERVER = 8,
/** Group Object Table Object */
OT_GRP_OBJ_TABLE = 9,
/** Polling Master */
OT_POLLING_MASTER = 10,
/** KNXnet/IP Parameter Object */
OT_IP_PARAMETER = 11,
/** Reserved. Shall not be used. */
OT_RESERVED = 12,
/** File Server Object */
OT_FILE_SERVER = 13
};
class InterfaceObject: public SaveRestore
{
public:
virtual ~InterfaceObject() {}
virtual void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data) = 0;
virtual void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count) = 0;
virtual uint8_t propertySize(PropertyID id) = 0;
protected:
};

302
ip_parameter_object.cpp Normal file
View File

@ -0,0 +1,302 @@
#include "ip_parameter_object.h"
#include "device_object.h"
#include "platform.h"
#include "bits.h"
//224.0.23.12
#define DEFAULT_MULTICAST_ADDR 0xE000170C
IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platform): _deviceObject(deviceObject),
_platform(platform)
{}
void IpParameterObject::readProperty(PropertyID propertyId, uint32_t start, uint32_t count, uint8_t* data)
{
switch (propertyId)
{
case PID_LOAD_STATE_CONTROL:
data[0] = _state;
break;
case PID_OBJECT_TYPE:
pushWord(OT_IP_PARAMETER, data);
break;
case PID_PROJECT_INSTALLATION_ID:
pushWord(_projectInstallationId, data);
break;
case PID_KNX_INDIVIDUAL_ADDRESS:
pushWord(_deviceObject.induvidualAddress(), data);
break;
case PID_IP_ASSIGNMENT_METHOD:
data[0] = _ipAssignmentMethod;
break;
case PID_IP_CAPABILITIES:
data[0] = _ipCapabilities;
break;
case PID_CURRENT_IP_ADDRESS:
pushInt(_platform.currentIpAddress(), data);
break;
case PID_CURRENT_SUBNET_MASK:
pushInt(_platform.currentSubnetMask(), data);
break;
case PID_CURRENT_DEFAULT_GATEWAY:
pushInt(_platform.currentDefaultGateway(), data);
break;
case PID_IP_ADDRESS:
pushInt(_ipAddress, data);
break;
case PID_SUBNET_MASK:
pushInt(_subnetMask, data);
break;
case PID_DEFAULT_GATEWAY:
pushInt(_defaultGateway, data);
break;
case PID_MAC_ADDRESS:
{
uint8_t macAddr[6];
_platform.macAddress(macAddr);
pushByteArray(macAddr, 6, data);
break;
}
case PID_SYSTEM_SETUP_MULTICAST_ADDRESS:
pushInt(DEFAULT_MULTICAST_ADDR, data);
break;
case PID_ROUTING_MULTICAST_ADDRESS:
pushInt(_multicastAddress, data);
break;
case PID_TTL:
data[0] = ttl();
break;
case PID_KNXNETIP_DEVICE_CAPABILITIES:
data[0] = 0x1;
break;
case PID_FRIENDLY_NAME:
for (uint8_t i = start; i < start + count; i++)
data[i-start] = _friendlyName[i-1];
break;
}
}
void IpParameterObject::writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count)
{
switch (id)
{
case PID_LOAD_STATE_CONTROL:
loadEvent(data);
break;
case PID_PROJECT_INSTALLATION_ID:
_projectInstallationId = getWord(data);
break;
case PID_KNX_INDIVIDUAL_ADDRESS:
_deviceObject.induvidualAddress(getWord(data));
break;
case PID_IP_ASSIGNMENT_METHOD:
_ipAssignmentMethod = data[0];
break;
case PID_IP_ADDRESS:
_ipAddress = getInt(data);
break;
case PID_SUBNET_MASK:
_subnetMask = getInt(data);
break;
case PID_DEFAULT_GATEWAY:
_defaultGateway = getInt(data);
break;
case PID_ROUTING_MULTICAST_ADDRESS:
_multicastAddress = getInt(data);
break;
case PID_TTL:
_ttl = data[0];
break;
case PID_FRIENDLY_NAME:
for (uint8_t i = start; i < start + count; i++)
_friendlyName[i-1] = data[i - start];
break;
}
}
uint8_t IpParameterObject::propertySize(PropertyID id)
{
switch (id)
{
case PID_IP_ASSIGNMENT_METHOD:
case PID_LOAD_STATE_CONTROL:
case PID_IP_CAPABILITIES:
case PID_TTL:
case PID_KNXNETIP_DEVICE_CAPABILITIES:
case PID_FRIENDLY_NAME:
return 1;
case PID_OBJECT_TYPE:
case PID_PROJECT_INSTALLATION_ID:
case PID_KNX_INDIVIDUAL_ADDRESS:
return 2;
case PID_CURRENT_IP_ADDRESS:
case PID_CURRENT_SUBNET_MASK:
case PID_CURRENT_DEFAULT_GATEWAY:
case PID_IP_ADDRESS:
case PID_SUBNET_MASK:
case PID_DEFAULT_GATEWAY:
case PID_SYSTEM_SETUP_MULTICAST_ADDRESS:
case PID_ROUTING_MULTICAST_ADDRESS:
return 4;
case PID_MAC_ADDRESS:
return 6;
}
return 0;
}
uint8_t* IpParameterObject::save(uint8_t* buffer)
{
buffer = pushWord(_projectInstallationId, buffer);
buffer = pushByte(_ipAssignmentMethod, buffer);
buffer = pushByte(_ipCapabilities, buffer);
buffer = pushInt(_ipAddress, buffer);
buffer = pushInt(_subnetMask, buffer);
buffer = pushInt(_defaultGateway, buffer);
buffer = pushInt(_multicastAddress, buffer);
buffer = pushByte(_ttl, buffer);
buffer = pushByteArray((uint8_t*)_friendlyName, 30, buffer);
return buffer;
}
uint8_t* IpParameterObject::restore(uint8_t* buffer)
{
buffer = popWord(_projectInstallationId, buffer);
buffer = popByte(_ipAssignmentMethod, buffer);
buffer = popByte(_ipCapabilities, buffer);
buffer = popInt(_ipAddress, buffer);
buffer = popInt(_subnetMask, buffer);
buffer = popInt(_defaultGateway, buffer);
buffer = popInt(_multicastAddress, buffer);
buffer = popByte(_ttl, buffer);
buffer = popByteArray((uint8_t*)_friendlyName, 30, buffer);
return buffer;
}
uint32_t IpParameterObject::multicastAddress() const
{
if (_multicastAddress == 0)
return DEFAULT_MULTICAST_ADDR;
return _multicastAddress;
}
void IpParameterObject::loadEvent(uint8_t* data)
{
switch (_state)
{
case LS_UNLOADED:
loadEventUnloaded(data);
break;
case LS_LOADING:
loadEventLoading(data);
break;
case LS_LOADED:
loadEventLoaded(data);
break;
case LS_ERROR:
loadEventError(data);
break;
}
}
void IpParameterObject::loadState(LoadState newState)
{
if (newState == _state)
return;
//beforeStateChange(newState);
_state = newState;
}
void IpParameterObject::loadEventUnloaded(uint8_t* data)
{
uint8_t event = data[0];
switch (event)
{
case LE_NOOP:
case LE_LOAD_COMPLETED:
case LE_ADDITIONAL_LOAD_CONTROLS:
case LE_UNLOAD:
break;
case LE_START_LOADING:
loadState(LS_LOADING);
break;
default:
loadState(LS_ERROR);
_errorCode = E_GOT_UNDEF_LOAD_CMD;
}
}
void IpParameterObject::loadEventLoading(uint8_t* data)
{
uint8_t event = data[0];
switch (event)
{
case LE_NOOP:
case LE_START_LOADING:
break;
case LE_LOAD_COMPLETED:
loadState(LS_LOADED);
break;
case LE_UNLOAD:
loadState(LS_UNLOADED);
break;
case LE_ADDITIONAL_LOAD_CONTROLS:
additionalLoadControls(data);
break;
default:
loadState(LS_ERROR);
_errorCode = E_GOT_UNDEF_LOAD_CMD;
}
}
void IpParameterObject::loadEventLoaded(uint8_t* data)
{
uint8_t event = data[0];
switch (event)
{
case LE_NOOP:
case LE_LOAD_COMPLETED:
break;
case LE_START_LOADING:
loadState(LS_LOADING);
break;
case LE_UNLOAD:
loadState(LS_UNLOADED);
break;
case LE_ADDITIONAL_LOAD_CONTROLS:
loadState(LS_ERROR);
_errorCode = E_INVALID_OPCODE;
break;
default:
loadState(LS_ERROR);
_errorCode = E_GOT_UNDEF_LOAD_CMD;
}
}
void IpParameterObject::loadEventError(uint8_t* data)
{
uint8_t event = data[0];
switch (event)
{
case LE_NOOP:
case LE_LOAD_COMPLETED:
case LE_ADDITIONAL_LOAD_CONTROLS:
case LE_START_LOADING:
break;
case LE_UNLOAD:
loadState(LS_UNLOADED);
break;
default:
loadState(LS_ERROR);
_errorCode = E_GOT_UNDEF_LOAD_CMD;
}
}
void IpParameterObject::additionalLoadControls(uint8_t* data)
{
loadState(LS_ERROR);
_errorCode = E_INVALID_OPCODE;
return;
}

42
ip_parameter_object.h Normal file
View File

@ -0,0 +1,42 @@
#pragma once
#include "interface_object.h"
#include "device_object.h"
#include "platform.h"
class IpParameterObject: public InterfaceObject
{
public:
IpParameterObject(DeviceObject& deviceObject, Platform& platform);
void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data);
void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count);
uint8_t propertySize(PropertyID id);
uint8_t* save(uint8_t* buffer);
uint8_t* restore(uint8_t* buffer);
uint32_t multicastAddress() const;
uint8_t ttl() const { return _ttl; }
private:
uint16_t _projectInstallationId = 0;
uint8_t _ipAssignmentMethod = 0;
uint8_t _ipCapabilities = 0;
uint32_t _ipAddress = 0;
uint32_t _subnetMask = 0;
uint32_t _defaultGateway = 0;
uint32_t _multicastAddress = 0;
uint8_t _ttl = 60;
char _friendlyName[30] = { 0 };
DeviceObject& _deviceObject;
Platform& _platform;
void loadEvent(uint8_t* data);
void loadEventUnloaded(uint8_t* data);
void loadEventLoading(uint8_t* data);
void loadEventLoaded(uint8_t* data);
void loadEventError(uint8_t* data);
void additionalLoadControls(uint8_t* data);
void loadState(LoadState newState);
LoadState _state = LS_UNLOADED;
ErrorCode _errorCode = E_NO_FAULT;
};

103
knx_types.h Normal file
View File

@ -0,0 +1,103 @@
#pragma once
enum FrameFormat
{
ExtendedFrame = 0,
StandardFrame = 0x80
};
enum Priority
{
LowPriority = 0xC,
NormalPriority = 0x4,
UrgentPriority = 0x8,
SystemPriority = 0x0
};
enum AckType
{
AckDontCare = 0,
AckRequested = 0x2,
};
enum AddressType
{
InduvidualAddress = 0,
GroupAddress = 0x80,
};
enum MessageCode
{
L_data_ind = 0x29,
};
enum Repetition
{
NoRepitiion = 0,
WasRepeated = 0,
RepititionAllowed = 0x20,
WasNotRepeated = 0x20,
};
enum SystemBroadcast
{
SysBroadcast = 0,
Broadcast = 0x10,
};
enum Confirm
{
ConfirmNoError = 0,
ConfirmError = 1,
};
enum HopCountType
{
UnlimitedRouting,
NetworkLayerParameter
};
enum TpduType
{
DataBroadcast,
DataGroup,
DataInduvidual,
DataConnected,
Connect,
Disconnect,
Ack,
Nack,
};
enum ApduType
{
GroupValueRead = 0x000,
GroupValueResponse = 0x040,
GroupValueWrite = 0x080,
IndividualAddressWrite = 0x0c0,
IndividualAddressRead = 0x100,
IndividualAddressResponse = 0x140,
MemoryRead = 0x200,
MemoryResponse = 0x240,
MemoryWrite = 0x280,
UserMemoryRead = 0x2C0,
UserMemoryResponse = 0x2C1,
UserMemoryWrite = 0x2C2,
UserManufacturerInfoRead = 0x2C5,
UserManufacturerInfoResponse = 0x2C6,
DeviceDescriptorRead = 0x300,
DeviceDescriptorResponse = 0x340,
Restart = 0x380,
AuthorizeRequest = 0x3d1,
AuthorizeResponse = 0x3d2,
KeyWrite = 0x3d3,
KeyResponse = 0x3d4,
PropertyValueRead = 0x3d5,
PropertyValueResponse = 0x3d6,
PropertyValueWrite = 0x3d7,
PropertyDescriptionRead = 0x3d8,
PropertyDescriptionResponse = 0x3d9,
IndividualAddressSerialNumberRead = 0x3dc,
IndividualAddressSerialNumberResponse = 0x3dd,
IndividualAddressSerialNumberWrite = 0x3de,
};

56
memory.cpp Normal file
View File

@ -0,0 +1,56 @@
#include "memory.h"
Memory::Memory(Platform & platform): _platform(platform)
{
}
void Memory::memoryModified()
{
_modified = true;
}
bool Memory::isMemoryModified()
{
return _modified;
}
void Memory::readMemory()
{
_data = _platform.getEepromBuffer(512);
if (_data[0] != 0xDE || _data[1] != 0xAD || _data[2] != 0xAF || _data[3] != 0xFE)
return;
uint8_t* buffer = _data + 4;
int size = _saveCount;
for (int i = 0; i < size; i++)
{
buffer = _saveRestores[i]->restore(buffer);
}
}
void Memory::writeMemory()
{
_data[0] = 0xDE;
_data[1] = 0xAD;
_data[2] = 0xAF;
_data[3] = 0xFE;
uint8_t* buffer = _data + 4;
int size = _saveCount;
for (int i = 0; i < size; i++)
{
buffer = _saveRestores[i]->save(buffer);
}
_platform.commitToEeprom();
_modified = false;
}
void Memory::addSaveRestore(SaveRestore * obj)
{
if (_saveCount >= MAXSAVE - 1)
return;
_saveRestores[_saveCount] = obj;
_saveCount += 1;
}

24
memory.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
#include "save_restore.h"
#include "platform.h"
#define MAXSAVE 10
class Memory
{
public:
Memory(Platform& platform);
void memoryModified();
bool isMemoryModified();
void readMemory();
void writeMemory();
void addSaveRestore(SaveRestore* obj);
private:
Platform& _platform;
bool _modified = false;
SaveRestore* _saveRestores[MAXSAVE];
int _saveCount = 0;
uint8_t* _data = 0;
};

127
network_layer.cpp Normal file
View File

@ -0,0 +1,127 @@
#include "network_layer.h"
#include "tpdu.h"
#include "cemi_frame.h"
#include "data_link_layer.h"
#include "bits.h"
NetworkLayer::NetworkLayer(TransportLayer& layer): _transportLayer(layer)
{
}
void NetworkLayer::dataLinkLayer(DataLinkLayer& layer)
{
_dataLinkLayer = &layer;
}
uint8_t NetworkLayer::hopCount() const
{
return _hopCount;
}
void NetworkLayer::hopCount(uint8_t value)
{
_hopCount = value & 0x7;
}
void NetworkLayer::dataIndication(AckType ack, AddressType addrType, uint16_t destination, FrameFormat format, NPDU& npdu, Priority priority, uint16_t source)
{
HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter;
if (addrType == InduvidualAddress)
{
//if (npdu.octetCount() > 0)
//{
// print.print("<- NL ");
// npdu.frame().apdu().printPDU();
//}
_transportLayer.dataIndividualIndication(destination, hopType, priority, source, npdu.tpdu());
return;
}
// group-address type
if (destination != 0)
{
_transportLayer.dataGroupIndication(destination, hopType, priority, source, npdu.tpdu());
return;
}
// destination == 0
_transportLayer.dataBroadcastIndication(hopType, priority, source, npdu.tpdu());
}
void NetworkLayer::dataConfirm(AckType ack, AddressType addressType, uint16_t destination, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status)
{
HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter;
if (addressType == InduvidualAddress)
{
_transportLayer.dataIndividualConfirm(ack, destination, hopType, priority, npdu.tpdu(), status);
return;
}
// group-address type
if (destination != 0)
{
_transportLayer.dataGroupConfirm(ack, source, destination, hopType, priority, npdu.tpdu(), status);
return;
}
// destination == 0
_transportLayer.dataBroadcastConfirm(ack, hopType, priority, npdu.tpdu(), status);
}
void NetworkLayer::systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu, Priority priority, uint16_t source)
{
HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter;
_transportLayer.dataBroadcastIndication(hopType, priority, source, npdu.tpdu());
}
void NetworkLayer::systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status)
{
HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter;
_transportLayer.dataBroadcastConfirm(ack, hopType, priority, npdu.tpdu(), status);
}
void NetworkLayer::dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu)
{
//if (tpdu.apdu().length() > 0)
//{
// print.print("-> NL ");
// tpdu.apdu().printPDU();
//}
sendDataRequest(tpdu, hopType, ack, destination, priority, InduvidualAddress);
}
void NetworkLayer::sendDataRequest(TPDU &tpdu, HopCountType hopType, AckType ack, uint16_t destination, Priority priority, AddressType addrType)
{
NPDU& npdu = tpdu.frame().npdu();
if (hopType == UnlimitedRouting)
npdu.hopCount(7);
else
npdu.hopCount(_hopCount);
FrameFormat frameFormat = npdu.octetCount() > 15 ? ExtendedFrame : StandardFrame;
_dataLinkLayer->dataRequest(ack, addrType, destination, frameFormat, priority, npdu);
}
void NetworkLayer::dataGroupRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu)
{
sendDataRequest(tpdu, hopType, ack, destination, priority, GroupAddress);
}
void NetworkLayer::dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu)
{
sendDataRequest(tpdu, hopType, ack, 0, priority, GroupAddress);
}
void NetworkLayer::dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu)
{
NPDU& npdu = tpdu.frame().npdu();
if (hopType == UnlimitedRouting)
npdu.hopCount(7);
else
npdu.hopCount(_hopCount);
FrameFormat frameFormat = npdu.octetCount() > 15 ? ExtendedFrame : StandardFrame;
_dataLinkLayer->systemBroadcastRequest(ack, frameFormat, priority, npdu);
}

38
network_layer.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include <stdint.h>
#include "knx_types.h"
#include "npdu.h"
#include "transport_layer.h"
class DataLinkLayer;
class NetworkLayer
{
public:
NetworkLayer(TransportLayer& layer);
void dataLinkLayer(DataLinkLayer& layer);
uint8_t hopCount() const;
void hopCount(uint8_t value);
// from data link layer
void dataIndication(AckType ack, AddressType addType, uint16_t destination, FrameFormat format, NPDU& npdu,
Priority priority, uint16_t source);
void dataConfirm(AckType ack, AddressType addressType, uint16_t destination, FrameFormat format, Priority priority,
uint16_t source, NPDU& npdu, bool status);
void systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu,
Priority priority, uint16_t source);
void systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status);
// from transport layer
void dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu);
void dataGroupRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu);
void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu);
void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu);
private:
void sendDataRequest(TPDU &tpdu, HopCountType hopType, AckType ack, uint16_t destination, Priority priority, AddressType addrType);
uint8_t _hopCount = 6;
DataLinkLayer* _dataLinkLayer;
TransportLayer& _transportLayer;
};

44
npdu.cpp Normal file
View File

@ -0,0 +1,44 @@
#include "npdu.h"
#include "cemi_frame.h"
#include <string.h>
NPDU::NPDU(uint8_t* data, CemiFrame& frame): _data(data), _frame(frame)
{
}
uint8_t NPDU::octetCount() const
{
return _data[0];
}
void NPDU::octetCount(uint8_t value)
{
_data[0] = value;
}
uint8_t NPDU::length() const
{
return _data[0] + 2; // +1 for length field, +1 for TCPI
}
uint8_t NPDU::hopCount() const
{
return _frame.hopCount();
}
void NPDU::hopCount(uint8_t value)
{
_frame.hopCount(value);
}
CemiFrame& NPDU::frame()
{
return _frame;
}
TPDU& NPDU::tpdu()
{
return _frame.tpdu();
}

24
npdu.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
class CemiFrame;
class TPDU;
class NPDU
{
friend class CemiFrame;
public:
NPDU(uint8_t* data, CemiFrame& frame);
uint8_t octetCount() const;
void octetCount(uint8_t value);
uint8_t length() const;
uint8_t hopCount() const;
void hopCount(uint8_t value);
CemiFrame& frame();
TPDU& tpdu();
private:
uint8_t* _data;
CemiFrame& _frame;
};

26
platform.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include <stdint.h>
#include "save_restore.h"
class Platform
{
public:
virtual uint32_t currentIpAddress() = 0;
virtual uint32_t currentSubnetMask() = 0;
virtual uint32_t currentDefaultGateway() = 0;
virtual void macAddress(uint8_t* data) = 0;
virtual uint32_t millis() = 0;
virtual void restart() = 0;
virtual void fatalError() = 0;
virtual void mdelay(uint32_t millis) = 0;
virtual void setupMultiCast(uint32_t addr, uint16_t port) = 0;
virtual void closeMultiCast() = 0;
virtual bool sendBytes(uint8_t* buffer, uint16_t len) = 0;
virtual int readBytes(uint8_t* buffer, uint16_t maxLen) = 0;
virtual uint8_t* getEepromBuffer(uint16_t size) = 0;
virtual void commitToEeprom() = 0;
};

166
property_types.h Normal file
View File

@ -0,0 +1,166 @@
/*
* property_types.h - BCU 2 property types of EIB objects.
*
* Copyright (c) 2014 Stefan Taferner <stefan.taferner@gmx.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*/
#pragma once
enum PropertyDataType
{
PDT_CONTROL = 0x00, //!< length: 1 read, 10 write
PDT_CHAR = 0x01, //!< length: 1
PDT_UNSIGNED_CHAR = 0x02, //!< length: 1
PDT_INT = 0x03, //!< length: 2
PDT_UNSIGNED_INT = 0x04, //!< length: 2
PDT_KNX_FLOAT = 0x05, //!< length: 2
PDT_DATE = 0x06, //!< length: 3
PDT_TIME = 0x07, //!< length: 3
PDT_LONG = 0x08, //!< length: 4
PDT_UNSIGNED_LONG = 0x09, //!< length: 4
PDT_FLOAT = 0x0a, //!< length: 4
PDT_DOUBLE = 0x0b, //!< length: 8
PDT_CHAR_BLOCK = 0x0c, //!< length: 10
PDT_POLL_GROUP_SETTING = 0x0d, //!< length: 3
PDT_SHORT_CHAR_BLOCK = 0x0e, //!< length: 5
PDT_DATE_TIME = 0x0f, //!< length: 8
PDT_VARIABLE_LENGTH = 0x10,
PDT_GENERIC_01 = 0x11, //!< length: 1
PDT_GENERIC_02 = 0x12, //!< length: 2
PDT_GENERIC_03 = 0x13, //!< length: 3
PDT_GENERIC_04 = 0x14, //!< length: 4
PDT_GENERIC_05 = 0x15, //!< length: 5
PDT_GENERIC_06 = 0x16, //!< length: 6
PDT_GENERIC_07 = 0x17, //!< length: 7
PDT_GENERIC_08 = 0x18, //!< length: 8
PDT_GENERIC_09 = 0x19, //!< length: 9
PDT_GENERIC_10 = 0x1a, //!< length: 10
PDT_GENERIC_11 = 0x1b, //!< length: 11
PDT_GENERIC_12 = 0x1c, //!< length: 12
PDT_GENERIC_13 = 0x1d, //!< length: 13
PDT_GENERIC_14 = 0x1e, //!< length: 14
PDT_GENERIC_15 = 0x1f, //!< length: 15
PDT_GENERIC_16 = 0x20, //!< length: 16
PDT_GENERIC_17 = 0x21, //!< length: 17
PDT_GENERIC_18 = 0x22, //!< length: 18
PDT_GENERIC_19 = 0x23, //!< length: 19
PDT_GENERIC_20 = 0x24, //!< length: 20
PDT_UTF8 = 0x2f, //!< length: 3
PDT_VERSION = 0x30, //!< length: 3
PDT_ALARM_INFO = 0x31, //!< length: 3
PDT_BINARY_INFORMATION = 0x32, //!< length: 3
PDT_BITSET8 = 0x33, //!< length: 3
PDT_BITSET16 = 0x34, //!< length: 3
PDT_ENUM8 = 0x35, //!< length: 3
PDT_SCALING = 0x36, //!< length: 3
PDT_NE_VL = 0x3c, //!< length: 3
PDT_NE_FL = 0x3d, //!< length: 3
PDT_FUNCTION = 0x3e, //!< length: 3
PDT_ESCAPE = 0x3f, //!< length: 3
};
enum PropertyID
{
/** Interface Object Type independent Properties */
PID_OBJECT_TYPE = 1,
PID_LOAD_STATE_CONTROL = 5,
PID_RUN_STATE_CONTROL = 6,
PID_TABLE_REFERENCE = 7,
PID_SERVICE_CONTROL = 8,
PID_FIRMWARE_REVISION = 9,
PID_SERIAL_NUMBER = 11,
PID_MANUFACTURER_ID = 12,
PID_PROG_VERSION = 13,
PID_DEVICE_CONTROL = 14,
PID_ORDER_INFO = 15,
PID_PEI_TYPE = 16,
PID_PORT_CONFIGURATION = 17,
PID_VERSION = 25,
PID_MCB_TABLE = 27,
PID_ERROR_CODE = 28,
/** Properties in the Device Object */
PID_ROUTING_COUNT = 51,
PID_PROG_MODE = 54,
PID_MAX_APDU_LENGTH = 56,
PID_SUBNET_ADDR = 57,
PID_DEVICE_ADDR = 58,
PID_IO_LIST = 71,
PID_HARDWARE_TYPE = 78,
PID_DEVICE_DESCRIPTOR = 83,
/** KNXnet/IP Parameter Object */
PID_PROJECT_INSTALLATION_ID = 51,
PID_KNX_INDIVIDUAL_ADDRESS = 52,
PID_ADDITIONAL_INDIVIDUAL_ADDRESSES = 53,
PID_CURRENT_IP_ASSIGNMENT_METHOD = 54,
PID_IP_ASSIGNMENT_METHOD = 55,
PID_IP_CAPABILITIES = 56,
PID_CURRENT_IP_ADDRESS = 57,
PID_CURRENT_SUBNET_MASK = 58,
PID_CURRENT_DEFAULT_GATEWAY = 59,
PID_IP_ADDRESS = 60,
PID_SUBNET_MASK = 61,
PID_DEFAULT_GATEWAY = 62,
PID_DHCP_BOOTP_SERVER = 63,
PID_MAC_ADDRESS = 64,
PID_SYSTEM_SETUP_MULTICAST_ADDRESS = 65,
PID_ROUTING_MULTICAST_ADDRESS = 66,
PID_TTL = 67,
PID_KNXNETIP_DEVICE_CAPABILITIES = 68,
PID_KNXNETIP_DEVICE_STATE = 69,
PID_KNXNETIP_ROUTING_CAPABILITIES = 70,
PID_PRIORITY_FIFO_ENABLED = 71,
PID_QUEUE_OVERFLOW_TO_IP = 72,
PID_QUEUE_OVERFLOW_TO_KNX = 73,
PID_MSG_TRANSMIT_TO_IP = 74,
PID_MSG_TRANSMIT_TO_KNX = 75,
PID_FRIENDLY_NAME = 76,
PID_ROUTING_BUSY_WAIT_TIME = 78,
};
enum LoadState
{
LS_UNLOADED = 0,
LS_LOADED = 1,
LS_LOADING = 2,
LS_ERROR = 3,
LS_UNLOADING = 4,
LS_LOADCOMPLETING = 5
};
enum LoadEvents
{
LE_NOOP = 0,
LE_START_LOADING = 1,
LE_LOAD_COMPLETED = 2,
LE_ADDITIONAL_LOAD_CONTROLS = 3,
LE_UNLOAD = 4
};
// 20.011 DPT_ErrorClass_System
enum ErrorCode
{
E_NO_FAULT = 0,
E_GENERAL_DEVICE_FAULT = 1,
E_COMMUNICATION_FAULT = 2,
E_CONFIGURATION_FAULT = 3,
E_HARDWARE_FAULT = 4,
E_SOFTWARE_FAULT = 5,
E_INSUFFICIENT_NON_VOLATILE_MEMORY = 6,
E_INSUFFICIENT_VOLATILE_MEMORY = 7,
E_GOT_MEM_ALLOC_ZERO = 8,
E_CRC_ERROR = 9,
E_WATCHDOG_RESET = 10,
E_INVALID_OPCODE = 11,
E_GENERAL_PROTECTION_FAULT = 12,
E_MAX_TABLE_LENGTH_EXEEDED = 13,
E_GOT_UNDEF_LOAD_CMD = 14,
E_GAT_NOT_SORTED = 15,
E_INVALID_CONNECTION_NUMBER = 16,
E_INVALID_GO_NUMBER = 17,
E_GO_TYPE_TOO_BIG = 18
};

10
save_restore.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <stdint.h>
class SaveRestore
{
public:
virtual uint8_t* save(uint8_t* buffer) = 0;
virtual uint8_t* restore(uint8_t* buffer) = 0;
};

257
table_object.cpp Normal file
View File

@ -0,0 +1,257 @@
#include <stdlib.h>
#include <string.h>
#include "table_object.h"
#include "bits.h"
TableObject::TableObject(uint8_t* memoryReference): _memoryReference(memoryReference)
{
}
void TableObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data)
{
switch (id)
{
case PID_LOAD_STATE_CONTROL:
data[0] = _state;
break;
case PID_TABLE_REFERENCE:
if (_state == LS_UNLOADED)
pushInt(0, data);
else
pushInt(tableReference(), data);
break;
case PID_ERROR_CODE:
data[0] = _errorCode;
break;
}
}
void TableObject::writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count)
{
switch (id)
{
case PID_LOAD_STATE_CONTROL:
loadEvent(data);
break;
//case PID_MCB_TABLE:
// TODO
// break;
}
}
uint8_t TableObject::propertySize(PropertyID id)
{
switch (id)
{
case PID_LOAD_STATE_CONTROL:
return 1;
case PID_TABLE_REFERENCE:
return 4;
case PID_ERROR_CODE:
return 1;
}
return 0;
}
TableObject::~TableObject()
{
if (_data != 0)
free(_data);
}
LoadState TableObject::loadState()
{
return _state;
}
void TableObject::loadState(LoadState newState)
{
if (newState == _state)
return;
beforeStateChange(newState);
_state = newState;
}
uint8_t* TableObject::save(uint8_t* buffer)
{
buffer = pushByte(_state, buffer);
buffer = pushByte(_errorCode, buffer);
buffer = pushInt(_size, buffer);
buffer = pushByteArray(_data, _size, buffer);
return buffer;
}
uint8_t* TableObject::restore(uint8_t* buffer)
{
uint8_t state = 0;
uint8_t errorCode = 0;
buffer = popByte(state, buffer);
buffer = popByte(errorCode, buffer);
_state = (LoadState)state;
_errorCode = (ErrorCode)errorCode;
buffer = popInt(_size, buffer);
if (_data)
free(_data);
_data = (uint8_t*) malloc(_size);
buffer = popByteArray(_data, _size, buffer);
return buffer;
}
uint32_t TableObject::tableReference()
{
return (uint32_t)(_data - _memoryReference);
}
bool TableObject::allocTable(uint32_t size, bool doFill, uint8_t fillByte)
{
if (_data)
{
free(_data);
_size = 0;
}
_data = (uint8_t*)malloc(size);
if (!_data)
return false;
_size = size;
if (doFill)
memset(_data, fillByte, size);
return true;
}
void TableObject::loadEvent(uint8_t* data)
{
switch (_state)
{
case LS_UNLOADED:
loadEventUnloaded(data);
break;
case LS_LOADING:
loadEventLoading(data);
break;
case LS_LOADED:
loadEventLoaded(data);
break;
case LS_ERROR:
loadEventError(data);
break;
}
}
void TableObject::loadEventUnloaded(uint8_t* data)
{
uint8_t event = data[0];
switch (event)
{
case LE_NOOP:
case LE_LOAD_COMPLETED:
case LE_ADDITIONAL_LOAD_CONTROLS:
case LE_UNLOAD:
break;
case LE_START_LOADING:
loadState(LS_LOADING);
break;
default:
loadState(LS_ERROR);
_errorCode = E_GOT_UNDEF_LOAD_CMD;
}
}
void TableObject::loadEventLoading(uint8_t* data)
{
uint8_t event = data[0];
switch (event)
{
case LE_NOOP:
case LE_START_LOADING:
break;
case LE_LOAD_COMPLETED:
loadState(LS_LOADED);
break;
case LE_UNLOAD:
loadState(LS_UNLOADED);
break;
case LE_ADDITIONAL_LOAD_CONTROLS:
additionalLoadControls(data);
break;
default:
loadState(LS_ERROR);
_errorCode = E_GOT_UNDEF_LOAD_CMD;
}
}
void TableObject::loadEventLoaded(uint8_t* data)
{
uint8_t event = data[0];
switch (event)
{
case LE_NOOP:
case LE_LOAD_COMPLETED:
break;
case LE_START_LOADING:
loadState(LS_LOADING);
break;
case LE_UNLOAD:
loadState(LS_UNLOADED);
break;
case LE_ADDITIONAL_LOAD_CONTROLS:
loadState(LS_ERROR);
_errorCode = E_INVALID_OPCODE;
break;
default:
loadState(LS_ERROR);
_errorCode = E_GOT_UNDEF_LOAD_CMD;
}
}
void TableObject::loadEventError(uint8_t* data)
{
uint8_t event = data[0];
switch (event)
{
case LE_NOOP:
case LE_LOAD_COMPLETED:
case LE_ADDITIONAL_LOAD_CONTROLS:
case LE_START_LOADING:
break;
case LE_UNLOAD:
loadState(LS_UNLOADED);
break;
default:
loadState(LS_ERROR);
_errorCode = E_GOT_UNDEF_LOAD_CMD;
}
}
void TableObject::additionalLoadControls(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;
}
}

33
table_object.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include "interface_object.h"
class TableObject: public InterfaceObject
{
public:
TableObject(uint8_t* memoryReference);
virtual void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data);
virtual void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count);
virtual uint8_t propertySize(PropertyID id);
virtual ~TableObject();
LoadState loadState();
virtual uint8_t* save(uint8_t* buffer);
virtual uint8_t* restore(uint8_t* buffer);
protected:
virtual void beforeStateChange(LoadState& newState) {}
uint8_t* _data = 0;
uint32_t _size = 0;
ErrorCode _errorCode = E_NO_FAULT;
private:
uint32_t tableReference();
bool allocTable(uint32_t size, bool doFill, uint8_t fillByte);
void loadEvent(uint8_t* data);
void loadEventUnloaded(uint8_t* data);
void loadEventLoading(uint8_t* data);
void loadEventLoaded(uint8_t* data);
void loadEventError(uint8_t* data);
void additionalLoadControls(uint8_t* data);
void loadState(LoadState newState);
LoadState _state = LS_UNLOADED;
uint8_t* _memoryReference;
};

127
tpdu.cpp Normal file
View File

@ -0,0 +1,127 @@
#include "tpdu.h"
#include "cemi_frame.h"
TPDU::TPDU(uint8_t* data, CemiFrame& frame): _data(data), _frame(frame)
{
}
TpduType TPDU::type() const
{
if (control())
{
if (numbered())
{
if ((_data[0] & 1) == 0)
return Ack;
else
return Nack;
}
else if ((_data[0] & 1) == 0)
return Connect;
else
return Disconnect;
}
else
{
if (_frame.addressType() == GroupAddress)
{
if (_frame.destinationAddress() == 0)
return DataBroadcast;
else
return DataGroup;
}
else if (numbered())
return DataConnected;
else
return DataInduvidual;
}
}
void TPDU::type(TpduType type)
{
switch (type)
{
case DataBroadcast:
case DataGroup:
case DataInduvidual:
_data[0] &= 0x3;
break;
case DataConnected:
_data[0] &= 0xC3;
_data[0] |= 0x40;
break;
case Connect:
_data[0] = 0x80;
break;
case Disconnect:
_data[0] = 0x81;
break;
case Ack:
_data[0] &= ~0xC3;
_data[0] |= 0xC2;
break;
case Nack:
_data[0] |= 0xC3;
break;
}
}
bool TPDU::numbered() const
{
return (_data[0] & 0x40) > 0;
}
void TPDU::numbered(bool value)
{
if (value)
_data[0] |= 0x40;
else
_data[0] &= ~0x40;
}
bool TPDU::control() const
{
return (_data[0] & 0x80) > 0;
}
void TPDU::control(bool value)
{
if (value)
_data[0] |= 0x80;
else
_data[0] &= ~0x80;
}
uint8_t TPDU::sequenceNumber() const
{
return ((_data[0] >> 2) & 0xF);
}
void TPDU::sequenceNumber(uint8_t value)
{
_data[0] &= ~(0xF << 2);
_data[0] |= ((value & 0xF) << 2);
}
APDU& TPDU::apdu()
{
return _frame.apdu();
}
CemiFrame& TPDU::frame()
{
return _frame;
}
void TPDU::printPDU()
{
/* print.print("TPDU: ");
print.print(type(), HEX, 2);
print.print(" ");
for (uint8_t i = 0; i < apdu().length() + 1; ++i)
{
if (i) print.print(" ");
print.print(_data[i], HEX, 2);
}
print.println()*/;
}

32
tpdu.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include "stdint.h"
#include "knx_types.h"
class CemiFrame;
class APDU;
class TPDU
{
friend class CemiFrame;
public:
TPDU(uint8_t* data, CemiFrame& frame);
TpduType type() const;
void type(TpduType type);
bool numbered() const;
void numbered(bool value);
bool control() const;
void control(bool value);
uint8_t sequenceNumber() const;
void sequenceNumber(uint8_t value);
APDU& apdu();
CemiFrame& frame();
void printPDU();
private:
uint8_t* _data;
CemiFrame& _frame;
};

711
transport_layer.cpp Normal file
View File

@ -0,0 +1,711 @@
#include "transport_layer.h"
#include "apdu.h"
#include "cemi_frame.h"
#include "network_layer.h"
#include "application_layer.h"
#include "platform.h"
#include <stdio.h>
TransportLayer::TransportLayer(ApplicationLayer& layer, AddressTableObject& gat, Platform& platform): _savedFrame(0),
_savedFrameConnecting(0), _applicationLayer(layer), _groupAddressTable(gat), _platform(platform)
{
_currentState = Closed;
}
void TransportLayer::networkLayer(NetworkLayer& layer)
{
_networkLayer = &layer;
}
void TransportLayer::dataIndividualIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu)
{
//if (tpdu.apdu().length() > 0)
//{
// print.print("<- TL ");
// tpdu.printPDU();
// print.print("<- TL ");
// tpdu.apdu().printPDU();
//}
uint8_t sequenceNo = tpdu.sequenceNumber();
switch (tpdu.type())
{
case DataInduvidual:
_applicationLayer.dataIndividualIndication(hopType, priority, source, tpdu.apdu());
return;
case DataConnected:
if (source == _connectionAddress)
{
if (sequenceNo == _seqNoRecv)
{
//E4
switch (_currentState)
{
case Closed:
//A0 nothing
break;
case OpenIdle:
case OpenWait:
A2(source, priority, tpdu.apdu());
break;
case Connecting:
_currentState = Closed;
A6(destination);
break;
}
}
else if(sequenceNo == ((_seqNoRecv -1) & 0xF))
{
//E5
switch (_currentState)
{
case Closed:
//A0
break;
case OpenIdle:
case OpenWait:
case Connecting:
A3(source, priority, tpdu);
break;
}
}
else
{
//E6
switch (_currentState)
{
case Closed:
//A0
break;
case OpenIdle:
case OpenWait:
A4(source, priority, tpdu);
break;
case Connecting:
A6(destination);
break;
}
}
}
else
{
//E7
switch (_currentState)
{
case Closed:
case OpenIdle:
case OpenWait:
//A0
break;
case Connecting:
A10(source);
break;
}
}
break;
case Connect:
if (source == _connectionAddress)
{
//E0
switch (_currentState)
{
case Closed:
_currentState = OpenIdle;
A1(source);
break;
case OpenWait:
case OpenIdle:
case Connecting:
//A0: do nothing
break;
}
}
else
{
//E1
switch (_currentState)
{
case Closed:
_currentState = OpenIdle;
A1(source);
break;
case OpenIdle:
case OpenWait:
case Connecting:
A10(source);
break;
}
}
break;
case Disconnect:
if (source == _connectionAddress)
{
//E2
switch (_currentState)
{
case Closed:
//A0 do nothing
break;
case OpenIdle:
case OpenWait:
case Connecting:
_currentState = Closed;
A5(source);
break;
default:
break;
}
}
else
{
//E3
//A0: do nothing
}
break;
case Ack:
if (source == _connectionAddress)
{
if (sequenceNo == _seqNoSend)
{
//E8
switch (_currentState)
{
case Closed:
case OpenIdle:
//A0
break;
case OpenWait:
_currentState = OpenIdle;
A8();
break;
case Connecting:
_currentState = Closed;
A6(source);
break;
}
}
else
{
//E9
switch (_currentState)
{
case Closed:
case OpenIdle:
//A0
break;
case OpenWait:
case Connecting:
_currentState = Closed;
A6(source);
break;
}
}
}
else
{
//E10
switch (_currentState)
{
case Connecting:
A10(source);
break;
}
}
break;
case Nack:
if (source == _connectionAddress)
{
if (sequenceNo != _seqNoSend)
{
//E11
switch (_currentState)
{
case Closed:
case OpenIdle:
case OpenWait:
//A0
break;
case Connecting:
_currentState = Closed;
A6(source);
break;
}
}
else
{
if (_repCount < _maxRepCount)
{
//E12
switch (_currentState)
{
case Closed:
//A0
break;
case Connecting:
case OpenIdle:
_currentState = Closed;
A6(source);
break;
case OpenWait:
A9();
break;
}
}
else
{
//E13
switch (_currentState)
{
case Closed:
//A0
break;
case OpenIdle:
case OpenWait:
case Connecting:
_currentState = Closed;
A6(source);
break;
}
}
}
}
else
{
//E14
switch (_currentState)
{
case Closed:
case OpenIdle:
case OpenWait:
//A0
break;
case Connecting:
A10(source);
break;
default:
break;
}
}
break;
default:
break;
}
}
void TransportLayer::dataIndividualConfirm(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu, bool status)
{
TpduType type = tpdu.type();
switch (type)
{
case DataInduvidual:
_applicationLayer.dataIndividualConfirm(ack, hopType, priority, destination, tpdu.apdu(), status);
break;
case DataConnected:
//E22
//A0: do nothing
break;
case Connect:
if (status)
{
//E19
switch (_currentState)
{
case Closed:
case OpenIdle:
case OpenWait:
//A0: do nothing
break;
case Connecting:
_currentState = OpenIdle;
A13(destination);
break;
}
}
else
{
//E20
switch (_currentState)
{
case Closed:
case OpenIdle:
case OpenWait:
//A0: do nothing
break;
case Connecting:
A5(destination);
break;
}
}
break;
case Disconnect:
//E21
//A0: do nothing
break;
case Ack:
//E23
//A0: do nothing
break;
case Nack:
//E24
//A0: do nothing
break;
}
}
void TransportLayer::dataGroupIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu)
{
uint16_t tsap = _groupAddressTable.getTsap(destination);
_applicationLayer.dataGroupIndication(hopType, priority, tsap, tpdu.apdu());
}
void TransportLayer::dataGroupConfirm(AckType ack, uint16_t source, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu, bool status)
{
_applicationLayer.dataGroupConfirm(ack, hopType, priority, destination, tpdu.apdu(), status);
}
void TransportLayer::dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu)
{
_applicationLayer.dataBroadcastIndication(hopType, priority, source, tpdu.apdu());
}
void TransportLayer::dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu, bool status)
{
_applicationLayer.dataBroadcastConfirm(ack, hopType, priority, tpdu.apdu(), status);
}
void TransportLayer::dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu)
{
_applicationLayer.dataSystemBroadcastIndication(hopType, priority, source, tpdu.apdu());
}
void TransportLayer::dataSystemBroadcastConfirm(AckType ack, HopCountType hopType, TPDU& tpdu, Priority priority, bool status)
{
_applicationLayer.dataSystemBroadcastConfirm(hopType, priority, tpdu.apdu(), status);
}
void TransportLayer::dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu)
{
uint16_t groupAdress = _groupAddressTable.getGa(tsap);
TPDU& tpdu = apdu.frame().tpdu();
_networkLayer->dataGroupRequest(ack, groupAdress, hopType, priority, tpdu);
}
void TransportLayer::dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu)
{
TPDU& tpdu = apdu.frame().tpdu();
_networkLayer->dataBroadcastRequest(ack, hopType, priority, tpdu);
}
void TransportLayer::dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu)
{
TPDU& tpdu = apdu.frame().tpdu();
return _networkLayer->dataSystemBroadcastRequest(ack, hopType, priority, tpdu);
}
void TransportLayer::dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu)
{
//print.print("-> TL ");
//apdu.printPDU();
TPDU& tpdu = apdu.frame().tpdu();
_networkLayer->dataIndividualRequest(ack, destination, hopType, priority, tpdu);
}
void TransportLayer::connectRequest(uint16_t destination, Priority priority)
{
//E25
switch (_currentState)
{
case Closed:
_currentState = Connecting;
A12(destination, priority);
break;
case OpenIdle:
case OpenWait:
case Connecting:
_currentState = Closed;
A6(destination);
break;
}
}
void TransportLayer::disconnectRequest(uint16_t tsap, Priority priority)
{
//E26
switch (_currentState)
{
case Closed:
A15(priority, tsap);
break;
case OpenIdle:
case OpenWait:
case Connecting:
_currentState = Closed;
A14(tsap, priority);
break;
}
}
void TransportLayer::dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu)
{
//print.print("-> TL ");
//apdu.printPDU();
//E15
switch (_currentState)
{
case Closed:
//A0
break;
case OpenIdle:
_currentState = OpenWait;
A7(priority, apdu);
break;
case OpenWait:
case Connecting:
A11(tsap, priority, apdu);
break;
default:
break;
}
}
void TransportLayer::connectionTimeoutIndication()
{
//E16
switch (_currentState)
{
case Closed:
//A0: do nothing
break;
case OpenIdle:
case OpenWait:
case Connecting:
_currentState = Closed;
A6(_connectionAddress);
break;
}
}
void TransportLayer::ackTimeoutIndication()
{
if (_repCount < _maxRepCount)
{
//E17
switch (_currentState)
{
case Closed:
case OpenIdle:
case Connecting:
//A0: do nothing
break;
case OpenWait:
A9();
break;
}
}
else
{
//E18
switch (_currentState)
{
case Closed:
case OpenIdle:
case Connecting:
//A0: do nothing
break;
case OpenWait:
_currentState = Closed;
A6(_connectionAddress);
break;
}
}
}
void TransportLayer::loop()
{
uint32_t millis = _platform.millis();
if (_connectionTimeoutEnabled
&& (millis - _connectionTimeoutStartMillis) > _connectionTimeoutMillis)
connectionTimeoutIndication();
if (_ackTimeoutEnabled
&& (millis - _ackTimeoutStartMillis) > _ackTimeoutMillis)
ackTimeoutIndication();
if (_savedConnectingValid)
{
//retry saved event
_savedConnectingValid = false;
dataConnectedRequest(_savedTsapConnecting, _savedPriorityConnecting, _savedFrameConnecting.apdu());
}
}
void TransportLayer::sendControlTelegram(TpduType pduType, uint8_t seqNo)
{
CemiFrame frame(0);
TPDU& tpdu = frame.tpdu();
tpdu.type(pduType);
tpdu.sequenceNumber(seqNo);
_networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter,
SystemPriority, tpdu);
}
void TransportLayer::A0()
{
/* do nothing */
}
void TransportLayer::A1(uint16_t source)
{
_connectionAddress = source;
_applicationLayer.connectIndication(source);
_seqNoSend = 0;
_seqNoRecv = 0;
}
void incSeqNr(uint8_t& seqNr)
{
seqNr += 1;
if (seqNr > 0xf)
seqNr = 0;
}
void TransportLayer::A2(uint16_t source, Priority priority, APDU& apdu)
{
sendControlTelegram(Ack, _seqNoRecv);
incSeqNr(_seqNoRecv);
_applicationLayer.dataConnectedIndication(priority, source, apdu);
enableConnectionTimeout();
}
void TransportLayer::A3(uint16_t source, Priority priority, TPDU& recTpdu)
{
sendControlTelegram(Ack, recTpdu.sequenceNumber());
enableConnectionTimeout();
}
void TransportLayer::A4(uint16_t source, Priority priority, TPDU& recTpdu)
{
sendControlTelegram(Nack, recTpdu.sequenceNumber());
enableConnectionTimeout();
}
void TransportLayer::A5(uint16_t tsap)
{
_applicationLayer.disconnectIndication(tsap);
disableConnectionTimeout();
disableAckTimeout();
}
void TransportLayer::A6(uint16_t tsap)
{
sendControlTelegram(Disconnect, 0);
_applicationLayer.disconnectIndication(tsap);
disableConnectionTimeout();
disableAckTimeout();
}
void TransportLayer::A7(Priority priority, APDU& apdu)
{
_savedPriority = priority;
_savedFrame = apdu.frame();
TPDU& tpdu = apdu.frame().tpdu();
tpdu.type(DataConnected);
tpdu.sequenceNumber(_seqNoSend);
_networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter, priority, tpdu);
_repCount = 0;
enableAckTimeout();
enableConnectionTimeout();
}
void TransportLayer::A8()
{
disableAckTimeout();
incSeqNr(_seqNoSend);
_applicationLayer.dataConnectedConfirm(0);
enableConnectionTimeout();
}
void TransportLayer::A9()
{
TPDU& tpdu = _savedFrame.tpdu();
// tpdu is still initialized from last send
_networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter, _savedPriority, tpdu);
_repCount += 1;
enableAckTimeout();
enableConnectionTimeout();
}
void TransportLayer::A10(uint16_t source)
{
CemiFrame frame(0);
TPDU& tpdu = frame.tpdu();
tpdu.type(Disconnect);
tpdu.sequenceNumber(0);
_networkLayer->dataIndividualRequest(AckRequested, source, NetworkLayerParameter, SystemPriority, tpdu);
}
void TransportLayer::A11(uint16_t tsap, Priority priority, APDU& apdu)
{
_savedTsapConnecting = tsap;
_savedPriorityConnecting = priority;
_savedFrameConnecting = apdu.frame();
_savedConnectingValid = true;
}
void TransportLayer::A12(uint16_t destination, Priority priority)
{
_connectionAddress = destination;
CemiFrame frame(0);
TPDU& tpdu = frame.tpdu();
tpdu.type(Connect);
_seqNoRecv = 0;
_seqNoSend = 0;
enableConnectionTimeout();
}
void TransportLayer::A13(uint16_t destination)
{
_applicationLayer.connectConfirm(destination, 0, true);
}
void TransportLayer::A14(uint16_t tsap, Priority priority)
{
CemiFrame frame(0);
TPDU& tpdu = frame.tpdu();
tpdu.type(Disconnect);
tpdu.sequenceNumber(0);
_networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter, SystemPriority, tpdu);
_applicationLayer.disconnectConfirm(priority, tsap, true);
}
void TransportLayer::A15(Priority priority, uint16_t tsap)
{
_applicationLayer.disconnectConfirm(priority, tsap, true);
disableConnectionTimeout();
disableAckTimeout();
}
void TransportLayer::enableConnectionTimeout()
{
_connectionTimeoutStartMillis = _platform.millis();
_connectionTimeoutEnabled = true;
}
void TransportLayer::disableConnectionTimeout()
{
_connectionTimeoutEnabled = false;
}
void TransportLayer::enableAckTimeout()
{
_ackTimeoutStartMillis = _platform.millis();
_ackTimeoutEnabled = true;
}
void TransportLayer::disableAckTimeout()
{
_ackTimeoutEnabled = false;
}

98
transport_layer.h Normal file
View File

@ -0,0 +1,98 @@
#pragma once
#include <stdint.h>
#include "knx_types.h"
#include "tpdu.h"
#include "address_table_object.h"
#include "cemi_frame.h"
class ApplicationLayer;
class APDU;
class NetworkLayer;
class Platform;
enum StateType { Closed, OpenIdle, OpenWait, Connecting };
class TransportLayer
{
public:
TransportLayer(ApplicationLayer& layer, AddressTableObject& gat, Platform& platform);
void networkLayer(NetworkLayer& layer);
// from network layer
void dataIndividualIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu);
void dataIndividualConfirm(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu, bool status);
void dataGroupIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu);
void dataGroupConfirm(AckType ack, uint16_t source, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu, bool status);
void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu);
void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu, bool status);
void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu);
void dataSystemBroadcastConfirm(AckType ack, HopCountType hopType, TPDU& tpdu, Priority priority, bool status);
// fromp application layer
void dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu);
void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu);
void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu);
void dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu);
void connectRequest(uint16_t destination, Priority priority);
void disconnectRequest(uint16_t tsap, Priority priority);
// apdu must be valid until it was confirmed
void dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu);
// other
void connectionTimeoutIndication();
void ackTimeoutIndication();
void loop();
private:
#pragma region States
Priority _savedPriority = LowPriority;
CemiFrame _savedFrame;
Priority _savedPriorityConnecting;
CemiFrame _savedFrameConnecting;
uint16_t _savedTsapConnecting;
bool _savedConnectingValid = false;
enum StateEvent
{
E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14,
E15, E16, E17, E18, E19, E20, E21, E22, E23, E24, E25, E26, E27
};
StateType _currentState = Closed;
void sendControlTelegram(TpduType pduType, uint8_t seqNo);
void A0();
void A1(uint16_t source);
void A2(uint16_t source, Priority priority, APDU& apdu);
void A3(uint16_t source, Priority priority, TPDU& recTpdu);
void A4(uint16_t source, Priority priority, TPDU& recTpdu);
void A5(uint16_t source);
void A6(uint16_t source);
void A7(Priority priority, APDU& apdu);
void A8();
void A9();
void A10(uint16_t source);
void A11(uint16_t tsap, Priority priority, APDU& apdu);
void A12(uint16_t destination, Priority priority);
void A13(uint16_t destination);
void A14(uint16_t destination, Priority priority);
void A15(Priority priority, uint16_t tsap);
void enableConnectionTimeout();
void disableConnectionTimeout();
void enableAckTimeout();
void disableAckTimeout();
uint16_t _connectionAddress = 0;
uint8_t _seqNoSend = 0;
uint8_t _seqNoRecv = 0;
bool _connectionTimeoutEnabled = false;
uint32_t _connectionTimeoutStartMillis = 0;
uint16_t _connectionTimeoutMillis = 6000;
bool _ackTimeoutEnabled = false;
uint32_t _ackTimeoutStartMillis = 0;
uint16_t _ackTimeoutMillis = 3000;
uint8_t _repCount = 0;
uint8_t _maxRepCount = 3;
#pragma endregion
ApplicationLayer& _applicationLayer;
AddressTableObject& _groupAddressTable;
NetworkLayer* _networkLayer;
Platform& _platform;
};