blob: 485c2a68b0866f49d8cb4a58a4e6a9b29ae309fa [file] [log] [blame]
/* Copyright (c) 2024 BayLibre SAS
*
* SPDX-License-Identifier: Apache-2.0
*/
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L /* for strnlen() */
#include <errno.h>
#include <string.h>
#include "settings/settings_zms.h"
#include "settings_priv.h"
#include <zephyr/settings/settings.h>
#include <zephyr/sys/hash_function.h>
#include <zephyr/storage/flash_map.h>
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(settings, CONFIG_SETTINGS_LOG_LEVEL);
#if DT_HAS_CHOSEN(zephyr_settings_partition)
#define SETTINGS_PARTITION DT_FIXED_PARTITION_ID(DT_CHOSEN(zephyr_settings_partition))
#else
#define SETTINGS_PARTITION FIXED_PARTITION_ID(storage_partition)
#endif
struct settings_zms_read_fn_arg {
struct zms_fs *fs;
uint32_t id;
};
static int settings_zms_load(struct settings_store *cs, const struct settings_load_arg *arg);
static ssize_t settings_zms_load_one(struct settings_store *cs, const char *name, char *buf,
size_t buf_len);
static int settings_zms_save(struct settings_store *cs, const char *name, const char *value,
size_t val_len);
static void *settings_zms_storage_get(struct settings_store *cs);
static int settings_zms_get_last_hash_ids(struct settings_zms *cf);
static ssize_t settings_zms_get_val_len(struct settings_store *cs, const char *name);
static struct settings_store_itf settings_zms_itf = {.csi_load = settings_zms_load,
.csi_load_one = settings_zms_load_one,
.csi_save = settings_zms_save,
.csi_storage_get = settings_zms_storage_get,
.csi_get_val_len = settings_zms_get_val_len};
static ssize_t settings_zms_read_fn(void *back_end, void *data, size_t len)
{
struct settings_zms_read_fn_arg *rd_fn_arg;
rd_fn_arg = (struct settings_zms_read_fn_arg *)back_end;
return zms_read(rd_fn_arg->fs, rd_fn_arg->id, data, len);
}
static int settings_zms_src(struct settings_zms *cf)
{
cf->cf_store.cs_itf = &settings_zms_itf;
settings_src_register(&cf->cf_store);
return 0;
}
static int settings_zms_dst(struct settings_zms *cf)
{
cf->cf_store.cs_itf = &settings_zms_itf;
settings_dst_register(&cf->cf_store);
return 0;
}
#ifndef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
static int settings_zms_unlink_ll_node(struct settings_zms *cf, uint32_t name_hash)
{
int rc = 0;
struct settings_hash_linked_list settings_element;
struct settings_hash_linked_list settings_update_element;
/* let's update the linked list */
rc = zms_read(&cf->cf_zms, ZMS_LL_NODE_FROM_NAME_ID(name_hash), &settings_element,
sizeof(struct settings_hash_linked_list));
if (rc < 0) {
return rc;
}
/* update the previous element */
if (settings_element.previous_hash) {
rc = zms_read(&cf->cf_zms, settings_element.previous_hash, &settings_update_element,
sizeof(struct settings_hash_linked_list));
if (rc < 0) {
return rc;
}
if (!settings_element.next_hash) {
/* we are deleting the last element of the linked list,
* let's update the second_to_last_hash_id
*/
cf->second_to_last_hash_id = settings_update_element.previous_hash;
}
settings_update_element.next_hash = settings_element.next_hash;
rc = zms_write(&cf->cf_zms, settings_element.previous_hash,
&settings_update_element, sizeof(struct settings_hash_linked_list));
if (rc < 0) {
return rc;
}
}
/* Now delete the current linked list element */
rc = zms_delete(&cf->cf_zms, ZMS_LL_NODE_FROM_NAME_ID(name_hash));
if (rc < 0) {
return rc;
}
/* update the next element */
if (settings_element.next_hash) {
rc = zms_read(&cf->cf_zms, settings_element.next_hash, &settings_update_element,
sizeof(struct settings_hash_linked_list));
if (rc < 0) {
return rc;
}
settings_update_element.previous_hash = settings_element.previous_hash;
rc = zms_write(&cf->cf_zms, settings_element.next_hash, &settings_update_element,
sizeof(struct settings_hash_linked_list));
if (rc < 0) {
return rc;
}
if (!settings_update_element.next_hash) {
/* update second_to_last_hash_id */
cf->second_to_last_hash_id = settings_element.previous_hash;
}
} else {
/* we are deleting the last element of the linked list
* let's update the last_hash_id.
*/
cf->last_hash_id = settings_element.previous_hash;
}
return 0;
}
#endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
static int settings_zms_delete(struct settings_zms *cf, uint32_t name_hash)
{
int rc = 0;
rc = zms_delete(&cf->cf_zms, name_hash);
if (rc >= 0) {
rc = zms_delete(&cf->cf_zms, ZMS_DATA_ID_FROM_NAME(name_hash));
}
if (rc < 0) {
return rc;
}
#ifndef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
#ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
cf->ll_has_changed = true;
#endif
rc = settings_zms_unlink_ll_node(cf, name_hash);
#endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
return rc;
}
#ifdef CONFIG_SETTINGS_ZMS_LOAD_SUBTREE_PATH
/* Loads first the key which is defined by the name found in "subtree" root.
* If the key is not found or further keys under the same subtree are needed
* by the caller, returns 0.
*/
static int settings_zms_load_subtree(struct settings_store *cs, const struct settings_load_arg *arg)
{
struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
struct settings_zms_read_fn_arg read_fn_arg;
char name[SETTINGS_FULL_NAME_LEN];
ssize_t rc1;
ssize_t rc2;
uint32_t name_hash;
size_t name_len = strnlen(arg->subtree, SETTINGS_FULL_NAME_LEN);
name_hash = sys_hash32(arg->subtree, name_len) & ZMS_HASH_MASK;
for (int i = 0; i <= cf->hash_collision_num; i++) {
name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, i);
/* Get the name entry from ZMS */
rc1 = zms_read(&cf->cf_zms, ZMS_NAME_ID_FROM_HASH(name_hash), &name,
sizeof(name) - 1);
/* get the length of data and verify that it exists */
rc2 = zms_get_data_length(&cf->cf_zms, ZMS_DATA_ID_FROM_HASH(name_hash));
if ((rc1 <= 0) || (rc2 <= 0)) {
/* Name or data doesn't exist */
continue;
}
/* Found a name, this might not include a trailing \0 */
name[rc1] = '\0';
if (strcmp(arg->subtree, name)) {
/* Names are not equal let's continue to the next collision hash
* if it exists.
*/
continue;
}
/* At this steps the names are equal, let's set the handler */
read_fn_arg.fs = &cf->cf_zms;
read_fn_arg.id = ZMS_DATA_ID_FROM_HASH(name_hash);
/* We should return here as there is no need to look for the next
* hash collision
*/
return settings_call_set_handler(arg->subtree, rc2, settings_zms_read_fn,
&read_fn_arg, arg);
}
return 0;
}
#endif /* CONFIG_SETTINGS_ZMS_LOAD_SUBTREE_PATH */
/* Search for the name_hash that corresponds to name.
* If no hash that corresponds to name is found in the persistent storage,
* returns 0.
*/
static uint32_t settings_zms_find_hash_from_name(struct settings_zms *cf, const char *name)
{
uint32_t name_hash = 0;
int rc = 0;
char r_name[SETTINGS_FULL_NAME_LEN];
size_t name_len = strnlen(name, SETTINGS_FULL_NAME_LEN);
name_hash = sys_hash32(name, name_len) & ZMS_HASH_MASK;
for (int i = 0; i <= cf->hash_collision_num; i++) {
name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, i);
/* Get the name entry from ZMS */
rc = zms_read(&cf->cf_zms, ZMS_NAME_ID_FROM_HASH(name_hash), r_name,
sizeof(r_name) - 1);
if (rc <= 0) {
/* Name with current collision number doesn't exist, but there might be
* one with a higher collision number
*/
continue;
}
/* Found a name, this might not include a trailing \0 */
r_name[rc] = '\0';
if (strcmp(name, r_name)) {
/* Names are not equal let's continue to the next collision hash
* if it exists.
*/
continue;
}
/* At this step names are equal, we found the corresponding hash */
return name_hash;
}
return 0;
}
static ssize_t settings_zms_load_one(struct settings_store *cs, const char *name, char *buf,
size_t buf_len)
{
struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
uint32_t name_hash = 0;
ssize_t rc = 0;
uint32_t value_id;
/* verify that name is not NULL */
if (!name || !buf) {
return -EINVAL;
}
name_hash = settings_zms_find_hash_from_name(cf, name);
if (name_hash) {
/* we found a name_hash corresponding to name */
value_id = ZMS_DATA_ID_FROM_HASH(name_hash);
rc = zms_read(&cf->cf_zms, value_id, buf, buf_len);
return (rc == buf_len) ? zms_get_data_length(&cf->cf_zms, value_id) : rc;
}
return 0;
}
/* Gets the next linked list node either from cache (if enabled) or from persistent
* storage if cache is full or cache is not enabled.
* It updates as well the next cache index and the next linked list node ID.
*/
static int settings_zms_get_next_ll(struct settings_zms *cf, uint32_t *ll_hash_id,
uint32_t *ll_cache_index __maybe_unused)
{
struct settings_hash_linked_list settings_element;
int ret = 0;
#ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
if (*ll_cache_index < cf->ll_cache_next) {
settings_element = cf->ll_cache[*ll_cache_index];
*ll_cache_index = *ll_cache_index + 1;
} else if (*ll_hash_id == cf->second_to_last_hash_id) {
/* The last ll node is not stored in the cache as it is already
* in the cf->last_hash_id.
*/
settings_element.next_hash = cf->last_hash_id;
} else {
#endif
ret = zms_read(&cf->cf_zms, *ll_hash_id, &settings_element,
sizeof(struct settings_hash_linked_list));
if (ret < 0) {
return ret;
}
#ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
}
#endif
/* update next ll_hash_id */
*ll_hash_id = settings_element.next_hash;
return 0;
}
static int settings_zms_load(struct settings_store *cs, const struct settings_load_arg *arg)
{
int ret = 0;
struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
struct settings_zms_read_fn_arg read_fn_arg;
char name[SETTINGS_FULL_NAME_LEN];
ssize_t rc1;
ssize_t rc2;
uint32_t ll_hash_id;
uint32_t prev_ll_hash_id;
uint32_t ll_cache_index = 0;
#ifdef CONFIG_SETTINGS_ZMS_LOAD_SUBTREE_PATH
/* If arg->subtree is not null we must first load settings in that subtree */
if (arg->subtree != NULL) {
ret = settings_zms_load_subtree(cs, arg);
if (ret) {
return ret;
}
}
#endif /* CONFIG_SETTINGS_ZMS_LOAD_SUBTREE_PATH */
#ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
if (cf->ll_has_changed) {
/* reload the linked list in cache */
ret = settings_zms_get_last_hash_ids(cf);
if (ret < 0) {
return ret;
}
}
#endif
/* Load all found Settings */
ll_hash_id = ZMS_LL_HEAD_HASH_ID;
ret = settings_zms_get_next_ll(cf, &ll_hash_id, &ll_cache_index);
if (ret < 0) {
return ret;
}
while (ll_hash_id) {
/* In the ZMS backend, each setting item is stored in two ZMS
* entries one for the setting's name and one with the
* setting's value.
*/
rc1 = zms_read(&cf->cf_zms, ZMS_NAME_ID_FROM_LL_NODE(ll_hash_id), &name,
sizeof(name) - 1);
/* get the length of data and verify that it exists */
rc2 = zms_get_data_length(&cf->cf_zms, ZMS_DATA_ID_FROM_LL_NODE(ll_hash_id));
/* updated the next linked list node in case the called handler will
* delete this settings entry.
*/
prev_ll_hash_id = ll_hash_id;
ret = settings_zms_get_next_ll(cf, &ll_hash_id, &ll_cache_index);
if (ret < 0) {
return ret;
}
if ((rc1 <= 0) || (rc2 <= 0)) {
/* In case we are not updating the linked list, this is an empty mode
* Just continue
*/
#ifndef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
/* Otherwise, Settings item is not stored correctly in the ZMS.
* ZMS entry's name or value is either missing or deleted.
* Clean dirty entries to make space for future settings items.
*/
ret = settings_zms_delete(cf, ZMS_NAME_ID_FROM_LL_NODE(prev_ll_hash_id));
if (ret < 0) {
return ret;
}
#endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
continue;
}
/* Found a name, this might not include a trailing \0 */
name[rc1] = '\0';
read_fn_arg.fs = &cf->cf_zms;
read_fn_arg.id = ZMS_DATA_ID_FROM_LL_NODE(prev_ll_hash_id);
ret = settings_call_set_handler(name, rc2, settings_zms_read_fn, &read_fn_arg, arg);
if (ret) {
return ret;
}
}
return ret;
}
static int settings_zms_save(struct settings_store *cs, const char *name, const char *value,
size_t val_len)
{
struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
struct settings_hash_linked_list settings_element;
char rdname[SETTINGS_FULL_NAME_LEN];
uint32_t name_hash;
uint32_t collision_num = 0;
bool delete;
bool write_name;
bool hash_collision;
int rc = 0;
int first_available_hash_index = -1;
size_t name_len;
if (!name) {
return -EINVAL;
}
/* get the name length */
name_len = strnlen(name, SETTINGS_FULL_NAME_LEN);
/* Find out if we are doing a delete */
delete = ((value == NULL) || (val_len == 0));
name_hash = sys_hash32(name, name_len) & ZMS_HASH_MASK;
/* MSB is always 1 */
name_hash |= BIT(31);
/* Let's find out if there are hash collisions in the storage */
write_name = true;
hash_collision = true;
for (int i = 0; i <= cf->hash_collision_num; i++) {
rc = zms_read(&cf->cf_zms, name_hash + i * LSB_GET(ZMS_COLLISIONS_MASK), &rdname,
sizeof(rdname) - 1);
if (rc == -ENOENT) {
if (first_available_hash_index < 0) {
first_available_hash_index = i;
}
continue;
} else if (rc < 0) {
/* error while reading */
return rc;
}
/* Settings entry exist, let's verify if this is the same
* name
*/
__ASSERT_NO_MSG(rc < sizeof(rdname));
rdname[rc] = '\0';
if ((rc == name_len) && !memcmp(name, rdname, rc)) {
/* Hash exist and the names are equal, we should
* not write the names again.
*/
write_name = false;
name_hash += i * LSB_GET(ZMS_COLLISIONS_MASK);
goto no_hash_collision;
}
/* At this step a Hash collision exists and names are different.
* If we are in the middle of the loop, we should continue checking
* all other possible hash collisions.
* If we reach the end of the loop, either we should select the first
* free hash value otherwise we increment it to the next free value and
* update hash_collision_num
*/
collision_num++;
}
if (collision_num <= cf->hash_collision_num) {
/* At this step there is a free hash found */
name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, first_available_hash_index);
goto no_hash_collision;
} else if (collision_num > cf->hash_collision_num) {
/* We must create a new hash based on incremented collision_num */
if (collision_num > ZMS_MAX_COLLISIONS) {
/* At this step there is no more space to store hash values */
LOG_ERR("Maximum hash collisions reached");
return -ENOSPC;
}
cf->hash_collision_num = collision_num;
name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, collision_num);
}
no_hash_collision:
if (delete) {
if (write_name) {
/* hash doesn't exist, do not write anything here */
return 0;
}
rc = settings_zms_delete(cf, name_hash);
return rc;
}
/* write the value */
rc = zms_write(&cf->cf_zms, ZMS_DATA_ID_FROM_NAME(name_hash), value, val_len);
if (rc < 0) {
return rc;
}
/* write the name if required */
if (write_name) {
/* First let's update the linked list */
#ifdef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
/* verify that the ll_node doesn't exist otherwise do not update it */
rc = zms_read(&cf->cf_zms, ZMS_LL_NODE_FROM_NAME_ID(name_hash), &settings_element,
sizeof(struct settings_hash_linked_list));
if (rc >= 0) {
goto no_ll_update;
} else if (rc != -ENOENT) {
return rc;
}
/* else the LL node doesn't exist let's update it */
#endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
/* write linked list structure element */
settings_element.next_hash = 0;
/* Verify first that the linked list last element is not broken.
* Settings subsystem uses ID that starts from ZMS_LL_HEAD_HASH_ID.
*/
if (cf->last_hash_id < ZMS_LL_HEAD_HASH_ID) {
LOG_WRN("Linked list for hashes is broken, Trying to recover");
rc = settings_zms_get_last_hash_ids(cf);
if (rc < 0) {
return rc;
}
}
settings_element.previous_hash = cf->last_hash_id;
rc = zms_write(&cf->cf_zms, ZMS_LL_NODE_FROM_NAME_ID(name_hash), &settings_element,
sizeof(struct settings_hash_linked_list));
if (rc < 0) {
return rc;
}
/* Now update the previous linked list element */
settings_element.next_hash = ZMS_LL_NODE_FROM_NAME_ID(name_hash);
settings_element.previous_hash = cf->second_to_last_hash_id;
rc = zms_write(&cf->cf_zms, cf->last_hash_id, &settings_element,
sizeof(struct settings_hash_linked_list));
if (rc < 0) {
return rc;
}
cf->second_to_last_hash_id = cf->last_hash_id;
cf->last_hash_id = ZMS_LL_NODE_FROM_NAME_ID(name_hash);
#ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
if (cf->ll_cache_next < CONFIG_SETTINGS_ZMS_LL_CACHE_SIZE) {
cf->ll_cache[cf->ll_cache_next] = settings_element;
cf->ll_cache_next = cf->ll_cache_next + 1;
}
#endif
#ifdef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
no_ll_update:
#endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
/* Now let's write the name */
rc = zms_write(&cf->cf_zms, name_hash, name, name_len);
if (rc < 0) {
return rc;
}
}
return 0;
}
static ssize_t settings_zms_get_val_len(struct settings_store *cs, const char *name)
{
struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
uint32_t name_hash = 0;
/* verify that name is not NULL */
if (!name) {
return -EINVAL;
}
name_hash = settings_zms_find_hash_from_name(cf, name);
if (name_hash) {
return zms_get_data_length(&cf->cf_zms, ZMS_DATA_ID_FROM_HASH(name_hash));
}
return 0;
}
/* This function inits the linked list head if it doesn't exist or recover it
* if the ll_last_hash_id is different than the head hash ID
*/
static int settings_zms_init_or_recover_ll(struct settings_zms *cf, uint32_t ll_last_hash_id)
{
struct settings_hash_linked_list settings_element;
int rc = 0;
if (ll_last_hash_id == ZMS_LL_HEAD_HASH_ID) {
/* header doesn't exist */
settings_element.previous_hash = 0;
settings_element.next_hash = 0;
rc = zms_write(&cf->cf_zms, ZMS_LL_HEAD_HASH_ID, &settings_element,
sizeof(struct settings_hash_linked_list));
if (rc < 0) {
return rc;
}
cf->last_hash_id = ZMS_LL_HEAD_HASH_ID;
cf->second_to_last_hash_id = 0;
} else {
/* let's recover it by keeping all nodes until the last one */
settings_element.previous_hash = cf->second_to_last_hash_id;
settings_element.next_hash = 0;
rc = zms_write(&cf->cf_zms, cf->last_hash_id, &settings_element,
sizeof(struct settings_hash_linked_list));
if (rc < 0) {
return rc;
}
}
return 0;
}
static int settings_zms_get_last_hash_ids(struct settings_zms *cf)
{
struct settings_hash_linked_list settings_element;
uint32_t ll_last_hash_id = ZMS_LL_HEAD_HASH_ID;
uint32_t previous_ll_hash_id = 0;
int rc = 0;
#ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
cf->ll_cache_next = 0;
#endif
cf->hash_collision_num = 0;
do {
rc = zms_read(&cf->cf_zms, ll_last_hash_id, &settings_element,
sizeof(settings_element));
if (rc == -ENOENT) {
/* header doesn't exist or linked list broken, reinitialize the header
* if it doesn't exist and recover it if it is broken
*/
return settings_zms_init_or_recover_ll(cf, ll_last_hash_id);
} else if (rc < 0) {
return rc;
}
if (settings_element.previous_hash != previous_ll_hash_id) {
/* This is a special case that can happen when a power down occurred
* when deleting a linked list node.
* If the power down occurred after updating the previous linked list node,
* then we would end up with a state where the previous_hash of the linked
* list is broken. Let's recover from this
*/
rc = zms_delete(&cf->cf_zms, settings_element.previous_hash);
if (rc < 0) {
return rc;
}
/* Now recover the linked list */
settings_element.previous_hash = previous_ll_hash_id;
rc = zms_write(&cf->cf_zms, ll_last_hash_id, &settings_element,
sizeof(struct settings_hash_linked_list));
if (rc < 0) {
return rc;
}
}
previous_ll_hash_id = ll_last_hash_id;
#ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
if ((cf->ll_cache_next < CONFIG_SETTINGS_ZMS_LL_CACHE_SIZE) &&
(settings_element.next_hash)) {
cf->ll_cache[cf->ll_cache_next] = settings_element;
cf->ll_cache_next = cf->ll_cache_next + 1;
}
#endif
/* increment hash collision number if necessary */
if (ZMS_COLLISION_NUM(ll_last_hash_id) > cf->hash_collision_num) {
cf->hash_collision_num = ZMS_COLLISION_NUM(ll_last_hash_id);
}
cf->last_hash_id = ll_last_hash_id;
cf->second_to_last_hash_id = settings_element.previous_hash;
ll_last_hash_id = settings_element.next_hash;
} while (settings_element.next_hash);
#ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
cf->ll_has_changed = false;
#endif
return 0;
}
/* Initialize the zms backend. */
static int settings_zms_backend_init(struct settings_zms *cf)
{
int rc;
cf->cf_zms.flash_device = cf->flash_dev;
if (cf->cf_zms.flash_device == NULL) {
return -ENODEV;
}
rc = zms_mount(&cf->cf_zms);
if (rc) {
return rc;
}
cf->hash_collision_num = 0;
rc = settings_zms_get_last_hash_ids(cf);
LOG_DBG("ZMS backend initialized");
return rc;
}
int settings_backend_init(void)
{
static struct settings_zms default_settings_zms;
int rc;
uint32_t cnt = 0;
size_t zms_sector_size;
const struct flash_area *fa;
struct flash_sector hw_flash_sector;
uint32_t sector_cnt = 1;
rc = flash_area_open(SETTINGS_PARTITION, &fa);
if (rc) {
return rc;
}
rc = flash_area_get_sectors(SETTINGS_PARTITION, &sector_cnt, &hw_flash_sector);
if (rc != 0 && rc != -ENOMEM) {
return rc;
}
zms_sector_size = CONFIG_SETTINGS_ZMS_SECTOR_SIZE_MULT * hw_flash_sector.fs_size;
if (zms_sector_size > UINT32_MAX) {
return -EDOM;
}
#if defined(CONFIG_SETTINGS_ZMS_CUSTOM_SECTOR_COUNT)
size_t zms_size = 0;
while (cnt < CONFIG_SETTINGS_ZMS_SECTOR_COUNT) {
zms_size += zms_sector_size;
if (zms_size > fa->fa_size) {
break;
}
cnt++;
}
#else
cnt = fa->fa_size / zms_sector_size;
#endif
/* initialize the zms file system structure using the page_info */
default_settings_zms.cf_zms.sector_size = zms_sector_size;
default_settings_zms.cf_zms.sector_count = cnt;
default_settings_zms.cf_zms.offset = fa->fa_off;
default_settings_zms.flash_dev = fa->fa_dev;
rc = settings_zms_backend_init(&default_settings_zms);
if (rc) {
return rc;
}
rc = settings_zms_src(&default_settings_zms);
if (rc) {
return rc;
}
rc = settings_zms_dst(&default_settings_zms);
return rc;
}
static void *settings_zms_storage_get(struct settings_store *cs)
{
struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
return &cf->cf_zms;
}