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