Contiki 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 typedef struct {
69 #if SETTINGS_CONF_SUPPORT_LARGE_VALUES
70  uint8_t size_extra;
71 #endif
72  uint8_t size_low;
73  uint8_t size_check;
74  settings_key_t key;
75 } item_header_t;
76 
77 /*****************************************************************************/
78 // MARK: - Public Travesal Functions
79 /*****************************************************************************/
80 
81 /*---------------------------------------------------------------------------*/
82 settings_iter_t
84 {
85  return settings_iter_is_valid(SETTINGS_TOP_ADDR) ? SETTINGS_TOP_ADDR : 0;
86 }
87 
88 /*---------------------------------------------------------------------------*/
89 settings_iter_t
90 settings_iter_next(settings_iter_t ret)
91 {
92  if(ret) {
93  /* A settings iterator always points to the first byte
94  * after the actual key-value pair in memory. This means that
95  * the address of our value in EEPROM just happens
96  * to be the address of our next iterator.
97  */
98  ret = settings_iter_get_value_addr(ret);
99  return settings_iter_is_valid(ret) ? ret : 0;
100  }
101  return SETTINGS_INVALID_ITER;
102 }
103 
104 /*---------------------------------------------------------------------------*/
105 uint8_t
106 settings_iter_is_valid(settings_iter_t iter)
107 {
108  item_header_t header = { 0 };
109 
110  if(iter == EEPROM_NULL) {
111  return 0;
112  }
113 
114  if(iter < SETTINGS_BOTTOM_ADDR + sizeof(header)) {
115  return 0;
116  }
117 
118  eeprom_read(iter - sizeof(header), (uint8_t *)&header, sizeof(header));
119 
120  if((uint8_t) header.size_check != (uint8_t) ~ header.size_low) {
121  return 0;
122  }
123 
124  if(iter < SETTINGS_BOTTOM_ADDR + sizeof(header) + settings_iter_get_value_length(iter)) {
125  return 0;
126  }
127 
128  return 1;
129 }
130 
131 /*---------------------------------------------------------------------------*/
132 settings_key_t
133 settings_iter_get_key(settings_iter_t iter)
134 {
135  item_header_t header;
136 
137  eeprom_read(iter - sizeof(header), (uint8_t *)&header, sizeof(header));
138 
139  if((uint8_t) header.size_check != (uint8_t)~header.size_low) {
140  return SETTINGS_INVALID_KEY;
141  }
142 
143  return header.key;
144 }
145 
146 /*---------------------------------------------------------------------------*/
147 settings_length_t
148 settings_iter_get_value_length(settings_iter_t iter)
149 {
150  item_header_t header;
151 
152  settings_length_t ret = 0;
153 
154  eeprom_read(iter - sizeof(header), (uint8_t *)&header, sizeof(header) );
155 
156  if((uint8_t)header.size_check == (uint8_t)~header.size_low) {
157  ret = header.size_low;
158 
159 #if SETTINGS_CONF_SUPPORT_LARGE_VALUES
160  if(ret & (1 << 7)) {
161  ret = ((ret & ~(1 << 7)) << 7) | header.size_extra;
162  }
163 #endif
164  }
165 
166  return ret;
167 }
168 
169 /*---------------------------------------------------------------------------*/
170 eeprom_addr_t
171 settings_iter_get_value_addr(settings_iter_t iter)
172 {
173  settings_length_t len = settings_iter_get_value_length(iter);
174 #if SETTINGS_CONF_SUPPORT_LARGE_VALUES
175  len += (len >= 128);
176 #endif
177  return iter - sizeof(item_header_t) - len;
178 }
179 
180 /*---------------------------------------------------------------------------*/
181 settings_length_t
182 settings_iter_get_value_bytes(settings_iter_t iter, void *bytes,
183  settings_length_t max_length)
184 {
185  max_length = MIN(max_length, settings_iter_get_value_length(iter));
186 
187  eeprom_read(settings_iter_get_value_addr(iter), bytes, max_length);
188 
189  return max_length;
190 }
191 
192 /*---------------------------------------------------------------------------*/
193 settings_status_t
194 settings_iter_delete(settings_iter_t iter)
195 {
196  settings_status_t ret = SETTINGS_STATUS_FAILURE;
197 
198  settings_iter_t next = settings_iter_next(iter);
199 
200  if(!next) {
201  /* Special case: we are the last item. we can get away with
202  * just wiping out our own header.
203  */
204  item_header_t header;
205 
206  memset(&header, 0xFF, sizeof(header));
207 
208  eeprom_write(iter - sizeof(header), (uint8_t *)&header, sizeof(header));
209 
210  ret = SETTINGS_STATUS_OK;
211  }
212 
213  /* This case requires the settings store to be shifted.
214  * Currently unimplemented. TODO: Writeme!
215  */
216  ret = SETTINGS_STATUS_UNIMPLEMENTED;
217 
218  return ret;
219 }
220 
221 /*****************************************************************************/
222 // MARK: - Public Functions
223 /*****************************************************************************/
224 
225 /*---------------------------------------------------------------------------*/
226 uint8_t
227 settings_check(settings_key_t key, uint8_t index)
228 {
229  uint8_t ret = 0;
230 
231  settings_iter_t iter;
232 
233  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
234  if(settings_iter_get_key(iter) == key) {
235  if(!index) {
236  ret = 1;
237  break;
238  } else {
239  index--;
240  }
241  }
242  }
243 
244  return ret;
245 }
246 
247 /*---------------------------------------------------------------------------*/
248 settings_status_t
249 settings_get(settings_key_t key, uint8_t index, uint8_t *value,
250  settings_length_t * value_size)
251 {
252  settings_status_t ret = SETTINGS_STATUS_NOT_FOUND;
253 
254  settings_iter_t iter;
255 
256  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
257  if(settings_iter_get_key(iter) == key) {
258  if(!index) {
259  /* We found it! */
260  *value_size = settings_iter_get_value_bytes(iter,
261  (void *)value,
262  *value_size);
263  ret = SETTINGS_STATUS_OK;
264  break;
265  } else {
266  /* Nope, keep looking */
267  index--;
268  }
269  }
270  }
271 
272  return ret;
273 }
274 
275 /*---------------------------------------------------------------------------*/
276 settings_status_t
277 settings_add(settings_key_t key, const uint8_t *value,
278  settings_length_t value_size)
279 {
280  settings_status_t ret = SETTINGS_STATUS_FAILURE;
281 
282  settings_iter_t iter;
283 
284  item_header_t header;
285 
286  /* Find the last item. */
287  for(iter = settings_iter_begin(); settings_iter_next(iter);
288  iter = settings_iter_next(iter)) {
289  /* This block intentionally left blank. */
290  }
291 
292  if(iter) {
293  /* Value address of item is the same as the iterator for next item. */
294  iter = settings_iter_get_value_addr(iter);
295  } else {
296  /* This will be the first setting! */
297  iter = SETTINGS_TOP_ADDR;
298  }
299 
300  if(iter < SETTINGS_BOTTOM_ADDR + value_size + sizeof(header)) {
301  /* This value is too big to store. */
302  ret = SETTINGS_STATUS_OUT_OF_SPACE;
303  goto bail;
304  }
305 
306  header.key = key;
307 
308  if(value_size < 0x80) {
309  /* If the value size is less than 128, then
310  * we can get away with only using one byte
311  * to store the size.
312  */
313  header.size_low = value_size;
314  }
315 #if SETTINGS_CONF_SUPPORT_LARGE_VALUES
316  else if(value_size <= SETTINGS_MAX_VALUE_SIZE) {
317  /* If the value size is larger than or equal to 128,
318  * then we need to use two bytes. Store
319  * the most significant 7 bits in the first
320  * size byte (with MSB set) and store the
321  * least significant bits in the second
322  * byte (with LSB clear)
323  */
324  header.size_low = (value_size >> 7) | 0x80;
325  header.size_extra = value_size & ~0x80;
326  }
327 #endif
328  else {
329  /* Value size way too big! */
330  ret = SETTINGS_STATUS_VALUE_TOO_BIG;
331  goto bail;
332  }
333 
334  header.size_check = ~header.size_low;
335 
336  /* Write the header first */
337  eeprom_write(iter - sizeof(header), (uint8_t *)&header, sizeof(header));
338 
339  /* Sanity check, remove once confident */
340  if(settings_iter_get_value_length(iter) != value_size) {
341  goto bail;
342  }
343 
344  /* Now write the data */
345  eeprom_write(settings_iter_get_value_addr(iter), (uint8_t *)value, value_size);
346 
347  /* This should be the last item. If this is not the case,
348  * then we need to clear out the phantom setting.
349  */
350  if((iter = settings_iter_next(iter))) {
351  memset(&header, 0xFF, sizeof(header));
352 
353  eeprom_write(iter - sizeof(header),(uint8_t *)&header, sizeof(header));
354  }
355 
356  ret = SETTINGS_STATUS_OK;
357 
358 bail:
359  return ret;
360 }
361 
362 /*---------------------------------------------------------------------------*/
363 settings_status_t
364 settings_set(settings_key_t key, const uint8_t *value,
365  settings_length_t value_size)
366 {
367  settings_status_t ret = SETTINGS_STATUS_FAILURE;
368 
369  settings_iter_t iter;
370 
371  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
372  if(settings_iter_get_key(iter) == key) {
373  break;
374  }
375  }
376 
377  if((iter == EEPROM_NULL) || !settings_iter_is_valid(iter)) {
378  ret = settings_add(key, value, value_size);
379  goto bail;
380  }
381 
382  if(value_size != settings_iter_get_value_length(iter)) {
383  /* Requires the settings store to be shifted. Currently unimplemented. */
384  ret = SETTINGS_STATUS_UNIMPLEMENTED;
385  goto bail;
386  }
387 
388  /* Now write the data */
389  eeprom_write(settings_iter_get_value_addr(iter),
390  (uint8_t *)value, value_size);
391 
392  ret = SETTINGS_STATUS_OK;
393 
394 bail:
395  return ret;
396 }
397 
398 /*---------------------------------------------------------------------------*/
399 settings_status_t
400 settings_delete(settings_key_t key, uint8_t index)
401 {
402  settings_status_t ret = SETTINGS_STATUS_NOT_FOUND;
403 
404  settings_iter_t iter;
405 
406  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
407  if(settings_iter_get_key(iter) == key) {
408  if(!index) {
409  /* We found it! */
410  ret = settings_iter_delete(iter);
411  break;
412  } else {
413  /* Nope, keep looking */
414  index--;
415  }
416  }
417  }
418 
419  return ret;
420 }
421 
422 /*---------------------------------------------------------------------------*/
423 void
424 settings_wipe(void)
425 {
426  /* Simply making the first item invalid will effectively
427  * clear the key-value store.
428  */
429  const uint32_t x = 0xFFFFFF;
430 
431  eeprom_write(SETTINGS_TOP_ADDR - sizeof(x), (uint8_t *)&x, sizeof(x));
432 }
433 
434 /*****************************************************************************/
435 // MARK: - Other Functions
436 /*****************************************************************************/
437 
438 #if DEBUG
439 #include <stdio.h>
440 /*---------------------------------------------------------------------------*/
441 void
442 settings_debug_dump(void)
443 {
444  settings_iter_t iter;
445 
446  printf("{\n");
447  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
448  settings_length_t len = settings_iter_get_value_length(iter);
449  eeprom_addr_t addr = settings_iter_get_value_addr(iter);
450  uint8_t byte;
451 
452  union {
453  settings_key_t key;
454  char bytes[0];
455  } u;
456 
457  u.key = settings_iter_get_key(iter);
458 
459  printf("\t\"%c%c\" = <", u.bytes[0], u.bytes[1]);
460 
461  for(; len; len--, addr++) {
462  eeprom_read(addr, &byte, 1);
463  printf("%02X", byte);
464  if(len != 1) {
465  printf(" ");
466  }
467  }
468 
469  printf(">;\n");
470  }
471  printf("}\n");
472 }
473 #endif /* DEBUG */
474 
475 #endif /* CONTIKI_CONF_SETTINGS_MANAGER */
#define SETTINGS_INVALID_KEY
Returned when key is invalid.
Definition: settings.h:140
settings_status_t settings_get(settings_key_t key, uint8_t index, uint8_t *value, settings_length_t *value_size)
Fetches the value associated with the given key.
settings_status_t settings_add(settings_key_t key, const uint8_t *value, settings_length_t value_size)
Adds the given key-value pair to the end of the settings store.
static uip_ds6_addr_t * addr
Pointer to a router list entry.
Definition: uip-nd6.c:124
void eeprom_read(eeprom_addr_t addr, unsigned char *buf, int size)
Read data from the EEPROM.
Definition: eeprom.c:49
Settings Manager.
settings_iter_t settings_iter_begin(void)
Will return SETTINGS_INVALID_ITER if the settings store is empty.
settings_iter_t settings_iter_next(settings_iter_t iter)
Will return SETTINGS_INVALID_ITER if at the end of settings list.
settings_status_t settings_delete(settings_key_t key, uint8_t index)
Removes the given key (at the given index) from the settings store.
void settings_wipe(void)
Reinitializes all of the EEPROM used by settings.
EEPROM functions.
void eeprom_write(eeprom_addr_t addr, unsigned char *buf, int size)
Write a buffer into EEPROM.
Definition: eeprom.c:42
uint8_t settings_check(settings_key_t key, uint8_t index)
Checks to see if the given key exists.
settings_status_t settings_set(settings_key_t key, const uint8_t *value, settings_length_t value_size)
Sets the value for the given key.
#define SETTINGS_INVALID_ITER
Returned if no (further) element was found.
Definition: settings.h:142