Save work

This commit is contained in:
nanosonde 2019-11-07 14:28:34 +01:00
parent f6ace390ee
commit dfeb087db8
11 changed files with 827 additions and 0 deletions

View File

@ -51,6 +51,10 @@ InterfaceObject* Bau27B0::getInterfaceObject(uint8_t idx)
return nullptr;
case 6:
return &_rfMediumObj;
#ifdef USE_USBIF
case 7:
return &_cemiServerObject;
#endif
default:
return nullptr;
}

View File

@ -20,8 +20,13 @@ class Bau27B0 : public BauSystemB
RfMediumObject _rfMediumObj;
uint8_t _descriptor[2] = {0x27, 0xB0};
#ifdef USE_USBIF
const uint32_t _ifObjs[8] = { 7, // length
OT_DEVICE, OT_ADDR_TABLE, OT_ASSOC_TABLE, OT_GRP_OBJ_TABLE, OT_APPLICATION_PROG, OT_RF_MEDIUM, OT_CEMI_SERVER};
#else
const uint32_t _ifObjs[7] = { 6, // length
OT_DEVICE, OT_ADDR_TABLE, OT_ASSOC_TABLE, OT_GRP_OBJ_TABLE, OT_APPLICATION_PROG, OT_RF_MEDIUM};
#endif
void domainAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, uint8_t* rfDoA,
uint8_t* knxSerialNumber);

View File

@ -15,6 +15,10 @@ BauSystemB::BauSystemB(Platform& platform): _memory(platform), _addrTable(platfo
_assocTable(platform), _groupObjTable(platform), _appProgram(platform),
_platform(platform), _appLayer(_assocTable, *this),
_transLayer(_appLayer, _addrTable), _netLayer(_transLayer)
#ifdef USE_USBIF
, _dlLayerUsb(_deviceObj, _addrTable, _netLayer, _platform),
_cemiServer(_dlLayerUsb, *this)
#endif
{
_appLayer.transportLayer(_transLayer);
_transLayer.networkLayer(_netLayer);
@ -23,11 +27,17 @@ BauSystemB::BauSystemB(Platform& platform): _memory(platform), _addrTable(platfo
_memory.addSaveRestore(&_addrTable);
_memory.addSaveRestore(&_assocTable);
_memory.addSaveRestore(&_groupObjTable);
#ifdef USE_USBIF
_memory.addSaveRestore(&_cemiServerObject);
#endif
}
void BauSystemB::loop()
{
dataLinkLayer().loop();
#ifdef USE_USBIF
_dlLayerUsb.loop();
#endif
_transLayer.loop();
sendNextGroupTelegram();
nextRestartState();
@ -41,6 +51,9 @@ bool BauSystemB::enabled()
void BauSystemB::enabled(bool value)
{
dataLinkLayer().enabled(value);
#ifdef USE_USBIF
_dlLayerUsb.enabled(value);
#endif
}
void BauSystemB::sendNextGroupTelegram()

View File

@ -12,6 +12,11 @@
#include "tpuart_data_link_layer.h"
#include "platform.h"
#include "memory.h"
#ifdef USE_USBIF
#include "usb_data_link_layer.h"
#include "cemi_server.h"
#include "cemi_server_object.h"
#endif
class BauSystemB : protected BusAccessUnit
{
@ -88,4 +93,9 @@ class BauSystemB : protected BusAccessUnit
bool _configured = true;
RestartState _restartState = Idle;
uint32_t _restartDelay = 0;
#ifdef USE_USBIF
UsbDataLinkLayer _dlLayerUsb;
CemiServer _cemiServer;
CemiServerObject _cemiServerObject;
#endif
};

178
src/knx/cemi_server.cpp Normal file
View File

@ -0,0 +1,178 @@
#include "cemi_server.h"
#include "cemi_frame.h"
#include "bau.h"
#include "usb_data_link_layer.h"
#include "string.h"
#include "bits.h"
#include <stdio.h>
CemiServer::CemiServer(UsbDataLinkLayer& dlLayer, BusAccessUnit& bau):
_dlLayer(dlLayer),
_bau(bau)
{
}
#pragma region DLL Callbacks
void CemiServer::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 CemiServer::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 CemiServer::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 CemiServer::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 CemiServer::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(PropertyDescriptionResponse);
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 CemiServer::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);
}
void CemiServer::individualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU & apdu)
{
uint8_t* data = apdu.data();
switch (apdu.type())
{
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;
}
}
void CemiServer::individualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU & apdu, bool status)
{
uint8_t* data = apdu.data();
switch (apdu.type())
{
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;
}
}

54
src/knx/cemi_server.h Normal file
View File

