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