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 #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  }
216 
217  /* This case requires the settings store to be shifted.
218  * Currently unimplemented. TODO: Writeme!
219  */
220  ret = SETTINGS_STATUS_UNIMPLEMENTED;
221 
222  return ret;
223 }
224 
225 /*****************************************************************************/
226 // MARK: - Public Functions
227 /*****************************************************************************/
228 
229 /*---------------------------------------------------------------------------*/
230 uint8_t
231 settings_check(settings_key_t key, uint8_t index)
232 {
233  uint8_t ret = 0;
234 
235  settings_iter_t iter;
236 
237  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
238  if(settings_iter_get_key(iter) == key) {
239  if(!index) {
240  ret = 1;
241  break;
242  } else {
243  index--;
244  }
245  }
246  }
247 
248  return ret;
249 }
250 
251 /*---------------------------------------------------------------------------*/
252 settings_status_t
253 settings_get(settings_key_t key, uint8_t index, uint8_t *value,
254  settings_length_t * value_size)
255 {
256  settings_status_t ret = SETTINGS_STATUS_NOT_FOUND;
257 
258  settings_iter_t iter;
259 
260  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
261  if(settings_iter_get_key(iter) == key) {
262  if(!index) {
263  /* We found it! */
264  *value_size = settings_iter_get_value_bytes(iter,
265  (void *)value,
266  *value_size);
267  ret = SETTINGS_STATUS_OK;
268  break;
269  } else {
270  /* Nope, keep looking */
271  index--;
272  }
273  }
274  }
275 
276  return ret;
277 }
278 
279 /*---------------------------------------------------------------------------*/
280 settings_status_t
281 settings_add(settings_key_t key, const uint8_t *value,
282  settings_length_t value_size)
283 {
284  settings_status_t ret = SETTINGS_STATUS_FAILURE;
285 
286  settings_iter_t iter;
287 
288  item_header_t header;
289 
290  /* Find the last item. */
291  for(iter = settings_iter_begin(); settings_iter_next(iter);
292  iter = settings_iter_next(iter)) {
293  /* This block intentionally left blank. */
294  }
295 
296  if(iter) {
297  /* Value address of item is the same as the iterator for next item. */
298  iter = settings_iter_get_value_addr(iter);
299  } else {
300  /* This will be the first setting! */
301  iter = SETTINGS_TOP_ADDR;
302  }
303 
304  if(iter < SETTINGS_BOTTOM_ADDR + value_size + sizeof(header)) {
305  /* This value is too big to store. */
306  ret = SETTINGS_STATUS_OUT_OF_SPACE;
307  goto bail;
308  }
309 
310  header.key = key;
311 
312  if(value_size < 0x80) {
313  /* If the value size is less than 128, then
314  * we can get away with only using one byte
315  * to store the size.
316  */
317  header.size_low = value_size;
318  }
319 #if SETTINGS_CONF_SUPPORT_LARGE_VALUES
320  else if(value_size <= SETTINGS_MAX_VALUE_SIZE) {
321  /* If the value size is larger than or equal to 128,
322  * then we need to use two bytes. Store
323  * the most significant 7 bits in the first
324  * size byte (with MSB set) and store the
325  * least significant bits in the second
326  * byte (with LSB clear)
327  */
328  header.size_low = (value_size >> 7) | 0x80;
329  header.size_extra = value_size & ~0x80;
330  }
331 #endif
332  else {
333  /* Value size way too big! */
334  ret = SETTINGS_STATUS_VALUE_TOO_BIG;
335  goto bail;
336  }
337 
338  header.size_check = ~header.size_low;
339 
340  /* Write the header first */
341  eeprom_write(iter - sizeof(header), (uint8_t *)&header, sizeof(header));
342 
343  /* Sanity check, remove once confident */
344  if(settings_iter_get_value_length(iter) != value_size) {
345  goto bail;
346  }
347 
348  /* Now write the data */
349  eeprom_write(settings_iter_get_value_addr(iter), (uint8_t *)value, value_size);
350 
351  /* This should be the last item. If this is not the case,
352  * then we need to clear out the phantom setting.
353  */
354  if((iter = settings_iter_next(iter))) {
355  memset(&header, 0xFF, sizeof(header));
356 
357  eeprom_write(iter - sizeof(header),(uint8_t *)&header, sizeof(header));
358  }
359 
360  ret = SETTINGS_STATUS_OK;
361 
362 bail:
363  return ret;
364 }
365 
366 /*---------------------------------------------------------------------------*/
367 settings_status_t
368 settings_set(settings_key_t key, const uint8_t *value,
369  settings_length_t value_size)
370 {
371  settings_status_t ret = SETTINGS_STATUS_FAILURE;
372 
373  settings_iter_t iter;
374 
375  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
376  if(settings_iter_get_key(iter) == key) {
377  break;
378  }
379  }
380 
381  if((iter == EEPROM_NULL) || !settings_iter_is_valid(iter)) {
382  ret = settings_add(key, value, value_size);
383  goto bail;
384  }
385 
386  if(value_size != settings_iter_get_value_length(iter)) {
387  /* Requires the settings store to be shifted. Currently unimplemented. */
388  ret = SETTINGS_STATUS_UNIMPLEMENTED;
389  goto bail;
390  }
391 
392  /* Now write the data */
393  eeprom_write(settings_iter_get_value_addr(iter),
394  (uint8_t *)value, value_size);
395 
396  ret = SETTINGS_STATUS_OK;
397 
398 bail:
399  return ret;
400 }
401 
402 /*---------------------------------------------------------------------------*/
403 settings_status_t
404 settings_delete(settings_key_t key, uint8_t index)
405 {
406  settings_status_t ret = SETTINGS_STATUS_NOT_FOUND;
407 
408  settings_iter_t iter;
409 
410  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
411  if(settings_iter_get_key(iter) == key) {
412  if(!index) {
413  /* We found it! */
414  ret = settings_iter_delete(iter);
415  break;
416  } else {
417  /* Nope, keep looking */
418  index--;
419  }
420  }
421  }
422 
423  return ret;
424 }
425 
426 /*---------------------------------------------------------------------------*/
427 void
428 settings_wipe(void)
429 {
430  /* Simply making the first item invalid will effectively
431  * clear the key-value store.
432  */
433  const uint32_t x = 0xFFFFFF;
434 
435  eeprom_write(SETTINGS_TOP_ADDR - sizeof(x), (uint8_t *)&x, sizeof(x));
436 }
437 
438 /*****************************************************************************/
439 // MARK: - Other Functions
440 /*****************************************************************************/
441 
442 #if DEBUG
443 #include <stdio.h>
444 /*---------------------------------------------------------------------------*/
445 void
446 settings_debug_dump(void)
447 {
448  settings_iter_t iter;
449 
450  printf("{\n");
451  for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) {
452  settings_length_t len = settings_iter_get_value_length(iter);
453  eeprom_addr_t addr = settings_iter_get_value_addr(iter);
454  uint8_t byte;
455 
456  union {
457  settings_key_t key;
458  char bytes[0];
459  } u;
460 
461  u.key = settings_iter_get_key(iter);
462 
463  printf("\t\"%c%c\" = <", u.bytes[0], u.bytes[1]);
464 
465  for(; len; len--, addr++) {
466  eeprom_read(addr, &byte, 1);
467  printf("%02X", byte);
468  if(len != 1) {
469  printf(" ");
470  }
471  }
472 
473  printf(">;\n");
474  }
475  printf("}\n");
476 }
477 #endif /* DEBUG */
478 
479 #endif /* CONTIKI_CONF_SETTINGS_MANAGER */
void eeprom_read(eeprom_addr_t addr, unsigned char *buf, int size)
Read data from the EEPROM.
Definition: eeprom.c:49
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_delete(settings_key_t key, uint8_t index)
Removes the given key (at the given index) from the settings store.
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.
uint8_t settings_check(settings_key_t key, uint8_t index)
Checks to see if the given key exists.
#define SETTINGS_INVALID_ITER
Returned if no (further) element was found.
Definition: settings.h:142
void eeprom_write(eeprom_addr_t addr, unsigned char *buf, int size)
Write a buffer into EEPROM.
Definition: eeprom.c:42
settings_iter_t settings_iter_next(settings_iter_t iter)
Will return SETTINGS_INVALID_ITER if at the end of settings list.
EEPROM functions.
void settings_wipe(void)
Reinitializes all of the EEPROM used by settings.
Settings Manager.
#define SETTINGS_INVALID_KEY
Returned when key is invalid.
Definition: settings.h:140
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.
settings_iter_t settings_iter_begin()
Will return SETTINGS_INVALID_ITER if the settings store is empty.