1 /*
2 * Copyright (c) 2020 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/settings/settings.h>
8 #include "dfu_slot.h"
9 #include <string.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <zephyr/sys/util.h>
13 #include <common/bt_str.h>
14
15 #define LOG_LEVEL CONFIG_BT_MESH_DFU_LOG_LEVEL
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(bt_mesh_dfu_slot);
18
19 #define SLOT_ENTRY_BUFLEN 25
20
21 #define DFU_SLOT_SETTINGS_PATH "bt/mesh-dfu/slot"
22
23 #define HEADER_SIZE offsetof(struct slot, slot.fwid)
24
25 #define PROP_HEADER "h"
26 #define PROP_FWID "id"
27 #define PROP_METADATA "m"
28
29 static sys_slist_t list;
30
31 static struct slot {
32 uint32_t idx;
33 struct bt_mesh_dfu_slot slot;
34 sys_snode_t n;
35 } slots[CONFIG_BT_MESH_DFU_SLOT_CNT];
36
37 static uint32_t slot_index;
38
slot_entry_encode(uint16_t idx,char buf[SLOT_ENTRY_BUFLEN],const char * property)39 static char *slot_entry_encode(uint16_t idx, char buf[SLOT_ENTRY_BUFLEN],
40 const char *property)
41 {
42 snprintf(buf, SLOT_ENTRY_BUFLEN, DFU_SLOT_SETTINGS_PATH "/%x/%s", idx,
43 property);
44
45 return buf;
46 }
47
slot_eq(const struct bt_mesh_dfu_slot * slot,const uint8_t * fwid,size_t fwid_len)48 static bool slot_eq(const struct bt_mesh_dfu_slot *slot,
49 const uint8_t *fwid, size_t fwid_len)
50 {
51 return (slot->fwid_len == fwid_len) &&
52 !memcmp(fwid, slot->fwid, fwid_len);
53 }
54
is_slot_committed(struct slot * slot_to_check)55 static bool is_slot_committed(struct slot *slot_to_check)
56 {
57 struct slot *s;
58
59 SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
60 if (s == slot_to_check) {
61 return true;
62 }
63 }
64
65 return false;
66 }
67
slot_store(const struct slot * slot_to_store)68 static int slot_store(const struct slot *slot_to_store)
69 {
70 uint16_t idx = ARRAY_INDEX(slots, slot_to_store);
71 char buf[SLOT_ENTRY_BUFLEN];
72 int err;
73
74 err = settings_save_one(slot_entry_encode(idx, buf, PROP_HEADER),
75 slot_to_store, HEADER_SIZE);
76 if (err) {
77 return err;
78 }
79
80 err = settings_save_one(slot_entry_encode(idx, buf, PROP_FWID),
81 slot_to_store->slot.fwid, slot_to_store->slot.fwid_len);
82 if (err) {
83 return err;
84 }
85
86 err = settings_save_one(slot_entry_encode(idx, buf,
87 PROP_METADATA),
88 slot_to_store->slot.metadata, slot_to_store->slot.metadata_len);
89
90 return err;
91 }
92
slot_erase(struct slot * slot_to_erase)93 static void slot_erase(struct slot *slot_to_erase)
94 {
95 uint16_t idx = ARRAY_INDEX(slots, slot_to_erase);
96 char buf[SLOT_ENTRY_BUFLEN];
97
98 settings_delete(slot_entry_encode(idx, buf, PROP_HEADER));
99 settings_delete(slot_entry_encode(idx, buf, PROP_FWID));
100 settings_delete(slot_entry_encode(idx, buf, PROP_METADATA));
101 }
102
slot_index_defrag(void)103 static void slot_index_defrag(void)
104 {
105 slot_index = 0;
106 struct slot *s;
107
108 SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
109 s->idx = ++slot_index;
110 slot_store(s);
111 }
112 }
113
bt_mesh_dfu_slot_count(void)114 int bt_mesh_dfu_slot_count(void)
115 {
116 int cnt = 0;
117 sys_snode_t *n;
118
119 SYS_SLIST_FOR_EACH_NODE(&list, n) {
120 cnt++;
121 }
122
123 return cnt;
124 }
125
bt_mesh_dfu_slot_reserve(void)126 struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_reserve(void)
127 {
128 struct slot *slot = NULL;
129
130 for (int i = 0; i < ARRAY_SIZE(slots); ++i) {
131 if (slots[i].idx == 0) {
132 slot = &slots[i];
133 break;
134 }
135 }
136
137 if (!slot) {
138 LOG_WRN("No space");
139 return NULL;
140 }
141
142 if (slot_index == UINT32_MAX) {
143 slot_index_defrag();
144 }
145
146 slot->slot.fwid_len = 0;
147 slot->slot.metadata_len = 0;
148 slot->slot.size = 0;
149 slot->idx = ++slot_index;
150
151 LOG_DBG("Reserved slot #%u", slot - &slots[0]);
152
153 return &slot->slot;
154 }
155
bt_mesh_dfu_slot_info_set(struct bt_mesh_dfu_slot * dfu_slot,size_t size,const uint8_t * metadata,size_t metadata_len)156 int bt_mesh_dfu_slot_info_set(struct bt_mesh_dfu_slot *dfu_slot, size_t size,
157 const uint8_t *metadata, size_t metadata_len)
158 {
159 struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot);
160
161 if (metadata_len > CONFIG_BT_MESH_DFU_METADATA_MAXLEN) {
162 return -EFBIG;
163 }
164
165 if (slot->idx == 0 || is_slot_committed(slot)) {
166 return -EINVAL;
167 }
168
169 slot->slot.size = size;
170 slot->slot.metadata_len = metadata_len;
171 memcpy(slot->slot.metadata, metadata, metadata_len);
172 return 0;
173 }
174
bt_mesh_dfu_slot_fwid_set(struct bt_mesh_dfu_slot * dfu_slot,const uint8_t * fwid,size_t fwid_len)175 int bt_mesh_dfu_slot_fwid_set(struct bt_mesh_dfu_slot *dfu_slot,
176 const uint8_t *fwid, size_t fwid_len)
177 {
178 struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot);
179
180 if (fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN) {
181 return -EFBIG;
182 }
183
184 if (slot->idx == 0 || is_slot_committed(slot)) {
185 return -EINVAL;
186 }
187
188 for (int i = 0; i < ARRAY_SIZE(slots); i++) {
189 if (slots[i].idx != 0 &&
190 slot_eq(&slots[i].slot, fwid, fwid_len)) {
191 return is_slot_committed(&slots[i]) ?
192 -EEXIST : -EALREADY;
193 }
194 }
195
196 slot->slot.fwid_len = fwid_len;
197 memcpy(slot->slot.fwid, fwid, fwid_len);
198 return 0;
199 }
200
bt_mesh_dfu_slot_commit(struct bt_mesh_dfu_slot * dfu_slot)201 int bt_mesh_dfu_slot_commit(struct bt_mesh_dfu_slot *dfu_slot)
202 {
203 int err;
204 struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot);
205
206 if (slot->idx == 0 ||
207 slot->slot.fwid_len == 0 ||
208 slot->slot.size == 0 ||
209 is_slot_committed(slot)) {
210 return -EINVAL;
211 }
212
213 err = slot_store(slot);
214 if (err) {
215 LOG_WRN("Store failed (err: %d)", err);
216 return err;
217 }
218
219 sys_slist_append(&list, &slot->n);
220
221 LOG_DBG("Stored slot #%u: %s", ARRAY_INDEX(slots, slot),
222 bt_hex(slot->slot.fwid, slot->slot.fwid_len));
223 return 0;
224 }
225
bt_mesh_dfu_slot_release(const struct bt_mesh_dfu_slot * dfu_slot)226 void bt_mesh_dfu_slot_release(const struct bt_mesh_dfu_slot *dfu_slot)
227 {
228 struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot);
229
230 if (is_slot_committed(slot)) {
231 return;
232 }
233
234 slot->idx = 0;
235 }
236
bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot * dfu_slot)237 int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *dfu_slot)
238 {
239 struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot);
240
241 if (!sys_slist_find_and_remove(&list, &slot->n)) {
242 return -EINVAL;
243 }
244
245 int idx = ARRAY_INDEX(slots, slot);
246
247 LOG_DBG("%u", idx);
248
249 slot_erase(slot);
250 slot->idx = 0;
251
252 return 0;
253 }
254
bt_mesh_dfu_slot_del_all(void)255 void bt_mesh_dfu_slot_del_all(void)
256 {
257 struct slot *s;
258 SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
259 slot_erase(s);
260 s->idx = 0;
261 }
262
263 sys_slist_init(&list);
264 }
265
bt_mesh_dfu_slot_at(uint16_t img_idx)266 const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t img_idx)
267 {
268 struct slot *s;
269
270 SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
271 if (!img_idx--) {
272 return &s->slot;
273 }
274 }
275
276 return NULL;
277 }
278
bt_mesh_dfu_slot_get(const uint8_t * fwid,size_t fwid_len,struct bt_mesh_dfu_slot ** slot)279 int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, struct bt_mesh_dfu_slot **slot)
280 {
281 struct slot *s;
282 int idx = 0;
283
284 SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
285 if (slot_eq(&s->slot, fwid, fwid_len)) {
286 if (slot) {
287 *slot = &s->slot;
288 }
289 return idx;
290 }
291 idx++;
292 }
293
294 return -ENOENT;
295 }
296
bt_mesh_dfu_slot_img_idx_get(const struct bt_mesh_dfu_slot * dfu_slot)297 int bt_mesh_dfu_slot_img_idx_get(const struct bt_mesh_dfu_slot *dfu_slot)
298 {
299 struct slot *s;
300 int idx = 0;
301
302 SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
303 if (&s->slot == dfu_slot) {
304 return idx;
305 }
306 idx++;
307 }
308
309 return -ENOENT;
310 }
311
bt_mesh_dfu_slot_foreach(bt_mesh_dfu_slot_cb_t cb,void * user_data)312 size_t bt_mesh_dfu_slot_foreach(bt_mesh_dfu_slot_cb_t cb, void *user_data)
313 {
314 enum bt_mesh_dfu_iter iter;
315 size_t cnt = 0;
316 struct slot *s;
317
318 SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
319 cnt++;
320
321 if (!cb) {
322 continue;
323 }
324
325 iter = cb(&s->slot, user_data);
326 if (iter != BT_MESH_DFU_ITER_CONTINUE) {
327 break;
328 }
329 }
330
331 return cnt;
332 }
333
slot_data_load(const char * key,size_t len_rd,settings_read_cb read_cb,void * cb_arg)334 static int slot_data_load(const char *key, size_t len_rd,
335 settings_read_cb read_cb, void *cb_arg)
336 {
337 const char *prop;
338 size_t len;
339 uint16_t idx;
340
341 idx = strtol(key, NULL, 16);
342
343 if (idx >= ARRAY_SIZE(slots)) {
344 return 0;
345 }
346
347 len = settings_name_next(key, &prop);
348
349 if (!strncmp(prop, PROP_HEADER, len)) {
350 if (read_cb(cb_arg, &slots[idx], HEADER_SIZE) > 0) {
351 struct slot *s, *prev = NULL;
352
353 SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
354 if (s->idx > slots[idx].idx) {
355 break;
356 }
357
358 prev = s;
359 }
360
361 if (prev == NULL) {
362 sys_slist_prepend(&list, &slots[idx].n);
363 } else {
364 sys_slist_insert(&list, &prev->n, &slots[idx].n);
365 }
366
367 if (slots[idx].idx >= slot_index) {
368 slot_index = slots[idx].idx + 1;
369 }
370 }
371 return 0;
372 }
373
374 if (!strncmp(prop, PROP_FWID, len)) {
375 if (read_cb(cb_arg, &slots[idx].slot.fwid,
376 sizeof(slots[idx].slot.fwid)) < 0) {
377 slots[idx].idx = 0;
378 sys_slist_find_and_remove(&list, &slots[idx].n);
379 return 0;
380 }
381
382 slots[idx].slot.fwid_len = len_rd;
383 return 0;
384 }
385
386 if (!strncmp(prop, PROP_METADATA, len)) {
387 if (read_cb(cb_arg, &slots[idx].slot.metadata,
388 sizeof(slots[idx].slot.metadata)) < 0) {
389 slots[idx].idx = 0;
390 sys_slist_find_and_remove(&list, &slots[idx].n);
391 return 0;
392 }
393
394 slots[idx].slot.metadata_len = len_rd;
395 return 0;
396 }
397
398 return 0;
399 }
400
401 SETTINGS_STATIC_HANDLER_DEFINE(bt_mesh_dfu_slots, DFU_SLOT_SETTINGS_PATH, NULL,
402 slot_data_load, NULL, NULL);
403