contiki/core/lib/settings.h

370 lines
12 KiB
C
Raw Normal View History

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
2012-05-17 16:03:52 +00:00
/*
* 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_
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
2012-05-17 16:03:52 +00:00
/** @file settings.h
* @brief Settings Manager
* @author Robert Quattlebaum <darco@deepdarc.com>
*
* ## 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:
* <http://wiki.laptop.org/go/Manufacturing_data>
*
* 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:
* <table>
* <thead>
* <td>Order</td>
* <td>Size<small> (in bytes)</small></td>
* <td>Name</td>
* <td>Description</td>
* </thead>
* <tr>
* <td>0</td>
* <td>2</td>
* <td>key</td>
* <td></td>
* </tr>
* <tr>
* <td>-2</td>
* <td>1</td>
* <td>size_check</td>
* <td>One's-complement of next byte</td>
* </tr>
* <tr>
* <td>-3</td>
* <td>1 or 2</td>
* <td>size</td>
* <td>The size of the value, in bytes.</td>
* </tr>
* <tr>
* <td>-4 or -5</td>
* <td>variable</td>
* <td>value</td>
* </tr>
* </table>
*
* 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 <stdint.h>
#include <string.h>
#include "dev/eeprom.h"
#include "sys/cc.h"
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
2012-05-17 16:03:52 +00:00
/*****************************************************************************/
// 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
/* Unfortunately, some platforms don't properly drop unreferenced functions,
* so on these broken platforms we can save a significant amount
* of space by skipping the definition of the convenience functions.
*/
#if !SETTINGS_CONF_SKIP_CONVENIENCE_FUNCS
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
2012-05-17 16:03:52 +00:00
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 /* !SETTINGS_CONF_SKIP_CONVENIENCE_FUNCS */
#endif /* !defined(CONTIKI_SETTINGS_H_) */