Files
src.dualinventive.com/fw/libdi_fw-tests/libdi/src/config.c
2024-08-09 12:10:16 +02:00

409 lines
10 KiB
C
Executable File

#include <string.h>
#include <di/config.h>
#include <di/log.h>
#include <di/crc.h>
#define DI_CONFIG_FILL_BYTE 0x00 /**< Configuration fill byte pattern */
#define DI_CONFIG_CRC_CCIT16_OFFSET 2U /**< CRC offset/size written before application data */
/**
* Search for a configuration item by unique id
* @param ctx Configuration context
* @param uid Unique ID
*/
static struct di_config_item *di_config_uid_search(struct di_config_ctx *ctx, unsigned int uid)
{
di_array_foreach(ctx->items, it, struct di_config_item) {
if (it->uid == uid)
return it;
}
return NULL;
}
/**
* Wrapper for di_config_get and ctx->read
* It first will read the CRC-CCIT16 and then read the data.
* Then the crc and returned read size checked
*
* @param ctx Configuration context
* @param offset Memory offset in bytes to read from
* @param data Address of buffer to read into
* @param size Size to read
*
* @retval DNE_IOFAILED When there was a error reading, the amount of requested bytes read doesn't match
* @retval DNE_CHECKSUM Invalid checksum after read
* @retval DNOK When read was succesfull and requested bytes are successfully read and checksummed
*/
static di_errno_t di_config_read(struct di_config_ctx *ctx, uint32_t offset, void *data, size_t size)
{
uint16_t crc;
di_errno_t ret = DNOK;
// read checksum
ret = ctx->read(ctx, offset, &crc, sizeof(crc));
if (ret != DNOK)
return ret;
// read data
ret = ctx->read(ctx, offset + DI_CONFIG_CRC_CCIT16_OFFSET, data, size);
if (ret != DNOK)
return ret;
if (crc != di_crc16_ccitt(data, size))
ret = DNE_CHECKSUM;
return ret;
}
/**
* Calculate the crc of data and optional zero fill
* @param data Application data
* @param size Size of application data
* @param fill Amount of bytes to fill with zero after application data
*/
static uint16_t di_config_calc_crc(const void *data, size_t size, size_t fill)
{
uint8_t fill_byte = DI_CONFIG_FILL_BYTE;
uint16_t crc;
crc = di_crc16_ccitt(data, size);
if (fill > 0) {
for (size_t n = 0; n < fill; n++)
di_crc16_ccitt_update(&crc, &fill_byte, 1);
}
return crc;
}
/**
* Write fill byte(s)
* @param ctx Configuration context
* @param offset Write offset start
* @param size Amount of fill bytes to write
*/
static di_errno_t di_config_write_fill(struct di_config_ctx *ctx, uint32_t offset, size_t size)
{
size_t n;
uint8_t fill_byte = DI_CONFIG_FILL_BYTE;
di_errno_t ret = DNOK;
for (n = 0; n < size; n++) {
if (ret != DNOK)
break;
ret = ctx->write(ctx, offset, &fill_byte, sizeof(fill_byte));
offset += 1;
}
return ret;
}
/**
* Wrapper for di_config_set and ctx->write
* It first will write the CRC-CCIT16 and then write the data.
* When writes are successful it runs the low-level sync callback.
*
* @param ctx Configuration context
* @param uid Item unique id
* @param offset Memory offset in bytes to write into
* @param data Address of buffer to write data from
* @param size Size of data buffer
*
* @retval DNE_FIRMWARE_EEPROM When there was an error writing
* @retval DNOK When write was succesfull and requested bytes are successfully written
*/
static di_errno_t di_config_write(struct di_config_ctx *ctx, unsigned int uid, uint32_t offset,
const void *data, size_t size)
{
struct di_config_item *item;
size_t fill = 0;
uint16_t crc;
di_errno_t ret = DNOK;
/* Fetch item from uid to get the maximum size in bytes, calculate remaining fill */
item = di_config_uid_search(ctx, uid);
if (size < item->size)
fill = item->size - size;
crc = di_config_calc_crc(data, size, fill);
ret = ctx->write(ctx, offset, &crc, sizeof(crc));
if (ret != DNOK)
return ret;
/* Write application config item data */
ret = ctx->write(ctx, offset + DI_CONFIG_CRC_CCIT16_OFFSET, data, size);
if (ret != DNOK)
return ret;
/* Fill remainder after application data size */
if (fill)
ret = di_config_write_fill(ctx, offset + DI_CONFIG_CRC_CCIT16_OFFSET + (uint32_t)size, fill);
if (ctx->sync)
ctx->sync(ctx);
return ret;
}
/**
* Check requested size is less than or equal to the assigned id size.
* The requester my read or write less or equal the amount of bytes defined by the id.
* This because strings and blobs can have dynamic sizes.
*
* @param ctx The config context
* @param uid The item unique id
* @param size The size of the operation (read/write)
*
* @retval DNE_NOTFOUND Item unique id not found
* @retval DNE_RANGE Size is out of range
* @retval DNOK Size is valid
*/
static di_errno_t di_config_uid_check_size(struct di_config_ctx *ctx, unsigned int uid, size_t size)
{
struct di_config_item *_uid;
_uid = di_config_uid_search(ctx, uid);
if (!_uid)
return DNE_NOTFOUND;
if (size <= _uid->size)
return DNOK;
return DNE_RANGE;
}
/**
* Calculate data offset of item unique id, the offset is the first byte pointing at the CRC
*
* @param ctx Config context
* @param uid Item unique id
* @param[out] offset Offset when id is found
*
* @retval DNE_NOTFOUND when id is not known
* @retval DNOK when id is known and offset is calculated
*/
static di_errno_t di_config_uid_get_offset(struct di_config_ctx *ctx, unsigned int uid, uint32_t *offset)
{
di_errno_t ret = DNE_NOTFOUND;
*offset = 0;
di_array_foreach(ctx->items, it, struct di_config_item) {
if (it->uid == uid) {
ret = DNOK;
break;
}
*offset += (uint32_t)(DI_CONFIG_CRC_CCIT16_OFFSET + it->size);
}
return ret;
}
void di_config_init(struct di_config_ctx *ctx)
{
ctx->read = NULL;
ctx->write = NULL;
ctx->sync = NULL;
ctx->items.begin = NULL;
ctx->items.size = 0;
di_bsem_init_unlocked(&ctx->lock);
}
di_errno_t di_config_set_items(struct di_config_ctx *ctx, const struct di_config_item *items, size_t size)
{
di_errno_t ret = DNOK;
unsigned int duplicates = 0;
ctx->items.begin = (void *)items;
ctx->items.size = size;
/* Search for duplicate items */
di_array_foreach(ctx->items, itx, struct di_config_item) {
duplicates = 0;
di_array_foreach(ctx->items, ity, struct di_config_item) {
if (itx->uid == ity->uid)
duplicates++;
}
/* One duplicate is the item itself */
if (duplicates > 1) {
ctx->items.begin = NULL;
ctx->items.size = 0;
ret = DNE_PARAM;
break;
}
}
return ret;
}
di_errno_t di_config_set_default(struct di_config_ctx *ctx, unsigned int uid)
{
const uint8_t *data;
size_t size;
struct di_config_item *item;
item = di_config_uid_search(ctx, uid);
if (!item)
return DNE_NOTFOUND;
switch (item->type) {
case DI_CONFIG_ITEM_DEFAULT_TYPE_BOOL:
data = (const uint8_t *)&item->def.b;
size = sizeof(item->def.b);
break;
case DI_CONFIG_ITEM_DEFAULT_TYPE_INT8:
data = (const uint8_t *)&item->def.s8;
size = sizeof(item->def.s8);
break;
case DI_CONFIG_ITEM_DEFAULT_TYPE_INT16:
data = (const uint8_t *)&item->def.s16;
size = sizeof(item->def.s16);
break;
case DI_CONFIG_ITEM_DEFAULT_TYPE_INT32:
data = (const uint8_t *)&item->def.s32;
size = sizeof(item->def.s32);
break;
case DI_CONFIG_ITEM_DEFAULT_TYPE_INT64:
data = (const uint8_t *)&item->def.s64;
size = sizeof(item->def.s64);
break;
case DI_CONFIG_ITEM_DEFAULT_TYPE_UINT8:
data = (const uint8_t *)&item->def.u8;
size = sizeof(item->def.u8);
break;
case DI_CONFIG_ITEM_DEFAULT_TYPE_UINT16:
data = (const uint8_t *)&item->def.u16;
size = sizeof(item->def.u16);
break;
case DI_CONFIG_ITEM_DEFAULT_TYPE_UINT32:
data = (const uint8_t *)&item->def.u32;
size = sizeof(item->def.u32);
break;
case DI_CONFIG_ITEM_DEFAULT_TYPE_UINT64:
data = (const uint8_t *)&item->def.u64;
size = sizeof(item->def.u64);
break;
case DI_CONFIG_ITEM_DEFAULT_TYPE_FLOAT:
data = (const uint8_t *)&item->def.f;
size = sizeof(item->def.f);
break;
case DI_CONFIG_ITEM_DEFAULT_TYPE_DOUBLE:
data = (const uint8_t *)&item->def.d;
size = sizeof(item->def.d);
break;
case DI_CONFIG_ITEM_DEFAULT_TYPE_STR:
data = (const uint8_t *)item->def.str;
size = 0;
break;
default:
return DNE_PARAM;
}
return _di_config_set(ctx, uid, data, size);
}
di_errno_t _di_config_set(struct di_config_ctx *ctx, unsigned int uid, const uint8_t *data, size_t size)
{
di_errno_t ret;
uint32_t offset;
if (!ctx->write || !ctx->items.begin)
return DNE_OPNOTSUPP;
/* We calculate the string size, and add space for a null terminator (with fill) */
if (size == 0)
size = strlen((const char *)data) + 1;
ret = di_config_uid_check_size(ctx, uid, size);
if (ret != DNOK)
return ret;
ret = di_config_uid_get_offset(ctx, uid, &offset);
if (ret != DNOK)
return ret;
/* Check if set operation is out-of-bound */
if ((offset + size) >= ctx->size)
return DNE_RANGE;
di_bsem_wait(&ctx->lock);
ret = di_config_write(ctx, uid, offset, data, size);
di_bsem_post(&ctx->lock);
return ret;
}
di_errno_t _di_config_get(struct di_config_ctx *ctx, unsigned int uid, uint8_t *data, size_t size)
{
di_errno_t ret;
uint32_t offset;
if (!ctx->read || !ctx->items.begin)
return DNE_OPNOTSUPP;
ret = di_config_uid_check_size(ctx, uid, size);
if (ret != DNOK)
return ret;
ret = di_config_uid_get_offset(ctx, uid, &offset);
if (ret != DNOK)
return ret;
/* Check if get operation is out-of-bound */
if ((offset + size) >= ctx->size)
return DNE_RANGE;
di_bsem_wait(&ctx->lock);
ret = di_config_read(ctx, offset, data, size);
di_bsem_post(&ctx->lock);
return ret;
}
di_errno_t _di_config_get_or_load_default(struct di_config_ctx *ctx, unsigned int uid,
uint8_t *data, size_t size)
{
di_errno_t ret = DNE_IOFAILED;
ret = _di_config_get(ctx, uid, data, size);
if (ret == DNOK)
return ret;
ret = di_config_set_default(ctx, uid);
if (ret != DNOK)
return ret;
ret = _di_config_get(ctx, uid, data, size);
return ret;
}
di_errno_t di_config_get_or_load_default_string(struct di_config_ctx *ctx, unsigned int uid,
char *str, size_t size)
{
di_errno_t ret = DNE_IOFAILED;
ret = di_config_get_string(ctx, uid, str, size);
if (ret == DNOK)
return ret;
ret = di_config_set_default(ctx, uid);
if (ret != DNOK)
return ret;
ret = _di_config_get(ctx, uid, (uint8_t *)str, size);
return ret;
}