diff --git a/include/linker/common-rom.ld b/include/linker/common-rom.ld index 2863b5dff61..6b700941d08 100644 --- a/include/linker/common-rom.ld +++ b/include/linker/common-rom.ld @@ -100,6 +100,15 @@ _bt_services_end = .; } GROUP_LINK_IN(ROMABLE_REGION) +#if defined(CONFIG_SETTINGS) + SECTION_DATA_PROLOGUE(_settings_handlers_area,,SUBALIGN(4)) + { + _settings_handler_static_list_start = .; + KEEP(*(SORT_BY_NAME("._settings_handler_static.static.*"))) + _settings_handler_static_list_end = .; + } GROUP_LINK_IN(ROMABLE_REGION) +#endif + #if defined(CONFIG_BT_SETTINGS) SECTION_DATA_PROLOGUE(_bt_settings_area,,SUBALIGN(4)) { diff --git a/include/settings/settings.h b/include/settings/settings.h index e64f047b325..30110218eb8 100644 --- a/include/settings/settings.h +++ b/include/settings/settings.h @@ -42,8 +42,64 @@ typedef ssize_t (*settings_read_cb)(void *cb_arg, void *data, size_t len); * These are registered using a call to @ref settings_register. */ struct settings_handler { + + char *name; + /**< Name of subtree. */ + + int (*h_get)(const char *key, char *val, int val_len_max); + /**< Get values handler of settings items identified by keyword names. + * + * Parameters: + * - key[in] the name with skipped part that was used as name in + * handler registration + * - val[out] buffer to receive value. + * - val_len_max[in] size of that buffer. + */ + + int (*h_set)(const char *key, size_t len, settings_read_cb read_cb, + void *cb_arg); + /**< Set value handler of settings items identified by keyword names. + * + * Parameters: + * - key[in] the name with skipped part that was used as name in + * handler registration + * - len[in] the size of the data found in the backend. + * - read_cb[in] function provided to read the data from the backend. + * - cb_arg[in] arguments for the read function provided by the + * backend. + */ + + int (*h_commit)(void); + /**< This handler gets called after settings has been loaded in full. + * User might use it to apply setting to the application. + */ + + int (*h_export)(int (*export_func)(const char *name, const void *val, + size_t val_len)); + /**< This gets called to dump all current settings items. + * + * This happens when @ref settings_save tries to save the settings. + * Parameters: + * - export_func: the pointer to the internal function which appends + * a single key-value pair to persisted settings. Don't store + * duplicated value. The name is subtree/key string, val is the string + * with value. + * + * @remarks The User might limit a implementations of handler to serving + * only one keyword at one call - what will impose limit to get/set + * values using full subtree/key name. + */ + sys_snode_t node; /**< Linked list node info for module internal usage. */ +}; + +/** + * @struct settings_handler_static + * Config handlers without the node element, used for static handlers. + * These are registered using a call to SETTINGS_REGISTER_STATIC(). + */ +struct settings_handler_static { char *name; /**< Name of subtree. */ @@ -93,6 +149,19 @@ struct settings_handler { */ }; +/** + * Register a static handler for settings items + * + * @param _handler Structure containing registration info, this must be const + * The handler name in ROM needs to be unique, it is generated from + * _handler and the linenumber of SETTINGS_REGISTER_STATIC() + * + */ +#define SETTINGS_STATIC_HANDLER_DEFINE(_handler) \ + const Z_STRUCT_SECTION_ITERABLE(settings_handler_static, \ + _CONCAT(_handler, __LINE__))\ + = _handler + /** * Initialization of settings and backend * @@ -105,7 +174,7 @@ struct settings_handler { int settings_subsys_init(void); /** - * Register a handler for settings items. + * Register a handler for settings items stored in RAM. * * @param cf Structure containing registration info. * @@ -274,10 +343,10 @@ void settings_dst_register(struct settings_store *cs); * @param[in] name in string format * @param[out] next remaining of name after matched handler * - * @return settings_handler node on success, NULL on failure. + * @return settings_handler_static on success, NULL on failure. */ -struct settings_handler *settings_parse_and_lookup(const char *name, - const char **next); +struct settings_handler_static *settings_parse_and_lookup(const char *name, + const char **next); /* diff --git a/subsys/settings/Kconfig b/subsys/settings/Kconfig index dead1e764ac..6978f0491c2 100644 --- a/subsys/settings/Kconfig +++ b/subsys/settings/Kconfig @@ -24,6 +24,13 @@ config SETTINGS_RUNTIME help Enables runtime storage back-end. +config SETTINGS_DYNAMIC_HANDLERS + bool "dynamic settings handlers" + depends on SETTINGS + default y + help + Enables the use of dynamic settings handlers + # Hidden option to enable encoding length into settings entry config SETTINGS_ENCODE_LEN depends on SETTINGS diff --git a/subsys/settings/src/settings.c b/subsys/settings/src/settings.c index 51b70c39454..0e86ec740ea 100644 --- a/subsys/settings/src/settings.c +++ b/subsys/settings/src/settings.c @@ -17,7 +17,10 @@ #include LOG_MODULE_REGISTER(settings, CONFIG_SETTINGS_LOG_LEVEL); +#if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS) sys_slist_t settings_handlers; +#endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */ + struct k_mutex settings_lock; @@ -25,17 +28,27 @@ void settings_store_init(void); void settings_init(void) { +#if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS) sys_slist_init(&settings_handlers); +#endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */ settings_store_init(); } +#if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS) int settings_register(struct settings_handler *handler) { int rc; - struct settings_handler *ch; k_mutex_lock(&settings_lock, K_FOREVER); + Z_STRUCT_SECTION_FOREACH(settings_handler_static, ch) { + if (strcmp(handler->name, ch->name) == 0) { + rc = -EEXIST; + goto end; + } + } + + struct settings_handler *ch; SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) { if (strcmp(handler->name, ch->name) == 0) { rc = -EEXIST; @@ -48,6 +61,7 @@ end: k_mutex_unlock(&settings_lock); return rc; } +#endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */ int settings_name_steq(const char *name, const char *key, const char **next) { @@ -120,10 +134,10 @@ int settings_name_next(const char *name, const char **next) return rc; } -struct settings_handler *settings_parse_and_lookup(const char *name, - const char **next) +struct settings_handler_static *settings_parse_and_lookup(const char *name, + const char **next) { - struct settings_handler *ch, *bestmatch; + struct settings_handler_static *bestmatch; const char *tmpnext; bestmatch = NULL; @@ -131,29 +145,66 @@ struct settings_handler *settings_parse_and_lookup(const char *name, *next = NULL; } - SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) { - if (settings_name_steq(name, ch->name, &tmpnext)) { - if ((!bestmatch) || - (settings_name_steq(ch->name, - bestmatch->name, NULL))) { - bestmatch = ch; - if (next) { - *next = tmpnext; - } + Z_STRUCT_SECTION_FOREACH(settings_handler_static, ch) { + if (!settings_name_steq(name, ch->name, &tmpnext)) { + continue; + } + if (!bestmatch) { + bestmatch = ch; + if (next) { + *next = tmpnext; + } + continue; + } + if (settings_name_steq(ch->name, bestmatch->name, NULL)) { + bestmatch = ch; + if (next) { + *next = tmpnext; } } } + +#if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS) + struct settings_handler *ch; + + SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) { + if (!settings_name_steq(name, ch->name, &tmpnext)) { + continue; + } + if (!bestmatch) { + bestmatch = (struct settings_handler_static *)ch; + if (next) { + *next = tmpnext; + } + continue; + } + if (settings_name_steq(ch->name, bestmatch->name, NULL)) { + bestmatch = (struct settings_handler_static *)ch; + if (next) { + *next = tmpnext; + } + } + } +#endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */ return bestmatch; } int settings_commit(void) { - struct settings_handler *ch; + return settings_commit_subtree(NULL); +} + +int settings_commit_subtree(const char *subtree) +{ int rc; int rc2; rc = 0; - SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) { + + Z_STRUCT_SECTION_FOREACH(settings_handler_static, ch) { + if (subtree && !settings_name_steq(ch->name, subtree, NULL)) { + continue; + } if (ch->h_commit) { rc2 = ch->h_commit(); if (!rc) { @@ -161,17 +212,9 @@ int settings_commit(void) } } } - return rc; -} -int settings_commit_subtree(const char *subtree) -{ +#if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS) struct settings_handler *ch; - - int rc; - int rc2; - - rc = 0; SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) { if (subtree && !settings_name_steq(ch->name, subtree, NULL)) { continue; @@ -183,5 +226,7 @@ int settings_commit_subtree(const char *subtree) } } } +#endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */ + return rc; } diff --git a/subsys/settings/src/settings_line.c b/subsys/settings/src/settings_line.c index 95d3c68be51..cfd4a539783 100644 --- a/subsys/settings/src/settings_line.c +++ b/subsys/settings/src/settings_line.c @@ -534,7 +534,7 @@ void settings_line_load_cb(const char *name, void *val_read_cb_ctx, off_t off, void *cb_arg) { const char *name_key; - struct settings_handler *ch; + struct settings_handler_static *ch; struct settings_line_read_value_cb_ctx value_ctx; int rc; size_t len; diff --git a/subsys/settings/src/settings_runtime.c b/subsys/settings/src/settings_runtime.c index 288f69caa76..dfb8aee3f57 100644 --- a/subsys/settings/src/settings_runtime.c +++ b/subsys/settings/src/settings_runtime.c @@ -17,7 +17,7 @@ static ssize_t settings_runtime_read_cb(void *cb_arg, void *data, size_t len) int settings_runtime_set(const char *name, void *data, size_t len) { - struct settings_handler *ch; + struct settings_handler_static *ch; const char *name_key; ch = settings_parse_and_lookup(name, &name_key); @@ -30,7 +30,7 @@ int settings_runtime_set(const char *name, void *data, size_t len) int settings_runtime_get(const char *name, void *data, size_t len) { - struct settings_handler *ch; + struct settings_handler_static *ch; const char *name_key; ch = settings_parse_and_lookup(name, &name_key); @@ -43,7 +43,7 @@ int settings_runtime_get(const char *name, void *data, size_t len) int settings_runtime_commit(const char *name) { - struct settings_handler *ch; + struct settings_handler_static *ch; const char *name_key; ch = settings_parse_and_lookup(name, &name_key); diff --git a/subsys/settings/src/settings_store.c b/subsys/settings/src/settings_store.c index ef43e50d8d8..5e8d01cd04b 100644 --- a/subsys/settings/src/settings_store.c +++ b/subsys/settings/src/settings_store.c @@ -96,7 +96,6 @@ int settings_delete(const char *name) int settings_save(void) { struct settings_store *cs; - struct settings_handler *ch; int rc; int rc2; @@ -110,6 +109,17 @@ int settings_save(void) } rc = 0; + Z_STRUCT_SECTION_FOREACH(settings_handler_static, ch) { + if (ch->h_export) { + rc2 = ch->h_export(settings_save_one); + if (!rc) { + rc = rc2; + } + } + } + +#if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS) + struct settings_handler *ch; SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) { if (ch->h_export) { rc2 = ch->h_export(settings_save_one); @@ -118,6 +128,8 @@ int settings_save(void) } } } +#endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */ + if (cs->cs_itf->csi_save_end) { cs->cs_itf->csi_save_end(cs); }