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