knx
ETS configurable knx-stack
rf_data_link_layer.cpp
Go to the documentation of this file.
1 #include "config.h"
2 #ifdef USE_RF
3 
4 #if defined(DeviceFamily_CC13X0)
6 #else
8 #endif
9 #include "rf_data_link_layer.h"
10 
11 #include "bits.h"
12 #include "platform.h"
13 #include "device_object.h"
14 #include "address_table_object.h"
15 #include "rf_medium_object.h"
16 #include "cemi_frame.h"
17 
18 #include <stdio.h>
19 #include <string.h>
20 
22 {
23  if (!_enabled)
24  return;
25 
26  _rfPhy.loop();
27 }
28 
29 bool RfDataLinkLayer::sendFrame(CemiFrame& frame)
30 {
31  // If no serial number of domain address was set,
32  // use our own SN/DoA
33  if (frame.rfSerialOrDoA() == nullptr)
34  {
35  // Depending on this flag, use either KNX Serial Number
36  // or the RF domain address that was programmed by ETS
37  if (frame.systemBroadcast() == SysBroadcast)
38  {
39  frame.rfSerialOrDoA((uint8_t*)_deviceObject.propertyData(PID_SERIAL_NUMBER));
40  }
41  else
42  {
43  frame.rfSerialOrDoA(_rfMediumObj.rfDomainAddress());
44  }
45  }
46 
47  // If Link Layer frame is set to 0xFF,
48  // use our own counter
49  if (frame.rfLfn() == 0xFF)
50  {
51  // Set Data Link Layer Frame Number
52  frame.rfLfn(_frameNumber);
53  // Link Layer frame number counts 0..7
54  _frameNumber = (_frameNumber + 1) & 0x7;
55  }
56 
57  // bidirectional device, battery is ok, signal strength indication is void (no measurement)
58  frame.rfInfo(0x02);
59 
60  if (!_enabled)
61  {
62  dataConReceived(frame, false);
63  return false;
64  }
65 
66  // TODO: Is queueing really required?
67  // According to the spec. the upper layer may only send a new L_Data.req if it received
68  // the L_Data.con for the previous L_Data.req.
69  addFrameTxQueue(frame);
70 
71  // TODO: For now L_data.req is confirmed immediately (L_Data.con)
72  // see 3.6.3 p.80: L_Data.con shall be generated AFTER transmission of the corresponsing frame
73  // RF sender will never generate L_Data.con with C=1 (Error), but only if the TX buffer overflows
74  // The RF sender cannot detect if the RF frame was transmitted successfully or not according to the spec.
75  dataConReceived(frame, true);
76 
77  return true;
78 }
79 
81  NetworkLayerEntity& netLayerEntity, Platform& platform)
82  : DataLinkLayer(devObj, netLayerEntity, platform),
83  _rfMediumObj(rfMediumObj),
84  _rfPhy(*this, platform)
85 {
86 }
87 
88 void RfDataLinkLayer::frameBytesReceived(uint8_t* rfPacketBuf, uint16_t length)
89 {
90  // RF data link layer frame format
91  // See 3.2.5 p.22
92 
93  // First block + smallest KNX telegram will give a minimum size of 22 bytes with checksum bytes
94  if (length < 21)
95  {
96  print("Received packet is too small. length: ");
97  println(length);
98  return;
99  }
100 
101 #if defined(DeviceFamily_CC13X0)
102  // Small optimization:
103  // We do not calculate the CRC16-DNP again for the first block.
104  // It was already done in the CC13x0 RX driver during reception.
105  // Also the two fixed bytes 0x44 and 0xFF are also there.
106  // So if we get here we can assume a valid block 1
107 #else
108  // CRC16-DNP of first block is always located here
109  uint16_t block1Crc = rfPacketBuf[10] << 8 | rfPacketBuf[11];
110 
111  // If the checksum was ok and the other
112  // two constant header bytes match the KNX-RF spec. (C-field: 0x44 and ESC-field: 0xFF)...
113  // then we seem to have a valid first block of an KNX RF frame.
114  // The first block basically contains the RF-info field and the KNX SN/Domain address.
115  if ((rfPacketBuf[1] == 0x44) &&
116  (rfPacketBuf[2] == 0xFF) &&
117  (crc16Dnp(rfPacketBuf, 10) == block1Crc))
118 #endif
119  {
120  // bytes left from the remaining block(s)
121  uint16_t bytesLeft = length - 12;
122  // we use two pointers to move over the two buffers
123  uint8_t* pRfPacketBuf = &rfPacketBuf[12]; // pointer to start of RF frame block 2 (with CTRL field)
124  // Reserve 1 byte (+1) for the second ctrl field
125  // cEMI frame has two CTRL fields, but RF frame has only one, but uses ALWAYS extended frames
126  // Information for BOTH cEMI CTRL fields is distributed in a RF frame (RF CTRL field and RF L/NPCI field)
127  // So we cannot just copy an RF frame with CTRL fields as is
128  // KNX RF frame will be placed starting at cEMI CTRL2 field (so RF CTRL field is CTRL2 field cEMI)
129  uint8_t* pBuffer = &_buffer[CEMI_HEADER_SIZE + 1];
130  // New length of the packet with CRC bytes removed, add space for CEMI header and the second CTRL field
131  uint16_t newLength = CEMI_HEADER_SIZE + 1;
132 
133  // Now check each block checksum and copy the payload of the block
134  // into a new buffer without checksum
135  uint16_t blockCrc;
136  bool crcOk = true;
137 
138  while (bytesLeft > 18)
139  {
140  // Get CRC16 from end of the block
141  blockCrc = pRfPacketBuf[16] << 8 | pRfPacketBuf[17];
142 
143  if (crc16Dnp(pRfPacketBuf, 16) == blockCrc)
144  {
145  // Copy only the payload without the checksums
146  memcpy(pBuffer, pRfPacketBuf, 16);
147  }
148  else
149  {
150  crcOk = false;
151  break;
152  }
153 
154  pBuffer += 16;
155  pRfPacketBuf += 18;
156  newLength += 16;
157  bytesLeft -= 18;
158  }
159 
160  // Now process the last block
161  blockCrc = pRfPacketBuf[bytesLeft - 2] << 8 | pRfPacketBuf[bytesLeft - 1];
162  crcOk = crcOk && (crc16Dnp(&pRfPacketBuf[0], bytesLeft - 2) == blockCrc);
163 
164  // If all checksums were ok, then...
165  if (crcOk)
166  {
167  // Copy rest of the received packet without checksum
168  memcpy(pBuffer, pRfPacketBuf, bytesLeft - 2);
169  newLength += bytesLeft - 2;
170 
171  // Prepare CEMI by writing/overwriting certain fields in the buffer (contiguous frame without CRC checksums)
172  // See 3.6.3 p.79: L_Data services for KNX RF asynchronous frames
173  // For now we do not use additional info, but use normal method arguments for CEMI
174  _buffer[0] = (uint8_t) L_data_ind; // L_data.ind
175  _buffer[1] = 0; // Additional info length (spec. says that local dev management is not required to use AddInfo internally)
176  _buffer[2] = 0; // CTRL1 field (will be set later, this is the field we reserved space for)
177  _buffer[3] &= 0x0F; // CTRL2 field (take only RFCtrl.b3..0, b7..4 shall always be 0 for asynchronous KNX RF)
178 
179  // Now get all control bits from the L/NPCI field of the RF frame
180  // so that we can overwrite it afterwards with the correct NPDU length
181  // Get data link layer frame number (LFN field) from L/NPCI.LFN (bit 3..1)
182  uint8_t lfn = (_buffer[8] & 0x0E) >> 1;
183  // Get address type from L/NPCI.LFN (bit 7)
184  AddressType addressType = (_buffer[8] & 0x80) ? GroupAddress : IndividualAddress;
185  // Get routing counter from L/NPCI.LFN (bit 6..4) and map to hop count in Ctrl2.b6-4
186  uint8_t hopCount = (_buffer[8] & 0x70) >> 4;
187  // Get AddrExtensionType from L/NPCI.LFN (bit 7) and map to system broadcast flag in Ctrl1.b4
188  SystemBroadcast systemBroadcast = (_buffer[8] & 0x01) ? Broadcast : SysBroadcast;
189 
190  // Setup L field of the cEMI frame with the NPDU length
191  // newLength -8 bytes (NPDU_LPDU_DIFF, no AddInfo) -1 byte length field -1 byte TPCI/APCI bits
192  _buffer[8] = newLength - NPDU_LPDU_DIFF - 1 - 1;
193 
194  // If we have a broadcast message (within the domain),
195  // then we received the domain address and not the KNX serial number
196  if (systemBroadcast == Broadcast)
197  {
198  // Check if the received RF domain address matches the one stored in the RF medium object
199  // If it does not match then skip the remaining processing
200  if (memcmp(_rfMediumObj.rfDomainAddress(), &rfPacketBuf[4], 6))
201  {
202  println("RX domain address does not match. Skipping...");
203  return;
204  }
205  }
206 
207  // TODO
208  // Frame duplication prevention based on LFN (see KKNX RF spec. 3.2.5 p.28)
209 
210  // Prepare the cEMI frame
211  CemiFrame frame(_buffer, newLength);
212  frame.frameType(ExtendedFrame); // KNX RF uses only extended frame format
213  frame.priority(SystemPriority); // Not used in KNX RF
214  frame.ack(AckDontCare); // Not used in KNX RF
215  frame.systemBroadcast(systemBroadcast); // Mapped from flag AddrExtensionType (KNX serial(0) or Domain Address(1))
216  frame.hopCount(hopCount); // Hop count from routing counter
217  frame.addressType(addressType); // Group address or individual address
218  frame.rfSerialOrDoA(&rfPacketBuf[4]); // Copy pointer to field Serial or Domain Address (check broadcast flag what it is exactly)
219  frame.rfInfo(rfPacketBuf[3]); // RF-info field (1 byte)
220  frame.rfLfn(lfn); // Data link layer frame number (LFN field)
221  /*
222  print("RX LFN: ");
223  print(lfn);
224  print(" len: ");
225  print(newLength);
226 
227  print(" data: ");
228  printHex(" data: ", _buffer, newLength);
229  */
230  frameReceived(frame);
231  }
232  }
233 }
234 
235 void RfDataLinkLayer::enabled(bool value)
236 {
237  if (value && !_enabled)
238  {
239  if (_rfPhy.InitChip())
240  {
241  _enabled = true;
242  print("ownaddr ");
243  println(_deviceObject.individualAddress(), HEX);
244  }
245  else
246  {
247  _enabled = false;
248  println("ERROR, RF transceiver not responding");
249  }
250 
251  return;
252  }
253 
254  if (!value && _enabled)
255  {
256  _rfPhy.stopChip();
257  _enabled = false;
258  return;
259  }
260 }
261 
263 {
264  return _enabled;
265 }
266 
268 {
269  return DptMedium::KNX_RF;
270 }
271 
272 void RfDataLinkLayer::fillRfFrame(CemiFrame& frame, uint8_t* data)
273 {
274  uint16_t crc;
275  uint16_t length = frame.telegramLengthtRF();
276 
277  data[0] = 9 + length; // Length block1 (always 9 bytes, without length itself) + Length of KNX telegram without CRCs
278  data[1] = 0x44; // C field: According to IEC870-5. KNX only uses SEND/NO REPLY (C = 44h)
279  data[2] = 0xFF; // ESC field: This field shall have the fixed value FFh.
280  data[3] = frame.rfInfo(); // RF-info field
281 
282  // Generate CRC16-DNP over the first block of data
283  pushByteArray(frame.rfSerialOrDoA(), 6, &data[4]);
284  crc = crc16Dnp(&data[0], 10);
285  pushWord(crc, &data[10]);
286 
287  // Put the complete KNX telegram into a temporary buffer
288  // as we have to add CRC16 checksums after each block of 16 bytes
289  frame.fillTelegramRF(_buffer);
290 
291  // Create a checksum for each block of full 16 bytes
292  uint16_t bytesLeft = length;
293  uint8_t* pBuffer = &_buffer[0];
294  uint8_t* pData = &data[12];
295 
296  while (bytesLeft > 16)
297  {
298  memcpy(pData, pBuffer, 16);
299  crc = crc16Dnp(pData, 16);
300  pushWord(crc, &pData[16]);
301 
302  pBuffer += 16;
303  pData += 18;
304  bytesLeft -= 16;
305  }
306 
307  // Copy remaining bytes of last block. Could be less than 16 bytes
308  memcpy(pData, pBuffer, bytesLeft);
309  // And add last CRC
310  crc = crc16Dnp(pData, bytesLeft);
311  pushWord(crc, &pData[bytesLeft]);
312 }
313 
314 void RfDataLinkLayer::addFrameTxQueue(CemiFrame& frame)
315 {
316  _tx_queue_frame_t* tx_frame = new _tx_queue_frame_t;
317 
318  uint16_t length = frame.telegramLengthtRF(); // Just the pure KNX telegram from CTRL field until end of APDU
319  uint8_t nrFullBlocks = length / 16; // Number of full (16 bytes) RF blocks required
320  uint8_t bytesLeft = length % 16; // Remaining bytes of the last packet
321 
322  // Calculate total number of bytes required to store the complete raw RF frame
323  // Block1 always requires 12 bytes including Length and CRC
324  // Each full block has 16 bytes payload plus 2 bytes CRC
325  // Add remaining bytes of the last block and add 2 bytes for CRC
326  uint16_t totalLength = 12 + (nrFullBlocks * 18) + bytesLeft + 2;
327 
328  tx_frame->length = totalLength;
329  tx_frame->data = new uint8_t[tx_frame->length];
330  tx_frame->next = NULL;
331 
332  // Prepare the raw RF frame
333  fillRfFrame(frame, tx_frame->data);
334 
335  /*
336  print("TX LFN: ");
337  print(frame.rfLfn());
338 
339  print(" len: ");
340  print(totalLength);
341 
342  printHex(" data:", tx_frame->data, totalLength);
343  */
344  if (_tx_queue.back == NULL)
345  {
346  _tx_queue.front = _tx_queue.back = tx_frame;
347  }
348  else
349  {
350  _tx_queue.back->next = tx_frame;
351  _tx_queue.back = tx_frame;
352  }
353 }
354 
355 bool RfDataLinkLayer::isTxQueueEmpty()
356 {
357  if (_tx_queue.front == NULL)
358  {
359  return true;
360  }
361 
362  return false;
363 }
364 
365 void RfDataLinkLayer::loadNextTxFrame(uint8_t** sendBuffer, uint16_t* sendBufferLength)
366 {
367  if (_tx_queue.front == NULL)
368  {
369  return;
370  }
371 
372  _tx_queue_frame_t* tx_frame = _tx_queue.front;
373  *sendBuffer = tx_frame->data;
374  *sendBufferLength = tx_frame->length;
375  _tx_queue.front = tx_frame->next;
376 
377  if (_tx_queue.front == NULL)
378  {
379  _tx_queue.back = NULL;
380  }
381 
382  delete tx_frame;
383 }
384 
385 #endif
void print(const char *s)
void println(const char *s)
uint16_t crc16Dnp(uint8_t *input, uint16_t length)
Definition: bits.cpp:153
uint8_t * pushByteArray(const uint8_t *src, uint32_t size, uint8_t *data)
Definition: bits.cpp:82
uint8_t * pushWord(uint16_t w, uint8_t *data)
Definition: bits.cpp:64
void fillTelegramRF(uint8_t *data)
Definition: cemi_frame.cpp:176
SystemBroadcast systemBroadcast() const
Definition: cemi_frame.cpp:237
uint8_t * rfSerialOrDoA() const
Definition: cemi_frame.cpp:327
uint8_t hopCount() const
Definition: cemi_frame.cpp:292
AckType ack() const
Definition: cemi_frame.cpp:259
uint8_t rfLfn() const
Definition: cemi_frame.cpp:347
uint8_t rfInfo() const
Definition: cemi_frame.cpp:337
AddressType addressType() const
Definition: cemi_frame.cpp:281
FrameFormat frameType() const
Definition: cemi_frame.cpp:215
Priority priority() const
Definition: cemi_frame.cpp:248
uint16_t telegramLengthtRF() const
Definition: cemi_frame.cpp:171
uint16_t individualAddress()
const uint8_t * propertyData(PropertyID id)
const uint8_t * rfDomainAddress()
@ SystemPriority
Mainly used by ETS for device programming.
Definition: knx_types.h:14
@ ExtendedFrame
Definition: knx_types.h:5
@ L_data_ind
Definition: knx_types.h:43
@ AckDontCare
We don't care about DataLinkLayer acknowledgement.
Definition: knx_types.h:19
DptMedium
Definition: knx_types.h:254
@ KNX_RF
Definition: knx_types.h:259
AddressType
Definition: knx_types.h:33
@ GroupAddress
Definition: knx_types.h:35
@ IndividualAddress
Definition: knx_types.h:34
SystemBroadcast
Definition: knx_types.h:112
@ Broadcast
Definition: knx_types.h:114
@ SysBroadcast
Definition: knx_types.h:113
@ PID_SERIAL_NUMBER
Definition: property.h:78