@ -0,0 +1,54 @@
#pragma once
#include <stdint.h>
#include "knx_types.h"
class UsbDataLinkLayer;
class BusAccessUnit;
/**
* This is an implementation of the cEMI server as specified in @cite knx:3/6/3.
* It provides methods for the BusAccessUnit to do different things and translates this
* call to an cEMI frame and calls the correct method of the data link layer.
* It also takes calls from data link layer, decodes the submitted cEMI frames and calls the corresponding
* methods of the BusAccessUnit class.
*/
class CemiServer
{
public:
/**
* The constructor.
* @param assocTable The AssociationTable is used to translate between asap (i.e. group objects) and group addresses.
* @param bau methods are called here depending of the content of the APDU
*/
CemiServer(UsbDataLinkLayer& dlLayer, BusAccessUnit& bau);
// from data link layer
#pragma region Data - Link - Layer - Callbacks
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);
#pragma endregion
#pragma region from bau
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);
#pragma endregion
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 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);
UsbDataLinkLayer& _dlLayer;
BusAccessUnit& _bau;
};

View File

@ -0,0 +1,104 @@
#include <cstring>
#include "cemi_server_object.h"
#include "bits.h"
void CemiServerObject::readProperty(PropertyID propertyId, uint32_t start, uint32_t& count, uint8_t* data)
{
switch (propertyId)
{
case PID_OBJECT_TYPE:
pushWord(OT_CEMI_SERVER, data);
break;
case PID_MEDIUM_TYPE: // PDT_BITSET16
#if MEDIUM_TYPE==0
data[0] = 2; // TP1 supported
#elif MEDIUM_TYPE==2
data[0] = 16; // RF supported
#elif MEDIUM_TYPE==5
data[0] = 32; // IP supported
#endif
break;
case PID_COMM_MODE: // PDT_ENUM8
// See KNX spec. cEMI 3/6/3 p.110
data[0] = 0x00; // Only Data Link Layer mode supported and we do not allow switching (read-only)
break;
case PID_MEDIUM_AVAILABILITY: // PDT_BITSET16
#if MEDIUM_TYPE==0
data[0] = 2; // TP1 active
#elif MEDIUM_TYPE==2
data[0] = 16; // RF active
#elif MEDIUM_TYPE==5
data[0] = 32; // IP active
#endif
break;
case PID_ADD_INFO_TYPES: // PDT_ENUM8[]
pushByteArray((uint8_t*)_addInfoTypesTable, sizeof(_addInfoTypesTable), data);
break;
// Not supported yet
break;
default:
count = 0;
}
}
void CemiServerObject::writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count)
{
switch (id)
{
case PID_COMM_MODE:
//_commMode = data[0]; // TODO: only Data Link Layer supported for now
// Property is also marked as read-only, normally it is read/write.
break;
default:
break;
}
}
uint8_t CemiServerObject::propertySize(PropertyID id)
{
switch (id)
{
case PID_COMM_MODE:
return 1;
case PID_OBJECT_TYPE:
case PID_MEDIUM_TYPE:
case PID_MEDIUM_AVAILABILITY:
return 2;
case PID_ADD_INFO_TYPES:
return sizeof(_addInfoTypesTable);
default:
break;
}
return 0;
}
uint8_t* CemiServerObject::save(uint8_t* buffer)
{
return buffer;
}
uint8_t* CemiServerObject::restore(uint8_t* buffer)
{
return buffer;
}
static PropertyDescription _propertyDescriptions[] =
{
{ PID_OBJECT_TYPE, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0 },
{ PID_MEDIUM_TYPE, false, PDT_BITSET16, 1, ReadLv3 | WriteLv0 },
{ PID_COMM_MODE, false, PDT_ENUM8, 1, ReadLv3 | WriteLv0 },
{ PID_MEDIUM_AVAILABILITY, false, PDT_BITSET16, 1, ReadLv3 | WriteLv0 },
{ PID_ADD_INFO_TYPES, false, PDT_ENUM8, 1, ReadLv3 | WriteLv0 }
};
static uint8_t _propertyCount = sizeof(_propertyDescriptions) / sizeof(PropertyDescription);
uint8_t CemiServerObject::propertyCount()
{
return _propertyCount;
}
PropertyDescription* CemiServerObject::propertyDescriptions()
{
return _propertyDescriptions;
}

View File

