Contiki-Inga 3.x
settings.c
1 /*
2  * Copyright (c) 2013, Robert Quattlebaum.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 #ifdef SETTINGS_CONF_SKIP_CONVENIENCE_FUNCS
34 #undef SETTINGS_CONF_SKIP_CONVENIENCE_FUNCS
35 #endif
36 
37 #define SETTINGS_CONF_SKIP_CONVENIENCE_FUNCS 1
38 
39 #include "contiki.h"
40 #include "settings.h"
41 #include "dev/eeprom.h"
42 
43 #if CONTIKI_CONF_SETTINGS_MANAGER
44 
45 #if !EEPROM_CONF_SIZE
46 #error CONTIKI_CONF_SETTINGS_MANAGER has been set, but EEPROM_CONF_SIZE hasnt!
47 #endif
48 
49 #ifndef EEPROM_END_ADDR
50 #define EEPROM_END_ADDR (EEPROM_CONF_SIZE - 1)
51 #endif
52 
53 #ifndef SETTINGS_MAX_SIZE
54 /** The maximum amount EEPROM dedicated to settings. */
55 #define SETTINGS_MAX_SIZE (127) /**< Defaults to 127 bytes */
56 #endif
57 
58 #ifndef SETTINGS_TOP_ADDR
59 /** The top address in EEPROM that settings should use. Inclusive. */
60 #define SETTINGS_TOP_ADDR (settings_iter_t)(EEPROM_END_ADDR)
61 #endif
62 
63 #ifndef SETTINGS_BOTTOM_ADDR
64 /** The lowest address in EEPROM that settings should use. Inclusive. */
65 #define SETTINGS_BOTTOM_ADDR (SETTINGS_TOP_ADDR + 1 - SETTINGS_MAX_SIZE)
66 #endif
67 
68 #ifndef MIN
69 #define MIN(a,b) ((a)<(b)?a:b)
70 #endif
71 
72 typedef struct {
73 #if SETTINGS_CONF_SUPPORT_LARGE_VALUES
74  uint8_t size_extra;
75 #endif
76  uint8_t size_low;
77  uint8_t size_check;
78  settings_key_t key;
79 } item_header_t;
80 
81 /*****************************************************************************/
82 // MARK: - Public Travesal Functions
83 /*****************************************************************************/
84 
85 /*---------------------------------------------------------------------------*/
86 settings_iter_t
88 {
89  return settings_iter_is_valid(SETTINGS_TOP_ADDR) ? SETTINGS_TOP_ADDR : 0;
90 }
91 
92 /*---------------------------------------------------------------------------*/
93 settings_iter_t
94 settings_iter_next(settings_iter_t ret)
95 {
96  if(ret) {
97  /* A settings iterator always points to the first byte
98  * after the actual key-value pair in memory. This means that
99  * the address of our value in EEPROM just happens
100  * to be the address of our next iterator.
101  */
102  ret = settings_iter_get_value_addr(ret);
103  return settings_iter_is_valid(ret) ? ret : 0;
104  }
105  return SETTINGS_INVALID_ITER;
106 }
107 
108 /*---------------------------------------------------------------------------*/
109 uint8_t
110 settings_iter_is_valid(settings_iter_t iter)
111 {
112  item_header_t header = { 0 };
113 
114  if(iter == EEPROM_NULL) {
115  return 0;
116  }
117 
118  if(iter < SETTINGS_BOTTOM_ADDR + sizeof(header)) {
119  return 0;
120  }
121 
122  eeprom_read(iter - sizeof(header), (uint8_t *)&header, sizeof(header));
123 
124  if((uint8_t) header.size_check != (uint8_t) ~ header.size_low) {
125  return 0;
126  }
127 
128  if(iter < SETTINGS_BOTTOM_ADDR + sizeof(header) + settings_iter_get_value_length(iter)) {
129  return 0;
130  }
131 
132  return 1;
133 }
134 
135 /*---------------------------------------------------------------------------*/
136 settings_key_t
137 settings_iter_get_key(settings_iter_t iter)
138 {
139  item_header_t header;
140 
141  eeprom_read(iter - sizeof(header), (uint8_t *)&header, sizeof(header));
142 
143  if((uint8_t) header.size_check != (uint8_t)~header.size_low) {
144  return SETTINGS_INVALID_KEY;
145  }
146 
147  return header.key;
148 }
149 
150 /*---------------------------------------------------------------------------*/
151 settings_length_t
152 settings_iter_get_value_length(settings_iter_t iter)
153 {
154  item_header_t header;
155 
156  settings_length_t ret = 0;
157 
158  eeprom_read(iter - sizeof(header), (uint8_t *)&header, sizeof(header) );
159 
160  if((uint8_t)header.size_check == (uint8_t)~header.size_low) {
161  ret = header.size_low;
162 
163 #if SETTINGS_CONF_SUPPORT_LARGE_VALUES
164  if(ret & (1 << 7)) {
165  ret = ((ret & ~(1 << 7)) << 7) | header.size_extra;
166  }
167 #endif
168  }
169 
170  return ret;
171 }
172 
173 /*---------------------------------------------------------------------------*/
174 eeprom_addr_t
175 settings_iter_get_value_addr(settings_iter_t iter)
176 {
177  settings_length_t len = settings_iter_get_value_length(iter);
178 #if SETTINGS_CONF_SUPPORT_LARGE_VALUES
179  len += (len >= 128);
180 #endif
181  return iter - sizeof(item_header_t) - len;
182 }
183 
184 /*---------------------------------------------------------------------------*/
185 settings_length_t
186 settings_iter_get_value_bytes(settings_iter_t iter, void *bytes,
187  settings_length_t max_length)
188 {
189  max_length = MIN(max_length, settings_iter_get_value_length(iter));
190 
191  eeprom_read(settings_iter_get_value_addr(iter), bytes, max_length);
192 
193  return max_length;
194 }
195 
196 /*---------------------------------------------------------------------------*/
197 settings_status_t
198 settings_iter_delete(settings_iter_t iter)
199 {
200  settings_status_t ret = SETTINGS_STATUS_FAILURE;
201 
202  settings_iter_t next = settings_iter_next(iter);
203 
204  if(!next) {
205  /* Special case: we are the last item. we can get away with
206  * just wiping out our own header.
207  */
208  item_header_t header;
209 
210  memset(&header, 0xFF, sizeof(header));
211 
212  eeprom_write(iter - sizeof(header), (uint8_t *)&header, sizeof(header));
213 
214  ret = SETTINGS_STATUS_OK;
215  } else {
216 
217  /* This case requires the settings store to be shifted.
218  * Currently unimplemented. TODO: Writeme!
219  */
220  ret = SETTINGS_STATUS_UNIMPLEMENTED;
221  printf("SETTINGS_STATUS_UNIMPLEMENTED\n");
222 
223  }
224 
225  return ret;
226 }
227 
228 /*****************************************************************************/
229 // MARK: - Public Functions
230 /*****************************************************************************/
231 
232 /*---------------------------------------------------------------------------*/
233 uint8_t
234 settings_check(settings_key_t key, uint8_t index)
235 {
236  uint8_t ret = 0;
237 
238  settings_iter_t iter;
239 
240  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
241  if(settings_iter_get_key(iter) == key) {
242  if(!index) {
243  ret = 1;
244  break;
245  } else {
246  index--;
247  }
248  }
249  }
250 
251  return ret;
252 }
253 
254 /*---------------------------------------------------------------------------*/
255 settings_status_t
256 settings_get(settings_key_t key, uint8_t index, uint8_t *value,
257  settings_length_t value_size)
258 {
259  settings_status_t ret = SETTINGS_STATUS_NOT_FOUND;
260 
261  settings_iter_t iter;
262 
263  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
264  if(settings_iter_get_key(iter) == key) {
265  if(!index) {
266  /* We found it! */
267  value_size = settings_iter_get_value_bytes(iter,
268  (void *)value,
269  value_size);
270  ret = SETTINGS_STATUS_OK;
271  break;
272  } else {
273  /* Nope, keep looking */
274  index--;
275  }
276  }
277  }
278 
279  return ret;
280 }
281 
282 /*---------------------------------------------------------------------------*/
283 settings_status_t
284 settings_add(settings_key_t key, const uint8_t *value,
285  settings_length_t value_size)
286 {
287  settings_status_t ret = SETTINGS_STATUS_FAILURE;
288 
289  settings_iter_t iter;
290 
291  item_header_t header;
292 
293  /* Find the last item. */
294  for(iter = settings_iter_begin(); settings_iter_next(iter);
295  iter = settings_iter_next(iter)) {
296  /* This block intentionally left blank. */
297  }
298 
299  if(iter) {
300  /* Value address of item is the same as the iterator for next item. */
301  iter = settings_iter_get_value_addr(iter);
302  } else {
303  /* This will be the first setting! */
304  iter = SETTINGS_TOP_ADDR;
305  }
306 
307  if(iter < SETTINGS_BOTTOM_ADDR + value_size + sizeof(header)) {
308  /* This value is too big to store. */
309  ret = SETTINGS_STATUS_OUT_OF_SPACE;
310  goto bail;
311  }
312 
313  header.key = key;
314 
315  if(value_size < 0x80) {
316  /* If the value size is less than 128, then
317  * we can get away with only using one byte
318  * to store the size.
319  */
320  header.size_low = value_size;
321  }
322 #if SETTINGS_CONF_SUPPORT_LARGE_VALUES
323  else if(value_size <= SETTINGS_MAX_VALUE_SIZE) {
324  /* If the value size is larger than or equal to 128,
325  * then we need to use two bytes. Store
326  * the most significant 7 bits in the first
327  * size byte (with MSB set) and store the
328  * least significant bits in the second
329  * byte (with LSB clear)
330  */
331  header.size_low = (value_size >> 7) | 0x80;
332  header.size_extra = value_size & ~0x80;
333  }
334 #endif
335  else {
336  /* Value size way too big! */
337  ret = SETTINGS_STATUS_VALUE_TOO_BIG;
338  goto bail;
339  }
340 
341  header.size_check = ~header.size_low;
342 
343  /* Write the header first */
344  eeprom_write(iter - sizeof(header), (uint8_t *)&header, sizeof(header));
345 
346  /* Sanity check, remove once confident */
347  if(settings_iter_get_value_length(iter) != value_size) {
348  goto bail;
349  }
350 
351  /* Now write the data */
352  eeprom_write(settings_iter_get_value_addr(iter), (uint8_t *)value, value_size);
353 
354  /* This should be the last item. If this is not the case,
355  * then we need to clear out the phantom setting.
356  */
357  if((iter = settings_iter_next(iter))) {
358  memset(&header, 0xFF, sizeof(header));
359 
360  eeprom_write(iter - sizeof(header),(uint8_t *)&header, sizeof(header));
361  }
362 
363  ret = SETTINGS_STATUS_OK;
364 
365 bail:
366  return ret;
367 }
368 
369 /*---------------------------------------------------------------------------*/
370 settings_status_t
371 settings_set(settings_key_t key, const uint8_t *value,
372  settings_length_t value_size)
373 {
374  settings_status_t ret = SETTINGS_STATUS_FAILURE;
375 
376  settings_iter_t iter;
377 
378  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
379  if(settings_iter_get_key(iter) == key) {
380  break;
381  }
382  }
383 
384  if((iter == EEPROM_NULL) || !settings_iter_is_valid(iter)) {
385  ret = settings_add(key, value, value_size);
386  goto bail;
387  }
388 
389  if(value_size != settings_iter_get_value_length(iter)) {
390  /* TODO: Requires the settings store to be shifted. Currently unimplemented. */
391  ret = SETTINGS_STATUS_UNIMPLEMENTED;
392  goto bail;
393  }
394 
395  /* Now write the data */
396  eeprom_write(settings_iter_get_value_addr(iter),
397  (uint8_t *)value, value_size);
398 
399  ret = SETTINGS_STATUS_OK;
400 
401 bail:
402  return ret;
403 }
404 
405 /*---------------------------------------------------------------------------*/
406 settings_status_t
407 settings_delete(settings_key_t key, uint8_t index)
408 {
409  settings_status_t ret = SETTINGS_STATUS_NOT_FOUND;
410 
411  settings_iter_t iter;
412 
413  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
414  if(settings_iter_get_key(iter) == key) {
415  if(!index) {
416  /* We found it! */
417  ret = settings_iter_delete(iter);
418  break;
419  } else {
420  /* Nope, keep looking */
421  index--;
422  }
423  }
424  }
425 
426  return ret;
427 }
428 
429 /*---------------------------------------------------------------------------*/
430 void
431 settings_wipe(void)
432 {
433  /* Simply making the first item invalid will effectively
434  * clear the key-value store.
435  */
436  const uint32_t x = 0x000000;
437 
438  eeprom_write(SETTINGS_TOP_ADDR - sizeof(x), (uint8_t *)&x, sizeof(x));
439 }
440 
441 /*****************************************************************************/
442 // MARK: - Other Functions
443 /*****************************************************************************/
444 
445 #if DEBUG
446 #include <stdio.h>
447 /*---------------------------------------------------------------------------*/
448 void
449 settings_debug_dump(void)
450 {
451  settings_iter_t iter;
452 
453  printf("{\n");
454  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
455  settings_length_t len = settings_iter_get_value_length(iter);
456  eeprom_addr_t addr = settings_iter_get_value_addr(iter);
457  uint8_t byte;
458 
459  union {
460  settings_key_t key;
461  char bytes[0];
462  } u;
463 
464  u.key = settings_iter_get_key(iter);
465 
466  printf("\t\"%c%c\" = <", u.bytes[0], u.bytes[1]);
467 
468  for(; len; len--, addr++) {
469  eeprom_read(addr, &byte, 1);
470  printf("%02X", byte);
471  if(len != 1) {
472  printf(" ");
473  }
474  }
475 
476  printf(">;\n");
477  }
478  printf("}\n");
479 }
480 #endif /* DEBUG */
481 
482 #endif /* CONTIKI_CONF_SETTINGS_MANAGER */