Merge pull request #9848 from ZetaR60/RIOT_eepreg2
sys/eepreg: EEPROM registration support (version 2)
This commit is contained in:
commit
aedc5260d8
@ -1,6 +1,9 @@
|
||||
ifneq (,$(filter csma_sender,$(USEMODULE)))
|
||||
DIRS += net/link_layer/csma_sender
|
||||
endif
|
||||
ifneq (,$(filter eepreg,$(USEMODULE)))
|
||||
DIRS += eepreg
|
||||
endif
|
||||
ifneq (,$(filter posix_semaphore,$(USEMODULE)))
|
||||
DIRS += posix/semaphore
|
||||
endif
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
ifneq (,$(filter eepreg,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_eeprom
|
||||
endif
|
||||
|
||||
ifneq (,$(filter prng_fortuna,$(USEMODULE)))
|
||||
CFLAGS += -DCRYPTO_AES
|
||||
endif
|
||||
|
||||
1
sys/eepreg/Makefile
Normal file
1
sys/eepreg/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
498
sys/eepreg/eepreg.c
Normal file
498
sys/eepreg/eepreg.c
Normal file
@ -0,0 +1,498 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Acutam Automation, LLC
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup sys_eepreg
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief eepreg implementation
|
||||
*
|
||||
* @author Matthew Blue <matthew.blue.neuro@gmail.com>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "eepreg.h"
|
||||
#include "periph/eeprom.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
/* EEPREG magic number */
|
||||
static const char eepreg_magic[] = "RIOTREG";
|
||||
|
||||
/* constant lengths */
|
||||
#define MAGIC_SIZE (sizeof(eepreg_magic) - 1) /* -1 to remove null */
|
||||
#define ENT_LEN_SIZ (1U)
|
||||
|
||||
/* constant locations */
|
||||
#define REG_START (EEPROM_RESERV_CPU_LOW + EEPROM_RESERV_BOARD_LOW)
|
||||
#define REG_MAGIC_LOC (REG_START)
|
||||
#define REG_END_PTR_LOC (REG_MAGIC_LOC + MAGIC_SIZE)
|
||||
#define REG_ENT1_LOC (REG_END_PTR_LOC + EEPREG_PTR_LEN)
|
||||
#define DAT_START (EEPROM_SIZE - EEPROM_RESERV_CPU_HI \
|
||||
- EEPROM_RESERV_BOARD_HI - 1)
|
||||
|
||||
static inline uint32_t _read_meta_uint(uint32_t loc)
|
||||
{
|
||||
uint8_t data[4];
|
||||
uint32_t ret;
|
||||
|
||||
eeprom_read(loc, data, EEPREG_PTR_LEN);
|
||||
|
||||
/* unused array members will be discarded */
|
||||
ret = ((uint32_t)data[0] << 24)
|
||||
| ((uint32_t)data[1] << 16)
|
||||
| ((uint32_t)data[2] << 8)
|
||||
| ((uint32_t)data[3]);
|
||||
|
||||
/* bit shift to discard unused array members */
|
||||
ret >>= 8 * (4 - EEPREG_PTR_LEN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void _write_meta_uint(uint32_t loc, uint32_t val)
|
||||
{
|
||||
uint8_t data[4];
|
||||
|
||||
val <<= 8 * (4 - EEPREG_PTR_LEN);
|
||||
|
||||
data[0] = (uint8_t)(val >> 24);
|
||||
data[1] = (uint8_t)(val >> 16);
|
||||
data[2] = (uint8_t)(val >> 8);
|
||||
data[3] = (uint8_t)val;
|
||||
|
||||
eeprom_write(loc, data, EEPREG_PTR_LEN);
|
||||
}
|
||||
|
||||
static inline uint32_t _get_reg_end(void)
|
||||
{
|
||||
return _read_meta_uint(REG_END_PTR_LOC);
|
||||
}
|
||||
|
||||
static inline void _set_reg_end(uint32_t loc)
|
||||
{
|
||||
_write_meta_uint(REG_END_PTR_LOC, loc);
|
||||
}
|
||||
|
||||
static inline uint32_t _get_last_loc(uint32_t reg_end)
|
||||
{
|
||||
if (reg_end == REG_ENT1_LOC) {
|
||||
/* no entries yet */
|
||||
return DAT_START;
|
||||
}
|
||||
|
||||
return _read_meta_uint(reg_end - EEPREG_PTR_LEN);
|
||||
}
|
||||
|
||||
static inline uint32_t _calc_free_space(uint32_t reg_end, uint32_t last_loc)
|
||||
{
|
||||
return last_loc - reg_end;
|
||||
}
|
||||
|
||||
static inline uint8_t _get_meta_len(uint32_t meta_loc)
|
||||
{
|
||||
return eeprom_read_byte(meta_loc);
|
||||
}
|
||||
|
||||
static inline void _set_meta_len(uint32_t meta_loc, uint8_t meta_len)
|
||||
{
|
||||
eeprom_write_byte(meta_loc, meta_len);
|
||||
}
|
||||
|
||||
static inline uint32_t _get_data_loc(uint32_t meta_loc, uint8_t meta_len)
|
||||
{
|
||||
/* data location is at the end of meta-data */
|
||||
return _read_meta_uint(meta_loc + meta_len - EEPREG_PTR_LEN);
|
||||
}
|
||||
|
||||
static inline void _set_data_loc(uint32_t meta_loc, uint8_t meta_len,
|
||||
uint32_t data_loc)
|
||||
{
|
||||
/* data location is at the end of meta-data */
|
||||
_write_meta_uint(meta_loc + meta_len - EEPREG_PTR_LEN, data_loc);
|
||||
}
|
||||
|
||||
static inline uint8_t _calc_name_len(uint8_t meta_len)
|
||||
{
|
||||
/* entry contents: meta-data length, name, data pointer */
|
||||
return meta_len - ENT_LEN_SIZ - EEPREG_PTR_LEN;
|
||||
}
|
||||
|
||||
static inline void _get_name(uint32_t meta_loc, char *name, uint8_t meta_len)
|
||||
{
|
||||
/* name is after entry length */
|
||||
eeprom_read(meta_loc + ENT_LEN_SIZ, (uint8_t *)name,
|
||||
_calc_name_len(meta_len));
|
||||
}
|
||||
|
||||
static inline int _cmp_name(uint32_t meta_loc, const char *name,
|
||||
uint8_t meta_len)
|
||||
{
|
||||
/* name is after entry length */
|
||||
uint32_t loc = meta_loc + ENT_LEN_SIZ;
|
||||
|
||||
uint8_t len = _calc_name_len(meta_len);
|
||||
|
||||
uint8_t offset;
|
||||
for (offset = 0; offset < len; offset++) {
|
||||
if (name[offset] == '\0') {
|
||||
/* entry name is longer than name */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (eeprom_read_byte(loc + offset) != (uint8_t)name[offset]) {
|
||||
/* non-matching character */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (name[offset] == '\0') {
|
||||
/* entry name is the same length as name */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* entry name is shorter than name */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline uint32_t _get_meta_loc(const char *name)
|
||||
{
|
||||
uint32_t meta_loc = REG_ENT1_LOC;
|
||||
uint32_t reg_end = _get_reg_end();
|
||||
|
||||
while (meta_loc < reg_end) {
|
||||
uint8_t meta_len = _get_meta_len(meta_loc);
|
||||
|
||||
if (_cmp_name(meta_loc, name, meta_len)) {
|
||||
return meta_loc;
|
||||
}
|
||||
|
||||
meta_loc += meta_len;
|
||||
}
|
||||
|
||||
/* no meta-data found */
|
||||
return (uint32_t)UINT_MAX;
|
||||
}
|
||||
|
||||
static inline uint32_t _get_data_len(uint32_t meta_loc, uint32_t data_loc)
|
||||
{
|
||||
uint32_t prev_loc;
|
||||
if (meta_loc == REG_ENT1_LOC) {
|
||||
prev_loc = DAT_START;
|
||||
}
|
||||
else {
|
||||
/* previous entry data pointer is just before this entry */
|
||||
prev_loc = _read_meta_uint(meta_loc - EEPREG_PTR_LEN);
|
||||
}
|
||||
|
||||
return prev_loc - data_loc;
|
||||
}
|
||||
|
||||
static inline int _new_entry(const char *name, uint32_t data_len)
|
||||
{
|
||||
uint32_t reg_end = _get_reg_end();
|
||||
uint32_t last_loc = _get_last_loc(reg_end);
|
||||
uint32_t free_space = _calc_free_space(reg_end, last_loc);
|
||||
|
||||
uint8_t name_len = (uint8_t)strlen(name);
|
||||
uint8_t meta_len = ENT_LEN_SIZ + name_len + EEPREG_PTR_LEN;
|
||||
|
||||
/* check to see if there is enough room */
|
||||
if (free_space < meta_len + data_len) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/* set the length of the meta-data */
|
||||
_set_meta_len(reg_end, meta_len);
|
||||
|
||||
/* write name of entry */
|
||||
eeprom_write(reg_end + ENT_LEN_SIZ, (uint8_t *)name, name_len);
|
||||
|
||||
/* set the location of the data */
|
||||
_set_data_loc(reg_end, meta_len, last_loc - data_len);
|
||||
|
||||
/* update end of the registry */
|
||||
_set_reg_end(reg_end + meta_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void _move_data(uint32_t oldpos, uint32_t newpos, uint32_t len)
|
||||
{
|
||||
for (uint32_t count = 0; count < len; count++) {
|
||||
uint32_t offset;
|
||||
|
||||
if (newpos < oldpos) {
|
||||
/* move from beginning of data */
|
||||
offset = count;
|
||||
}
|
||||
else {
|
||||
/* move from end of data */
|
||||
offset = len - count;
|
||||
}
|
||||
|
||||
uint8_t byte = eeprom_read_byte(oldpos + offset);
|
||||
|
||||
eeprom_write_byte(newpos + offset, byte);
|
||||
}
|
||||
}
|
||||
|
||||
int eepreg_add(uint32_t *pos, const char *name, uint32_t len)
|
||||
{
|
||||
int ret = eepreg_check();
|
||||
if (ret == -ENOENT) {
|
||||
/* reg does not exist, so make a new one */
|
||||
eepreg_reset();
|
||||
}
|
||||
else if (ret < 0) {
|
||||
DEBUG("[eepreg_add] eepreg_check failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t reg_end = _get_reg_end();
|
||||
|
||||
uint32_t meta_loc = _get_meta_loc(name);
|
||||
|
||||
if (meta_loc == (uint32_t)UINT_MAX) {
|
||||
/* entry does not exist, so make a new one */
|
||||
|
||||
/* location of the new data */
|
||||
*pos = _get_last_loc(reg_end) - len;
|
||||
|
||||
if (_new_entry(name, len) < 0) {
|
||||
DEBUG("[eepreg_add] not enough space for %s\n", name);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
*pos = _get_data_loc(meta_loc, _get_meta_len(meta_loc));
|
||||
|
||||
if (len != _get_data_len(meta_loc, *pos)) {
|
||||
DEBUG("[eepreg_add] %s already exists with different length\n", name);
|
||||
return -EADDRINUSE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eepreg_read(uint32_t *pos, const char *name)
|
||||
{
|
||||
int ret = eepreg_check();
|
||||
if (ret < 0) {
|
||||
DEBUG("[eepreg_read] eepreg_check failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t meta_loc = _get_meta_loc(name);
|
||||
|
||||
if (meta_loc == (uint32_t)UINT_MAX) {
|
||||
DEBUG("[eepreg_read] no entry for %s\n", name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
*pos = _get_data_loc(meta_loc, _get_meta_len(meta_loc));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eepreg_write(uint32_t *pos, const char *name, uint32_t len)
|
||||
{
|
||||
uint32_t reg_end = _get_reg_end();
|
||||
|
||||
int ret = eepreg_check();
|
||||
if (ret == -ENOENT) {
|
||||
/* reg does not exist, so make a new one */
|
||||
eepreg_reset();
|
||||
}
|
||||
else if (ret < 0) {
|
||||
DEBUG("[eepreg_write] eepreg_check failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* location of the new data */
|
||||
*pos = _get_last_loc(reg_end) - len;
|
||||
|
||||
if (_new_entry(name, len) < 0) {
|
||||
DEBUG("[eepreg_write] not enough space for %s\n", name);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eepreg_rm(const char *name)
|
||||
{
|
||||
int ret = eepreg_check();
|
||||
if (ret < 0) {
|
||||
DEBUG("[eepreg_rm] eepreg_check failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t meta_loc = _get_meta_loc(name);
|
||||
|
||||
if (meta_loc == (uint32_t)UINT_MAX) {
|
||||
DEBUG("[eepreg_rm] no entry for %s\n", name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
uint32_t reg_end = _get_reg_end();
|
||||
uint32_t last_loc = _get_last_loc(reg_end);
|
||||
|
||||
uint8_t meta_len = _get_meta_len(meta_loc);
|
||||
uint32_t tot_meta_len = reg_end - meta_loc;
|
||||
|
||||
uint32_t data_loc = _get_data_loc(meta_loc, meta_len);
|
||||
uint32_t data_len = _get_data_len(meta_loc, data_loc);
|
||||
|
||||
/* data_loc is above last_loc due to descending order */
|
||||
uint32_t tot_data_len = data_loc - last_loc;
|
||||
|
||||
_move_data(meta_loc + meta_len, meta_loc, tot_meta_len);
|
||||
|
||||
_move_data(last_loc, last_loc + data_len, tot_data_len);
|
||||
|
||||
reg_end -= meta_len;
|
||||
_set_reg_end(reg_end);
|
||||
|
||||
/* update data locations */
|
||||
while (meta_loc < reg_end) {
|
||||
meta_len = _get_meta_len(meta_loc);
|
||||
data_loc = _get_data_loc(meta_loc, meta_len);
|
||||
|
||||
/* addition due to descending order */
|
||||
_set_data_loc(meta_loc, meta_len, data_loc + data_len);
|
||||
|
||||
meta_loc += meta_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eepreg_iter(eepreg_iter_cb_t cb, void *arg)
|
||||
{
|
||||
uint32_t reg_end = _get_reg_end();
|
||||
|
||||
int ret = eepreg_check();
|
||||
if (ret < 0) {
|
||||
DEBUG("[eepreg_len] eepreg_check failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t meta_loc = REG_ENT1_LOC;
|
||||
while (meta_loc < reg_end) {
|
||||
uint8_t meta_len = _get_meta_len(meta_loc);
|
||||
|
||||
/* size of memory allocation */
|
||||
uint8_t name_len = _calc_name_len(meta_len);
|
||||
|
||||
char name[name_len + 1];
|
||||
|
||||
/* terminate string */
|
||||
name[name_len] = '\0';
|
||||
|
||||
_get_name(meta_loc, name, meta_len);
|
||||
|
||||
/* execute callback */
|
||||
ret = cb(name, arg);
|
||||
|
||||
if (ret < 0) {
|
||||
DEBUG("[eepreg_iter] callback reports failure\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* only advance if cb didn't delete entry */
|
||||
if (_cmp_name(meta_loc, name, meta_len)) {
|
||||
meta_loc += meta_len;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eepreg_check(void)
|
||||
{
|
||||
char magic[MAGIC_SIZE];
|
||||
|
||||
/* get magic number from EEPROM */
|
||||
if (eeprom_read(REG_MAGIC_LOC, (uint8_t *)magic, MAGIC_SIZE)
|
||||
!= MAGIC_SIZE) {
|
||||
|
||||
DEBUG("[eepreg_check] EEPROM read error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* check to see if magic number is the same */
|
||||
if (strncmp(magic, eepreg_magic, MAGIC_SIZE) != 0) {
|
||||
DEBUG("[eepreg_check] No registry detected\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eepreg_reset(void)
|
||||
{
|
||||
/* write new registry magic number */
|
||||
if (eeprom_write(REG_MAGIC_LOC, (uint8_t *)eepreg_magic, MAGIC_SIZE)
|
||||
!= MAGIC_SIZE) {
|
||||
|
||||
DEBUG("[eepreg_reset] EEPROM write error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* new registry has no entries */
|
||||
_set_reg_end(REG_ENT1_LOC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eepreg_len(uint32_t *len, const char *name)
|
||||
{
|
||||
int ret = eepreg_check();
|
||||
if (ret < 0) {
|
||||
DEBUG("[eepreg_len] eepreg_check failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t meta_loc = _get_meta_loc(name);
|
||||
|
||||
if (meta_loc == (uint32_t)UINT_MAX) {
|
||||
DEBUG("[eepreg_len] no entry for %s\n", name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
uint32_t data_loc = _get_data_loc(meta_loc, _get_meta_len(meta_loc));
|
||||
|
||||
*len = _get_data_len(meta_loc, data_loc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eepreg_free(uint32_t *len)
|
||||
{
|
||||
int ret = eepreg_check();
|
||||
if (ret < 0) {
|
||||
DEBUG("[eepreg_free] eepreg_check failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t reg_end = _get_reg_end();
|
||||
uint32_t last_loc = _get_last_loc(reg_end);
|
||||
*len = _calc_free_space(reg_end, last_loc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
271
sys/include/eepreg.h
Normal file
271
sys/include/eepreg.h
Normal file
@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Acutam Automation, LLC
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup sys_eepreg EEPROM registration
|
||||
* @ingroup sys
|
||||
* @brief eepreg provides a facility to easily manage the locations of
|
||||
* data stored in EEPROM via a meta-data registry.
|
||||
*
|
||||
* The structure of the meta-data registry is intended to make it easy to
|
||||
* detect the exact layout of existent data so that automatic tools may be
|
||||
* written to migrate legacy data to new formats. It also allows the addition
|
||||
* and removal of new entries dynamically.
|
||||
*
|
||||
* @note Names are used as identifiers and must be unique! It is also
|
||||
* recommended to keep them as short as possible (while still being unique and
|
||||
* human readable), as many systems have very small amounts of EEPROM.
|
||||
* Disemvowelment can shorten long names while still retaining readability.
|
||||
*
|
||||
* @code {unparsed}
|
||||
* The layout of the EEPROM used looks like this:
|
||||
* EEPROM_RESERV_CPU_LOW
|
||||
* EEPROM_RESERV_BOARD_LOW
|
||||
* Registry magic number ("RIOTREG")
|
||||
* Registry end pointer
|
||||
* Registry entry 1 meta-data length (1 byte)
|
||||
* Registry entry 1 name (unterminated)
|
||||
* Registry entry 1 data pointer
|
||||
* Registry entry 2 meta-data length
|
||||
* Registry entry 2 name
|
||||
* Registry entry 2 data pointer
|
||||
* ... (new registry meta-data may be added in ascending order)
|
||||
* unused space
|
||||
* ... (new data locations may be added in descending order)
|
||||
* Entry 2 data
|
||||
* Entry 1 data
|
||||
* EEPROM_RESERV_BOARD_HI
|
||||
* EEPROM_RESERV_CPU_HI
|
||||
* @endcode
|
||||
*
|
||||
* Pointer length is dependent on the size of the available EEPROM (see
|
||||
* EEPREG_PTR_LEN below).
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief eepreg interface definitions
|
||||
*
|
||||
* @author Matthew Blue <matthew.blue.neuro@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef EEPREG_H
|
||||
#define EEPREG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "periph_cpu.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef EEPROM_RESERV_CPU_LOW
|
||||
/**
|
||||
* @brief EEPROM reserved near beginning for use by CPU and related
|
||||
*
|
||||
* Change with care, as it may make existing data difficult to migrate
|
||||
*/
|
||||
#define EEPROM_RESERV_CPU_LOW (0U)
|
||||
#endif
|
||||
|
||||
#ifndef EEPROM_RESERV_CPU_HI
|
||||
/**
|
||||
* @brief EEPROM reserved near end for use by CPU and related
|
||||
*
|
||||
* Change with care, as it may make existing data difficult to migrate
|
||||
*/
|
||||
#define EEPROM_RESERV_CPU_HI (0U)
|
||||
#endif
|
||||
|
||||
#ifndef EEPROM_RESERV_BOARD_LOW
|
||||
/**
|
||||
* @brief EEPROM reserved near beginning for use by board and related
|
||||
*
|
||||
* Change with care, as it may make existing data difficult to migrate
|
||||
*/
|
||||
#define EEPROM_RESERV_BOARD_LOW (0U)
|
||||
#endif
|
||||
|
||||
#ifndef EEPROM_RESERV_BOARD_HI
|
||||
/**
|
||||
* @brief EEPROM reserved near end for use by board and related
|
||||
*
|
||||
* Change with care, as it may make existing data difficult to migrate
|
||||
*/
|
||||
#define EEPROM_RESERV_BOARD_HI (0U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Size in bytes of pointer meta-data in EEPROM
|
||||
*/
|
||||
#if (EEPROM_SIZE > 0x1000000)
|
||||
#define EEPREG_PTR_LEN (4U)
|
||||
#elif (EEPROM_SIZE > 0x10000)
|
||||
#define EEPREG_PTR_LEN (3U)
|
||||
#elif (EEPROM_SIZE > 0x100)
|
||||
#define EEPREG_PTR_LEN (2U)
|
||||
#else
|
||||
#define EEPREG_PTR_LEN (1U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Signature of callback for iterating over entries in EEPROM registry
|
||||
*
|
||||
* @param[in] name name of an entry in the registry
|
||||
* @param[in] arg argument for cb
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return < 0 on failure
|
||||
*/
|
||||
typedef int (*eepreg_iter_cb_t)(char *name, void *arg);
|
||||
|
||||
/**
|
||||
* @brief Load or write meta-data in EEPROM registry
|
||||
*
|
||||
* This checks to see if relevant meta-data exists in the EEPROM registry, and
|
||||
* returns that data position if it exists. If an entry does not exist in the
|
||||
* registry, meta-data is written and allocated data space if there is enough
|
||||
* remaining. Requesting a different length for an existent entry returns an
|
||||
* error.
|
||||
*
|
||||
* @param[out] pos pointer to position variable
|
||||
* @param[in] name name of entry to load or write
|
||||
* @param[in] len requested amount of data storage
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on EEPROM I/O error
|
||||
* @return -ENOSPC on insufficient EEPROM for entry
|
||||
* @return -EADDRINUSE on existing entry with different length
|
||||
*/
|
||||
int eepreg_add(uint32_t *pos, const char *name, uint32_t len);
|
||||
|
||||
/**
|
||||
* @brief Read position meta-data from EEPROM registry
|
||||
*
|
||||
* This is similar to eepreg_add, except it never writes meta-data.
|
||||
*
|
||||
* @param[out] pos pointer to position variable
|
||||
* @param[in] name name of entry to load
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on EEPROM I/O error
|
||||
* @return -ENOENT on non-existent registry or entry
|
||||
*/
|
||||
int eepreg_read(uint32_t *pos, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Write meta-data to EEPROM registry
|
||||
*
|
||||
* This ignores existing meta-data and always makes a new entry in the
|
||||
* registry. Typical use should be through eepreg_add and not eepreg_write.
|
||||
* If multiple entries with the same name exist, eepreg functions will find
|
||||
* the oldest. Mainly intended for use by migration utilities.
|
||||
*
|
||||
* @param[out] pos pointer to position variable
|
||||
* @param[in] name name of entry to write
|
||||
* @param[in] len requested amount of data storage
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on EEPROM I/O error
|
||||
* @return -ENOSPC on insufficient EEPROM for entry
|
||||
*/
|
||||
int eepreg_write(uint32_t *pos, const char *name, uint32_t len);
|
||||
|
||||
/**
|
||||
* @brief Remove entry from EEPROM registry and free space
|
||||
*
|
||||
* This removes an entry from the EEPROM registry and its corresponding data
|
||||
* and moves the data and meta-data of entries after removed entry to occupy
|
||||
* the freed space. This preserves the structure of the EEPROM registry.
|
||||
* Warning: this is a read/write intensive operation! Mainly intended for use
|
||||
* by migration utilities.
|
||||
*
|
||||
* @param[in] name name of entry to remove
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on EEPROM I/O error
|
||||
* @return -ENOENT on non-existent registry or entry
|
||||
*/
|
||||
int eepreg_rm(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Iterate over meta-data entries in EEPROM registry
|
||||
*
|
||||
* This executes a callback over each name in the EEPROM registry. The intended
|
||||
* work-flow for migration is to: iterate over each entry, check to see if
|
||||
* migration is needed, duplicate using eepreg_write if needed, migrate data to
|
||||
* duplicate entry, then delete old entry using eepreg_rm.
|
||||
*
|
||||
* @note It is safe for the callback to remove the entry it is called with,
|
||||
* or to add new entries.
|
||||
*
|
||||
* @param[in] cb callback to iterate over entries
|
||||
* @param[in] arg argument for cb
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on EEPROM I/O error
|
||||
* @return -ENOENT on non-existent registry
|
||||
* @return return value of cb when cb returns < 0
|
||||
*/
|
||||
int eepreg_iter(eepreg_iter_cb_t cb, void *arg);
|
||||
|
||||
/**
|
||||
* @brief Check for the presence of meta-data registry
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on EEPROM I/O error
|
||||
* @return -ENOENT on non-existent registry
|
||||
*/
|
||||
int eepreg_check(void);
|
||||
|
||||
/**
|
||||
* @brief Clear existing meta-data registry
|
||||
*
|
||||
* This removes any existing meta-data registry by writing a new registry with
|
||||
* no entries.
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on EEPROM I/O error
|
||||
*/
|
||||
int eepreg_reset(void);
|
||||
|
||||
/**
|
||||
* @brief Calculate data length from meta-data in EEPROM registry
|
||||
*
|
||||
* @note This information is typically already available to code that has
|
||||
* called eepreg_add.
|
||||
*
|
||||
* @param[out] len pointer to length variable
|
||||
* @param[in] name name of entry to load or write
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on EEPROM I/O error
|
||||
* @return -ENOENT on non-existent registry or entry
|
||||
*/
|
||||
int eepreg_len(uint32_t *len, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Calculate length of remaining EEPROM free space
|
||||
*
|
||||
* @param[out] len pointer to length variable
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on EEPROM I/O error
|
||||
* @return -ENOENT on non-existent registry
|
||||
*/
|
||||
int eepreg_free(uint32_t *len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /* EEPREG_H */
|
||||
7
tests/eepreg/Makefile
Normal file
7
tests/eepreg/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
FEATURES_REQUIRED = periph_eeprom
|
||||
|
||||
USEMODULE += eepreg
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
6
tests/eepreg/README.md
Normal file
6
tests/eepreg/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# EEPROM registry (eepreg) test
|
||||
|
||||
This test will verify the functionality of the eepreg module.
|
||||
|
||||
WARNING: This will write to your EEPROM and probably corrupt any data that is
|
||||
there!
|
||||
203
tests/eepreg/main.c
Normal file
203
tests/eepreg/main.c
Normal file
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Acutam Automation, LLC
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief eepreg test application
|
||||
*
|
||||
* @author Matthew Blue <matthew.blue.neuro@gmail.com>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "eepreg.h"
|
||||
#include "periph/eeprom.h"
|
||||
|
||||
#define DAT_START (EEPROM_SIZE - EEPROM_RESERV_CPU_HI \
|
||||
- EEPROM_RESERV_BOARD_HI - 1)
|
||||
|
||||
#define ENT1_NAME "foo"
|
||||
#define ENT1_SIZE (12U)
|
||||
#define ENT2_NAME "bar"
|
||||
#define ENT2_SIZE (34U)
|
||||
#define DATA "spam and eggs"
|
||||
|
||||
int eepreg_iter_cb(char *name, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
printf("%s ", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int ret;
|
||||
uint32_t tmp1, tmp2, tmp3;
|
||||
char data[sizeof(DATA)];
|
||||
|
||||
puts("EEPROM registry (eepreg) test routine");
|
||||
|
||||
printf("Testing new registry creation: ");
|
||||
|
||||
printf("reset ");
|
||||
ret = eepreg_reset();
|
||||
if (ret < 0) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("check ");
|
||||
ret = eepreg_check();
|
||||
if (ret < 0) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
puts("[SUCCESS]");
|
||||
|
||||
printf("Testing writing and reading entries: ");
|
||||
|
||||
printf("add ");
|
||||
ret = eepreg_add(&tmp1, ENT1_NAME, ENT1_SIZE);
|
||||
if (ret < 0 || tmp1 != DAT_START - ENT1_SIZE) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("write ");
|
||||
ret = eepreg_write(&tmp2, ENT2_NAME, ENT2_SIZE);
|
||||
if (ret < 0 || tmp2 != DAT_START - ENT1_SIZE - ENT2_SIZE) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* read via add */
|
||||
printf("add ");
|
||||
ret = eepreg_add(&tmp3, ENT1_NAME, ENT1_SIZE);
|
||||
if (ret < 0 || tmp1 != tmp3) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("read ");
|
||||
ret = eepreg_read(&tmp1, ENT2_NAME);
|
||||
if (ret < 0 || tmp1 != tmp2) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
puts("[SUCCESS]");
|
||||
|
||||
printf("Testing detection of conflicting size: ");
|
||||
|
||||
printf("add ");
|
||||
ret = eepreg_add(&tmp1, ENT1_NAME, ENT1_SIZE + 1);
|
||||
if (ret != -EADDRINUSE) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
puts("[SUCCESS]");
|
||||
|
||||
printf("Testing calculation of lengths: ");
|
||||
|
||||
printf("len ");
|
||||
ret = eepreg_len(&tmp1, ENT1_NAME);
|
||||
if (ret < 0 || tmp1 != ENT1_SIZE) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("len ");
|
||||
ret = eepreg_len(&tmp2, ENT2_NAME);
|
||||
if (ret < 0 || tmp2 != ENT2_SIZE) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
puts("[SUCCESS]");
|
||||
|
||||
printf("Testing of successful data move after rm: ");
|
||||
|
||||
printf("rm ");
|
||||
eepreg_read(&tmp1, ENT1_NAME);
|
||||
eepreg_read(&tmp2, ENT2_NAME);
|
||||
eeprom_write(tmp2, (uint8_t *)DATA, sizeof(DATA));
|
||||
ret = eepreg_rm(ENT1_NAME);
|
||||
if (ret < 0) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("read ");
|
||||
ret = eepreg_read(&tmp3, ENT2_NAME);
|
||||
if (ret < 0 || tmp3 != (DAT_START - ENT2_SIZE)) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("data ");
|
||||
eeprom_read(tmp3, (uint8_t *)data, sizeof(DATA));
|
||||
if (strcmp(data, DATA) != 0) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
puts("[SUCCESS]");
|
||||
|
||||
printf("Testing of free space change after write: ");
|
||||
|
||||
printf("free ");
|
||||
ret = eepreg_free(&tmp1);
|
||||
if (ret < 0) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("add ");
|
||||
ret = eepreg_add(&tmp3, ENT1_NAME, ENT1_SIZE);
|
||||
if (ret < 0) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("free ");
|
||||
ret = eepreg_free(&tmp2);
|
||||
if (ret < 0
|
||||
|| tmp1 != (tmp2 + ENT1_SIZE + sizeof(ENT1_NAME) + EEPREG_PTR_LEN)) {
|
||||
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
puts("[SUCCESS]");
|
||||
|
||||
printf("Testing of iteration over registry: ");
|
||||
|
||||
printf("iter ");
|
||||
ret = eepreg_iter(eepreg_iter_cb, NULL);
|
||||
if (ret < 0) {
|
||||
puts("[FAILED]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
puts("[SUCCESS]");
|
||||
|
||||
puts("Tests complete!");
|
||||
|
||||
return 0;
|
||||
}
|
||||
26
tests/eepreg/tests/01-run.py
Executable file
26
tests/eepreg/tests/01-run.py
Executable file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2018 Acutam Automation, LLC
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
import sys
|
||||
from testrunner import run
|
||||
|
||||
|
||||
def testfunc(child):
|
||||
child.expect_exact("EEPROM registry (eepreg) test routine")
|
||||
child.expect_exact("Testing new registry creation: reset check [SUCCESS]")
|
||||
child.expect_exact("Testing writing and reading entries: add write add read [SUCCESS]")
|
||||
child.expect_exact("Testing detection of conflicting size: add [SUCCESS]")
|
||||
child.expect_exact("Testing calculation of lengths: len len [SUCCESS]")
|
||||
child.expect_exact("Testing of successful data move after rm: rm read data [SUCCESS]")
|
||||
child.expect_exact("Testing of free space change after write: free add free [SUCCESS]")
|
||||
child.expect_exact("Testing of iteration over registry: iter bar foo [SUCCESS]")
|
||||
child.expect_exact("Tests complete!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(run(testfunc))
|
||||
Loading…
x
Reference in New Issue
Block a user