@ -0,0 +1,23 @@
#pragma once
#include "interface_object.h"
class CemiServerObject: 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);
void readPropertyDescription(uint8_t propertyId, uint8_t& propertyIndex, bool& writeEnable, uint8_t& type, uint16_t& numberOfElements, uint8_t& access);
protected:
uint8_t propertyCount();
PropertyDescription* propertyDescriptions();
private:
// cEMI additional info types supported by this cEMI server: only 0x02 (RF Control Octet and Serial Number or DoA)
uint8_t _addInfoTypesTable[1] = { 0x02 };
uint8_t _commMode = 0x00;
};

View File

@ -135,6 +135,16 @@ enum PropertyID
PID_MSG_TRANSMIT_TO_KNX = 75,
PID_FRIENDLY_NAME = 76,
PID_ROUTING_BUSY_WAIT_TIME = 78,
/** cEMI Server Object */
PID_MEDIUM_TYPE = 51,
PID_COMM_MODE = 52,
PID_MEDIUM_AVAILABILITY = 53,
PID_ADD_INFO_TYPES = 54,
PID_TIME_BASE = 55,
PID_TRANSP_ENABLE = 56,
PID_CLIENT_SNA = 57,
PID_CLIENT_DEVICE_ADDRESS = 58
};
enum LoadState

View File

