RSeries astromech firmware
EEPROMSettings.h
Go to the documentation of this file.
1 #ifndef EEPROMSettings_h
2 #define EEPROMSettings_h
3 
4 #include "ReelTwo.h"
5 #include <EEPROM.h>
6 
7 #ifdef EEPROM_FLASH_PARTITION_NAME
8  #ifndef EEPROM_SIZE
9  #define EEPROM_SIZE 4096
10  #endif
11 #else
12  #define EEPROM_SIZE EEPROM.length()
13 #endif
14 
15 template <class T, uint32_t VERSION = 0xba5eba11>
16 class EEPROMSettings : public T
17 {
18 public:
19  static constexpr int kMaximumCommandLength = 0xFF;
20 
21  T* data()
22  {
23  return (T*)this;
24  }
25 
26  bool read()
27  {
28  uint32_t offs = validate();
29  if (offs)
30  {
31  uint16_t siz = 0;
32  EEPROM.get(offs, siz); offs += sizeof(siz);
33  if (siz == sizeof(*this))
34  {
35  EEPROM.get(offs, *this);
36  return true;
37  }
38  }
39  return false;
40  }
41 
42  void write()
43  {
44  size_t offs = 0;
45  uint32_t magic = VERSION;
46  EEPROM.put(offs, magic); offs += sizeof(magic);
47 
48  uint16_t siz_offs = offs; offs += sizeof(siz_offs);
49  EEPROM.put(offs, *this);
50  offs += sizeof(*this);
51 
52  // Update offset
53  uint16_t siz = offs-siz_offs-sizeof(siz_offs);
54 
55  EEPROM.put(siz_offs, siz);
56 
57  // Check for the command section magic code
58  // if it's missing we need to write out a terminating byte
59  EEPROM.get(offs, magic);
60  if (magic != kCommandListMagic)
61  {
62  magic = kCommandListMagic;
63  EEPROM.put(offs, magic); offs += sizeof(magic);
64 
65  uint8_t tag = kEndTag;
66  EEPROM.put(offs, tag); offs += sizeof(tag);
67  }
68  updateCRC();
69  #ifdef EEPROM_FLASH_PARTITION_NAME
70  EEPROM.commit();
71  #endif
72  }
73 
75  {
76  uint32_t offs = validateCommandList();
77  if (offs)
78  {
79  uint8_t tag = kEndTag;
80  EEPROM.put(offs, tag);
81 
82  updateCRC();
83  #ifdef EEPROM_FLASH_PARTITION_NAME
84  EEPROM.commit();
85  #endif
86  return true;
87  }
88  return false;
89  }
90 
91  size_t getCommandCount()
92  {
93  unsigned count = 0;
94  uint32_t offs = validateCommandList();
95  if (offs)
96  {
97  while (offs <= EEPROM_SIZE)
98  {
99  uint8_t len;
100  uint8_t snum;
101  EEPROM.get(offs, snum); offs += sizeof(snum);
102  if (snum == kEndTag)
103  break;
104  count++;
105  EEPROM.get(offs, len); offs += sizeof(len) + len;
106  }
107  }
108  return count;
109  }
110 
111  size_t getCommands(uint8_t* buffer, size_t maxBufferSize)
112  {
113  size_t size = 0;
114  uint32_t offs = validateCommandList();
115  if (offs)
116  {
117  while (offs <= EEPROM_SIZE)
118  {
119  uint8_t len;
120  uint8_t snum;
121  EEPROM.get(offs, snum); offs += sizeof(snum);
122  if (snum == kEndTag)
123  break;
124  if (size < maxBufferSize)
125  buffer[size++] = snum;
126  EEPROM.get(offs, len); offs += sizeof(len) + len;
127  }
128  }
129  return size;
130  }
131 
132  bool listCommands(Print& stream)
133  {
134  uint32_t offs = validateCommandList();
135  if (offs)
136  {
137  while (offs <= EEPROM_SIZE)
138  {
139  uint8_t snum;
140  EEPROM.get(offs, snum); offs += sizeof(snum);
141  if (snum == kEndTag)
142  break;
143  stream.print('[');
144  stream.print(snum);
145  stream.print(']');
146  stream.print(' ');
147  uint8_t len;
148  EEPROM.get(offs, len); offs += sizeof(len);
149  while (len > 0)
150  {
151  char ch;
152  EEPROM.get(offs, ch); offs += sizeof(ch);
153  stream.print((char)ch);
154  len--;
155  }
156  stream.println();
157  }
158  return true;
159  }
160  return false;
161  }
162 
163  bool listSortedCommands(Print& stream)
164  {
165  typedef uint32_t BMAPWORD;
166  #define BMAPWORD_GRANULARITY (sizeof(BMAPWORD))
167  #define BMAPWORD_BIT_SIZE (sizeof(BMAPWORD) * 8)
168  #define BIT_MAPWORD(bitpos) (0x1L << (BMAPWORD_BIT_SIZE - 1 - (bitpos)))
169  #define SET_MAPWORD_BIT(value, bit) {\
170  uint8_t bitpos = (value) % sizeof(usedBitmap[0]); \
171  uint8_t wordpos = (value) / sizeof(usedBitmap[0]); \
172  usedBitmap[wordpos] = (usedBitmap[wordpos] & ~(BIT_MAPWORD(bitpos))) | (uint32_t(bit) << (BMAPWORD_BIT_SIZE - 1 - bitpos)); }
173  #define MAPWORD_BIT(value) \
174  ((usedBitmap[(value) / sizeof(usedBitmap[0])] & BIT_MAPWORD((value) % sizeof(usedBitmap[0]))) != 0)
175 
176  uint32_t usedBitmap[kMaxCommands / sizeof(uint32_t)+1];
177  memset(&usedBitmap, '\0', sizeof(usedBitmap));
178 
179  uint32_t offs = validateCommandList();
180  if (!offs)
181  return false;
182 
183  uint16_t scanoffs = offs;
184  while (scanoffs <= EEPROM_SIZE)
185  {
186  uint8_t snum = 0;
187  EEPROM.get(scanoffs, snum); scanoffs += sizeof(snum);
188  if (snum == kEndTag)
189  break;
190 
191  SET_MAPWORD_BIT(snum, 1);
192  uint8_t len = 0;
193  EEPROM.get(scanoffs, len); scanoffs += sizeof(len) + len;
194  }
195  for (unsigned i = 0; i < 100; i++)
196  {
197  if (MAPWORD_BIT(i))
198  {
199  uint16_t scanoffs = offs;
200  while (scanoffs <= EEPROM_SIZE)
201  {
202  uint8_t snum = 0;
203  EEPROM.get(scanoffs, snum); scanoffs += sizeof(snum);
204  if (snum == kEndTag)
205  break;
206  if (snum == i)
207  {
208  stream.print('[');
209  stream.print(snum);
210  stream.print(']');
211  stream.print(' ');
212  }
213  uint8_t len = 0;
214  EEPROM.get(scanoffs, len); scanoffs += sizeof(len);
215  if (snum == i)
216  {
217  while (len > 0)
218  {
219  char ch = 0;
220  EEPROM.get(scanoffs, ch); scanoffs += sizeof(ch);
221  stream.print((char)ch);
222  len--;
223  }
224  stream.println();
225  break;
226  }
227  else
228  {
229  scanoffs += len;
230  }
231  }
232  }
233  }
234  return true;
235  #undef MAPWORD_BIT
236  #undef SET_MAPWORD_BIT
237  #undef BIT_MAPWORD
238  #undef BMAPWORD_BIT_SIZE
239  #undef BMAPWORD_GRANULARITY
240  }
241 
242  bool readCommand(uint8_t num, char* cmd, size_t cmdBufferSize, const char* prefix = nullptr)
243  {
244  return readCommandInternal(num, cmd, cmdBufferSize, nullptr, prefix);
245  }
246 
247  bool deleteCommand(uint8_t num)
248  {
249  uint16_t writeoffs = 0;
250  if (!readCommandInternal(num, nullptr, 0, &writeoffs))
251  return false;
252 
253  uint8_t len = 0;
254  uint16_t readoffs = writeoffs + 1;
255  EEPROM.get(readoffs, len); readoffs += sizeof(len);
256  readoffs += len;
257 
258  while (readoffs < EEPROM_SIZE)
259  {
260  uint8_t tag = 0;
261  EEPROM.get(readoffs, tag); readoffs += sizeof(tag);
262  EEPROM.put(writeoffs, tag); writeoffs += sizeof(tag);
263  if (tag == kEndTag)
264  {
265  // End of buffer
266  break;
267  }
268  uint8_t readlen = 0;
269  EEPROM.get(readoffs, readlen); readoffs += sizeof(readlen);
270  EEPROM.put(writeoffs, readlen); writeoffs += sizeof(readlen);
271  while (readlen > 0)
272  {
273  char ch = 0;
274  EEPROM.get(readoffs, ch); readoffs += sizeof(ch);
275  EEPROM.put(writeoffs, ch); writeoffs += sizeof(ch);
276  readlen--;
277  }
278  }
279  updateCRC();
280  #ifdef EEPROM_FLASH_PARTITION_NAME
281  EEPROM.commit();
282  #endif
283  return true;
284  }
285 
286  bool writeCommand(uint8_t num, const char* cmd)
287  {
288  if (strlen(cmd) > kMaximumCommandLength)
289  {
290  // command too long
291  return false;
292  }
293  // delete old command if it exists
294  deleteCommand(num);
295 
296  uint32_t offs = validate();
297  if (offs)
298  {
299  uint16_t siz = 0;
300  EEPROM.get(offs, siz); offs += siz + sizeof(siz);
301 
302  uint32_t magic;
303  EEPROM.get(offs, magic); offs += sizeof(magic);
304  if (magic == kCommandListMagic)
305  {
306  // append command to end of buffer
307  while (offs <= EEPROM_SIZE)
308  {
309  uint8_t tag = 0;
310  EEPROM.get(offs, tag);
311  if (tag == kEndTag)
312  {
313  EEPROM.put(offs, num); offs += sizeof(num);
314 
315  uint8_t len = strlen(cmd);
316  EEPROM.put(offs, len); offs += sizeof(len);
317  while (len > 0)
318  {
319  EEPROM.put(offs, *cmd); offs += sizeof(*cmd);
320  cmd++;
321  len--;
322  }
323  // Write terminate byte
324  num = kEndTag;
325  EEPROM.put(offs, num); offs += sizeof(num);
326  updateCRC();
327  #ifdef EEPROM_FLASH_PARTITION_NAME
328  EEPROM.commit();
329  #endif
330  return true;
331  }
332  offs += sizeof(tag);
333  uint8_t len = 0;
334  EEPROM.get(offs, len); offs += sizeof(len) + len;
335  }
336  }
337  else
338  {
339  // start new command buffer
340  magic = kCommandListMagic;
341  EEPROM.put(offs, magic); offs += sizeof(magic);
342 
343  EEPROM.put(offs, num); offs += sizeof(num);
344 
345  uint8_t len = strlen(cmd);
346  EEPROM.put(offs, len); offs += sizeof(len);
347  while (len > 0)
348  {
349  EEPROM.put(offs, *cmd); offs += sizeof(*cmd);
350  cmd++;
351  len--;
352  }
353  // Write terminate byte
354  num = kEndTag;
355  EEPROM.put(offs, num); offs += sizeof(num);
356  updateCRC();
357  #ifdef EEPROM_FLASH_PARTITION_NAME
358  EEPROM.commit();
359  #endif
360  return true;
361  }
362  }
363  return false;
364  }
365 
366 private:
367  static uint32_t constexpr kCommandListMagic = 0xf005ba11;
368  static uint8_t constexpr kEndTag = 0xff;
369  static uint8_t constexpr kMaxCommands = 100;
370 
371  uint32_t validate()
372  {
373  uint16_t offs = 0;
374  uint32_t magic;
375  EEPROM.get(offs, magic);
376  uint32_t crc = crcFrom(sizeof(magic));
377  if (magic == (VERSION ^ crc))
378  {
379  offs += sizeof(magic);
380  }
381  return offs;
382  }
383 
384  uint32_t validateCommandList()
385  {
386  uint32_t offs = validate();
387  if (offs)
388  {
389  uint16_t siz = 0;
390  EEPROM.get(offs, siz); offs += siz + sizeof(siz);
391 
392  uint32_t magic;
393  EEPROM.get(offs, magic); offs += sizeof(magic);
394  if (magic == kCommandListMagic)
395  {
396  return offs;
397  }
398  }
399  return 0;
400  }
401 
402  bool readCommandInternal(uint8_t num, char* cmd, size_t cmdBufferSize, uint16_t* cmdoffs = nullptr, const char* prefix = nullptr)
403  {
404  uint32_t offs = validateCommandList();
405  if (offs)
406  {
407  while (offs <= EEPROM_SIZE)
408  {
409  uint8_t snum = 0;
410  EEPROM.get(offs, snum);
411  if (snum == kEndTag)
412  break;
413  if (snum == num && cmdoffs != nullptr)
414  *cmdoffs = offs;
415  uint8_t len = 0;
416  offs += sizeof(snum);
417  EEPROM.get(offs, len); offs += sizeof(len);
418  if (snum == num)
419  {
420  if (cmd != nullptr)
421  {
422  char ch;
423  char* cmd_end = cmd + cmdBufferSize - 1;
424  if (prefix != nullptr)
425  {
426  while (prefix != nullptr && (ch = *prefix++) != '\0' && cmd < cmd_end)
427  {
428  *cmd++ = ch;
429  }
430  }
431  while (len > 0)
432  {
433  EEPROM.get(offs, ch); offs += sizeof(ch);
434  if (cmd < cmd_end)
435  *cmd++ = ch;
436  len--;
437  }
438  *cmd = '\0';
439  }
440  return true;
441  }
442  else
443  {
444  offs += len;
445  }
446  }
447  }
448  return false;
449  }
450 
451  void updateCRC()
452  {
453  // Update header crc
454  uint32_t crc = crcFrom(sizeof(uint32_t));
455  EEPROM.put(0, VERSION ^ crc);
456  }
457 
458  static uint32_t crcFrom(unsigned offset = 0)
459  {
460  static const uint32_t crc_table[16] PROGMEM =
461  {
462  0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
463  0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
464  0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
465  0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
466  };
467  uint32_t crc = ~0L;
468  while (offset < EEPROM_SIZE)
469  {
470  uint8_t eepromByte = EEPROM.read(offset);
471  crc = pgm_read_uint32(&crc_table[(crc ^ eepromByte) & 0x0f]) ^ (crc >> 4);
472  crc = pgm_read_uint32(&crc_table[(crc ^ (eepromByte >> 4)) & 0x0f]) ^ (crc >> 4);
473  crc = ~crc;
474  offset += 1;
475  }
476  return crc;
477  }
478 
479  static inline uint32_t pgm_read_uint32(const uint32_t* p)
480  {
481  #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || \
482  defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) || \
483  defined(__AVR_ATmega128__) ||defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
484  return pgm_read_dword(p);
485  #else
486  return *p;
487  #endif
488  }
489 };
490 
491 #endif
EEPROMSettings::listSortedCommands
bool listSortedCommands(Print &stream)
Definition: EEPROMSettings.h:163
ReelTwo.h
EEPROMSettings::data
T * data()
Definition: EEPROMSettings.h:21
EEPROMSettings
Definition: EEPROMSettings.h:16
EEPROMSettings::getCommandCount
size_t getCommandCount()
Definition: EEPROMSettings.h:91
MAPWORD_BIT
#define MAPWORD_BIT(value)
EEPROM_SIZE
#define EEPROM_SIZE
Definition: EEPROMSettings.h:12
EEPROMSettings::getCommands
size_t getCommands(uint8_t *buffer, size_t maxBufferSize)
Definition: EEPROMSettings.h:111
SET_MAPWORD_BIT
#define SET_MAPWORD_BIT(value, bit)
EEPROMSettings::writeCommand
bool writeCommand(uint8_t num, const char *cmd)
Definition: EEPROMSettings.h:286
EEPROMSettings::clearCommands
bool clearCommands()
Definition: EEPROMSettings.h:74
EEPROMSettings::deleteCommand
bool deleteCommand(uint8_t num)
Definition: EEPROMSettings.h:247
EEPROMSettings::read
bool read()
Definition: EEPROMSettings.h:26
EEPROMSettings::write
void write()
Definition: EEPROMSettings.h:42
EEPROMSettings::readCommand
bool readCommand(uint8_t num, char *cmd, size_t cmdBufferSize, const char *prefix=nullptr)
Definition: EEPROMSettings.h:242
EEPROMSettings::kMaximumCommandLength
static constexpr int kMaximumCommandLength
Definition: EEPROMSettings.h:19
EEPROMSettings::listCommands
bool listCommands(Print &stream)
Definition: EEPROMSettings.h:132