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