From 619c6383d265aeb5d430b9a655aeaa5a1b659b4f Mon Sep 17 00:00:00 2001 From: nanosonde <2073569+nanosonde@users.noreply.github.com> Date: Sat, 16 Nov 2019 15:25:35 +0100 Subject: [PATCH] Save work --- src/knx/cemi_frame.cpp | 11 ++ src/knx/cemi_frame.h | 2 + src/knx/cemi_server.cpp | 283 +++++++++++------------------ src/knx/cemi_server.h | 6 + src/knx/data_link_layer.cpp | 41 ++++- src/knx/data_link_layer.h | 4 +- src/knx/rf_data_link_layer.cpp | 5 +- src/knx/tpuart_data_link_layer.cpp | 3 + src/knx/usb_data_link_layer.cpp | 4 +- 9 files changed, 169 insertions(+), 190 deletions(-) diff --git a/src/knx/cemi_frame.cpp b/src/knx/cemi_frame.cpp index a0135ff..2448afb 100644 --- a/src/knx/cemi_frame.cpp +++ b/src/knx/cemi_frame.cpp @@ -259,6 +259,17 @@ void CemiFrame::ack(AckType value) _ctrl1[0] |= value; } +Confirm CemiFrame::confirm() const +{ + return (Confirm)(_ctrl1[0] & ConfirmError); +} + +void CemiFrame::confirm(Confirm value) +{ + _ctrl1[0] &= ~ConfirmError; + _ctrl1[0] |= value; +} + AddressType CemiFrame::addressType() const { return (AddressType)(_ctrl1[1] & GroupAddress); diff --git a/src/knx/cemi_frame.h b/src/knx/cemi_frame.h index 2a8e8ac..49e80b8 100644 --- a/src/knx/cemi_frame.h +++ b/src/knx/cemi_frame.h @@ -45,6 +45,8 @@ class CemiFrame void priority(Priority value); AckType ack() const; void ack(AckType value); + Confirm confirm() const; + void confirm(Confirm value); AddressType addressType() const; void addressType(AddressType value); uint8_t hopCount() const; diff --git a/src/knx/cemi_server.cpp b/src/knx/cemi_server.cpp index 6a94618..1e3d303 100644 --- a/src/knx/cemi_server.cpp +++ b/src/knx/cemi_server.cpp @@ -13,6 +13,9 @@ CemiServer::CemiServer(BauSystemB& bau) _bau.deviceObject().maskVersion(), _bau.deviceObject().manufacturerId()) { + // The cEMI server will hand out the device address + 1 to the cEMI client (e.g. ETS), + // so that the device and the cEMI client/server connection(tunnel) can operate simultaneously. + _clientAddress = _bau.deviceObject().induvidualAddress() + 1; } void CemiServer::dataLinkLayer(DataLinkLayer& layer) @@ -20,10 +23,52 @@ void CemiServer::dataLinkLayer(DataLinkLayer& layer) _dataLinkLayer = &layer; } +uint16_t CemiServer::clientAddress() const +{ + return _clientAddress; +} + +void CemiServer::clientAddress(uint16_t value) +{ + _clientAddress = value; +} + +void CemiServer::dataConfirmationToTunnel(CemiFrame& frame) +{ + print("L_data_con: src: "); + print(frame.sourceAddress(), HEX); + print(" dst: "); + print(frame.destinationAddress(), HEX); + + printHex(" frame: ", frame.data(), frame.dataLength()); + + _usbTunnelInterface.sendCemiFrame(frame); +} + void CemiServer::dataIndicationToTunnel(CemiFrame& frame) { - println("L_data_ind: "); - _usbTunnelInterface.sendCemiFrame(frame); + static uint8_t lfn = 0; + uint8_t data[frame.dataLength() + 10]; + data[0] = L_data_ind; + data[1] = 10; + data[2] = 0x02; // RF Info add. info + data[3] = 0x08; // RF info length + data[4] = 0x02; // RF info field (batt ok) + pushByteArray(_bau.deviceObject().rfDomainAddress(), 6, &data[5]); + data[11] = lfn; + lfn = (lfn + 1) & 0x7; + memcpy(&data[12], &frame.data()[2], frame.dataLength() - 2); + + CemiFrame tmpFrame(data, sizeof(data)); + + print("L_data_ind: src: "); + print(tmpFrame.sourceAddress(), HEX); + print(" dst: "); + print(tmpFrame.destinationAddress(), HEX); + + printHex(" frame: ", tmpFrame.data(), tmpFrame.dataLength()); + + _usbTunnelInterface.sendCemiFrame(tmpFrame); } /* @@ -42,15 +87,21 @@ void CemiServer::frameReceived(CemiFrame& frame) { case L_data_req: { - println("L_data_req: "); + // Fill in the cEMI client address if the client sets + // source address to 0. + if(frame.sourceAddress() == 0x0000) + { + frame.sourceAddress(_clientAddress); + } - // Send as indication to data link layer - frame.messageCode(L_data_ind); - _dataLinkLayer->dataIndicationFromTunnel(frame); - - // Send local reply to tunnel - frame.messageCode(L_data_con); - _usbTunnelInterface.sendCemiFrame(frame); + print("L_data_req: src: "); + print(frame.sourceAddress(), HEX); + print(" dst: "); + print(frame.destinationAddress(), HEX); + + printHex(" frame: ", frame.data(), frame.dataLength()); + + _dataLinkLayer->dataRequestFromTunnel(frame); break; } @@ -81,6 +132,24 @@ void CemiServer::frameReceived(CemiFrame& frame) // propertyValueRead() allocates memory for the data! Needs to be deleted again! _bau.propertyValueRead((ObjectType)objectType, objectInstance, propertyId, numberOfElements, startIndex, &data, dataSize); + // Patch result for device address in device object + // The cEMI server will hand out the device address + 1 to the cEMI client (e.g. ETS), + // so that the device and the cEMI client/server connection(tunnel) can operate simultaneously. + // KNX IP Interfaces which offer multiple simultaneous tunnel connections seem to operate the same way. + // Each tunnel has its own cEMI client address which is based on the main device address. + if (((ObjectType) objectType == OT_DEVICE) && + (propertyId == PID_DEVICE_ADDR) && + (numberOfElements == 1)) + { + data[0] = (uint8_t) (_clientAddress & 0xFF); + } + else if (((ObjectType) objectType == OT_DEVICE) && + (propertyId == PID_SUBNET_ADDR) && + (numberOfElements == 1)) + { + data[0] = (uint8_t) ((_clientAddress >> 8) & 0xFF); + } + if (data && dataSize && numberOfElements) { printHex(" <- data: ", data, dataSize); @@ -95,7 +164,7 @@ void CemiServer::frameReceived(CemiFrame& frame) responseFrame.messageCode(M_PropRead_con); _usbTunnelInterface.sendCemiFrame(responseFrame); - delete data; + delete[] data; } else { @@ -141,7 +210,31 @@ void CemiServer::frameReceived(CemiFrame& frame) printHex(" -> data: ", requestData, requestDataSize); - _bau.propertyValueWrite((ObjectType)objectType, objectInstance, propertyId, numberOfElements, startIndex, requestData, requestDataSize); + // Patch request for device address in device object + if (((ObjectType) objectType == OT_DEVICE) && + (propertyId == PID_DEVICE_ADDR) && + (numberOfElements == 1)) + { + // Temporarily store new cEMI client address in memory + // We also be sent back if the client requests it again + _clientAddress = (_clientAddress & 0xFF00) | requestData[0]; + print("cEMI client address: "); + println(_clientAddress, HEX); + } + else if (((ObjectType) objectType == OT_DEVICE) && + (propertyId == PID_SUBNET_ADDR) && + (numberOfElements == 1)) + { + // Temporarily store new cEMI client address in memory + // We also be sent back if the client requests it again + _clientAddress = (_clientAddress & 0x00FF) | (requestData[0] << 8); + print("cEMI client address: "); + println(_clientAddress, HEX); + } + else + { + _bau.propertyValueWrite((ObjectType)objectType, objectInstance, propertyId, numberOfElements, startIndex, requestData, requestDataSize); + } if (numberOfElements) { @@ -214,169 +307,3 @@ void CemiServer::loop() { _usbTunnelInterface.loop(); } - -/* -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; - } -} - -*/ \ No newline at end of file diff --git a/src/knx/cemi_server.h b/src/knx/cemi_server.h index a78b95c..0b8646c 100644 --- a/src/knx/cemi_server.h +++ b/src/knx/cemi_server.h @@ -31,13 +31,19 @@ class CemiServer // from data link layer // Only L_Data service void dataIndicationToTunnel(CemiFrame& frame); + void dataConfirmationToTunnel(CemiFrame& frame); // From tunnel interface void frameReceived(CemiFrame& frame); + uint16_t clientAddress() const; + void clientAddress(uint16_t value); + void loop(); private: + uint16_t _clientAddress; + DataLinkLayer* _dataLinkLayer; BauSystemB& _bau; UsbDataLinkLayer _usbTunnelInterface; diff --git a/src/knx/data_link_layer.cpp b/src/knx/data_link_layer.cpp index 86598e7..78d237a 100644 --- a/src/knx/data_link_layer.cpp +++ b/src/knx/data_link_layer.cpp @@ -17,21 +17,27 @@ void DataLinkLayer::cemiServer(CemiServer& cemiServer) _cemiServer = &cemiServer; } -void DataLinkLayer::dataIndicationFromTunnel(CemiFrame& frame) +void DataLinkLayer::dataRequestFromTunnel(CemiFrame& frame) { uint16_t destination = frame.destinationAddress(); uint16_t ownAddr = _deviceObject.induvidualAddress(); AddressType addrType = frame.addressType(); + frame.messageCode(L_data_con); + _cemiServer->dataConfirmationToTunnel(frame); + + frame.messageCode(L_data_ind); + if (addrType == InduvidualAddress) { - // TODO: check if this is correct: shall we send a frame to the bus too if it was intended for us? if (destination == ownAddr) { // Send to local stack frameRecieved(frame); + // Send back a confirmation + //dataConReceived(frame, true); } - else + else // TODO: check if this is correct: shall we send a frame to the bus too if it was intended for us? { // Send to KNX medium sendFrame(frame); @@ -63,6 +69,8 @@ void DataLinkLayer::systemBroadcastRequest(AckType ack, FrameFormat format, Prio void DataLinkLayer::dataConReceived(CemiFrame& frame, bool success) { + frame.messageCode(L_data_con); + frame.confirm(success ? ConfirmNoError : ConfirmError); AckType ack = frame.ack(); AddressType addrType = frame.addressType(); uint16_t destination = frame.destinationAddress(); @@ -72,6 +80,17 @@ void DataLinkLayer::dataConReceived(CemiFrame& frame, bool success) NPDU& npdu = frame.npdu(); SystemBroadcast systemBroadcast = frame.systemBroadcast(); + if (_cemiServer) + { + // Only send our own confirmation messages to the tunnel + if (frame.sourceAddress() == _cemiServer->clientAddress()) + { + //_cemiServer->dataConfirmationToTunnel(frame); + // Stop processing here and do NOT send it the local network layer + return; + } + } + if (addrType == GroupAddress && destination == 0) if (systemBroadcast == SysBroadcast) _networkLayer.systemBroadcastConfirm(ack, type, priority, source, npdu, success); @@ -79,9 +98,8 @@ void DataLinkLayer::dataConReceived(CemiFrame& frame, bool success) _networkLayer.broadcastConfirm(ack, type, priority, source, npdu, success); else _networkLayer.dataConfirm(ack, addrType, destination, type, priority, source, npdu, success); - - } + void DataLinkLayer::frameRecieved(CemiFrame& frame) { AckType ack = frame.ack(); @@ -96,8 +114,11 @@ void DataLinkLayer::frameRecieved(CemiFrame& frame) if (_cemiServer) { -// if (source != _cemiServerObj.tunnelAddress) - _cemiServer->dataIndicationToTunnel(frame); + // Do not send our own message back to the tunnel + if (frame.sourceAddress() != _cemiServer->clientAddress()) + { + _cemiServer->dataIndicationToTunnel(frame); + } } if (source == ownAddr) @@ -157,6 +178,12 @@ bool DataLinkLayer::sendTelegram(NPDU & npdu, AckType ack, uint16_t destinationA // frame.apdu().printPDU(); // } + if (_cemiServer) + { + CemiFrame tmpFrame(frame.data(), frame.totalLenght()); + _cemiServer->dataIndicationToTunnel(tmpFrame); + } + return sendFrame(frame); } diff --git a/src/knx/data_link_layer.h b/src/knx/data_link_layer.h index 5674ce6..0ae948b 100644 --- a/src/knx/data_link_layer.h +++ b/src/knx/data_link_layer.h @@ -13,9 +13,9 @@ class DataLinkLayer DataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab, NetworkLayer& layer, Platform& platform); + // from tunnel void cemiServer(CemiServer& cemiServer); - - void dataIndicationFromTunnel(CemiFrame& frame); + void dataRequestFromTunnel(CemiFrame& frame); // from network layer void dataRequest(AckType ack, AddressType addrType, uint16_t destinationAddr, FrameFormat format, diff --git a/src/knx/rf_data_link_layer.cpp b/src/knx/rf_data_link_layer.cpp index f4b6ff6..699c56e 100644 --- a/src/knx/rf_data_link_layer.cpp +++ b/src/knx/rf_data_link_layer.cpp @@ -24,7 +24,10 @@ void RfDataLinkLayer::loop() bool RfDataLinkLayer::sendFrame(CemiFrame& frame) { if (!_enabled) + { + dataConReceived(frame, false); return false; + } // Depending on this flag, use either KNX Serial Number // or the RF domain address that was programmed by ETS @@ -166,7 +169,7 @@ void RfDataLinkLayer::frameBytesReceived(uint8_t* rfPacketBuf, uint16_t length) // Prepare CEMI by writing/overwriting certain fields in the buffer (contiguous frame without CRC checksums) // See 3.6.3 p.79: L_Data services for KNX RF asynchronous frames // For now we do not use additional info, but use normal method arguments for CEMI - _buffer[0] = 0x29; // L_data.ind + _buffer[0] = (uint8_t) L_data_ind; // L_data.ind _buffer[1] = 0; // Additional info length (spec. says that local dev management is not required to use AddInfo internally) _buffer[2] = 0; // CTRL1 field (will be set later, this is the field we reserved space for) _buffer[3] &= 0x0F; // CTRL2 field (take only RFCtrl.b3..0, b7..4 shall always be 0 for asynchronous KNX RF) diff --git a/src/knx/tpuart_data_link_layer.cpp b/src/knx/tpuart_data_link_layer.cpp index 48fef44..1c5ba5b 100644 --- a/src/knx/tpuart_data_link_layer.cpp +++ b/src/knx/tpuart_data_link_layer.cpp @@ -364,7 +364,10 @@ void TpUartDataLinkLayer::loop() bool TpUartDataLinkLayer::sendFrame(CemiFrame& frame) { if (!_enabled) + { + dataConReceived(frame, false); return false; + } addFrameTxQueue(frame); return true; diff --git a/src/knx/usb_data_link_layer.cpp b/src/knx/usb_data_link_layer.cpp index 09a7344..09be3f4 100644 --- a/src/knx/usb_data_link_layer.cpp +++ b/src/knx/usb_data_link_layer.cpp @@ -207,7 +207,7 @@ static void handleBusAccessServerProtocol(const uint8_t* requestData, uint16_t p { 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((packetLength) + respDataSize, DEC); @@ -219,7 +219,7 @@ static void handleBusAccessServerProtocol(const uint8_t* requestData, uint16_t p Serial1.print(" "); } Serial1.println(""); - +*/ usb_hid.sendReport(0, data, MAX_EP_SIZE); } }