knx
ETS configurable knx-stack
memory.cpp
Go to the documentation of this file.
1 #include "memory.h"
2 
3 #include <string.h>
4 
5 #include "bits.h"
6 
7 Memory::Memory(Platform& platform, DeviceObject& deviceObject)
8  : _platform(platform), _deviceObject(deviceObject)
9 {}
10 
12 {}
13 
15 {
16  println("readMemory");
17 
18  uint8_t* flashStart = _platform.getNonVolatileMemoryStart();
19  size_t flashSize = _platform.getNonVolatileMemorySize();
20 
21  if (flashStart == nullptr)
22  {
23  println("no user flash available;");
24  return;
25  }
26 
27  printHex("RESTORED ", flashStart, _metadataSize);
28 
29  uint16_t metadataBlockSize = alignToPageSize(_metadataSize);
30 
31  _freeList = new MemoryBlock(flashStart + metadataBlockSize, flashSize - metadataBlockSize);
32 
33  uint16_t apiVersion = 0;
34  const uint8_t* buffer = popWord(apiVersion, flashStart);
35 
36  uint16_t manufacturerId = 0;
37  buffer = popWord(manufacturerId, buffer);
38 
39  uint8_t hardwareType[LEN_HARDWARE_TYPE] = {0};
40  buffer = popByteArray(hardwareType, LEN_HARDWARE_TYPE, buffer);
41 
42  uint16_t version = 0;
43  buffer = popWord(version, buffer);
44 
45  VersionCheckResult versionCheck = FlashAllInvalid;
46 
47  // first check correct format of deviceObject-API
48  if (_deviceObject.apiVersion == apiVersion)
49  {
50  if (_versionCheckCallback != 0)
51  {
52  versionCheck = _versionCheckCallback(manufacturerId, hardwareType, version);
53  // callback should provide infomation about version check failure reasons
54  }
55  else if (_deviceObject.manufacturerId() == manufacturerId &&
56  memcmp(_deviceObject.hardwareType(), hardwareType, LEN_HARDWARE_TYPE) == 0)
57  {
58  if (_deviceObject.version() == version)
59  {
60  versionCheck = FlashValid;
61  }
62  else
63  {
64  versionCheck = FlashTablesInvalid;
65  }
66  }
67  else
68  {
69  println("manufacturerId or hardwareType are different");
70  print("expexted manufacturerId: ");
71  print(_deviceObject.manufacturerId(), HEX);
72  print(", stored manufacturerId: ");
73  println(manufacturerId, HEX);
74  print("expexted hardwareType: ");
75  printHex("", _deviceObject.hardwareType(), LEN_HARDWARE_TYPE);
76  print(", stored hardwareType: ");
77  printHex("", hardwareType, LEN_HARDWARE_TYPE);
78  println("");
79  }
80  }
81  else
82  {
83  println("DataObject api changed, any data stored in flash is invalid.");
84  print("expexted DataObject api version: ");
85  print(_deviceObject.apiVersion, HEX);
86  print(", stored api version: ");
87  println(apiVersion, HEX);
88  }
89 
90  if (versionCheck == FlashAllInvalid)
91  {
92  println("ETS has to reprogram PA and application!");
93  return;
94  }
95 
96  println("restoring data from flash...");
97  print("saverestores ");
98  println(_saveCount);
99 
100  for (int i = 0; i < _saveCount; i++)
101  {
102  println(flashStart - buffer);
103  println(".");
104  buffer = _saveRestores[i]->restore(buffer);
105  }
106 
107  println("restored saveRestores");
108 
109  if (versionCheck == FlashTablesInvalid)
110  {
111  println("TableObjects are referring to an older firmware version and are not loaded");
112  return;
113  }
114 
115  print("tableObjs ");
116  println(_tableObjCount);
117 
118  for (int i = 0; i < _tableObjCount; i++)
119  {
120  println(flashStart - buffer);
121  println(".");
122  buffer = _tableObjects[i]->restore(buffer);
123  uint16_t memorySize = 0;
124  buffer = popWord(memorySize, buffer);
125  println(memorySize);
126 
127  if (memorySize == 0)
128  continue;
129 
130  // this works because TableObject saves a relative addr and restores it itself
131  addNewUsedBlock(_tableObjects[i]->_data, memorySize);
132  }
133 
134  println("restored Tableobjects");
135 }
136 
138 {
139  // first get the necessary size of the writeBuffer
140  uint16_t writeBufferSize = _metadataSize;
141 
142  for (int i = 0; i < _saveCount; i++)
143  writeBufferSize = MAX(writeBufferSize, _saveRestores[i]->saveSize());
144 
145  for (int i = 0; i < _tableObjCount; i++)
146  writeBufferSize = MAX(writeBufferSize, _tableObjects[i]->saveSize() + 2 /*for memory pos*/);
147 
148  uint8_t buffer[writeBufferSize];
149  uint32_t flashPos = 0;
150  uint8_t* bufferPos = buffer;
151 
152  bufferPos = pushWord(_deviceObject.apiVersion, bufferPos);
153  bufferPos = pushWord(_deviceObject.manufacturerId(), bufferPos);
154  bufferPos = pushByteArray(_deviceObject.hardwareType(), LEN_HARDWARE_TYPE, bufferPos);
155  bufferPos = pushWord(_deviceObject.version(), bufferPos);
156 
157  flashPos = _platform.writeNonVolatileMemory(flashPos, buffer, bufferPos - buffer);
158 
159  print("save saveRestores ");
160  println(_saveCount);
161 
162  for (int i = 0; i < _saveCount; i++)
163  {
164  bufferPos = _saveRestores[i]->save(buffer);
165  flashPos = _platform.writeNonVolatileMemory(flashPos, buffer, bufferPos - buffer);
166  }
167 
168  print("save tableobjs ");
169  println(_tableObjCount);
170 
171  for (int i = 0; i < _tableObjCount; i++)
172  {
173  bufferPos = _tableObjects[i]->save(buffer);
174 
175  //save to size of the memoryblock for tableobject too, so that we can rebuild the usedList and freeList
176  if (_tableObjects[i]->_data != nullptr)
177  {
178  MemoryBlock* block = findBlockInList(_usedList, _tableObjects[i]->_data);
179 
180  if (block == nullptr)
181  {
182  println("_data of TableObject not in _usedList");
183  clearMemory();
184  _platform.fatalError();
185  }
186 
187  bufferPos = pushWord(block->size, bufferPos);
188  }
189  else
190  bufferPos = pushWord(0, bufferPos);
191 
192  flashPos = _platform.writeNonVolatileMemory(flashPos, buffer, bufferPos - buffer);
193  }
194 
195  _platform.commitNonVolatileMemory();
196 }
197 
199 {
200  _platform.commitNonVolatileMemory();
201 }
202 
204 {
205  _platform.writeNonVolatileMemory(0, 0xFF, _metadataSize);
206  _platform.commitNonVolatileMemory();
207 }
208 
210 {
211  if (_saveCount >= MAXSAVE - 1)
212  return;
213 
214  _saveRestores[_saveCount] = obj;
215  _saveCount += 1;
216  _metadataSize += obj->saveSize();
217 }
218 
220 {
221  if (_tableObjCount >= MAXTABLEOBJ)
222  return;
223 
224  _tableObjects[_tableObjCount] = obj;
225  _tableObjCount += 1;
226  _metadataSize += obj->saveSize();
227  _metadataSize += 2; // for size
228 }
229 
230 uint8_t* Memory::allocMemory(size_t size)
231 {
232  // always allocate aligned to pagesize
233  size = alignToPageSize(size);
234 
235  MemoryBlock* freeBlock = _freeList;
236  MemoryBlock* blockToUse = nullptr;
237 
238  // find the smallest possible block that is big enough
239  while (freeBlock)
240  {
241  if (freeBlock->size >= size)
242  {
243  if (blockToUse != nullptr && (blockToUse->size - size) > (freeBlock->size - size))
244  blockToUse = freeBlock;
245  else if (blockToUse == nullptr)
246  blockToUse = freeBlock;
247  }
248 
249  freeBlock = freeBlock->next;
250  }
251 
252  if (!blockToUse)
253  {
254  println("No available non volatile memory!");
255  _platform.fatalError();
256  }
257 
258  if (blockToUse->size == size)
259  {
260  // use whole block
261  removeFromFreeList(blockToUse);
262  addToUsedList(blockToUse);
263  return blockToUse->address;
264  }
265  else
266  {
267  // split block
268  MemoryBlock* newBlock = new MemoryBlock(blockToUse->address, size);
269  addToUsedList(newBlock);
270 
271  blockToUse->address += size;
272  blockToUse->size -= size;
273 
274  return newBlock->address;
275  }
276 }
277 
278 
279 void Memory::freeMemory(uint8_t* ptr)
280 {
281  MemoryBlock* block = _usedList;
282  MemoryBlock* found = nullptr;
283 
284  while (block)
285  {
286  if (block->address == ptr)
287  {
288  found = block;
289  break;
290  }
291 
292  block = block->next;
293  }
294 
295  if (!found)
296  {
297  println("freeMemory for not used pointer called");
298  clearMemory();
299  _platform.fatalError();
300  }
301 
302  removeFromUsedList(block);
303  addToFreeList(block);
304  _saveTimeout = millis();
305  if (_saveTimeout == 0)
306  _saveTimeout = 1; // prevent 0=disabled; no impact by minimal increased timeout
307 }
308 
309 void Memory::writeMemory(uint32_t relativeAddress, size_t size, uint8_t* data)
310 {
311  if(_saveTimeout != 0)
312  {
313  _saveTimeout = millis();
314  if (_saveTimeout == 0)
315  _saveTimeout = 1; // prevent 0=disabled; no impact by minimal increased timeout
316  }
317  _platform.writeNonVolatileMemory(relativeAddress, data, size);
318 }
319 
320 void Memory::readMemory(uint32_t relativeAddress, size_t size, uint8_t* data)
321 {
322  _platform.readNonVolatileMemory(relativeAddress, data, size);
323 }
324 
325 
326 uint8_t* Memory::toAbsolute(uint32_t relativeAddress)
327 {
328  return _platform.getNonVolatileMemoryStart() + (ptrdiff_t)relativeAddress;
329 }
330 
331 
332 uint32_t Memory::toRelative(uint8_t* absoluteAddress)
333 {
334  return absoluteAddress - _platform.getNonVolatileMemoryStart();
335 }
336 
337 MemoryBlock* Memory::removeFromList(MemoryBlock* head, MemoryBlock* item)
338 {
339  if (head == item)
340  {
341  MemoryBlock* newHead = head->next;
342  head->next = nullptr;
343  return newHead;
344  }
345 
346  if (!head || !item)
347  {
348  println("invalid parameters of Memory::removeFromList");
349  clearMemory();
350  _platform.fatalError();
351  }
352 
353  bool found = false;
354  MemoryBlock* block = head;
355 
356  while (block)
357  {
358  if (block->next == item)
359  {
360  found = true;
361  block->next = item->next;
362  break;
363  }
364 
365  block = block->next;
366  }
367 
368  if (!found)
369  {
370  println("tried to remove block from list not in it");
371  clearMemory();
372  _platform.fatalError();
373  }
374 
375  item->next = nullptr;
376  return head;
377 }
378 
379 void Memory::removeFromFreeList(MemoryBlock* block)
380 {
381  _freeList = removeFromList(_freeList, block);
382 }
383 
384 
385 void Memory::removeFromUsedList(MemoryBlock* block)
386 {
387  _usedList = removeFromList(_usedList, block);
388 }
389 
390 
391 void Memory::addToUsedList(MemoryBlock* block)
392 {
393  block->next = _usedList;
394  _usedList = block;
395 }
396 
397 
398 void Memory::addToFreeList(MemoryBlock* block)
399 {
400  if (_freeList == nullptr)
401  {
402  _freeList = block;
403  return;
404  }
405 
406  // first insert free block in list
407  MemoryBlock* current = _freeList;
408 
409  while (current)
410  {
411  if (current->address <= block->address && (current->next == nullptr || block->address < current->next->address))
412  {
413  //add after current
414  block->next = current->next;
415  current->next = block;
416  break;
417  }
418  else if (current->address > block->address)
419  {
420  //add before current
421  block->next = current;
422 
423  if (current == _freeList)
424  _freeList = block;
425 
426  // swap current and block for merge
427  MemoryBlock* tmp = current;
428  current = block;
429  block = tmp;
430 
431  break;
432  }
433 
434  current = current->next;
435  }
436 
437  // now check if we can merge the blocks
438  // first current an block
439  if ((current->address + current->size) == block->address)
440  {
441  current->size += block->size;
442  current->next = block->next;
443  delete block;
444  // check further if now current can be merged with current->next
445  block = current;
446  }
447 
448  // if block is the last one, we are done
449  if (block->next == nullptr)
450  return;
451 
452  // now check block and block->next
453  if ((block->address + block->size) == block->next->address)
454  {
455  block->size += block->next->size;
456  block->next = block->next->next;
457  delete block->next;
458  }
459 }
460 
461 uint16_t Memory::alignToPageSize(size_t size)
462 {
463  size_t pageSize = 4; //_platform.flashPageSize(); // align to 32bit for now, as aligning to flash-page-size causes side effects in programming
464  // pagesize should be a multiply of two
465  return (size + pageSize - 1) & (-1 * pageSize);
466 }
467 
468 MemoryBlock* Memory::findBlockInList(MemoryBlock* head, uint8_t* address)
469 {
470  while (head != nullptr)
471  {
472  if (head->address == address)
473  return head;
474 
475  head = head->next;
476  }
477 
478  return nullptr;
479 }
480 
481 void Memory::addNewUsedBlock(uint8_t* address, size_t size)
482 {
483  MemoryBlock* smallerFreeBlock = _freeList;
484 
485  // find block in freeList where the new used block is contained in
486  while (smallerFreeBlock)
487  {
488  if (smallerFreeBlock->next == nullptr ||
489  (smallerFreeBlock->next != nullptr && smallerFreeBlock->next->address > address))
490  break;
491 
492  smallerFreeBlock = smallerFreeBlock->next;
493  }
494 
495  if (smallerFreeBlock == nullptr)
496  {
497  println("addNewUsedBlock: no smallerBlock found");
498  clearMemory();
499  _platform.fatalError();
500  }
501 
502  if ((smallerFreeBlock->address + smallerFreeBlock->size) < (address + size))
503  {
504  println("addNewUsedBlock: found block can't contain new block");
505  clearMemory();
506  _platform.fatalError();
507  }
508 
509  if (smallerFreeBlock->address == address && smallerFreeBlock->size == size)
510  {
511  // we take thow whole block
512  removeFromFreeList(smallerFreeBlock);
513  addToUsedList(smallerFreeBlock);
514  return;
515  }
516 
517  if (smallerFreeBlock->address == address)
518  {
519  // we take a front part of the block
520  smallerFreeBlock->address += size;
521  smallerFreeBlock->size -= size;
522  }
523  else
524  {
525  // we take a middle or end part of the block
526  uint8_t* oldEndAddr = smallerFreeBlock->address + smallerFreeBlock->size;
527  smallerFreeBlock->size = (address - smallerFreeBlock->address);
528 
529  if (address + size < oldEndAddr)
530  {
531  // we take the middle part of the block, so we need a new free block for the end part
532  MemoryBlock* newFreeBlock = new MemoryBlock();
533  newFreeBlock->next = smallerFreeBlock->next;
534  newFreeBlock->address = address + size;
535  newFreeBlock->size = oldEndAddr - newFreeBlock->address;
536  smallerFreeBlock->next = newFreeBlock;
537  }
538  }
539 
540  MemoryBlock* newUsedBlock = new MemoryBlock(address, size);
541  addToUsedList(newUsedBlock);
542 }
543 
545 {
546  _versionCheckCallback = func;
547 }
548 
550 {
551  return _versionCheckCallback;
552 }
553 
555 {
556  if(_saveTimeout != 0 && millis() - _saveTimeout > 5000)
557  {
558  println("saveMemory timeout");
559  _saveTimeout = 0;
560  writeMemory();
561  }
562 }
void print(const char *s)
void println(const char *s)
void printHex(const char *suffix, const uint8_t *data, size_t length, bool newline)
Definition: bits.cpp:12
uint8_t * pushByteArray(const uint8_t *src, uint32_t size, uint8_t *data)
Definition: bits.cpp:82
const uint8_t * popByteArray(uint8_t *dst, uint32_t size, const uint8_t *data)
Definition: bits.cpp:48
uint8_t * pushWord(uint16_t w, uint8_t *data)
Definition: bits.cpp:64
const uint8_t * popWord(uint16_t &w, const uint8_t *data)
Definition: bits.cpp:34
uint32_t millis()
const uint8_t * hardwareType()
uint16_t manufacturerId()
const uint16_t apiVersion
Definition: device_object.h:12
uint16_t version()
size_t size
Definition: memory.h:23
uint8_t * address
Definition: memory.h:22
MemoryBlock * next
Definition: memory.h:24
void clearMemory()
Definition: memory.cpp:203
virtual ~Memory()
Definition: memory.cpp:11
void freeMemory(uint8_t *ptr)
Definition: memory.cpp:279
uint8_t * toAbsolute(uint32_t relativeAddress)
Definition: memory.cpp:326
void loop()
Definition: memory.cpp:554
uint32_t toRelative(uint8_t *absoluteAddress)
Definition: memory.cpp:332
void saveMemory()
Definition: memory.cpp:198
VersionCheckCallback versionCheckCallback()
Definition: memory.cpp:549
void addSaveRestore(SaveRestore *obj)
Definition: memory.cpp:209
uint8_t * allocMemory(size_t size)
Definition: memory.cpp:230
void readMemory()
Definition: memory.cpp:14
Memory(Platform &platform, DeviceObject &deviceObject)
Definition: memory.cpp:7
void writeMemory()
Definition: memory.cpp:137
virtual void fatalError()=0
virtual uint8_t * getNonVolatileMemoryStart()
Definition: platform.cpp:152
virtual uint32_t writeNonVolatileMemory(uint32_t relativeAddress, uint8_t *buffer, size_t size)
Definition: platform.cpp:205
virtual size_t getNonVolatileMemorySize()
Definition: platform.cpp:166
virtual void commitNonVolatileMemory()
Definition: platform.cpp:180
virtual uint32_t readNonVolatileMemory(uint32_t relativeAddress, uint8_t *buffer, size_t size)
Definition: platform.cpp:251
Interface for classes that can save and restore data from a buffer.
Definition: save_restore.h:8
virtual uint8_t * save(uint8_t *buffer)
This method is called when the object should save its state to the buffer.
Definition: save_restore.h:18
virtual const uint8_t * restore(const uint8_t *buffer)
This method is called when the object should restore its state from the buffer.
Definition: save_restore.h:31
virtual uint16_t saveSize()
Definition: save_restore.h:39
This class provides common functionality for interface objects that are configured by ETS with MemorW...
Definition: table_object.h:13
const uint8_t * restore(const uint8_t *buffer) override
This method is called when the object should restore its state from the buffer.
uint16_t saveSize() override
uint8_t * save(uint8_t *buffer) override
This method is called when the object should save its state to the buffer.
VersionCheckResult
Definition: memory.h:28
@ FlashTablesInvalid
All table objects are invalid for this firmware, device object and saveRestores are OK.
Definition: memory.h:30
@ FlashAllInvalid
All flash content is not valid for this firmware, we delete it.
Definition: memory.h:29
@ FlashValid
Flash content is valid and will be used.
Definition: memory.h:31
VersionCheckResult(* VersionCheckCallback)(uint16_t manufacturerId, uint8_t *hardwareType, uint16_t version)
Definition: memory.h:34