@ -0,0 +1,374 @@
#include "usb_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 MIN(a, b) ((a < b) ? (a) : (b))
Adafruit_USBD_HID usb_hid;
// HID report descriptor using TinyUSB's template
// Generic In Out with 64 bytes report (max)
uint8_t const desc_hid_report[] =
{
//TUD_HID_REPORT_DESC_GENERIC_INOUT(64)
0x06, 0xA0, 0xFF, // Usage Page (Vendor Defined 0xFFA0)
0x09, 0x01, // Usage (0x01)
0xA1, 0x01, // Collection (Application)
0x09, 0x01, // Usage (0x01)
0xA1, 0x00, // Collection (Physical)
0x06, 0xA1, 0xFF, // Usage Page (Vendor Defined 0xFFA1)
0x09, 0x03, // Usage (0x03)
0x09, 0x04, // Usage (0x04)
0x15, 0x80, // Logical Minimum (-128)
0x25, 0x7F, // Logical Maximum (127)
0x35, 0x00, // Physical Minimum (0)
0x45, 0xFF, // Physical Maximum (-1)
0x75, 0x08, // Report Size (8)
0x85, 0x01, // Report ID (1)
0x95, 0x3F, // Report Count (63)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x05, // Usage (0x05)
0x09, 0x06, // Usage (0x06)
0x15, 0x80, // Logical Minimum (-128)
0x25, 0x7F, // Logical Maximum (127)
0x35, 0x00, // Physical Minimum (0)
0x45, 0xFF, // Physical Maximum (-1)
0x75, 0x08, // Report Size (8)
0x85, 0x01, // Report ID (1)
0x95, 0x3F, // Report Count (63)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xC0 // End Collection
};
uint16_t parseReport(uint8_t* data, uint16_t dataLength)
{
int8_t respDataSize = 0;
bool forceSend = false;
uint16_t length = 0;
popWord(length, &data[5]);
length = MIN(dataLength, length);
Serial1.print("RX HID report: len: ");
Serial1.println(length, DEC);
for (int i = 0; i < (length + data[4] + 3); i++)
{
if (data[i] < 16)
Serial1.print("0");
Serial1.print(data[i], HEX);
Serial1.print(" ");
}
Serial1.println("");
if ( data[0] == 0x01 && // ReportID (fixed 0x01)
data[1] == 0x13 && // PacketInfo must be 0x13 (SeqNo: 1, Type: 3)
data[3] == 0x00 && // Protocol version (fixed 0x00)
data[4] == 0x08 && // USB KNX Transfer Protocol Header Length (fixed 0x08)
data[7] == 0x0F ) // Bus Access Server Feature (0x0F)
{
uint8_t serviceId = data[8];
switch (serviceId)
{
case 0x01: // Device Feature Get
{
data[8] = 0x02; // Device Feature Response
uint8_t featureId = data[11];
switch (featureId)
{
case 0x01: // Supported EMI types
Serial1.println("Device Feature Get: Supported EMI types");
respDataSize = 2;
data[12] = 0x00; // USB KNX Transfer Protocol Body: Feature Data
data[13] = 0x04; // USB KNX Transfer Protocol Body: Feature Data -> only cEMI supported
break;
case 0x02: // Host Device Descriptor Type 0
Serial1.println("Device Feature Get: Host Device Descriptor Type 0");
respDataSize = 2;
data[12] = 0x00; // USB KNX Transfer Protocol Body: Feature Data
data[13] = 0x00; // USB KNX Transfer Protocol Body: Feature Data
break;
case 0x03: // Bus connection status
Serial1.println("Device Feature Get: Bus connection status");
respDataSize = 1;
data[12] = 1; // USB KNX Transfer Protocol Body: Feature Data
break;
case 0x04: // KNX manufacturer code
Serial1.println("Device Feature Get: KNX manufacturer code");
respDataSize = 2;
data[12] = 0x00; // USB KNX Transfer Protocol Body: Feature Data
data[13] = 0x00; // USB KNX Transfer Protocol Body: Feature Data -> Manufacturer Code
break;
case 0x05: // Active EMI type
Serial1.println("Device Feature Get: Active EMI type");
respDataSize = 1;
data[12] = 0x03; // USB KNX Transfer Protocol Body: Feature Data -> cEMI ID
break;
default:
respDataSize = 0;
break;
}
break;
}
case 0x03: // Device Feature Set
{
uint8_t featureId = data[11];
switch (featureId)
{
case 0x05: // Active EMI type
Serial1.print("Device Feature Set: Active EMI type: ");
if (data[12] < 16)
Serial1.print("0");
Serial1.println(data[12], HEX); // USB KNX Transfer Protocol Body: Feature Data -> EMI TYPE ID
break;
// All other featureIds must not be set
case 0x01: // Supported EMI types
case 0x02: // Host Device Descriptor Type 0
case 0x03: // Bus connection status
case 0x04: // KNX manufacturer code
default:
break;
}
break;
}
// These are only sent from the device to the host
case 0x02: // Device Feature Response
case 0x04: // Device Feature Info
case 0xEF: // reserved, not used
case 0xFF: // reserved (ESCAPE for future extensions)
default:
break;
}
}
else if ( data[0] == 0x01 && // ReportID (fixed 0x01)
data[1] == 0x13 && // PacketInfo must be 0x13 (SeqNo: 1, Type: 3)
data[3] == 0x00 && // Protocol version (fixed 0x00)
data[4] == 0x08 && // USB KNX Transfer Protocol Header Length (fixed 0x08)
data[7] == 0x01 && // KNX Tunneling (0x01)
data[8] == 0x03 ) // EMI ID: 3 -> cEMI
{
uint8_t messageCode = data[11];
switch(messageCode)
{
case 0xFC: // M_PropRead.req
{
data[11] = 0xFB; // M_PropRead.con
if (data[15] == 0x34)
{
data[18] = 00; // PID_COMM_MODE: 0: Data Link Layer
}
else
{
data[16] = 0; // Number of elements must be 0 if negative response
data[18] = 7; // Error code 7 (Void DP)
}
respDataSize = 1;
break;
}
case 0xF6: // M_PropWrite.req
{
data[11] = 0xF5; // M_PropWrite.con
if ((data[15] == 0x34) && (data[18] == 0x00))
{
respDataSize = -1;
}
else
{
data[16] = 0; // Number of elements must be 0 if negative response
data[18] = 6; // Error code 6 (illegal command)
respDataSize = 0;
forceSend = true;
}
break;
}
}
}
if ((respDataSize != 0) || forceSend)
{
data[2] += respDataSize; // HID Report Header: Packet Length
data[6] += respDataSize; // USB KNX Transfer Protocol Header: Body Length
Serial1.print("TX HID report: len: ");
Serial1.println((length + data[4] + 3) + respDataSize, DEC);
for (int i = 0; i < ((length + data[4] + 3) + respDataSize); i++)
{
if (data[i] < 16)
Serial1.print("0");
Serial1.print(data[i], HEX);
Serial1.print(" ");
}
Serial1.println("");
}
return respDataSize;
}
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
void set_report_callback(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
{
// we don't use multiple report and report ID
(void) report_id;
(void) report_type;
uint8_t tmpbuf[bufsize];
memcpy(tmpbuf, buffer, bufsize);
if (parseReport(tmpbuf, bufsize) > 0)
{
usb_hid.sendReport(tmpbuf[0], &tmpbuf[1], bufsize);
}
}
void UsbDataLinkLayer::loop()
{
if (!_enabled)
return;
}
bool UsbDataLinkLayer::sendFrame(CemiFrame& frame)
{
if (!_enabled)
return false;
// TODO: Is queueing really required?
// According to the spec. the upper layer may only send a new L_Data.req if it received
// the L_Data.con for the previous L_Data.req.
addFrameTxQueue(frame);
// TODO: For now L_data.req is confirmed immediately (L_Data.con)
// see 3.6.3 p.80: L_Data.con shall be generated AFTER transmission of the corresponsing frame
// RF sender will never generate L_Data.con with C=1 (Error), but only if the TX buffer overflows
// The RF sender cannot detect if the RF frame was transmitted successfully or not according to the spec.
dataConReceived(frame, true);
return true;
}
UsbDataLinkLayer::UsbDataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab,
NetworkLayer& layer, Platform& platform)
: DataLinkLayer(devObj, addrTab, layer, platform)
{
}
void UsbDataLinkLayer::frameBytesReceived(uint8_t* _buffer, uint16_t length)
{
// Prepare the cEMI frame
CemiFrame frame(_buffer, length);
/*
frame.frameType(ExtendedFrame); // KNX RF uses only extended frame format
frame.priority(SystemPriority); // Not used in KNX RF
frame.ack(AckDontCare); // Not used in KNX RF
frame.systemBroadcast(systemBroadcast); // Mapped from flag AddrExtensionType (KNX serial(0) or Domain Address(1))
frame.hopCount(hopCount); // Hop count from routing counter
frame.addressType(addressType); // Group address or individual address
frame.rfSerialOrDoA(&rfPacketBuf[4]); // Copy pointer to field Serial or Domain Address (check broadcast flag what it is exactly)
frame.rfInfo(rfPacketBuf[3]); // RF-info field (1 byte)
frame.rfLfn(lfn); // Data link layer frame number (LFN field)
*/
/*
print(" len: ");
print(newLength);
print(" data: ");
printHex(" data: ", _buffer, newLength);
*/
frameRecieved(frame);
}
void UsbDataLinkLayer::enabled(bool value)
{
if (value && !_enabled)
{
usb_hid.enableOutEndpoint(true);
usb_hid.setPollInterval(2);
usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
usb_hid.setReportCallback(NULL, set_report_callback);
usb_hid.begin();
// wait until device mounted
while( !USBDevice.mounted() ) delay(1);
_enabled = true;
print("ownaddr ");
println(_deviceObject.induvidualAddress(), HEX);
return;
}
if (!value && _enabled)
{
println("USB data link layer cannot be disabled once enabled!");
return;
}
}
bool UsbDataLinkLayer::enabled() const
{
return _enabled;
}
void UsbDataLinkLayer::addFrameTxQueue(CemiFrame& frame)
{
_tx_queue_frame_t* tx_frame = new _tx_queue_frame_t;
tx_frame->length = frame.totalLenght();
tx_frame->data = new uint8_t[tx_frame->length];
tx_frame->next = NULL;
/*
print(" len: ");
print(totalLength);
printHex(" data:", tx_frame->data, totalLength);
*/
if (_tx_queue.back == NULL)
{
_tx_queue.front = _tx_queue.back = tx_frame;
}
else
{
_tx_queue.back->next = tx_frame;
_tx_queue.back = tx_frame;
}
}
bool UsbDataLinkLayer::isTxQueueEmpty()
{
if (_tx_queue.front == NULL)
{
return true;
}
return false;
}
void UsbDataLinkLayer::loadNextTxFrame(uint8_t** sendBuffer, uint16_t* sendBufferLength)
{
if (_tx_queue.front == NULL)
{
return;
}
_tx_queue_frame_t* tx_frame = _tx_queue.front;
*sendBuffer = tx_frame->data;
*sendBufferLength = tx_frame->length;
_tx_queue.front = tx_frame->next;
if (_tx_queue.front == NULL)
{
_tx_queue.back = NULL;
}
delete tx_frame;
}

