/* * 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 <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" /*****************************************************************************/ // 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 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__) */