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