diff --git a/Makefile.include b/Makefile.include index f24507a1a..68b35e62d 100644 --- a/Makefile.include +++ b/Makefile.include @@ -60,7 +60,7 @@ SYSTEM = process.c procinit.c autostart.c elfloader.c profile.c \ timetable.c timetable-aggregate.c compower.c serial-line.c THREADS = mt.c LIBS = memb.c mmem.c timer.c list.c etimer.c ctimer.c energest.c rtimer.c stimer.c \ - print-stats.c ifft.c crc16.c random.c checkpoint.c ringbuf.c + print-stats.c ifft.c crc16.c random.c checkpoint.c ringbuf.c settings.c DEV = nullradio.c include $(CONTIKI)/core/net/Makefile.uip diff --git a/core/lib/settings.c b/core/lib/settings.c new file mode 100644 index 000000000..88101fb9d --- /dev/null +++ b/core/lib/settings.c @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2013, Robert Quattlebaum. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + */ + +#include "contiki.h" +#include "settings.h" +#include "dev/eeprom.h" + +#if CONTIKI_CONF_SETTINGS_MANAGER + +#if !EEPROM_CONF_SIZE +#error CONTIKI_CONF_SETTINGS_MANAGER has been set, but EEPROM_CONF_SIZE hasnt! +#endif + +#ifndef EEPROM_END_ADDR +#define EEPROM_END_ADDR (EEPROM_CONF_SIZE - 1) +#endif + +#ifndef SETTINGS_MAX_SIZE +/** The maximum amount EEPROM dedicated to settings. */ +#define SETTINGS_MAX_SIZE (127) /**< Defaults to 127 bytes */ +#endif + +#ifndef SETTINGS_TOP_ADDR +/** The top address in EEPROM that settings should use. Inclusive. */ +#define SETTINGS_TOP_ADDR (settings_iter_t)(EEPROM_END_ADDR) +#endif + +#ifndef SETTINGS_BOTTOM_ADDR +/** The lowest address in EEPROM that settings should use. Inclusive. */ +#define SETTINGS_BOTTOM_ADDR (SETTINGS_TOP_ADDR + 1 - SETTINGS_MAX_SIZE) +#endif + +#ifndef MIN +#define MIN(a,b) ((a)<(b)?a:b) +#endif + +typedef struct { +#if SETTINGS_CONF_SUPPORT_LARGE_VALUES + uint8_t size_extra; +#endif + uint8_t size_low; + uint8_t size_check; + settings_key_t key; +} item_header_t; + +/*****************************************************************************/ +// MARK: - Public Travesal Functions +/*****************************************************************************/ + +/*---------------------------------------------------------------------------*/ +settings_iter_t +settings_iter_begin() +{ + return settings_iter_is_valid(SETTINGS_TOP_ADDR) ? SETTINGS_TOP_ADDR : 0; +} + +/*---------------------------------------------------------------------------*/ +settings_iter_t +settings_iter_next(settings_iter_t ret) +{ + if(ret) { + /* A settings iterator always points to the first byte + * after the actual key-value pair in memory. This means that + * the address of our value in EEPROM just happens + * to be the address of our next iterator. + */ + ret = settings_iter_get_value_addr(ret); + return settings_iter_is_valid(ret) ? ret : 0; + } + return SETTINGS_INVALID_ITER; +} + +/*---------------------------------------------------------------------------*/ +uint8_t +settings_iter_is_valid(settings_iter_t iter) +{ + item_header_t header = { 0 }; + + if(iter == EEPROM_NULL) { + return 0; + } + + if(iter < SETTINGS_BOTTOM_ADDR + sizeof(header)) { + return 0; + } + + eeprom_read(iter - sizeof(header), (uint8_t *)&header, sizeof(header)); + + if((uint8_t) header.size_check != (uint8_t) ~ header.size_low) { + return 0; + } + + if(iter < SETTINGS_BOTTOM_ADDR + sizeof(header) + settings_iter_get_value_length(iter)) { + return 0; + } + + return 1; +} + +/*---------------------------------------------------------------------------*/ +settings_key_t +settings_iter_get_key(settings_iter_t iter) +{ + item_header_t header; + + eeprom_read(iter - sizeof(header), (uint8_t *)&header, sizeof(header)); + + if((uint8_t) header.size_check != (uint8_t)~header.size_low) { + return SETTINGS_INVALID_KEY; + } + + return header.key; +} + +/*---------------------------------------------------------------------------*/ +settings_length_t +settings_iter_get_value_length(settings_iter_t iter) +{ + item_header_t header; + + settings_length_t ret = 0; + + eeprom_read(iter - sizeof(header), (uint8_t *)&header, sizeof(header) ); + + if((uint8_t)header.size_check == (uint8_t)~header.size_low) { + ret = header.size_low; + +#if SETTINGS_CONF_SUPPORT_LARGE_VALUES + if(ret & (1 << 7)) { + ret = ((ret & ~(1 << 7)) << 7) | header.size_extra; + } +#endif + } + + return ret; +} + +/*---------------------------------------------------------------------------*/ +eeprom_addr_t +settings_iter_get_value_addr(settings_iter_t iter) +{ + settings_length_t len = settings_iter_get_value_length(iter); +#if SETTINGS_CONF_SUPPORT_LARGE_VALUES + len += (len >= 128); +#endif + return iter - sizeof(item_header_t) - len; +} + +/*---------------------------------------------------------------------------*/ +settings_length_t +settings_iter_get_value_bytes(settings_iter_t iter, void *bytes, + settings_length_t max_length) +{ + max_length = MIN(max_length, settings_iter_get_value_length(iter)); + + eeprom_read(settings_iter_get_value_addr(iter), bytes, max_length); + + return max_length; +} + +/*---------------------------------------------------------------------------*/ +settings_status_t +settings_iter_delete(settings_iter_t iter) +{ + settings_status_t ret = SETTINGS_STATUS_FAILURE; + + settings_iter_t next = settings_iter_next(iter); + + if(!next) { + /* Special case: we are the last item. we can get away with + * just wiping out our own header. + */ + item_header_t header; + + memset(&header, 0xFF, sizeof(header)); + + eeprom_write(iter - sizeof(header), (uint8_t *)&header, sizeof(header)); + + ret = SETTINGS_STATUS_OK; + } + + /* This case requires the settings store to be shifted. + * Currently unimplemented. TODO: Writeme! + */ + ret = SETTINGS_STATUS_UNIMPLEMENTED; + + return ret; +} + +/*****************************************************************************/ +// MARK: - Public Functions +/*****************************************************************************/ + +/*---------------------------------------------------------------------------*/ +uint8_t +settings_check(settings_key_t key, uint8_t index) +{ + uint8_t ret = 0; + + settings_iter_t iter; + + for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) { + if(settings_iter_get_key(iter) == key) { + if(!index) { + ret = 1; + break; + } else { + index--; + } + } + } + + return ret; +} + +/*---------------------------------------------------------------------------*/ +settings_status_t +settings_get(settings_key_t key, uint8_t index, uint8_t *value, + settings_length_t * value_size) +{ + settings_status_t ret = SETTINGS_STATUS_NOT_FOUND; + + settings_iter_t iter; + + for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) { + if(settings_iter_get_key(iter) == key) { + if(!index) { + /* We found it! */ + *value_size = settings_iter_get_value_bytes(iter, + (void *)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 uint8_t *value, + settings_length_t value_size) +{ + settings_status_t ret = SETTINGS_STATUS_FAILURE; + + settings_iter_t iter; + + item_header_t header; + + /* Find the last item. */ + for(iter = settings_iter_begin(); settings_iter_next(iter); + iter = settings_iter_next(iter)) { + /* This block intentionally left blank. */ + } + + if(iter) { + /* Value address of item is the same as the iterator for next item. */ + iter = settings_iter_get_value_addr(iter); + } else { + /* This will be the first setting! */ + iter = SETTINGS_TOP_ADDR; + } + + if(iter < SETTINGS_BOTTOM_ADDR + value_size + sizeof(header)) { + /* This value is too big to store. */ + ret = SETTINGS_STATUS_OUT_OF_SPACE; + goto bail; + } + + 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 + * to store the size. + */ + header.size_low = value_size; + } +#if SETTINGS_CONF_SUPPORT_LARGE_VALUES + else if(value_size <= SETTINGS_MAX_VALUE_SIZE) { + /* If the value size is larger than or equal to 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; + } +#endif + else { + /* Value size way too big! */ + ret = SETTINGS_STATUS_VALUE_TOO_BIG; + goto bail; + } + + header.size_check = ~header.size_low; + + /* Write the header first */ + eeprom_write(iter - sizeof(header), (uint8_t *)&header, sizeof(header)); + + /* Sanity check, remove once confident */ + if(settings_iter_get_value_length(iter) != value_size) { + goto bail; + } + + /* Now write the data */ + eeprom_write(settings_iter_get_value_addr(iter), (uint8_t *)value, value_size); + + /* This should be the last item. If this is not the case, + * then we need to clear out the phantom setting. + */ + if((iter = settings_iter_next(iter))) { + memset(&header, 0xFF, sizeof(header)); + + eeprom_write(iter - sizeof(header),(uint8_t *)&header, sizeof(header)); + } + + ret = SETTINGS_STATUS_OK; + +bail: + return ret; +} + +/*---------------------------------------------------------------------------*/ +settings_status_t +settings_set(settings_key_t key, const uint8_t *value, + settings_length_t value_size) +{ + settings_status_t ret = SETTINGS_STATUS_FAILURE; + + settings_iter_t iter; + + for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) { + if(settings_iter_get_key(iter) == key) { + break; + } + } + + if((iter == EEPROM_NULL) || !settings_iter_is_valid(iter)) { + ret = settings_add(key, value, value_size); + goto bail; + } + + if(value_size != settings_iter_get_value_length(iter)) { + /* Requires the settings store to be shifted. Currently unimplemented. */ + ret = SETTINGS_STATUS_UNIMPLEMENTED; + goto bail; + } + + /* Now write the data */ + eeprom_write(settings_iter_get_value_addr(iter), + (uint8_t *)value, value_size); + + ret = SETTINGS_STATUS_OK; + +bail: + return ret; +} + +/*---------------------------------------------------------------------------*/ +settings_status_t +settings_delete(settings_key_t key, uint8_t index) +{ + settings_status_t ret = SETTINGS_STATUS_NOT_FOUND; + + settings_iter_t iter; + + for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) { + if(settings_iter_get_key(iter) == key) { + if(!index) { + /* We found it! */ + ret = settings_iter_delete(iter); + break; + } else { + /* Nope, keep looking */ + index--; + } + } + } + + return ret; +} + +/*---------------------------------------------------------------------------*/ +void +settings_wipe(void) +{ + /* Simply making the first item invalid will effectively + * clear the key-value store. + */ + const uint32_t x = 0xFFFFFF; + + eeprom_write(SETTINGS_TOP_ADDR - sizeof(x), (uint8_t *)&x, sizeof(x)); +} + +/*****************************************************************************/ +// MARK: - Other Functions +/*****************************************************************************/ + +#if DEBUG +#include +/*---------------------------------------------------------------------------*/ +void +settings_debug_dump(void) +{ + settings_iter_t iter; + + printf("{\n"); + for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) { + settings_length_t len = settings_iter_get_value_length(iter); + eeprom_addr_t addr = settings_iter_get_value_addr(iter); + uint8_t byte; + + union { + settings_key_t key; + char bytes[0]; + } u; + + u.key = settings_iter_get_key(iter); + + printf("\t\"%c%c\" = <", u.bytes[0], u.bytes[1]); + + for(; len; len--, addr++) { + eeprom_read(addr, &byte, 1); + printf("%02X", byte); + if(len != 1) { + printf(" "); + } + } + + printf(">;\n"); + } + printf("}\n"); +} +#endif /* DEBUG */ + +#endif /* CONTIKI_CONF_SETTINGS_MANAGER */ diff --git a/core/lib/settings.h b/core/lib/settings.h new file mode 100644 index 000000000..7dc1ce4f5 --- /dev/null +++ b/core/lib/settings.h @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2013, Robert Quattlebaum + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + */ + +#ifndef __CONTIKI_SETTINGS_H__ +#define __CONTIKI_SETTINGS_H__ + +/** @file settings.h + * @brief Settings Manager + * @author Robert Quattlebaum + * + * ## Overview ## + * + * 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. + * + * ## Features ## + * + * * 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. + * + * ## Data Format ## + * + * The format was inspired by OLPC manufacturing data, as described here: + * + * + * Since the beginning of EEPROM often contains application-specific + * information, the best place to store settings is at the end of + * EEPROM. 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: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
OrderSize (in bytes)NameDescription
02key
-21size_checkOne's-complement of next byte
-31 or 2sizeThe size of the value, in bytes.
-4 or -5variablevalue
+ * + * 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. + * + */ + +#include +#include +#include "dev/eeprom.h" + +/*****************************************************************************/ +// MARK: - Types + +typedef enum { + SETTINGS_STATUS_OK = 0, + SETTINGS_STATUS_FAILURE, + SETTINGS_STATUS_INVALID_ARGUMENT, + SETTINGS_STATUS_NOT_FOUND, + SETTINGS_STATUS_OUT_OF_SPACE, + SETTINGS_STATUS_VALUE_TOO_BIG, + SETTINGS_STATUS_UNIMPLEMENTED, +} settings_status_t; + +typedef uint16_t settings_key_t; + +typedef uint16_t settings_length_t; + +/*****************************************************************************/ +// MARK: - Settings Keys + +/** Two-character constant macro */ +#define TCC(a,b) ((a)+(b)*256) + +/* All-capital-letter constants are always contiki-defined. */ +#define SETTINGS_KEY_EUI64 TCC('E','8') /**< EUI64 Address, 8 bytes */ +#define SETTINGS_KEY_EUI48 TCC('E','6') /*!< MAC Address, 6 bytes */ +#define SETTINGS_KEY_CHANNEL TCC('C','H') /*!< Channel number, uint8_t */ +#define SETTINGS_KEY_TXPOWER TCC('T','P') /*!< Transmit power, uint8_t */ +#define SETTINGS_KEY_PAN_ID TCC('P','N') /*!< PAN ID, uint16_t */ +#define SETTINGS_KEY_PAN_ADDR TCC('P','A') /*!< PAN address, uint16_t */ +#define SETTINGS_KEY_AES128KEY TCC('S','K') /*!< AES128 key, 16 bytes */ +#define SETTINGS_KEY_AES128ENABLED TCC('S','E') /*!< AES128 enabled, bool */ +#define SETTINGS_KEY_HOSTNAME TCC('H','N') /*!< Hostname, C-String */ +#define SETTINGS_KEY_DOMAINNAME TCC('D','N') /*!< Domainname, C-String */ + +/*****************************************************************************/ +// MARK: - Experimental Settings Keys + +#define SETTINGS_KEY_RDC_INDEX TCC('R','D') /*!< RDC index, uint8_t */ +#define SETTINGS_KEY_CHANNEL_MASK TCC('C','M') /*!< Channel mask, uint16_t */ + +/*****************************************************************************/ +// MARK: - Constants + +/** Use this when you want to retrieve the last item */ +#define SETTINGS_LAST_INDEX 0xFF + +#define SETTINGS_INVALID_KEY 0xFFFF + +#define SETTINGS_INVALID_ITER EEPROM_NULL + +#ifndef SETTINGS_CONF_SUPPORT_LARGE_VALUES +#define SETTINGS_CONF_SUPPORT_LARGE_VALUES 0 +#endif + +#if SETTINGS_CONF_SUPPORT_LARGE_VALUES +#define SETTINGS_MAX_VALUE_SIZE 0x3FFF /* 16383 bytes */ +#else +#define SETTINGS_MAX_VALUE_SIZE 0x7F /* 127 bytes */ +#endif + +/*****************************************************************************/ +// MARK: - Settings accessors + +/** Fetches the value associated with the given key. */ +extern settings_status_t settings_get(settings_key_t key, uint8_t index, + uint8_t *value, + settings_length_t * value_size); + +/** Adds the given key-value pair to the end of the settings store. */ +extern settings_status_t settings_add(settings_key_t key, + const uint8_t *value, + settings_length_t value_size); + +/** Checks to see if the given key exists. */ +extern uint8_t settings_check(settings_key_t key, uint8_t index); + +/** Reinitializes all of the EEPROM used by settings. */ +extern void settings_wipe(void); + +/** Sets the value for the given key. If the key already exists in + * the settings store, then its value will be replaced. + */ +extern settings_status_t settings_set(settings_key_t key, + const uint8_t *value, + settings_length_t value_size); + +/** Removes the given key (at the given index) from the settings store. */ +extern settings_status_t settings_delete(settings_key_t key, uint8_t index); + +/*****************************************************************************/ +// MARK: - Settings traversal functions + +typedef eeprom_addr_t settings_iter_t; + +/** Will return extern SETTINGS_INVALID_ITER if the settings store is empty. */ +extern settings_iter_t settings_iter_begin(); + +/** Will return extern SETTINGS_INVALID_ITER if at the end of settings list. */ +extern settings_iter_t settings_iter_next(settings_iter_t iter); + +extern uint8_t settings_iter_is_valid(settings_iter_t iter); + +extern settings_key_t settings_iter_get_key(settings_iter_t iter); + +extern settings_length_t settings_iter_get_value_length(settings_iter_t iter); + +extern eeprom_addr_t settings_iter_get_value_addr(settings_iter_t iter); + +extern settings_length_t settings_iter_get_value_bytes(settings_iter_t item, + void *bytes, + settings_length_t + max_length); + +extern settings_status_t settings_iter_delete(settings_iter_t item); + +/*****************************************************************************/ +// MARK: - inline convenience functions + +static CC_INLINE const char * +settings_get_cstr(settings_key_t key, uint8_t index, char *c_str, + settings_length_t c_str_size) +{ + /* Save room for the zero termination. */ + c_str_size--; + + if(settings_get(key, index, (uint8_t *)c_str, &c_str_size) == SETTINGS_STATUS_OK) { + /* Zero terminate. */ + c_str[c_str_size] = 0; + } else { + c_str = NULL; + } + return c_str; +} + +static CC_INLINE settings_status_t +settings_set_cstr(settings_key_t key, const char* c_str) +{ + return settings_set(key, (const uint8_t *)c_str, strlen(c_str)); +} + +static CC_INLINE settings_status_t +settings_add_cstr(settings_key_t key, const char* c_str) +{ + return settings_add(key, (const uint8_t *)c_str, strlen(c_str)); +} + +static CC_INLINE uint8_t +settings_get_bool_with_default(settings_key_t key, uint8_t index, + uint8_t default_value) +{ + uint8_t ret = default_value; + settings_length_t sizeof_uint8 = sizeof(uint8_t); + + settings_get(key, index, (uint8_t *)&ret, &sizeof_uint8); + return !!ret; +} + +static CC_INLINE uint8_t +settings_get_uint8(settings_key_t key, uint8_t index) +{ + uint8_t ret = 0; + settings_length_t sizeof_uint8 = sizeof(uint8_t); + + settings_get(key, index, (uint8_t *)&ret, &sizeof_uint8); + return ret; +} + +static CC_INLINE settings_status_t +settings_add_uint8(settings_key_t key, uint8_t value) +{ + return settings_add(key, (const uint8_t *)&value, sizeof(uint8_t)); +} + +static CC_INLINE settings_status_t +settings_set_uint8(settings_key_t key, uint8_t value) +{ + return settings_set(key, (const uint8_t *)&value, sizeof(uint8_t)); +} + +static CC_INLINE uint16_t +settings_get_uint16(settings_key_t key, uint8_t index) +{ + uint16_t ret = 0; + settings_length_t sizeof_uint16 = sizeof(uint16_t); + + settings_get(key, index, (uint8_t *)&ret, &sizeof_uint16); + return ret; +} + +static CC_INLINE settings_status_t +settings_add_uint16(settings_key_t key, uint16_t value) +{ + return settings_add(key, (const uint8_t *)&value, sizeof(uint16_t)); +} + +static CC_INLINE settings_status_t +settings_set_uint16(settings_key_t key, uint16_t value) +{ + return settings_set(key, (const uint8_t *)&value, sizeof(uint16_t)); +} + +static CC_INLINE uint32_t +settings_get_uint32(settings_key_t key, uint8_t index) +{ + uint32_t ret = 0; + settings_length_t sizeof_uint32 = sizeof(uint32_t); + + settings_get(key, index, (uint8_t *)&ret, &sizeof_uint32); + return ret; +} + +static CC_INLINE settings_status_t +settings_add_uint32(settings_key_t key, uint32_t value) +{ + return settings_add(key, (const uint8_t *)&value, sizeof(uint32_t)); +} + +static CC_INLINE settings_status_t +settings_set_uint32(settings_key_t key, uint32_t value) +{ + return settings_set(key, (const uint8_t *)&value, sizeof(uint32_t)); +} + +#if __int64_t_defined +static CC_INLINE uint64_t +settings_get_uint64(settings_key_t key, uint8_t index) +{ + uint64_t ret = 0; + settings_length_t sizeof_uint64 = sizeof(uint64_t); + + settings_get(key, index, (uint8_t *)&ret, &sizeof_uint64); + return ret; +} + +static CC_INLINE settings_status_t +settings_add_uint64(settings_key_t key, uint64_t value) +{ + return settings_add(key, (const uint8_t *)&value, sizeof(uint64_t)); +} + +static CC_INLINE settings_status_t +settings_set_uint64(settings_key_t key, uint64_t value) +{ + return settings_set(key, (const uint8_t *)&value, sizeof(uint64_t)); +} +#endif /* __int64_t_defined */ + +#endif /* !defined(__CONTIKI_SETTINGS_H__) */ diff --git a/cpu/avr/Makefile.avr b/cpu/avr/Makefile.avr index 264408066..42a51fb78 100644 --- a/cpu/avr/Makefile.avr +++ b/cpu/avr/Makefile.avr @@ -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 diff --git a/cpu/avr/settings.c b/cpu/avr/settings.c deleted file mode 100644 index 5c9b255ce..000000000 --- a/cpu/avr/settings.c +++ /dev/null @@ -1,275 +0,0 @@ - -#include -//#include -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#include -#include "settings.h" -#include "dev/eeprom.h" -#include -#include -#include -#include -#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(); - } -} - - diff --git a/cpu/avr/settings.h b/cpu/avr/settings.h deleted file mode 100644 index 9d1a2cff7..000000000 --- a/cpu/avr/settings.h +++ /dev/null @@ -1,118 +0,0 @@ -#ifndef __AVR_SETTINGS_H__ -#define __AVR_SETTINGS_H__ - -#include -#include -#include - -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 diff --git a/examples/settings-example/Makefile b/examples/settings-example/Makefile new file mode 100644 index 000000000..540adff63 --- /dev/null +++ b/examples/settings-example/Makefile @@ -0,0 +1,5 @@ +CONTIKI_PROJECT = settings-example +all: $(CONTIKI_PROJECT) +CFLAGS += -DCONTIKI_CONF_SETTINGS_MANAGER=1 +CONTIKI = ../.. +include $(CONTIKI)/Makefile.include diff --git a/examples/settings-example/settings-example.c b/examples/settings-example/settings-example.c new file mode 100644 index 000000000..023e1f3df --- /dev/null +++ b/examples/settings-example/settings-example.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2013, Robert Quattlebaum. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + */ + +/** + * @file settings-example.c + * @brief Contiki example showing how to use the settings manager. + * @author Robert Quattlebaum + */ + +#include "contiki.h" +#include "lib/settings.h" + +#include /* For printf() */ + +/*---------------------------------------------------------------------------*/ +PROCESS(settings_example_process, "Settings Example Process"); +AUTOSTART_PROCESSES(&settings_example_process); + +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(settings_example_process, ev, data) +{ + int i; + settings_status_t status; + settings_iter_t iter; + char hostname[30]; + uint16_t panid; + uint16_t channel; + + PROCESS_BEGIN(); + + /*************************************************************************/ + /* Basic setting of parameters */ + + status = settings_set_uint16(SETTINGS_KEY_PAN_ID, 0xABCD); + if(SETTINGS_STATUS_OK != status) { + printf("settings-example: `set` failed: %d\n", status); + } + + status = settings_set_uint8(SETTINGS_KEY_CHANNEL, 26); + if(SETTINGS_STATUS_OK != status) { + printf("settings-example: `set` failed: %d\n", status); + } + + status = settings_set_cstr(SETTINGS_KEY_HOSTNAME, "contiki.local"); + if(SETTINGS_STATUS_OK != status) { + printf("settings-example: `set` failed: %d\n", status); + } + + /*************************************************************************/ + /* Basic getting of parameters */ + + panid = settings_get_uint16(SETTINGS_KEY_PAN_ID, 0); + if(0xABCD != panid) { + printf("settings-example: `get` failed: value mismatch.\n"); + } + + channel = settings_get_uint16(SETTINGS_KEY_CHANNEL, 0); + if(26 != channel) { + printf("settings-example: `get` failed: value mismatch.\n"); + } + + if(!settings_get_cstr(SETTINGS_KEY_HOSTNAME, 0, hostname, sizeof(hostname))) { + printf("settings-example: `get` failed: settings_get_cstr returned NULL\n"); + } else if(strcmp(hostname, "contiki.local") != 0) { + printf("settings-example: `get` failed: value mismatch.\n"); + } + + /*************************************************************************/ + /* Adding multiple values with the same key */ + + for(i = 0; i < 10; i++) { + settings_add_uint8(TCC('e','x'), i + 20); + } + + /*************************************************************************/ + /* Reading multiple values with the same key */ + + for(i = 0; i < 10; i++) { + if(settings_get_uint8(TCC('e', 'x'), i) != i + 20) { + printf("settings-example: `get` failed: value mismatch.\n"); + } + } + + /*************************************************************************/ + /* Iterating thru all settings */ + + for(iter = settings_iter_begin(); iter; iter = settings_iter_next(iter)) { + settings_length_t len = settings_iter_get_value_length(iter); + eeprom_addr_t addr = settings_iter_get_value_addr(iter); + uint8_t byte; + + union { + settings_key_t key; + char bytes[0]; + } u; + + u.key = settings_iter_get_key(iter); + + if(u.bytes[0] >= 32 && u.bytes[0] < 127 + && u.bytes[1] >= 32 && u.bytes[1] < 127 + ) { + printf("settings-example: [%c%c] = <",u.bytes[0],u.bytes[1]); + } else { + printf("settings-example: <0x%04X> = <",u.key); + } + + for(; len; len--, addr++) { + eeprom_read(addr, &byte, 1); + printf("%02X", byte); + if(len != 1) { + printf(" "); + } + } + + printf(">\n"); + } + + printf("settings-example: Done.\n"); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/platform/avr-raven/contiki-conf.h b/platform/avr-raven/contiki-conf.h index 733b3cacb..d7db4c6f8 100644 --- a/platform/avr-raven/contiki-conf.h +++ b/platform/avr-raven/contiki-conf.h @@ -50,6 +50,12 @@ #define F_CPU 8000000UL #endif +#include + +/* Skip the last four bytes of the EEPROM, to leave room for things + * like the avrdude erase count and bootloader signaling. */ +#define EEPROM_CONF_SIZE ((E2END + 1) - 4) + /* MCU_CONF_LOW_WEAR will remove the signature and eeprom from the .elf file */ /* This reduces reprogramming wear during development */ //#define MCU_CONF_LOW_WEAR 1 diff --git a/platform/avr-ravenusb/contiki-conf.h b/platform/avr-ravenusb/contiki-conf.h index cef41e176..e3a9e33c0 100644 --- a/platform/avr-ravenusb/contiki-conf.h +++ b/platform/avr-ravenusb/contiki-conf.h @@ -58,6 +58,12 @@ #include #include +#include + +/* Skip the last four bytes of the EEPROM, to leave room for things + * like the avrdude erase count and bootloader signaling. */ +#define EEPROM_CONF_SIZE ((E2END + 1) - 4) + /* The AVR tick interrupt usually is done with an 8 bit counter around 128 Hz. * 125 Hz needs slightly more overhead during the interrupt, as does a 32 bit * clock_time_t. diff --git a/platform/avr-zigbit/contiki-conf.h b/platform/avr-zigbit/contiki-conf.h index 4a7c4ab8b..d48f85419 100644 --- a/platform/avr-zigbit/contiki-conf.h +++ b/platform/avr-zigbit/contiki-conf.h @@ -50,6 +50,11 @@ #endif #include +#include + +/* Skip the last four bytes of the EEPROM, to leave room for things + * like the avrdude erase count and bootloader signaling. */ +#define EEPROM_CONF_SIZE ((E2END + 1) - 4) /* The AVR tick interrupt usually is done with an 8 bit counter around 128 Hz. * 125 Hz needs slightly more overhead during the interrupt, as does a 32 bit diff --git a/regression-tests/01-compile/Makefile b/regression-tests/01-compile/Makefile index 67047b68c..e9f84534a 100644 --- a/regression-tests/01-compile/Makefile +++ b/regression-tests/01-compile/Makefile @@ -37,6 +37,7 @@ webserver-ipv6/sky \ webserver-ipv6/econotag \ wget/minimal-net \ z1/z1 \ +settings-example/avr-raven \ sensinode/sensinode \ sensinode/border-router/sensinode \ sensinode/udp-ipv6/sensinode \