View File

@ -0,0 +1,52 @@
#pragma once
#include <stdint.h>
#include <Adafruit_TinyUSB.h>
#include "data_link_layer.h"
#define MAX_KNX_TELEGRAM_SIZE 263
extern Adafruit_USBD_HID usb_hid;
class UsbDataLinkLayer : public DataLinkLayer
{
using DataLinkLayer::_deviceObject;
using DataLinkLayer::_groupAddressTable;
using DataLinkLayer::_platform;
public:
UsbDataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab, NetworkLayer& layer,
Platform& platform);
void loop();
void enabled(bool value);
bool enabled() const;
private:
bool _enabled = false;
uint8_t _loopState = 0;
uint8_t _buffer[512];
uint8_t _frameNumber = 0;
struct _tx_queue_frame_t
{
uint8_t* data;
uint16_t length;
_tx_queue_frame_t* next;
};
struct _tx_queue_t
{
_tx_queue_frame_t* front = NULL;
_tx_queue_frame_t* back = NULL;
} _tx_queue;
void addFrameTxQueue(CemiFrame& frame);
bool isTxQueueEmpty();
void loadNextTxFrame(uint8_t** sendBuffer, uint16_t* sendBufferLength);
bool sendFrame(CemiFrame& frame);
void frameBytesReceived(uint8_t* buffer, uint16_t length);
};