1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "dfu.h"
8 
9 #include <stdlib.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/sys/byteorder.h>
12 #include <zephyr/sys/util.h>
13 #include <zephyr/shell/shell.h>
14 #include <zephyr/bluetooth/mesh.h>
15 #include <zephyr/bluetooth/mesh/shell.h>
16 #include <zephyr/dfu/mcuboot.h>
17 #include <zephyr/storage/flash_map.h>
18 
19 #include "utils.h"
20 #include "blob.h"
21 #include "../dfu_slot.h"
22 
23 /***************************************************************************************************
24  * Implementation of models' instances
25  **************************************************************************************************/
26 
27 extern const struct shell *bt_mesh_shell_ctx_shell;
28 
29 #if defined(CONFIG_BT_MESH_SHELL_DFU_CLI)
30 
dfu_cli_ended(struct bt_mesh_dfu_cli * cli,enum bt_mesh_dfu_status reason)31 static void dfu_cli_ended(struct bt_mesh_dfu_cli *cli,
32 			  enum bt_mesh_dfu_status reason)
33 {
34 	shell_print(bt_mesh_shell_ctx_shell, "DFU ended: %u", reason);
35 }
36 
dfu_cli_applied(struct bt_mesh_dfu_cli * cli)37 static void dfu_cli_applied(struct bt_mesh_dfu_cli *cli)
38 {
39 	shell_print(bt_mesh_shell_ctx_shell, "DFU applied.");
40 }
41 
dfu_cli_lost_target(struct bt_mesh_dfu_cli * cli,struct bt_mesh_dfu_target * target)42 static void dfu_cli_lost_target(struct bt_mesh_dfu_cli *cli,
43 				struct bt_mesh_dfu_target *target)
44 {
45 	shell_print(bt_mesh_shell_ctx_shell, "DFU target lost: 0x%04x", target->blob.addr);
46 }
47 
dfu_cli_confirmed(struct bt_mesh_dfu_cli * cli)48 static void dfu_cli_confirmed(struct bt_mesh_dfu_cli *cli)
49 {
50 	shell_print(bt_mesh_shell_ctx_shell, "DFU confirmed");
51 }
52 
53 const struct bt_mesh_dfu_cli_cb dfu_cli_cb = {
54 	.ended = dfu_cli_ended,
55 	.applied = dfu_cli_applied,
56 	.lost_target = dfu_cli_lost_target,
57 	.confirmed = dfu_cli_confirmed,
58 };
59 
60 struct bt_mesh_dfu_cli bt_mesh_shell_dfu_cli = BT_MESH_DFU_CLI_INIT(&dfu_cli_cb);
61 
62 #endif /* CONFIG_BT_MESH_SHELL_DFU_CLI */
63 
64 #if defined(CONFIG_BT_MESH_SHELL_DFU_SRV)
65 
66 struct shell_dfu_fwid {
67 	uint8_t type;
68 	struct mcuboot_img_sem_ver ver;
69 };
70 
71 static struct bt_mesh_dfu_img dfu_imgs[] = { {
72 	.fwid = &((struct shell_dfu_fwid){ 0x01, { 1, 0, 0, 0 } }),
73 	.fwid_len = sizeof(struct shell_dfu_fwid),
74 } };
75 
dfu_meta_check(struct bt_mesh_dfu_srv * srv,const struct bt_mesh_dfu_img * img,struct net_buf_simple * metadata,enum bt_mesh_dfu_effect * effect)76 static int dfu_meta_check(struct bt_mesh_dfu_srv *srv,
77 			  const struct bt_mesh_dfu_img *img,
78 			  struct net_buf_simple *metadata,
79 			  enum bt_mesh_dfu_effect *effect)
80 {
81 	return 0;
82 }
83 
dfu_start(struct bt_mesh_dfu_srv * srv,const struct bt_mesh_dfu_img * img,struct net_buf_simple * metadata,const struct bt_mesh_blob_io ** io)84 static int dfu_start(struct bt_mesh_dfu_srv *srv,
85 		     const struct bt_mesh_dfu_img *img,
86 		     struct net_buf_simple *metadata,
87 		     const struct bt_mesh_blob_io **io)
88 {
89 	shell_print(bt_mesh_shell_ctx_shell, "DFU setup");
90 
91 	*io = bt_mesh_shell_blob_io;
92 
93 	return 0;
94 }
95 
dfu_end(struct bt_mesh_dfu_srv * srv,const struct bt_mesh_dfu_img * img,bool success)96 static void dfu_end(struct bt_mesh_dfu_srv *srv, const struct bt_mesh_dfu_img *img, bool success)
97 {
98 	if (!success) {
99 		shell_print(bt_mesh_shell_ctx_shell, "DFU failed");
100 		return;
101 	}
102 
103 	if (!bt_mesh_shell_blob_valid) {
104 		bt_mesh_dfu_srv_rejected(srv);
105 		return;
106 	}
107 
108 	bt_mesh_dfu_srv_verified(srv);
109 }
110 
dfu_apply(struct bt_mesh_dfu_srv * srv,const struct bt_mesh_dfu_img * img)111 static int dfu_apply(struct bt_mesh_dfu_srv *srv,
112 		     const struct bt_mesh_dfu_img *img)
113 {
114 	if (!bt_mesh_shell_blob_valid) {
115 		return -EINVAL;
116 	}
117 
118 	shell_print(bt_mesh_shell_ctx_shell, "Applying DFU transfer...");
119 
120 	return 0;
121 }
122 
123 static const struct bt_mesh_dfu_srv_cb dfu_handlers = {
124 	.check = dfu_meta_check,
125 	.start = dfu_start,
126 	.end = dfu_end,
127 	.apply = dfu_apply,
128 };
129 
130 struct bt_mesh_dfu_srv bt_mesh_shell_dfu_srv =
131 	BT_MESH_DFU_SRV_INIT(&dfu_handlers, dfu_imgs, ARRAY_SIZE(dfu_imgs));
132 
133 #endif /* CONFIG_BT_MESH_SHELL_DFU_SRV */
134 
bt_mesh_shell_dfu_cmds_init(void)135 void bt_mesh_shell_dfu_cmds_init(void)
136 {
137 #if defined(CONFIG_BT_MESH_SHELL_DFU_SRV) && defined(CONFIG_BOOTLOADER_MCUBOOT)
138 	struct mcuboot_img_header img_header;
139 
140 	int err = boot_read_bank_header(FIXED_PARTITION_ID(slot0_partition),
141 					&img_header, sizeof(img_header));
142 	if (!err) {
143 		struct shell_dfu_fwid *fwid =
144 			(struct shell_dfu_fwid *)dfu_imgs[0].fwid;
145 
146 		fwid->ver = img_header.h.v1.sem_ver;
147 
148 		boot_write_img_confirmed();
149 	}
150 #endif
151 }
152 
153 /***************************************************************************************************
154  * Shell Commands
155  **************************************************************************************************/
156 
157 #if defined(CONFIG_BT_MESH_SHELL_DFU_METADATA)
158 
159 NET_BUF_SIMPLE_DEFINE_STATIC(dfu_comp_data, BT_MESH_TX_SDU_MAX);
160 
cmd_dfu_comp_clear(const struct shell * sh,size_t argc,char * argv[])161 static int cmd_dfu_comp_clear(const struct shell *sh, size_t argc, char *argv[])
162 {
163 	net_buf_simple_reset(&dfu_comp_data);
164 	return 0;
165 }
166 
cmd_dfu_comp_add(const struct shell * sh,size_t argc,char * argv[])167 static int cmd_dfu_comp_add(const struct shell *sh, size_t argc, char *argv[])
168 {
169 	struct net_buf_simple_state state;
170 	int err = 0;
171 
172 	if (argc < 6) {
173 		return -EINVAL;
174 	}
175 
176 	if (net_buf_simple_tailroom(&dfu_comp_data) < 10) {
177 		shell_print(sh, "Buffer is too small: %u",
178 			    net_buf_simple_tailroom(&dfu_comp_data));
179 		return -EMSGSIZE;
180 	}
181 
182 	net_buf_simple_save(&dfu_comp_data, &state);
183 
184 	for (size_t i = 1; i <= 5; i++) {
185 		net_buf_simple_add_le16(&dfu_comp_data, shell_strtoul(argv[i], 0, &err));
186 	}
187 
188 	if (err) {
189 		net_buf_simple_restore(&dfu_comp_data, &state);
190 		shell_warn(sh, "Unable to parse input string argument");
191 		return err;
192 	}
193 
194 	return 0;
195 }
196 
cmd_dfu_comp_elem_add(const struct shell * sh,size_t argc,char * argv[])197 static int cmd_dfu_comp_elem_add(const struct shell *sh, size_t argc, char *argv[])
198 {
199 	uint8_t sig_model_count;
200 	uint8_t vnd_model_count;
201 	struct net_buf_simple_state state;
202 	int err = 0;
203 
204 	if (argc < 5) {
205 		return -EINVAL;
206 	}
207 
208 	net_buf_simple_save(&dfu_comp_data, &state);
209 
210 	sig_model_count = shell_strtoul(argv[2], 0, &err);
211 	vnd_model_count = shell_strtoul(argv[3], 0, &err);
212 
213 	if (argc < 4 + sig_model_count + vnd_model_count * 2) {
214 		return -EINVAL;
215 	}
216 
217 	if (net_buf_simple_tailroom(&dfu_comp_data) < 4 + sig_model_count * 2 +
218 	    vnd_model_count * 4) {
219 		shell_print(sh, "Buffer is too small: %u",
220 			    net_buf_simple_tailroom(&dfu_comp_data));
221 		return -EMSGSIZE;
222 	}
223 
224 	net_buf_simple_add_le16(&dfu_comp_data, shell_strtoul(argv[1], 0, &err));
225 	net_buf_simple_add_u8(&dfu_comp_data, sig_model_count);
226 	net_buf_simple_add_u8(&dfu_comp_data, vnd_model_count);
227 
228 	for (size_t i = 0; i < sig_model_count; i++) {
229 		net_buf_simple_add_le16(&dfu_comp_data, shell_strtoul(argv[4 + i], 0, &err));
230 	}
231 
232 	for (size_t i = 0; i < vnd_model_count; i++) {
233 		size_t arg_i = 4 + sig_model_count + i * 2;
234 
235 		net_buf_simple_add_le16(&dfu_comp_data, shell_strtoul(argv[arg_i], 0, &err));
236 		net_buf_simple_add_le16(&dfu_comp_data, shell_strtoul(argv[arg_i + 1], 0, &err));
237 	}
238 
239 	if (err) {
240 		net_buf_simple_restore(&dfu_comp_data, &state);
241 		shell_warn(sh, "Unable to parse input string argument");
242 		return err;
243 	}
244 
245 	return 0;
246 }
247 
cmd_dfu_comp_hash_get(const struct shell * sh,size_t argc,char * argv[])248 static int cmd_dfu_comp_hash_get(const struct shell *sh, size_t argc, char *argv[])
249 {
250 	uint8_t key[16] = {};
251 	uint32_t hash;
252 	int err;
253 
254 	if (dfu_comp_data.len < 14) {
255 		shell_print(sh, "Composition data is not set");
256 		return -EINVAL;
257 	}
258 
259 	if (argc > 1) {
260 		hex2bin(argv[1], strlen(argv[1]), key, sizeof(key));
261 	}
262 
263 	shell_print(sh, "Composition data to be hashed:");
264 	shell_print(sh, "\tCID: 0x%04x", sys_get_le16(&dfu_comp_data.data[0]));
265 	shell_print(sh, "\tPID: 0x%04x", sys_get_le16(&dfu_comp_data.data[2]));
266 	shell_print(sh, "\tVID: 0x%04x", sys_get_le16(&dfu_comp_data.data[4]));
267 	shell_print(sh, "\tCPRL: %u", sys_get_le16(&dfu_comp_data.data[6]));
268 	shell_print(sh, "\tFeatures: 0x%x", sys_get_le16(&dfu_comp_data.data[8]));
269 
270 	for (size_t i = 10; i < dfu_comp_data.len - 4;) {
271 		uint8_t sig_model_count = dfu_comp_data.data[i + 2];
272 		uint8_t vnd_model_count = dfu_comp_data.data[i + 3];
273 
274 		shell_print(sh, "\tElem: %u", sys_get_le16(&dfu_comp_data.data[i]));
275 		shell_print(sh, "\t\tNumS: %u", sig_model_count);
276 		shell_print(sh, "\t\tNumV: %u", vnd_model_count);
277 
278 		for (size_t j = 0; j < sig_model_count; j++) {
279 			shell_print(sh, "\t\tSIG Model ID: 0x%04x",
280 				    sys_get_le16(&dfu_comp_data.data[i + 4 + j * 2]));
281 		}
282 
283 		for (size_t j = 0; j < vnd_model_count; j++) {
284 			size_t arg_i = i + 4 + sig_model_count * 2 + j * 4;
285 
286 			shell_print(sh, "\t\tVnd Company ID: 0x%04x, Model ID: 0x%04x",
287 				    sys_get_le16(&dfu_comp_data.data[arg_i]),
288 				    sys_get_le16(&dfu_comp_data.data[arg_i + 2]));
289 		}
290 
291 		i += 4 + sig_model_count * 2 + vnd_model_count * 4;
292 	}
293 
294 	err = bt_mesh_dfu_metadata_comp_hash_get(&dfu_comp_data, key, &hash);
295 	if (err) {
296 		shell_print(sh, "Failed to compute composition data hash: %d\n", err);
297 		return err;
298 	}
299 
300 	shell_print(sh, "Composition data hash: 0x%04x", hash);
301 
302 	return 0;
303 }
304 
cmd_dfu_metadata_encode(const struct shell * sh,size_t argc,char * argv[])305 static int cmd_dfu_metadata_encode(const struct shell *sh, size_t argc, char *argv[])
306 {
307 	char md_str[2 * CONFIG_BT_MESH_DFU_METADATA_MAXLEN + 1];
308 	uint8_t user_data[CONFIG_BT_MESH_DFU_METADATA_MAXLEN - 18];
309 	struct bt_mesh_dfu_metadata md;
310 	size_t len;
311 	int err = 0;
312 
313 	NET_BUF_SIMPLE_DEFINE(buf, CONFIG_BT_MESH_DFU_METADATA_MAXLEN);
314 
315 	if (argc < 9) {
316 		return -EINVAL;
317 	}
318 
319 	md.fw_ver.major = shell_strtoul(argv[1], 0, &err);
320 	md.fw_ver.minor = shell_strtoul(argv[2], 0, &err);
321 	md.fw_ver.revision = shell_strtoul(argv[3], 0, &err);
322 	md.fw_ver.build_num = shell_strtoul(argv[4], 0, &err);
323 	md.fw_size = shell_strtoul(argv[5], 0, &err);
324 	md.fw_core_type = shell_strtoul(argv[6], 0, &err);
325 	md.comp_hash = shell_strtoul(argv[7], 0, &err);
326 	md.elems = shell_strtoul(argv[8], 0, &err);
327 
328 	if (err) {
329 		shell_warn(sh, "Unable to parse input string argument");
330 		return err;
331 	}
332 
333 	if (argc > 9) {
334 		if (sizeof(user_data) < strlen(argv[9]) / 2) {
335 			shell_print(sh, "User data is too big.");
336 			return -EINVAL;
337 		}
338 
339 		md.user_data_len = hex2bin(argv[9], strlen(argv[9]), user_data, sizeof(user_data));
340 		md.user_data = user_data;
341 	} else {
342 		md.user_data_len = 0;
343 	}
344 
345 	shell_print(sh, "Metadata to be encoded:");
346 	shell_print(sh, "\tVersion: %u.%u.%u+%u", md.fw_ver.major, md.fw_ver.minor,
347 		    md.fw_ver.revision, md.fw_ver.build_num);
348 	shell_print(sh, "\tSize: %u", md.fw_size);
349 	shell_print(sh, "\tCore Type: 0x%x", md.fw_core_type);
350 	shell_print(sh, "\tComposition data hash: 0x%x", md.comp_hash);
351 	shell_print(sh, "\tElements: %u", md.elems);
352 
353 	if (argc > 9) {
354 		shell_print(sh, "\tUser data: %s", argv[10]);
355 	}
356 
357 	shell_print(sh, "\tUser data length: %u", md.user_data_len);
358 
359 	err = bt_mesh_dfu_metadata_encode(&md, &buf);
360 	if (err) {
361 		shell_print(sh, "Failed to encode metadata: %d", err);
362 		return err;
363 	}
364 
365 	len = bin2hex(buf.data, buf.len, md_str, sizeof(md_str));
366 	md_str[len] = '\0';
367 	shell_print(sh, "Encoded metadata: %s", md_str);
368 
369 	return 0;
370 }
371 
372 #endif /* CONFIG_BT_MESH_SHELL_DFU_METADATA */
373 
374 #if defined(CONFIG_BT_MESH_SHELL_DFU_SLOT)
375 
cmd_dfu_slot_add(const struct shell * sh,size_t argc,char * argv[])376 static int cmd_dfu_slot_add(const struct shell *sh, size_t argc, char *argv[])
377 {
378 	struct bt_mesh_dfu_slot *slot;
379 	size_t size;
380 	uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN];
381 	size_t fwid_len = 0;
382 	uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN];
383 	size_t metadata_len = 0;
384 	int err = 0;
385 
386 	size = shell_strtoul(argv[1], 0, &err);
387 	if (err) {
388 		shell_warn(sh, "Unable to parse input string argument");
389 		return err;
390 	}
391 
392 	shell_print(sh, "Adding slot (size: %u)", size);
393 	slot = bt_mesh_dfu_slot_reserve();
394 
395 	if (!slot) {
396 		shell_print(sh, "Failed to reserve slot.");
397 		return 0;
398 	}
399 
400 	fwid_len = hex2bin(argv[2], strlen(argv[2]), fwid,
401 			   sizeof(fwid));
402 	bt_mesh_dfu_slot_fwid_set(slot, fwid, fwid_len);
403 
404 	if (argc > 3) {
405 		metadata_len = hex2bin(argv[3], strlen(argv[3]), metadata,
406 				       sizeof(metadata));
407 	}
408 
409 	bt_mesh_dfu_slot_info_set(slot, size, metadata, metadata_len);
410 
411 	err = bt_mesh_dfu_slot_commit(slot);
412 	if (err) {
413 		shell_print(sh, "Failed to commit slot: %d", err);
414 		bt_mesh_dfu_slot_release(slot);
415 		return err;
416 	}
417 
418 	shell_print(sh, "Slot added. Index: %u", bt_mesh_dfu_slot_img_idx_get(slot));
419 
420 	return 0;
421 }
422 
cmd_dfu_slot_del(const struct shell * sh,size_t argc,char * argv[])423 static int cmd_dfu_slot_del(const struct shell *sh, size_t argc, char *argv[])
424 {
425 	const struct bt_mesh_dfu_slot *slot;
426 	uint8_t idx;
427 	int err = 0;
428 
429 	idx = shell_strtoul(argv[1], 0, &err);
430 	if (err) {
431 		shell_warn(sh, "Unable to parse input string argument");
432 		return err;
433 	}
434 
435 	slot = bt_mesh_dfu_slot_at(idx);
436 	if (!slot) {
437 		shell_print(sh, "No slot at %u", idx);
438 		return 0;
439 	}
440 
441 	err = bt_mesh_dfu_slot_del(slot);
442 	if (err) {
443 		shell_print(sh, "Failed deleting slot %u (err: %d)", idx,
444 			    err);
445 		return 0;
446 	}
447 
448 	shell_print(sh, "Slot %u deleted.", idx);
449 	return 0;
450 }
451 
cmd_dfu_slot_del_all(const struct shell * sh,size_t argc,char * argv[])452 static int cmd_dfu_slot_del_all(const struct shell *sh, size_t argc, char *argv[])
453 {
454 	bt_mesh_dfu_slot_del_all();
455 	shell_print(sh, "All slots deleted.");
456 	return 0;
457 }
458 
slot_info_print(const struct shell * sh,const struct bt_mesh_dfu_slot * slot,const uint8_t * idx)459 static void slot_info_print(const struct shell *sh, const struct bt_mesh_dfu_slot *slot,
460 			    const uint8_t *idx)
461 {
462 	char fwid[2 * CONFIG_BT_MESH_DFU_FWID_MAXLEN + 1];
463 	char metadata[2 * CONFIG_BT_MESH_DFU_METADATA_MAXLEN + 1];
464 	size_t len;
465 
466 	len = bin2hex(slot->fwid, slot->fwid_len, fwid, sizeof(fwid));
467 	fwid[len] = '\0';
468 	len = bin2hex(slot->metadata, slot->metadata_len, metadata,
469 		      sizeof(metadata));
470 	metadata[len] = '\0';
471 
472 	if (idx != NULL) {
473 		shell_print(sh, "Slot %u:", *idx);
474 	} else {
475 		shell_print(sh, "Slot:");
476 	}
477 	shell_print(sh, "\tSize:     %u bytes", slot->size);
478 	shell_print(sh, "\tFWID:     %s", fwid);
479 	shell_print(sh, "\tMetadata: %s", metadata);
480 }
481 
cmd_dfu_slot_get(const struct shell * sh,size_t argc,char * argv[])482 static int cmd_dfu_slot_get(const struct shell *sh, size_t argc, char *argv[])
483 {
484 	const struct bt_mesh_dfu_slot *slot;
485 	uint8_t idx;
486 	int err = 0;
487 
488 	idx = shell_strtoul(argv[1], 0, &err);
489 	if (err) {
490 		shell_warn(sh, "Unable to parse input string argument");
491 		return err;
492 	}
493 
494 	slot = bt_mesh_dfu_slot_at(idx);
495 	if (!slot) {
496 		shell_print(sh, "No slot at %u", idx);
497 		return 0;
498 	}
499 
500 	slot_info_print(sh, slot, &idx);
501 	return 0;
502 }
503 
504 #endif /* defined(CONFIG_BT_MESH_SHELL_DFU_SLOT) */
505 
506 #if defined(CONFIG_BT_MESH_SHELL_DFU_CLI)
507 
508 static const struct bt_mesh_model *mod_cli;
509 
510 static struct {
511 	struct bt_mesh_dfu_target targets[32];
512 	struct bt_mesh_blob_target_pull pull[32];
513 	size_t target_cnt;
514 	struct bt_mesh_blob_cli_inputs inputs;
515 } dfu_tx;
516 
dfu_tx_prepare(void)517 static void dfu_tx_prepare(void)
518 {
519 	sys_slist_init(&dfu_tx.inputs.targets);
520 
521 	for (size_t i = 0; i < dfu_tx.target_cnt; i++) {
522 		/* Reset target context. */
523 		uint16_t addr = dfu_tx.targets[i].blob.addr;
524 
525 		memset(&dfu_tx.targets[i].blob, 0, sizeof(struct bt_mesh_blob_target));
526 		memset(&dfu_tx.pull[i], 0, sizeof(struct bt_mesh_blob_target_pull));
527 		dfu_tx.targets[i].blob.addr = addr;
528 		dfu_tx.targets[i].blob.pull = &dfu_tx.pull[i];
529 
530 		sys_slist_append(&dfu_tx.inputs.targets, &dfu_tx.targets[i].blob.n);
531 	}
532 }
533 
cmd_dfu_target(const struct shell * sh,size_t argc,char * argv[])534 static int cmd_dfu_target(const struct shell *sh, size_t argc, char *argv[])
535 {
536 	uint8_t img_idx;
537 	uint16_t addr;
538 	int err = 0;
539 
540 	addr = shell_strtoul(argv[1], 0, &err);
541 	img_idx = shell_strtoul(argv[2], 0, &err);
542 
543 	if (err) {
544 		shell_warn(sh, "Unable to parse input string argument");
545 		return err;
546 	}
547 
548 	if (dfu_tx.target_cnt == ARRAY_SIZE(dfu_tx.targets)) {
549 		shell_print(sh, "No room.");
550 		return 0;
551 	}
552 
553 	for (size_t i = 0; i < dfu_tx.target_cnt; i++) {
554 		if (dfu_tx.targets[i].blob.addr == addr) {
555 			shell_print(sh, "Target 0x%04x already exists", addr);
556 			return 0;
557 		}
558 	}
559 
560 	dfu_tx.targets[dfu_tx.target_cnt].blob.addr = addr;
561 	dfu_tx.targets[dfu_tx.target_cnt].img_idx = img_idx;
562 	sys_slist_append(&dfu_tx.inputs.targets, &dfu_tx.targets[dfu_tx.target_cnt].blob.n);
563 	dfu_tx.target_cnt++;
564 
565 	shell_print(sh, "Added target 0x%04x", addr);
566 	return 0;
567 }
568 
cmd_dfu_targets_reset(const struct shell * sh,size_t argc,char * argv[])569 static int cmd_dfu_targets_reset(const struct shell *sh, size_t argc, char *argv[])
570 {
571 	dfu_tx_prepare();
572 	return 0;
573 }
574 
cmd_dfu_target_state(const struct shell * sh,size_t argc,char * argv[])575 static int cmd_dfu_target_state(const struct shell *sh, size_t argc, char *argv[])
576 {
577 	struct bt_mesh_dfu_target_status rsp;
578 	struct bt_mesh_msg_ctx ctx = {
579 		.send_ttl = BT_MESH_TTL_DEFAULT,
580 		.net_idx = bt_mesh_shell_target_ctx.net_idx,
581 		.addr = bt_mesh_shell_target_ctx.dst,
582 		.app_idx = bt_mesh_shell_target_ctx.app_idx,
583 	};
584 	int err;
585 
586 	if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
587 		return -ENODEV;
588 	}
589 
590 	err = bt_mesh_dfu_cli_status_get((struct bt_mesh_dfu_cli *)mod_cli->rt->user_data,
591 					 &ctx, &rsp);
592 	if (err) {
593 		shell_print(sh, "Failed getting target status (err: %d)",
594 			    err);
595 		return 0;
596 	}
597 
598 	shell_print(sh, "Target 0x%04x:", bt_mesh_shell_target_ctx.dst);
599 	shell_print(sh, "\tStatus:     %u", rsp.status);
600 	shell_print(sh, "\tPhase:      %u", rsp.phase);
601 	if (rsp.phase != BT_MESH_DFU_PHASE_IDLE) {
602 		shell_print(sh, "\tEffect:       %u", rsp.effect);
603 		shell_print(sh, "\tImg Idx:      %u", rsp.img_idx);
604 		shell_print(sh, "\tTTL:          %u", rsp.ttl);
605 		shell_print(sh, "\tTimeout base: %u", rsp.timeout_base);
606 	}
607 
608 	return 0;
609 }
610 
dfu_img_cb(struct bt_mesh_dfu_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t idx,uint8_t total,const struct bt_mesh_dfu_img * img,void * cb_data)611 static enum bt_mesh_dfu_iter dfu_img_cb(struct bt_mesh_dfu_cli *cli,
612 					struct bt_mesh_msg_ctx *ctx,
613 					uint8_t idx, uint8_t total,
614 					const struct bt_mesh_dfu_img *img,
615 					void *cb_data)
616 {
617 	char fwid[2 * CONFIG_BT_MESH_DFU_FWID_MAXLEN + 1];
618 	size_t len;
619 
620 	len = bin2hex(img->fwid, img->fwid_len, fwid, sizeof(fwid));
621 	fwid[len] = '\0';
622 
623 	shell_print(bt_mesh_shell_ctx_shell, "Image %u:", idx);
624 	shell_print(bt_mesh_shell_ctx_shell, "\tFWID: %s", fwid);
625 	if (img->uri) {
626 		shell_print(bt_mesh_shell_ctx_shell, "\tURI:  %s", img->uri);
627 	}
628 
629 	return BT_MESH_DFU_ITER_CONTINUE;
630 }
631 
cmd_dfu_target_imgs(const struct shell * sh,size_t argc,char * argv[])632 static int cmd_dfu_target_imgs(const struct shell *sh, size_t argc, char *argv[])
633 {
634 	struct bt_mesh_msg_ctx ctx = {
635 		.send_ttl = BT_MESH_TTL_DEFAULT,
636 		.net_idx = bt_mesh_shell_target_ctx.net_idx,
637 		.addr = bt_mesh_shell_target_ctx.dst,
638 		.app_idx = bt_mesh_shell_target_ctx.app_idx,
639 	};
640 	uint8_t img_cnt = 0xff;
641 	int err = 0;
642 
643 	if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
644 		return -ENODEV;
645 	}
646 
647 	if (argc == 2) {
648 		img_cnt = shell_strtoul(argv[1], 0, &err);
649 		if (err) {
650 			shell_warn(sh, "Unable to parse input string argument");
651 			return err;
652 		}
653 	}
654 
655 	shell_print(sh, "Requesting DFU images in 0x%04x", bt_mesh_shell_target_ctx.dst);
656 
657 	err = bt_mesh_dfu_cli_imgs_get((struct bt_mesh_dfu_cli *)mod_cli->rt->user_data,
658 				       &ctx, dfu_img_cb, NULL, img_cnt);
659 	if (err) {
660 		shell_print(sh, "Request failed (err: %d)", err);
661 	}
662 
663 	return 0;
664 }
665 
cmd_dfu_target_check(const struct shell * sh,size_t argc,char * argv[])666 static int cmd_dfu_target_check(const struct shell *sh, size_t argc, char *argv[])
667 {
668 	struct bt_mesh_dfu_metadata_status rsp;
669 	const struct bt_mesh_dfu_slot *slot;
670 	struct bt_mesh_msg_ctx ctx = {
671 		.send_ttl = BT_MESH_TTL_DEFAULT,
672 		.net_idx = bt_mesh_shell_target_ctx.net_idx,
673 		.addr = bt_mesh_shell_target_ctx.dst,
674 		.app_idx = bt_mesh_shell_target_ctx.app_idx,
675 	};
676 	uint8_t slot_idx, img_idx;
677 	int err = 0;
678 
679 	if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
680 		return -ENODEV;
681 	}
682 
683 	slot_idx = shell_strtoul(argv[1], 0, &err);
684 	img_idx = shell_strtoul(argv[2], 0, &err);
685 
686 	if (err) {
687 		shell_warn(sh, "Unable to parse input string argument");
688 		return err;
689 	}
690 
691 	slot = bt_mesh_dfu_slot_at(slot_idx);
692 	if (!slot) {
693 		shell_print(sh, "No image in slot %u", slot_idx);
694 		return 0;
695 	}
696 
697 	err = bt_mesh_dfu_cli_metadata_check((struct bt_mesh_dfu_cli *)mod_cli->rt->user_data,
698 					     &ctx, img_idx, slot, &rsp);
699 	if (err) {
700 		shell_print(sh, "Metadata check failed. err: %d", err);
701 		return 0;
702 	}
703 
704 	shell_print(sh, "Slot %u check for 0x%04x image %u:", slot_idx,
705 		    bt_mesh_shell_target_ctx.dst, img_idx);
706 	shell_print(sh, "\tStatus: %u", rsp.status);
707 	shell_print(sh, "\tEffect: 0x%x", rsp.effect);
708 
709 	return 0;
710 }
711 
cmd_dfu_send(const struct shell * sh,size_t argc,char * argv[])712 static int cmd_dfu_send(const struct shell *sh, size_t argc, char *argv[])
713 {
714 	struct bt_mesh_dfu_cli_xfer_blob_params blob_params;
715 	struct bt_mesh_dfu_cli_xfer xfer = { 0 };
716 	uint8_t slot_idx;
717 	uint16_t group;
718 	int err = 0;
719 
720 	if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
721 		return -ENODEV;
722 	}
723 
724 	slot_idx = shell_strtoul(argv[1], 0, &err);
725 	if (argc > 2) {
726 		group = shell_strtoul(argv[2], 0, &err);
727 	} else {
728 		group = BT_MESH_ADDR_UNASSIGNED;
729 	}
730 
731 	if (argc > 3) {
732 		xfer.mode = shell_strtoul(argv[3], 0, &err);
733 	} else {
734 		xfer.mode = BT_MESH_BLOB_XFER_MODE_PUSH;
735 	}
736 
737 	if (argc > 5) {
738 		blob_params.block_size_log = shell_strtoul(argv[4], 0, &err);
739 		blob_params.chunk_size = shell_strtoul(argv[5], 0, &err);
740 		xfer.blob_params = &blob_params;
741 	} else {
742 		xfer.blob_params = NULL;
743 	}
744 
745 	if (err) {
746 		shell_warn(sh, "Unable to parse input string argument");
747 		return err;
748 	}
749 
750 	if (!dfu_tx.target_cnt) {
751 		shell_print(sh, "No targets.");
752 		return 0;
753 	}
754 
755 	xfer.slot = bt_mesh_dfu_slot_at(slot_idx);
756 	if (!xfer.slot) {
757 		shell_print(sh, "No image in slot %u", slot_idx);
758 		return 0;
759 	}
760 
761 	shell_print(sh, "Starting DFU from slot %u (%u targets)", slot_idx,
762 		    dfu_tx.target_cnt);
763 
764 	dfu_tx.inputs.group = group;
765 	dfu_tx.inputs.app_idx = bt_mesh_shell_target_ctx.app_idx;
766 	dfu_tx.inputs.ttl = BT_MESH_TTL_DEFAULT;
767 
768 	err = bt_mesh_dfu_cli_send((struct bt_mesh_dfu_cli *)mod_cli->rt->user_data,
769 				   &dfu_tx.inputs, bt_mesh_shell_blob_io, &xfer);
770 	if (err) {
771 		shell_print(sh, "Failed (err: %d)", err);
772 		return 0;
773 	}
774 	return 0;
775 }
776 
cmd_dfu_tx_cancel(const struct shell * sh,size_t argc,char * argv[])777 static int cmd_dfu_tx_cancel(const struct shell *sh, size_t argc, char *argv[])
778 {
779 	struct bt_mesh_msg_ctx ctx = {
780 		.send_ttl = BT_MESH_TTL_DEFAULT,
781 		.net_idx = bt_mesh_shell_target_ctx.net_idx,
782 		.addr = bt_mesh_shell_target_ctx.dst,
783 		.app_idx = bt_mesh_shell_target_ctx.app_idx,
784 	};
785 	int err = 0;
786 
787 	if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
788 		return -ENODEV;
789 	}
790 
791 	if (argc == 2) {
792 		ctx.addr = shell_strtoul(argv[1], 0, &err);
793 		if (err) {
794 			shell_warn(sh, "Unable to parse input string argument");
795 			return err;
796 		}
797 
798 		shell_print(sh, "Cancelling DFU for 0x%04x", ctx.addr);
799 	} else {
800 		shell_print(sh, "Cancelling DFU");
801 	}
802 
803 	err = bt_mesh_dfu_cli_cancel((struct bt_mesh_dfu_cli *)mod_cli->rt->user_data,
804 				     (argc == 2) ? &ctx : NULL);
805 	if (err) {
806 		shell_print(sh, "Failed (err: %d)", err);
807 	}
808 
809 	return 0;
810 }
811 
cmd_dfu_apply(const struct shell * sh,size_t argc,char * argv[])812 static int cmd_dfu_apply(const struct shell *sh, size_t argc, char *argv[])
813 {
814 	int err;
815 
816 	if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
817 		return -ENODEV;
818 	}
819 
820 	shell_print(sh, "Applying DFU");
821 
822 	err = bt_mesh_dfu_cli_apply((struct bt_mesh_dfu_cli *)mod_cli->rt->user_data);
823 	if (err) {
824 		shell_print(sh, "Failed (err: %d)", err);
825 	}
826 
827 	return 0;
828 }
829 
cmd_dfu_confirm(const struct shell * sh,size_t argc,char * argv[])830 static int cmd_dfu_confirm(const struct shell *sh, size_t argc, char *argv[])
831 {
832 	int err;
833 
834 	if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
835 		return -ENODEV;
836 	}
837 
838 	shell_print(sh, "Confirming DFU");
839 
840 	err = bt_mesh_dfu_cli_confirm((struct bt_mesh_dfu_cli *)mod_cli->rt->user_data);
841 	if (err) {
842 		shell_print(sh, "Failed (err: %d)", err);
843 	}
844 
845 	return 0;
846 }
847 
cmd_dfu_suspend(const struct shell * sh,size_t argc,char * argv[])848 static int cmd_dfu_suspend(const struct shell *sh, size_t argc, char *argv[])
849 {
850 	int err;
851 
852 	if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
853 		return -ENODEV;
854 	}
855 
856 	shell_print(sh, "Suspending DFU");
857 
858 	err = bt_mesh_dfu_cli_suspend((struct bt_mesh_dfu_cli *)mod_cli->rt->user_data);
859 	if (err) {
860 		shell_print(sh, "Failed (err: %d)", err);
861 	}
862 
863 	return 0;
864 }
865 
cmd_dfu_resume(const struct shell * sh,size_t argc,char * argv[])866 static int cmd_dfu_resume(const struct shell *sh, size_t argc, char *argv[])
867 {
868 	int err;
869 
870 	if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
871 		return -ENODEV;
872 	}
873 
874 	shell_print(sh, "Resuming DFU");
875 
876 	err = bt_mesh_dfu_cli_resume((struct bt_mesh_dfu_cli *)mod_cli->rt->user_data);
877 	if (err) {
878 		shell_print(sh, "Failed (err: %d)", err);
879 	}
880 
881 	return 0;
882 }
883 
cmd_dfu_tx_progress(const struct shell * sh,size_t argc,char * argv[])884 static int cmd_dfu_tx_progress(const struct shell *sh, size_t argc, char *argv[])
885 {
886 	if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
887 		return -ENODEV;
888 	}
889 
890 	shell_print(sh, "DFU progress: %u %%",
891 		    bt_mesh_dfu_cli_progress((struct bt_mesh_dfu_cli *)mod_cli->rt->user_data));
892 	return 0;
893 }
894 
895 #endif /* CONFIG_BT_MESH_SHELL_DFU_CLI */
896 
897 #if defined(CONFIG_BT_MESH_SHELL_DFU_SRV)
898 
899 static const struct bt_mesh_model *mod_srv;
900 
cmd_dfu_applied(const struct shell * sh,size_t argc,char * argv[])901 static int cmd_dfu_applied(const struct shell *sh, size_t argc, char *argv[])
902 {
903 	if (!mod_srv && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_SRV, &mod_srv)) {
904 		return -ENODEV;
905 	}
906 
907 	bt_mesh_dfu_srv_applied((struct bt_mesh_dfu_srv *)mod_srv->rt->user_data);
908 	return 0;
909 }
910 
cmd_dfu_rx_cancel(const struct shell * sh,size_t argc,char * argv[])911 static int cmd_dfu_rx_cancel(const struct shell *sh, size_t argc, char *argv[])
912 {
913 	if (!mod_srv && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_SRV, &mod_srv)) {
914 		return -ENODEV;
915 	}
916 
917 	bt_mesh_dfu_srv_cancel((struct bt_mesh_dfu_srv *)mod_srv->rt->user_data);
918 	return 0;
919 }
920 
cmd_dfu_rx_progress(const struct shell * sh,size_t argc,char * argv[])921 static int cmd_dfu_rx_progress(const struct shell *sh, size_t argc, char *argv[])
922 {
923 	if (!mod_srv && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_SRV, &mod_srv)) {
924 		return -ENODEV;
925 	}
926 
927 	shell_print(sh, "DFU progress: %u %%",
928 		    bt_mesh_dfu_srv_progress((struct bt_mesh_dfu_srv *)mod_srv->rt->user_data));
929 	return 0;
930 }
931 
932 #endif /* CONFIG_BT_MESH_SHELL_DFU_SRV */
933 
934 #if defined(CONFIG_BT_MESH_SHELL_DFU_CLI)
935 BT_MESH_SHELL_MDL_INSTANCE_CMDS(cli_instance_cmds, BT_MESH_MODEL_ID_DFU_CLI, mod_cli);
936 #endif
937 
938 #if defined(CONFIG_BT_MESH_SHELL_DFU_SRV)
939 BT_MESH_SHELL_MDL_INSTANCE_CMDS(srv_instance_cmds, BT_MESH_MODEL_ID_DFU_SRV, mod_srv);
940 #endif
941 
942 #if defined(CONFIG_BT_MESH_SHELL_DFU_METADATA)
943 SHELL_STATIC_SUBCMD_SET_CREATE(
944 	dfu_metadata_cmds,
945 	SHELL_CMD_ARG(comp-clear, NULL, NULL, cmd_dfu_comp_clear, 1, 0),
946 	SHELL_CMD_ARG(comp-add, NULL, "<CID> <ProductID> <VendorID> <Crpl> <Features>",
947 		      cmd_dfu_comp_add, 6, 0),
948 	SHELL_CMD_ARG(comp-elem-add, NULL, "<Loc> <NumS> <NumV> "
949 		      "{<SigMID>|<VndCID> <VndMID>}...",
950 		      cmd_dfu_comp_elem_add, 5, 10),
951 	SHELL_CMD_ARG(comp-hash-get, NULL, "[<Key>]", cmd_dfu_comp_hash_get, 1, 1),
952 	SHELL_CMD_ARG(encode, NULL, "<Major> <Minor> <Rev> <BuildNum> <Size> "
953 		      "<CoreType> <Hash> <Elems> [<UserData>]",
954 		      cmd_dfu_metadata_encode, 9, 1),
955 	SHELL_SUBCMD_SET_END);
956 #endif
957 
958 #if defined(CONFIG_BT_MESH_SHELL_DFU_SLOT)
959 SHELL_STATIC_SUBCMD_SET_CREATE(
960 	dfu_slot_cmds,
961 	SHELL_CMD_ARG(add, NULL,
962 		      "<Size> <FwID> [<Metadata>]",
963 		      cmd_dfu_slot_add, 3, 1),
964 	SHELL_CMD_ARG(del, NULL, "<SlotIdx>", cmd_dfu_slot_del, 2, 0),
965 	SHELL_CMD_ARG(del-all, NULL, NULL, cmd_dfu_slot_del_all, 1, 0),
966 	SHELL_CMD_ARG(get, NULL, "<SlotIdx>", cmd_dfu_slot_get, 2, 0),
967 	SHELL_SUBCMD_SET_END);
968 #endif
969 
970 #if defined(CONFIG_BT_MESH_SHELL_DFU_CLI)
971 SHELL_STATIC_SUBCMD_SET_CREATE(
972 	dfu_cli_cmds,
973 	/* DFU Client Model Operations */
974 	SHELL_CMD_ARG(target, NULL, "<Addr> <ImgIdx>", cmd_dfu_target, 3,
975 		      0),
976 	SHELL_CMD_ARG(targets-reset, NULL, NULL, cmd_dfu_targets_reset, 1, 0),
977 	SHELL_CMD_ARG(target-state, NULL, NULL, cmd_dfu_target_state, 1, 0),
978 	SHELL_CMD_ARG(target-imgs, NULL, "[<MaxCount>]",
979 		      cmd_dfu_target_imgs, 1, 1),
980 	SHELL_CMD_ARG(target-check, NULL, "<SlotIdx> <TargetImgIdx>",
981 		      cmd_dfu_target_check, 3, 0),
982 	SHELL_CMD_ARG(send, NULL, "<SlotIdx>  [<Group> "
983 		      "[<Mode(push, pull)> [<BlockSizeLog> <ChunkSize>]]]", cmd_dfu_send, 2, 4),
984 	SHELL_CMD_ARG(cancel, NULL, "[<Addr>]", cmd_dfu_tx_cancel, 1, 1),
985 	SHELL_CMD_ARG(apply, NULL, NULL, cmd_dfu_apply, 0, 0),
986 	SHELL_CMD_ARG(confirm, NULL, NULL, cmd_dfu_confirm, 0, 0),
987 	SHELL_CMD_ARG(suspend, NULL, NULL, cmd_dfu_suspend, 0, 0),
988 	SHELL_CMD_ARG(resume, NULL, NULL, cmd_dfu_resume, 0, 0),
989 	SHELL_CMD_ARG(progress, NULL, NULL, cmd_dfu_tx_progress, 1, 0),
990 	SHELL_CMD(instance, &cli_instance_cmds, "Instance commands", bt_mesh_shell_mdl_cmds_help),
991 	SHELL_SUBCMD_SET_END);
992 #endif
993 
994 #if defined(CONFIG_BT_MESH_SHELL_DFU_SRV)
995 SHELL_STATIC_SUBCMD_SET_CREATE(
996 	dfu_srv_cmds,
997 	SHELL_CMD_ARG(applied, NULL, NULL, cmd_dfu_applied, 1, 0),
998 	SHELL_CMD_ARG(rx-cancel, NULL, NULL, cmd_dfu_rx_cancel, 1, 0),
999 	SHELL_CMD_ARG(progress, NULL, NULL, cmd_dfu_rx_progress, 1, 0),
1000 	SHELL_CMD(instance, &srv_instance_cmds, "Instance commands", bt_mesh_shell_mdl_cmds_help),
1001 	SHELL_SUBCMD_SET_END);
1002 #endif
1003 
1004 SHELL_STATIC_SUBCMD_SET_CREATE(
1005 	dfu_cmds,
1006 #if defined(CONFIG_BT_MESH_SHELL_DFU_METADATA)
1007 	SHELL_CMD(metadata, &dfu_metadata_cmds, "Metadata commands", bt_mesh_shell_mdl_cmds_help),
1008 #endif
1009 #if defined(CONFIG_BT_MESH_SHELL_DFU_SLOT)
1010 	SHELL_CMD(slot, &dfu_slot_cmds, "Slot commands", bt_mesh_shell_mdl_cmds_help),
1011 #endif
1012 #if defined(CONFIG_BT_MESH_SHELL_DFU_CLI)
1013 	SHELL_CMD(cli, &dfu_cli_cmds, "DFU Cli commands", bt_mesh_shell_mdl_cmds_help),
1014 #endif
1015 #if defined(CONFIG_BT_MESH_SHELL_DFU_SRV)
1016 	SHELL_CMD(srv, &dfu_srv_cmds, "DFU Srv commands", bt_mesh_shell_mdl_cmds_help),
1017 #endif
1018 	SHELL_SUBCMD_SET_END);
1019 
1020 SHELL_SUBCMD_ADD((mesh, models), dfu, &dfu_cmds, "DFU models commands",
1021 		 bt_mesh_shell_mdl_cmds_help, 1, 1);
1022