1 /*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 * Copyright (c) 2015 Runtime Inc
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include <stdbool.h>
11 #include <errno.h>
12 #include <limits.h>
13 #include <zephyr/kernel.h>
14
15 #include <zephyr/settings/settings.h>
16 #include "settings_priv.h"
17 #include <zephyr/types.h>
18 #include <zephyr/sys/iterable_sections.h>
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_REGISTER(settings, CONFIG_SETTINGS_LOG_LEVEL);
21
22 #if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS)
23 sys_slist_t settings_handlers;
24 #endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */
25
26 #ifdef CONFIG_MULTITHREADING
27 static K_MUTEX_DEFINE(settings_lock);
28 #endif
29
30 void settings_store_init(void);
31
settings_init(void)32 void settings_init(void)
33 {
34 #if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS)
35 sys_slist_init(&settings_handlers);
36 #endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */
37 settings_store_init();
38 }
39
40 #if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS)
settings_register_with_cprio(struct settings_handler * handler,int cprio)41 int settings_register_with_cprio(struct settings_handler *handler, int cprio)
42 {
43 int rc = 0;
44
45 STRUCT_SECTION_FOREACH(settings_handler_static, ch) {
46 if (strcmp(handler->name, ch->name) == 0) {
47 return -EEXIST;
48 }
49 }
50
51 settings_lock_take();
52
53 struct settings_handler *ch;
54 SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
55 if (strcmp(handler->name, ch->name) == 0) {
56 rc = -EEXIST;
57 goto end;
58 }
59 }
60
61 handler->cprio = cprio;
62 sys_slist_append(&settings_handlers, &handler->node);
63
64 end:
65 settings_lock_release();
66 return rc;
67 }
68
settings_register(struct settings_handler * handler)69 int settings_register(struct settings_handler *handler)
70 {
71 return settings_register_with_cprio(handler, 0);
72 }
73 #endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */
74
settings_name_steq(const char * name,const char * key,const char ** next)75 int settings_name_steq(const char *name, const char *key, const char **next)
76 {
77 if (next) {
78 *next = NULL;
79 }
80
81 if ((!name) || (!key)) {
82 return 0;
83 }
84
85 /* name might come from flash directly, in flash the name would end
86 * with '=' or '\0' depending how storage is done. Flash reading is
87 * limited to what can be read
88 */
89
90 while ((*key != '\0') && (*key == *name) &&
91 (*name != '\0') && (*name != SETTINGS_NAME_END)) {
92 key++;
93 name++;
94 }
95
96 if (*key != '\0') {
97 return 0;
98 }
99
100 if (*name == SETTINGS_NAME_SEPARATOR) {
101 if (next) {
102 *next = name + 1;
103 }
104 return 1;
105 }
106
107 if ((*name == SETTINGS_NAME_END) || (*name == '\0')) {
108 return 1;
109 }
110
111 return 0;
112 }
113
settings_name_next(const char * name,const char ** next)114 int settings_name_next(const char *name, const char **next)
115 {
116 int rc = 0;
117
118 if (next) {
119 *next = NULL;
120 }
121
122 if (!name) {
123 return 0;
124 }
125
126 /* name might come from flash directly, in flash the name would end
127 * with '=' or '\0' depending how storage is done. Flash reading is
128 * limited to what can be read
129 */
130 while ((*name != '\0') && (*name != SETTINGS_NAME_END) &&
131 (*name != SETTINGS_NAME_SEPARATOR)) {
132 rc++;
133 name++;
134 }
135
136 if (*name == SETTINGS_NAME_SEPARATOR) {
137 if (next) {
138 *next = name + 1;
139 }
140 return rc;
141 }
142
143 return rc;
144 }
145
settings_parse_and_lookup(const char * name,const char ** next)146 struct settings_handler_static *settings_parse_and_lookup(const char *name,
147 const char **next)
148 {
149 struct settings_handler_static *bestmatch;
150 const char *tmpnext;
151
152 bestmatch = NULL;
153 if (next) {
154 *next = NULL;
155 }
156
157 STRUCT_SECTION_FOREACH(settings_handler_static, ch) {
158 if (!settings_name_steq(name, ch->name, &tmpnext)) {
159 continue;
160 }
161 if (!bestmatch) {
162 bestmatch = ch;
163 if (next) {
164 *next = tmpnext;
165 }
166 continue;
167 }
168 if (settings_name_steq(ch->name, bestmatch->name, NULL)) {
169 bestmatch = ch;
170 if (next) {
171 *next = tmpnext;
172 }
173 }
174 }
175
176 #if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS)
177 struct settings_handler *ch;
178
179 SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
180 if (!settings_name_steq(name, ch->name, &tmpnext)) {
181 continue;
182 }
183 if (!bestmatch) {
184 bestmatch = (struct settings_handler_static *)ch;
185 if (next) {
186 *next = tmpnext;
187 }
188 continue;
189 }
190 if (settings_name_steq(ch->name, bestmatch->name, NULL)) {
191 bestmatch = (struct settings_handler_static *)ch;
192 if (next) {
193 *next = tmpnext;
194 }
195 }
196 }
197 #endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */
198 return bestmatch;
199 }
200
settings_call_set_handler(const char * name,size_t len,settings_read_cb read_cb,void * read_cb_arg,const struct settings_load_arg * load_arg)201 int settings_call_set_handler(const char *name,
202 size_t len,
203 settings_read_cb read_cb,
204 void *read_cb_arg,
205 const struct settings_load_arg *load_arg)
206 {
207 int rc;
208 const char *name_key = name;
209
210 if (load_arg && load_arg->subtree &&
211 !settings_name_steq(name, load_arg->subtree, &name_key)) {
212 return 0;
213 }
214
215 if (load_arg && load_arg->cb) {
216 rc = load_arg->cb(name_key, len, read_cb, read_cb_arg,
217 load_arg->param);
218 } else {
219 struct settings_handler_static *ch;
220
221 ch = settings_parse_and_lookup(name, &name_key);
222 if (!ch) {
223 return 0;
224 }
225
226 rc = ch->h_set(name_key, len, read_cb, read_cb_arg);
227
228 if (rc != 0) {
229 LOG_ERR("set-value failure. key: %s error(%d)",
230 name, rc);
231 /* Ignoring the error */
232 rc = 0;
233 } else {
234 LOG_DBG("set-value OK. key: %s",
235 name);
236 }
237 }
238 return rc;
239 }
240
settings_commit(void)241 int settings_commit(void)
242 {
243 return settings_commit_subtree(NULL);
244 }
245
set_next_cprio(int handler_cprio,int cprio,int next_cprio)246 static int set_next_cprio(int handler_cprio, int cprio, int next_cprio)
247 {
248 if (handler_cprio <= cprio) {
249 return next_cprio;
250 }
251
252 /* If cprio and next_cprio are identical then next_cprio has not
253 * yet been set to any value and its initialized to the first
254 * handler_cprio above cprio.
255 */
256 if (cprio == next_cprio) {
257 return handler_cprio;
258 }
259
260 return MIN(handler_cprio, next_cprio);
261 }
262
settings_commit_subtree(const char * subtree)263 int settings_commit_subtree(const char *subtree)
264 {
265 int rc;
266 int rc2;
267 int cprio = INT_MIN;
268
269 rc = 0;
270
271 while (true) {
272 int next_cprio = cprio;
273
274 STRUCT_SECTION_FOREACH(settings_handler_static, ch) {
275 if (subtree && !settings_name_steq(ch->name, subtree, NULL)) {
276 continue;
277 }
278
279 if (ch->h_commit) {
280 next_cprio = set_next_cprio(ch->cprio, cprio, next_cprio);
281 if (ch->cprio != cprio) {
282 continue;
283 }
284
285 rc2 = ch->h_commit();
286 if (!rc) {
287 rc = rc2;
288 }
289 }
290 }
291
292 if (IS_ENABLED(CONFIG_SETTINGS_DYNAMIC_HANDLERS)) {
293 struct settings_handler *ch;
294
295 SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
296 if (subtree && !settings_name_steq(ch->name, subtree, NULL)) {
297 continue;
298 }
299
300 if (ch->h_commit) {
301 next_cprio = set_next_cprio(ch->cprio, cprio, next_cprio);
302 if (ch->cprio != cprio) {
303 continue;
304 }
305
306 rc2 = ch->h_commit();
307 if (!rc) {
308 rc = rc2;
309 }
310 }
311 }
312 }
313
314 if (cprio == next_cprio) {
315 break;
316 }
317
318 cprio = next_cprio;
319 }
320
321 return rc;
322 }
323
settings_lock_take(void)324 void settings_lock_take(void)
325 {
326 #ifdef CONFIG_MULTITHREADING
327 k_mutex_lock(&settings_lock, K_FOREVER);
328 #endif
329 }
330
settings_lock_release(void)331 void settings_lock_release(void)
332 {
333 #ifdef CONFIG_MULTITHREADING
334 k_mutex_unlock(&settings_lock);
335 #endif
336 }
337