#include #include #include #include #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; }