core/lib/settings: Generalized Settings Manager to work on any platform
This commit moves the Settings Manager from the AVR codebase into the Contiki core library. Any platform that implements the Contiki EEPROM API can now use the Settings Manager's key-value store for storing their persistent configuration info. The Settings Manager is a EEPROM-based key-value store. Keys are 16-bit integers and values may be up to 16,383 bytes long. It is intended to be used to store configuration-related information, like network settings, radio channels, etc. * Robust data format which requires no initialization. * Supports multiple values with the same key. * Data can be appended without erasing EEPROM. * Max size of settings data can be easily increased in the future, as long as it doesn't overlap with application data. The format was inspired by the [OLPC manufacturing data format][]. Since the beginning of EEPROM often contains application-specific information, the best place to store settings is at the end of EEPROM (the "top"). Because we are starting at the end of EEPROM, it makes sense to grow the list of key-value pairs downward, toward the start of EEPROM. Each key-value pair is stored in memory in the following format: Order | Size | Name | Description --------:|---------:|--------------|------------------------------- 0 | 2 | `key` | 16-bit key -2 | 1 | `size_check` | One's-complement of next byte -3 | 1 or 2 | `size` | The size of `value`, in bytes -4 or -5 | variable | `value` | Value associated with `key` The end of the key-value pairs is denoted by the first invalid entry. An invalid entry has any of the following attributes: * The `size_check` byte doesn't match the one's compliment of the `size` byte (or `size_low` byte). * The key has a value of 0x0000. [OLPC manufacturing data format]: http://wiki.laptop.org/go/Manufacturing_data
This commit is contained in:
parent
6ba28bf74f
commit
28a1e40ebd
12 changed files with 1009 additions and 395 deletions
|
@ -13,7 +13,7 @@ CONTIKI_CPU=$(CONTIKI)/cpu/avr
|
|||
### These directories will be searched for the specified source files
|
||||
### TARGETLIBS are platform-specific routines in the contiki library path
|
||||
CONTIKI_CPU_DIRS = . dev
|
||||
AVR = clock.c mtarch.c eeprom.c flash.c rs232.c leds-arch.c watchdog.c rtimer-arch.c bootloader.c settings.c
|
||||
AVR = clock.c mtarch.c eeprom.c flash.c rs232.c leds-arch.c watchdog.c rtimer-arch.c bootloader.c
|
||||
ELFLOADER = elfloader.c elfloader-avr.c symtab-avr.c
|
||||
TARGETLIBS = random.c leds.c
|
||||
|
||||
|
|
|
@ -1,275 +0,0 @@
|
|||
|
||||
#include <stdbool.h>
|
||||
//#include <sys/param.h>
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#include <avr/io.h>
|
||||
#include "settings.h"
|
||||
#include "dev/eeprom.h"
|
||||
#include <stdio.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include <avr/eeprom.h>
|
||||
#include <avr/wdt.h>
|
||||
#include "contiki.h"
|
||||
|
||||
#ifndef SETTINGS_TOP_ADDR
|
||||
#define SETTINGS_TOP_ADDR (E2END-4) //!< Defaults to end of EEPROM, minus 4 bytes for avrdude erase count
|
||||
#endif
|
||||
|
||||
#ifndef SETTINGS_MAX_SIZE
|
||||
#define SETTINGS_MAX_SIZE (1024) //!< Defaults to 1KB
|
||||
#endif
|
||||
|
||||
//#pragma mark -
|
||||
//#pragma mark Private Functions
|
||||
|
||||
typedef struct {
|
||||
uint8_t size_extra;
|
||||
uint8_t size_low;
|
||||
uint8_t size_check;
|
||||
settings_key_t key;
|
||||
} item_header_t;
|
||||
|
||||
inline static bool
|
||||
settings_is_item_valid_(eeprom_addr_t item_addr) {
|
||||
item_header_t header = {};
|
||||
|
||||
if(item_addr==EEPROM_NULL)
|
||||
return false;
|
||||
|
||||
// if((SETTINGS_TOP_ADDR-item_addr)>=SETTINGS_MAX_SIZE-3)
|
||||
// return false;
|
||||
|
||||
eeprom_read(
|
||||
item_addr+1-sizeof(header),
|
||||
(unsigned char*)&header,
|
||||
sizeof(header)
|
||||
);
|
||||
|
||||
if((uint8_t)header.size_check!=(uint8_t)~header.size_low)
|
||||
return false;
|
||||
|
||||
// TODO: Check length as well
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline static settings_key_t
|
||||
settings_get_key_(eeprom_addr_t item_addr) {
|
||||
item_header_t header;
|
||||
|
||||
eeprom_read(
|
||||
item_addr+1-sizeof(header),
|
||||
(unsigned char*)&header,
|
||||
sizeof(header)
|
||||
);
|
||||
|
||||
if((uint8_t)header.size_check!=(uint8_t)~header.size_low)
|
||||
return SETTINGS_INVALID_KEY;
|
||||
|
||||
return header.key;
|
||||
}
|
||||
|
||||
inline static size_t
|
||||
settings_get_value_length_(eeprom_addr_t item_addr) {
|
||||
item_header_t header;
|
||||
size_t ret = 0;
|
||||
|
||||
eeprom_read(
|
||||
item_addr+1-sizeof(header),
|
||||
(unsigned char*)&header,
|
||||
sizeof(header)
|
||||
);
|
||||
|
||||
if((uint8_t)header.size_check!=(uint8_t)~header.size_low)
|
||||
goto bail;
|
||||
|
||||
ret = header.size_low;
|
||||
|
||||
if(ret&(1<<7)) {
|
||||
ret = ((ret&~(1<<7))<<8) | header.size_extra;
|
||||
}
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline static eeprom_addr_t
|
||||
settings_get_value_addr_(eeprom_addr_t item_addr) {
|
||||
size_t len = settings_get_value_length_(item_addr);
|
||||
|
||||
if(len>128)
|
||||
return item_addr+1-sizeof(item_header_t)-len;
|
||||
|
||||
return item_addr+1-sizeof(item_header_t)+1-len;
|
||||
}
|
||||
|
||||
inline static eeprom_addr_t
|
||||
settings_next_item_(eeprom_addr_t item_addr) {
|
||||
return settings_get_value_addr_(item_addr)-1;
|
||||
}
|
||||
|
||||
|
||||
//#pragma mark -
|
||||
//#pragma mark Public Functions
|
||||
|
||||
bool
|
||||
settings_check(settings_key_t key,uint8_t index) {
|
||||
bool ret = false;
|
||||
eeprom_addr_t current_item = SETTINGS_TOP_ADDR;
|
||||
|
||||
for(current_item=SETTINGS_TOP_ADDR;settings_is_item_valid_(current_item);current_item=settings_next_item_(current_item)) {
|
||||
if(settings_get_key_(current_item)==key) {
|
||||
if(!index) {
|
||||
ret = true;
|
||||
break;
|
||||
} else {
|
||||
// Nope, keep looking
|
||||
index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
settings_status_t
|
||||
settings_get(settings_key_t key,uint8_t index,unsigned char* value,size_t* value_size) {
|
||||
settings_status_t ret = SETTINGS_STATUS_NOT_FOUND;
|
||||
eeprom_addr_t current_item = SETTINGS_TOP_ADDR;
|
||||
|
||||
for(current_item=SETTINGS_TOP_ADDR;settings_is_item_valid_(current_item);current_item=settings_next_item_(current_item)) {
|
||||
if(settings_get_key_(current_item)==key) {
|
||||
if(!index) {
|
||||
// We found it!
|
||||
*value_size = MIN(*value_size,settings_get_value_length_(current_item));
|
||||
eeprom_read(
|
||||
settings_get_value_addr_(current_item),
|
||||
value,
|
||||
*value_size
|
||||
);
|
||||
ret = SETTINGS_STATUS_OK;
|
||||
break;
|
||||
} else {
|
||||
// Nope, keep looking
|
||||
index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
settings_status_t
|
||||
settings_add(settings_key_t key,const unsigned char* value,size_t value_size) {
|
||||
settings_status_t ret = SETTINGS_STATUS_FAILURE;
|
||||
eeprom_addr_t current_item = SETTINGS_TOP_ADDR;
|
||||
item_header_t header;
|
||||
|
||||
// Find end of list
|
||||
for(current_item=SETTINGS_TOP_ADDR;settings_is_item_valid_(current_item);current_item=settings_next_item_(current_item));
|
||||
|
||||
if(current_item==EEPROM_NULL)
|
||||
goto bail;
|
||||
|
||||
// TODO: size check!
|
||||
|
||||
header.key = key;
|
||||
|
||||
if(value_size<0x80) {
|
||||
// If the value size is less than 128, then
|
||||
// we can get away with only using one byte
|
||||
// as the size.
|
||||
header.size_low = value_size;
|
||||
} else if(value_size<=SETTINGS_MAX_VALUE_SIZE) {
|
||||
// If the value size of larger than 128,
|
||||
// then we need to use two bytes. Store
|
||||
// the most significant 7 bits in the first
|
||||
// size byte (with MSB set) and store the
|
||||
// least significant bits in the second
|
||||
// byte (with LSB clear)
|
||||
header.size_low = (value_size>>7) | 0x80;
|
||||
header.size_extra = value_size & ~0x80;
|
||||
} else {
|
||||
// Value size way too big!
|
||||
goto bail;
|
||||
}
|
||||
|
||||
header.size_check = ~header.size_low;
|
||||
|
||||
// Write the header first
|
||||
eeprom_write(
|
||||
current_item+1-sizeof(header),
|
||||
(unsigned char*)&header,
|
||||
sizeof(header)
|
||||
);
|
||||
|
||||
// Sanity check, remove once confident
|
||||
if(settings_get_value_length_(current_item)!=value_size) {
|
||||
goto bail;
|
||||
}
|
||||
|
||||
// Now write the data
|
||||
eeprom_write(
|
||||
settings_get_value_addr_(current_item),
|
||||
(unsigned char*)value,
|
||||
value_size
|
||||
);
|
||||
|
||||
ret = SETTINGS_STATUS_OK;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
settings_status_t
|
||||
settings_set(settings_key_t key,const unsigned char* value,size_t value_size) {
|
||||
settings_status_t ret = SETTINGS_STATUS_FAILURE;
|
||||
eeprom_addr_t current_item = SETTINGS_TOP_ADDR;
|
||||
|
||||
for(current_item=SETTINGS_TOP_ADDR;settings_is_item_valid_(current_item);current_item=settings_next_item_(current_item)) {
|
||||
if(settings_get_key_(current_item)==key) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if((current_item==EEPROM_NULL) || !settings_is_item_valid_(current_item)) {
|
||||
ret = settings_add(key,value,value_size);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if(value_size!=settings_get_value_length_(current_item)) {
|
||||
// Requires the settings store to be shifted. Currently unimplemented.
|
||||
goto bail;
|
||||
}
|
||||
|
||||
// Now write the data
|
||||
eeprom_write(
|
||||
settings_get_value_addr_(current_item),
|
||||
(unsigned char*)value,
|
||||
value_size
|
||||
);
|
||||
|
||||
ret = SETTINGS_STATUS_OK;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
settings_status_t
|
||||
settings_delete(settings_key_t key,uint8_t index) {
|
||||
// Requires the settings store to be shifted. Currently unimplemented.
|
||||
// TODO: Writeme!
|
||||
return SETTINGS_STATUS_UNIMPLEMENTED;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
settings_wipe(void) {
|
||||
size_t i = SETTINGS_TOP_ADDR-SETTINGS_MAX_SIZE;
|
||||
for(;i<=SETTINGS_TOP_ADDR;i++) {
|
||||
eeprom_write_byte((uint8_t*)i,0xFF);
|
||||
wdt_reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
#ifndef __AVR_SETTINGS_H__
|
||||
#define __AVR_SETTINGS_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef uint16_t settings_key_t;
|
||||
|
||||
#define SETTINGS_KEY_EUI64 'E'*256+'8' //!< Value always 8 bytes long
|
||||
#define SETTINGS_KEY_EUI48 'E'*256+'6' //!< Value always 8 bytes long
|
||||
#define SETTINGS_KEY_CHANNEL 'C'*256+'H' //!< Value always 1 byte long
|
||||
#define SETTINGS_KEY_TXPOWER 'T'*256+'P' //!< Value always 1 byte long
|
||||
#define SETTINGS_KEY_PAN_ID 'P'*256+'N' //!< Value always 2 bytes long
|
||||
#define SETTINGS_KEY_PAN_ADDR 'P'*256+'A' //!< Value always 2 bytes long
|
||||
#define SETTINGS_KEY_AES128KEY 'S'*256+'K' //!< Value always 16 bytes long
|
||||
#define SETTINGS_KEY_AES128ENABLED 'S'*256+'E' //!< Value always 16 bytes long
|
||||
|
||||
typedef enum {
|
||||
SETTINGS_STATUS_OK=0,
|
||||
SETTINGS_STATUS_INVALID_ARGUMENT,
|
||||
SETTINGS_STATUS_NOT_FOUND,
|
||||
SETTINGS_STATUS_OUT_OF_SPACE,
|
||||
SETTINGS_STATUS_UNIMPLEMENTED,
|
||||
SETTINGS_STATUS_FAILURE,
|
||||
} settings_status_t;
|
||||
|
||||
// Use this when you want to retrieve the last item
|
||||
#define SETTINGS_LAST_INDEX (0xFF)
|
||||
|
||||
#define SETTINGS_INVALID_KEY (0x00)
|
||||
|
||||
#define SETTINGS_MAX_VALUE_SIZE (0x3FFF) // 16383 bytes
|
||||
|
||||
extern settings_status_t settings_get(settings_key_t key,uint8_t index,unsigned char* value,size_t* value_size);
|
||||
extern settings_status_t settings_add(settings_key_t key,const unsigned char* value,size_t value_size);
|
||||
extern bool settings_check(settings_key_t key,uint8_t index);
|
||||
extern void settings_wipe(void);
|
||||
|
||||
extern settings_status_t settings_set(settings_key_t key,const unsigned char* value,size_t value_size);
|
||||
extern settings_status_t settings_delete(settings_key_t key,uint8_t index);
|
||||
|
||||
//#pragma mark -
|
||||
//#pragma mark Inline convenience functions
|
||||
|
||||
static inline uint8_t
|
||||
settings_get_uint8(settings_key_t key,uint8_t index) {
|
||||
uint8_t ret = 0;
|
||||
size_t sizeof_uint8 = sizeof(uint8_t);
|
||||
settings_get(key,index,(unsigned char*)&ret,&sizeof_uint8);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline settings_status_t
|
||||
settings_add_uint8(settings_key_t key,uint8_t value) {
|
||||
return settings_add(key,(const unsigned char*)&value,sizeof(uint8_t));
|
||||
}
|
||||
|
||||
static inline settings_status_t
|
||||
settings_set_uint8(settings_key_t key,uint8_t value) {
|
||||
return settings_set(key,(const unsigned char*)&value,sizeof(uint8_t));
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
settings_get_uint16(settings_key_t key,uint8_t index) {
|
||||
uint16_t ret = 0;
|
||||
size_t sizeof_uint16 = sizeof(uint16_t);
|
||||
settings_get(key,index,(unsigned char*)&ret,&sizeof_uint16);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline settings_status_t
|
||||
settings_add_uint16(settings_key_t key,uint16_t value) {
|
||||
return settings_add(key,(const unsigned char*)&value,sizeof(uint16_t));
|
||||
}
|
||||
|
||||
static inline settings_status_t
|
||||
settings_set_uint16(settings_key_t key,uint16_t value) {
|
||||
return settings_set(key,(const unsigned char*)&value,sizeof(uint16_t));
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
settings_get_uint32(settings_key_t key,uint8_t index) {
|
||||
uint32_t ret = 0;
|
||||
size_t sizeof_uint32 = sizeof(uint32_t);
|
||||
settings_get(key,index,(unsigned char*)&ret,&sizeof_uint32);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline settings_status_t
|
||||
settings_add_uint32(settings_key_t key,uint32_t value) {
|
||||
return settings_add(key,(const unsigned char*)&value,sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static inline settings_status_t
|
||||
settings_set_uint32(settings_key_t key,uint32_t value) {
|
||||
return settings_set(key,(const unsigned char*)&value,sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
settings_get_uint64(settings_key_t key,uint8_t index) {
|
||||
uint64_t ret = 0;
|
||||
size_t sizeof_uint64 = sizeof(uint64_t);
|
||||
settings_get(key,index,(unsigned char*)&ret,&sizeof_uint64);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline settings_status_t
|
||||
settings_add_uint64(settings_key_t key,uint64_t value) {
|
||||
return settings_add(key,(const unsigned char*)&value,sizeof(uint64_t));
|
||||
}
|
||||
|
||||
static inline settings_status_t
|
||||
settings_set_uint64(settings_key_t key,uint64_t value) {
|
||||
return settings_set(key,(const unsigned char*)&value,sizeof(uint64_t));
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue