1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "blob.h"
8
9 #include <stdlib.h>
10 #include <zephyr/shell/shell.h>
11 #include <zephyr/bluetooth/mesh.h>
12 #include <zephyr/bluetooth/mesh/shell.h>
13
14 #include "common/bt_shell_private.h"
15 #include "utils.h"
16
17 /***************************************************************************************************
18 * Implementation of models' instances
19 **************************************************************************************************/
20
21 static uint8_t blob_rx_sum;
22 bool bt_mesh_shell_blob_valid;
23 static const char *blob_data = "blob";
24
blob_io_open(const struct bt_mesh_blob_io * io,const struct bt_mesh_blob_xfer * xfer,enum bt_mesh_blob_io_mode mode)25 static int blob_io_open(const struct bt_mesh_blob_io *io,
26 const struct bt_mesh_blob_xfer *xfer,
27 enum bt_mesh_blob_io_mode mode)
28 {
29 blob_rx_sum = 0;
30 bt_mesh_shell_blob_valid = true;
31 return 0;
32 }
33
blob_chunk_wr(const struct bt_mesh_blob_io * io,const struct bt_mesh_blob_xfer * xfer,const struct bt_mesh_blob_block * block,const struct bt_mesh_blob_chunk * chunk)34 static int blob_chunk_wr(const struct bt_mesh_blob_io *io,
35 const struct bt_mesh_blob_xfer *xfer,
36 const struct bt_mesh_blob_block *block,
37 const struct bt_mesh_blob_chunk *chunk)
38 {
39 int i;
40
41 for (i = 0; i < chunk->size; ++i) {
42 blob_rx_sum += chunk->data[i];
43 if (chunk->data[i] !=
44 blob_data[(i + chunk->offset) % sizeof(blob_data)]) {
45 bt_mesh_shell_blob_valid = false;
46 }
47 }
48
49 return 0;
50 }
51
blob_chunk_rd(const struct bt_mesh_blob_io * io,const struct bt_mesh_blob_xfer * xfer,const struct bt_mesh_blob_block * block,const struct bt_mesh_blob_chunk * chunk)52 static int blob_chunk_rd(const struct bt_mesh_blob_io *io,
53 const struct bt_mesh_blob_xfer *xfer,
54 const struct bt_mesh_blob_block *block,
55 const struct bt_mesh_blob_chunk *chunk)
56 {
57 for (int i = 0; i < chunk->size; ++i) {
58 chunk->data[i] =
59 blob_data[(i + chunk->offset) % sizeof(blob_data)];
60 }
61
62 return 0;
63 }
64
65 static const struct bt_mesh_blob_io dummy_blob_io = {
66 .open = blob_io_open,
67 .rd = blob_chunk_rd,
68 .wr = blob_chunk_wr,
69 };
70
71 const struct bt_mesh_blob_io *bt_mesh_shell_blob_io;
72
73 #if defined(CONFIG_BT_MESH_SHELL_BLOB_CLI)
74
75 static struct {
76 struct bt_mesh_blob_cli_inputs inputs;
77 struct bt_mesh_blob_target targets[32];
78 struct bt_mesh_blob_target_pull pull[32];
79 uint8_t target_count;
80 struct bt_mesh_blob_xfer xfer;
81 } blob_cli_xfer;
82
blob_cli_lost_target(struct bt_mesh_blob_cli * cli,struct bt_mesh_blob_target * target,enum bt_mesh_blob_status reason)83 static void blob_cli_lost_target(struct bt_mesh_blob_cli *cli,
84 struct bt_mesh_blob_target *target,
85 enum bt_mesh_blob_status reason)
86 {
87 bt_shell_print("Mesh Blob: Lost target 0x%04x (reason: %u)", target->addr, reason);
88 }
89
blob_cli_caps(struct bt_mesh_blob_cli * cli,const struct bt_mesh_blob_cli_caps * caps)90 static void blob_cli_caps(struct bt_mesh_blob_cli *cli,
91 const struct bt_mesh_blob_cli_caps *caps)
92 {
93 static const char * const modes[] = {
94 "none",
95 "push",
96 "pull",
97 "all",
98 };
99
100 if (!caps) {
101 bt_shell_print("None of the targets can be used for BLOB transfer");
102 return;
103 }
104
105 bt_shell_print("Mesh BLOB: capabilities:");
106 bt_shell_print("\tMax BLOB size: %u bytes", caps->max_size);
107 bt_shell_print("\tBlock size: %u-%u (%u-%u bytes)",
108 caps->min_block_size_log, caps->max_block_size_log,
109 1 << caps->min_block_size_log,
110 1 << caps->max_block_size_log);
111 bt_shell_print("\tMax chunks: %u", caps->max_chunks);
112 bt_shell_print("\tChunk size: %u", caps->max_chunk_size);
113 bt_shell_print("\tMTU size: %u", caps->mtu_size);
114 bt_shell_print("\tModes: %s", modes[caps->modes]);
115 }
116
blob_cli_end(struct bt_mesh_blob_cli * cli,const struct bt_mesh_blob_xfer * xfer,bool success)117 static void blob_cli_end(struct bt_mesh_blob_cli *cli,
118 const struct bt_mesh_blob_xfer *xfer, bool success)
119 {
120 if (success) {
121 bt_shell_print("Mesh BLOB transfer complete.");
122 } else {
123 bt_shell_print("Mesh BLOB transfer failed.");
124 }
125 }
126
get_progress(const struct bt_mesh_blob_xfer_info * info)127 static uint8_t get_progress(const struct bt_mesh_blob_xfer_info *info)
128 {
129 uint8_t total_blocks;
130 uint8_t blocks_not_rxed = 0;
131 uint8_t blocks_not_rxed_size;
132 int i;
133
134 total_blocks = DIV_ROUND_UP(info->size, 1U << info->block_size_log);
135
136 blocks_not_rxed_size = DIV_ROUND_UP(total_blocks, 8);
137
138 for (i = 0; i < blocks_not_rxed_size; i++) {
139 blocks_not_rxed += info->missing_blocks[i % 8] & (1 << (i % 8));
140 }
141
142 return (total_blocks - blocks_not_rxed) / total_blocks;
143 }
144
xfer_progress(struct bt_mesh_blob_cli * cli,struct bt_mesh_blob_target * target,const struct bt_mesh_blob_xfer_info * info)145 static void xfer_progress(struct bt_mesh_blob_cli *cli,
146 struct bt_mesh_blob_target *target,
147 const struct bt_mesh_blob_xfer_info *info)
148 {
149 uint8_t progress = get_progress(info);
150
151 bt_shell_print("BLOB transfer progress received from target 0x%04x:\n"
152 "\tphase: %d\n"
153 "\tprogress: %u%%",
154 target->addr, info->phase, progress);
155 }
156
xfer_progress_complete(struct bt_mesh_blob_cli * cli)157 static void xfer_progress_complete(struct bt_mesh_blob_cli *cli)
158 {
159 bt_shell_print("Determine BLOB transfer progress procedure complete");
160 }
161
162 static const struct bt_mesh_blob_cli_cb blob_cli_handlers = {
163 .lost_target = blob_cli_lost_target,
164 .caps = blob_cli_caps,
165 .end = blob_cli_end,
166 .xfer_progress = xfer_progress,
167 .xfer_progress_complete = xfer_progress_complete,
168 };
169
170 struct bt_mesh_blob_cli bt_mesh_shell_blob_cli = {
171 .cb = &blob_cli_handlers
172 };
173
174 #endif /* CONFIG_BT_MESH_SHELL_BLOB_CLI */
175
176 #if defined(CONFIG_BT_MESH_SHELL_BLOB_SRV)
177
178 static int64_t blob_time;
179
blob_srv_start(struct bt_mesh_blob_srv * srv,struct bt_mesh_msg_ctx * ctx,struct bt_mesh_blob_xfer * xfer)180 static int blob_srv_start(struct bt_mesh_blob_srv *srv,
181 struct bt_mesh_msg_ctx *ctx,
182 struct bt_mesh_blob_xfer *xfer)
183 {
184 bt_shell_print("BLOB start");
185 blob_time = k_uptime_get();
186 return 0;
187 }
188
blob_srv_end(struct bt_mesh_blob_srv * srv,uint64_t id,bool success)189 static void blob_srv_end(struct bt_mesh_blob_srv *srv, uint64_t id,
190 bool success)
191 {
192 if (success) {
193 int64_t duration = k_uptime_delta(&blob_time);
194
195 bt_shell_print("BLOB completed in %u.%03u s",
196 (uint32_t)(duration / MSEC_PER_SEC),
197 (uint32_t)(duration % MSEC_PER_SEC));
198 } else {
199 bt_shell_print("BLOB cancelled");
200 }
201 }
202
203 static const struct bt_mesh_blob_srv_cb blob_srv_cb = {
204 .start = blob_srv_start,
205 .end = blob_srv_end,
206 };
207
208 struct bt_mesh_blob_srv bt_mesh_shell_blob_srv = {
209 .cb = &blob_srv_cb
210 };
211
212 #endif /* CONFIG_BT_MESH_SHELL_BLOB_SRV */
213
bt_mesh_shell_blob_cmds_init(void)214 void bt_mesh_shell_blob_cmds_init(void)
215 {
216 bt_mesh_shell_blob_io = &dummy_blob_io;
217 }
218
219 /***************************************************************************************************
220 * Shell Commands
221 **************************************************************************************************/
222
223 #if defined(CONFIG_BT_MESH_SHELL_BLOB_IO_FLASH)
224
225 static struct bt_mesh_blob_io_flash blob_flash_stream;
226
cmd_flash_stream_set(const struct shell * sh,size_t argc,char * argv[])227 static int cmd_flash_stream_set(const struct shell *sh, size_t argc, char *argv[])
228 {
229 uint8_t area_id;
230 uint32_t offset = 0;
231 int err = 0;
232
233 if (argc < 2) {
234 return -EINVAL;
235 }
236
237 area_id = shell_strtoul(argv[1], 0, &err);
238
239 if (argc >= 3) {
240 offset = shell_strtoul(argv[2], 0, &err);
241 }
242
243 if (err) {
244 shell_warn(sh, "Unable to parse input string argument");
245 return err;
246 }
247
248 err = bt_mesh_blob_io_flash_init(&blob_flash_stream, area_id, offset);
249 if (err) {
250 shell_error(sh, "Failed to init BLOB IO Flash module: %d\n", err);
251 }
252
253 bt_mesh_shell_blob_io = &blob_flash_stream.io;
254
255 shell_print(sh, "Flash stream is initialized with area %u, offset: %u", area_id, offset);
256
257 return 0;
258 }
259
cmd_flash_stream_unset(const struct shell * sh,size_t argc,char * argv[])260 static int cmd_flash_stream_unset(const struct shell *sh, size_t argc, char *argv[])
261 {
262 bt_mesh_shell_blob_io = &dummy_blob_io;
263 return 0;
264 }
265
266 #endif /* CONFIG_BT_MESH_SHELL_BLOB_IO_FLASH */
267
268 #if defined(CONFIG_BT_MESH_SHELL_BLOB_CLI)
269
270 static const struct bt_mesh_model *mod_cli;
271
blob_cli_inputs_prepare(uint16_t group)272 static void blob_cli_inputs_prepare(uint16_t group)
273 {
274 int i;
275
276 blob_cli_xfer.inputs.ttl = BT_MESH_TTL_DEFAULT;
277 blob_cli_xfer.inputs.group = group;
278 blob_cli_xfer.inputs.app_idx = bt_mesh_shell_target_ctx.app_idx;
279 sys_slist_init(&blob_cli_xfer.inputs.targets);
280
281 for (i = 0; i < blob_cli_xfer.target_count; ++i) {
282 /* Reset target context. */
283 uint16_t addr = blob_cli_xfer.targets[i].addr;
284
285 memset(&blob_cli_xfer.targets[i], 0, sizeof(struct bt_mesh_blob_target));
286 memset(&blob_cli_xfer.pull[i], 0, sizeof(struct bt_mesh_blob_target_pull));
287 blob_cli_xfer.targets[i].addr = addr;
288 blob_cli_xfer.targets[i].pull = &blob_cli_xfer.pull[i];
289
290 sys_slist_append(&blob_cli_xfer.inputs.targets,
291 &blob_cli_xfer.targets[i].n);
292 }
293 }
294
cmd_tx(const struct shell * sh,size_t argc,char * argv[])295 static int cmd_tx(const struct shell *sh, size_t argc, char *argv[])
296 {
297 uint16_t group;
298 int err = 0;
299
300 if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_BLOB_CLI, &mod_cli)) {
301 return -ENODEV;
302 }
303
304 blob_cli_xfer.xfer.id = shell_strtoul(argv[1], 0, &err);
305 blob_cli_xfer.xfer.size = shell_strtoul(argv[2], 0, &err);
306 blob_cli_xfer.xfer.block_size_log = shell_strtoul(argv[3], 0, &err);
307 blob_cli_xfer.xfer.chunk_size = shell_strtoul(argv[4], 0, &err);
308
309 if (argc >= 6) {
310 group = shell_strtoul(argv[5], 0, &err);
311 } else {
312 group = BT_MESH_ADDR_UNASSIGNED;
313 }
314
315 if (argc < 7 || !strcmp(argv[6], "push")) {
316 blob_cli_xfer.xfer.mode = BT_MESH_BLOB_XFER_MODE_PUSH;
317 } else if (!strcmp(argv[6], "pull")) {
318 blob_cli_xfer.xfer.mode = BT_MESH_BLOB_XFER_MODE_PULL;
319 } else {
320 shell_print(sh, "Mode must be either push or pull");
321 return -EINVAL;
322 }
323
324 if (argc >= 8) {
325 blob_cli_xfer.inputs.timeout_base = shell_strtoul(argv[7], 0, &err);
326 } else {
327 blob_cli_xfer.inputs.timeout_base = 0;
328 }
329
330 if (err) {
331 shell_warn(sh, "Unable to parse input string argument");
332 return err;
333 }
334
335 if (!blob_cli_xfer.target_count) {
336 shell_print(sh, "Failed: No targets");
337 return 0;
338 }
339
340 blob_cli_inputs_prepare(group);
341
342 shell_print(sh,
343 "Sending transfer 0x%x (mode: %s, %u bytes) to 0x%04x",
344 (uint32_t)blob_cli_xfer.xfer.id,
345 blob_cli_xfer.xfer.mode == BT_MESH_BLOB_XFER_MODE_PUSH ?
346 "push" :
347 "pull",
348 blob_cli_xfer.xfer.size, group);
349
350 err = bt_mesh_blob_cli_send((struct bt_mesh_blob_cli *)mod_cli->rt->user_data,
351 &blob_cli_xfer.inputs,
352 &blob_cli_xfer.xfer, bt_mesh_shell_blob_io);
353 if (err) {
354 shell_print(sh, "BLOB transfer TX failed (err: %d)", err);
355 }
356
357 return 0;
358 }
359
cmd_target(const struct shell * sh,size_t argc,char * argv[])360 static int cmd_target(const struct shell *sh, size_t argc, char *argv[])
361 {
362 struct bt_mesh_blob_target *t;
363 int err = 0;
364
365 if (blob_cli_xfer.target_count ==
366 ARRAY_SIZE(blob_cli_xfer.targets)) {
367 shell_print(sh, "No more room");
368 return 0;
369 }
370
371 t = &blob_cli_xfer.targets[blob_cli_xfer.target_count];
372 t->addr = shell_strtoul(argv[1], 0, &err);
373
374 if (err) {
375 shell_warn(sh, "Unable to parse input string argument");
376 return err;
377 }
378
379 shell_print(sh, "Added target 0x%04x", t->addr);
380
381 blob_cli_xfer.target_count++;
382 return 0;
383 }
384
cmd_caps(const struct shell * sh,size_t argc,char * argv[])385 static int cmd_caps(const struct shell *sh, size_t argc, char *argv[])
386 {
387 uint16_t group;
388 int err = 0;
389
390 if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_BLOB_CLI, &mod_cli)) {
391 return -ENODEV;
392 }
393
394 shell_print(sh, "Retrieving transfer capabilities...");
395
396 if (argc > 1) {
397 group = shell_strtoul(argv[1], 0, &err);
398 } else {
399 group = BT_MESH_ADDR_UNASSIGNED;
400 }
401
402 if (argc > 2) {
403 blob_cli_xfer.inputs.timeout_base = shell_strtoul(argv[2], 0, &err);
404 } else {
405 blob_cli_xfer.inputs.timeout_base = 0;
406 }
407
408 if (err) {
409 shell_warn(sh, "Unable to parse input string argument");
410 return err;
411 }
412
413 if (!blob_cli_xfer.target_count) {
414 shell_print(sh, "Failed: No targets");
415 return 0;
416 }
417
418 blob_cli_inputs_prepare(group);
419
420 err = bt_mesh_blob_cli_caps_get((struct bt_mesh_blob_cli *)mod_cli->rt->user_data,
421 &blob_cli_xfer.inputs);
422 if (err) {
423 shell_print(sh, "Boundary check start failed (err: %d)", err);
424 }
425
426 return 0;
427 }
428
cmd_tx_cancel(const struct shell * sh,size_t argc,char * argv[])429 static int cmd_tx_cancel(const struct shell *sh, size_t argc,
430 char *argv[])
431 {
432 if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_BLOB_CLI, &mod_cli)) {
433 return -ENODEV;
434 }
435
436 shell_print(sh, "Cancelling transfer");
437 bt_mesh_blob_cli_cancel((struct bt_mesh_blob_cli *)mod_cli->rt->user_data);
438
439 return 0;
440 }
441
cmd_tx_get(const struct shell * sh,size_t argc,char * argv[])442 static int cmd_tx_get(const struct shell *sh, size_t argc, char *argv[])
443 {
444 uint16_t group;
445 int err;
446
447 if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_BLOB_CLI, &mod_cli)) {
448 return -ENODEV;
449 }
450
451 if (argc > 1) {
452 group = shell_strtoul(argv[1], 0, &err);
453 } else {
454 group = BT_MESH_ADDR_UNASSIGNED;
455 }
456
457 if (!blob_cli_xfer.target_count) {
458 shell_print(sh, "Failed: No targets");
459 return -EINVAL;
460 }
461
462 blob_cli_inputs_prepare(group);
463
464 err = bt_mesh_blob_cli_xfer_progress_get((struct bt_mesh_blob_cli *)mod_cli->rt->user_data,
465 &blob_cli_xfer.inputs);
466 if (err) {
467 shell_print(sh, "ERR %d", err);
468 return err;
469 }
470 return 0;
471 }
472
cmd_tx_suspend(const struct shell * sh,size_t argc,char * argv[])473 static int cmd_tx_suspend(const struct shell *sh, size_t argc,
474 char *argv[])
475 {
476 if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_BLOB_CLI, &mod_cli)) {
477 return -ENODEV;
478 }
479
480 shell_print(sh, "Suspending transfer");
481 bt_mesh_blob_cli_suspend((struct bt_mesh_blob_cli *)mod_cli->rt->user_data);
482
483 return 0;
484 }
485
cmd_tx_resume(const struct shell * sh,size_t argc,char * argv[])486 static int cmd_tx_resume(const struct shell *sh, size_t argc, char *argv[])
487 {
488 if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_BLOB_CLI, &mod_cli)) {
489 return -ENODEV;
490 }
491
492 shell_print(sh, "Resuming transfer");
493 bt_mesh_blob_cli_resume((struct bt_mesh_blob_cli *)mod_cli->rt->user_data);
494
495 return 0;
496 }
497
498 #endif /* CONFIG_BT_MESH_SHELL_BLOB_CLI */
499
500 #if defined(CONFIG_BT_MESH_SHELL_BLOB_SRV)
501
502 static const struct bt_mesh_model *mod_srv;
503
cmd_rx(const struct shell * sh,size_t argc,char * argv[])504 static int cmd_rx(const struct shell *sh, size_t argc, char *argv[])
505 {
506 uint16_t timeout_base;
507 uint32_t id;
508 int err = 0;
509
510 if (!mod_srv && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_BLOB_SRV, &mod_srv)) {
511 return -ENODEV;
512 }
513
514 id = shell_strtoul(argv[1], 0, &err);
515 blob_rx_sum = 0;
516
517 if (argc > 2) {
518 timeout_base = shell_strtoul(argv[2], 0, &err);
519 } else {
520 timeout_base = 0U;
521 }
522
523 if (err) {
524 shell_warn(sh, "Unable to parse input string argument");
525 return err;
526 }
527
528 shell_print(sh, "Receive BLOB 0x%x", id);
529 err = bt_mesh_blob_srv_recv((struct bt_mesh_blob_srv *)mod_srv->rt->user_data,
530 id, bt_mesh_shell_blob_io, BT_MESH_TTL_MAX, timeout_base);
531 if (err) {
532 shell_print(sh, "BLOB RX setup failed (%d)", err);
533 }
534
535 return 0;
536 }
537
cmd_rx_cancel(const struct shell * sh,size_t argc,char * argv[])538 static int cmd_rx_cancel(const struct shell *sh, size_t argc, char *argv[])
539 {
540 int err;
541
542 if (!mod_srv && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_BLOB_SRV, &mod_srv)) {
543 return -ENODEV;
544 }
545
546 shell_print(sh, "Cancelling BLOB rx");
547 err = bt_mesh_blob_srv_cancel((struct bt_mesh_blob_srv *)mod_srv->rt->user_data);
548 if (err) {
549 shell_print(sh, "BLOB cancel failed (%d)", err);
550 }
551
552 return 0;
553 }
554
555 #endif /* CONFIG_BT_MESH_SHELL_BLOB_SRV */
556
557 #if defined(CONFIG_BT_MESH_SHELL_BLOB_CLI)
558 BT_MESH_SHELL_MDL_INSTANCE_CMDS(cli_instance_cmds, BT_MESH_MODEL_ID_BLOB_CLI, mod_cli);
559 #endif
560 #if defined(CONFIG_BT_MESH_SHELL_BLOB_SRV)
561 BT_MESH_SHELL_MDL_INSTANCE_CMDS(srv_instance_cmds, BT_MESH_MODEL_ID_BLOB_SRV, mod_srv);
562 #endif
563
564 #if defined(CONFIG_BT_MESH_SHELL_BLOB_CLI)
565 SHELL_STATIC_SUBCMD_SET_CREATE(
566 blob_cli_cmds,
567 /* BLOB Client Model Operations */
568 SHELL_CMD_ARG(target, NULL, "<Addr>", cmd_target, 2, 0),
569 SHELL_CMD_ARG(caps, NULL, "[<Group> [<TimeoutBase>]]", cmd_caps, 1, 2),
570 SHELL_CMD_ARG(tx, NULL, "<ID> <Size> <BlockSizeLog> "
571 "<ChunkSize> [<Group> [<Mode(push, pull)> "
572 "[<TimeoutBase>]]]", cmd_tx, 5, 3),
573 SHELL_CMD_ARG(tx-cancel, NULL, NULL, cmd_tx_cancel, 1, 0),
574 SHELL_CMD_ARG(tx-get, NULL, "[Group]", cmd_tx_get, 1, 1),
575 SHELL_CMD_ARG(tx-suspend, NULL, NULL, cmd_tx_suspend, 1, 0),
576 SHELL_CMD_ARG(tx-resume, NULL, NULL, cmd_tx_resume, 1, 0),
577 SHELL_CMD(instance, &cli_instance_cmds, "Instance commands", bt_mesh_shell_mdl_cmds_help),
578 SHELL_SUBCMD_SET_END);
579 #endif
580
581 #if defined(CONFIG_BT_MESH_SHELL_BLOB_SRV)
582 SHELL_STATIC_SUBCMD_SET_CREATE(
583 blob_srv_cmds,
584 /* BLOB Server Model Operations */
585 SHELL_CMD_ARG(rx, NULL, "<ID> [<TimeoutBase(10s steps)>]", cmd_rx, 2, 1),
586 SHELL_CMD_ARG(rx-cancel, NULL, NULL, cmd_rx_cancel, 1, 0),
587 SHELL_CMD(instance, &srv_instance_cmds, "Instance commands", bt_mesh_shell_mdl_cmds_help),
588 SHELL_SUBCMD_SET_END);
589 #endif
590
591 SHELL_STATIC_SUBCMD_SET_CREATE(
592 blob_cmds,
593 #if defined(CONFIG_BT_MESH_SHELL_BLOB_IO_FLASH)
594 SHELL_CMD_ARG(flash-stream-set, NULL, "<AreaID> [<Offset>]",
595 cmd_flash_stream_set, 2, 1),
596 SHELL_CMD_ARG(flash-stream-unset, NULL, NULL, cmd_flash_stream_unset, 1, 0),
597 #endif
598 #if defined(CONFIG_BT_MESH_SHELL_BLOB_CLI)
599 SHELL_CMD(cli, &blob_cli_cmds, "BLOB Cli commands", bt_mesh_shell_mdl_cmds_help),
600 #endif
601 #if defined(CONFIG_BT_MESH_SHELL_BLOB_SRV)
602 SHELL_CMD(srv, &blob_srv_cmds, "BLOB Srv commands", bt_mesh_shell_mdl_cmds_help),
603 #endif
604 SHELL_SUBCMD_SET_END);
605
606 SHELL_SUBCMD_ADD((mesh, models), blob, &blob_cmds, "BLOB models commands",
607 bt_mesh_shell_mdl_cmds_help, 1, 1);
608