1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdlib.h>
8 #include <zephyr/shell/shell.h>
9 #include <zephyr/bluetooth/mesh.h>
10 #include <zephyr/bluetooth/mesh/shell.h>
11 
12 #include "utils.h"
13 
14 static const struct bt_mesh_model *mod;
15 
16 /***************************************************************************************************
17  * Implementation of the model's instance
18  **************************************************************************************************/
19 
20 extern const struct shell *bt_mesh_shell_ctx_shell;
21 
rpr_scan_report(struct bt_mesh_rpr_cli * cli,const struct bt_mesh_rpr_node * srv,struct bt_mesh_rpr_unprov * unprov,struct net_buf_simple * adv_data)22 static void rpr_scan_report(struct bt_mesh_rpr_cli *cli,
23 			    const struct bt_mesh_rpr_node *srv,
24 			    struct bt_mesh_rpr_unprov *unprov,
25 			    struct net_buf_simple *adv_data)
26 {
27 	char uuid_hex_str[32 + 1];
28 
29 	bin2hex(unprov->uuid, 16, uuid_hex_str, sizeof(uuid_hex_str));
30 
31 	shell_print(bt_mesh_shell_ctx_shell,
32 		    "Server 0x%04x:\n"
33 		    "\tuuid:   %s\n"
34 		    "\tOOB:    0x%04x",
35 		    srv->addr, uuid_hex_str, unprov->oob);
36 
37 	while (adv_data && adv_data->len > 2) {
38 		uint8_t len, type;
39 		uint8_t data[31];
40 
41 		len = net_buf_simple_pull_u8(adv_data);
42 		if (len == 0) {
43 			/* No data in this AD Structure. */
44 			continue;
45 		}
46 
47 		if (len > adv_data->len) {
48 			/* Malformed AD Structure. */
49 			break;
50 		}
51 
52 		type = net_buf_simple_pull_u8(adv_data);
53 		if ((--len) > 0) {
54 			uint8_t dlen;
55 
56 			/* Pull all length, but print only what fits into `data` array. */
57 			dlen = MIN(len, sizeof(data) - 1);
58 			memcpy(data, net_buf_simple_pull_mem(adv_data, len), dlen);
59 			len = dlen;
60 		}
61 		data[len] = '\0';
62 
63 		if (type == BT_DATA_URI) {
64 			shell_print(bt_mesh_shell_ctx_shell, "\tURI:    \"\\x%02x%s\"",
65 				    data[0], &data[1]);
66 		} else if (type == BT_DATA_NAME_COMPLETE) {
67 			shell_print(bt_mesh_shell_ctx_shell, "\tName:   \"%s\"", data);
68 		} else {
69 			char string[64 + 1];
70 
71 			bin2hex(data, len, string, sizeof(string));
72 			shell_print(bt_mesh_shell_ctx_shell, "\t0x%02x:  %s", type, string);
73 		}
74 	}
75 }
76 
77 struct bt_mesh_rpr_cli bt_mesh_shell_rpr_cli = {
78 	.scan_report = rpr_scan_report,
79 };
80 
81 /***************************************************************************************************
82  * Shell Commands
83  **************************************************************************************************/
84 
cmd_scan(const struct shell * sh,size_t argc,char * argv[])85 static int cmd_scan(const struct shell *sh, size_t argc, char *argv[])
86 {
87 	struct bt_mesh_rpr_scan_status rsp;
88 	const struct bt_mesh_rpr_node srv = {
89 		.addr = bt_mesh_shell_target_ctx.dst,
90 		.net_idx = bt_mesh_shell_target_ctx.net_idx,
91 		.ttl = BT_MESH_TTL_DEFAULT,
92 	};
93 	uint8_t uuid[16] = {0};
94 	uint8_t timeout;
95 	int err = 0;
96 
97 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) {
98 		return -ENODEV;
99 	}
100 
101 	timeout = shell_strtoul(argv[1], 0, &err);
102 	if (err) {
103 		shell_warn(sh, "Unable to parse input string argument");
104 		return err;
105 	}
106 
107 	if (argc > 2) {
108 		hex2bin(argv[2], strlen(argv[2]), uuid, 16);
109 	}
110 
111 	err = bt_mesh_rpr_scan_start((struct bt_mesh_rpr_cli *)mod->rt->user_data,
112 				     &srv, argc > 2 ? uuid : NULL, timeout,
113 				     BT_MESH_RPR_SCAN_MAX_DEVS_ANY, &rsp);
114 	if (err) {
115 		shell_print(sh, "Scan start failed: %d", err);
116 		return err;
117 	}
118 
119 	if (rsp.status == BT_MESH_RPR_SUCCESS) {
120 		shell_print(sh, "Scan started.");
121 	} else {
122 		shell_print(sh, "Scan start response: %d", rsp.status);
123 	}
124 
125 	return 0;
126 }
127 
cmd_scan_ext(const struct shell * sh,size_t argc,char * argv[])128 static int cmd_scan_ext(const struct shell *sh, size_t argc, char *argv[])
129 {
130 	const struct bt_mesh_rpr_node srv = {
131 		.addr = bt_mesh_shell_target_ctx.dst,
132 		.net_idx = bt_mesh_shell_target_ctx.net_idx,
133 		.ttl = BT_MESH_TTL_DEFAULT,
134 	};
135 	uint8_t ad_types[CONFIG_BT_MESH_RPR_AD_TYPES_MAX];
136 	uint8_t uuid[16] = {0};
137 	uint8_t timeout;
138 	int i, err = 0;
139 
140 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) {
141 		return -ENODEV;
142 	}
143 
144 	hex2bin(argv[2], strlen(argv[2]), uuid, 16);
145 
146 	for (i = 0; i < argc - 3; i++) {
147 		ad_types[i] = shell_strtoul(argv[3 + i], 0, &err);
148 	}
149 
150 	timeout = shell_strtoul(argv[1], 0, &err);
151 	if (err) {
152 		shell_warn(sh, "Unable to parse input string argument");
153 		return err;
154 	}
155 
156 	err = bt_mesh_rpr_scan_start_ext((struct bt_mesh_rpr_cli *)mod->rt->user_data,
157 					 &srv, uuid, timeout, ad_types,
158 					 (argc - 3));
159 	if (err) {
160 		shell_print(sh, "Scan start failed: %d", err);
161 		return err;
162 	}
163 
164 	shell_print(sh, "Extended scan started.");
165 
166 	return 0;
167 }
168 
cmd_scan_srv(const struct shell * sh,size_t argc,char * argv[])169 static int cmd_scan_srv(const struct shell *sh, size_t argc, char *argv[])
170 {
171 	const struct bt_mesh_rpr_node srv = {
172 		.addr = bt_mesh_shell_target_ctx.dst,
173 		.net_idx = bt_mesh_shell_target_ctx.net_idx,
174 		.ttl = BT_MESH_TTL_DEFAULT,
175 	};
176 	uint8_t ad_types[CONFIG_BT_MESH_RPR_AD_TYPES_MAX];
177 	int i, err = 0;
178 
179 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) {
180 		return -ENODEV;
181 	}
182 
183 	for (i = 0; i < argc - 1; i++) {
184 		ad_types[i] = shell_strtoul(argv[1 + i], 0, &err);
185 	}
186 
187 	if (err) {
188 		shell_warn(sh, "Unable to parse input string argument");
189 		return err;
190 	}
191 
192 	err = bt_mesh_rpr_scan_start_ext((struct bt_mesh_rpr_cli *)mod->rt->user_data,
193 					 &srv, NULL, 0, ad_types, (argc - 1));
194 	if (err) {
195 		shell_print(sh, "Scan start failed: %d", err);
196 		return err;
197 	}
198 
199 	return 0;
200 }
201 
cmd_scan_caps(const struct shell * sh,size_t argc,char * argv[])202 static int cmd_scan_caps(const struct shell *sh, size_t argc, char *argv[])
203 {
204 	struct bt_mesh_rpr_caps caps;
205 	const struct bt_mesh_rpr_node srv = {
206 		.addr = bt_mesh_shell_target_ctx.dst,
207 		.net_idx = bt_mesh_shell_target_ctx.net_idx,
208 		.ttl = BT_MESH_TTL_DEFAULT,
209 	};
210 	int err;
211 
212 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) {
213 		return -ENODEV;
214 	}
215 
216 	err = bt_mesh_rpr_scan_caps_get((struct bt_mesh_rpr_cli *)mod->rt->user_data, &srv, &caps);
217 	if (err) {
218 		shell_print(sh, "Scan capabilities get failed: %d", err);
219 		return err;
220 	}
221 
222 	shell_print(sh, "Remote Provisioning scan capabilities of 0x%04x:",
223 		    bt_mesh_shell_target_ctx.dst);
224 	shell_print(sh, "\tMax devices:     %u", caps.max_devs);
225 	shell_print(sh, "\tActive scanning: %s",
226 		    caps.active_scan ? "true" : "false");
227 	return 0;
228 }
229 
cmd_scan_get(const struct shell * sh,size_t argc,char * argv[])230 static int cmd_scan_get(const struct shell *sh, size_t argc, char *argv[])
231 {
232 	struct bt_mesh_rpr_scan_status rsp;
233 	const struct bt_mesh_rpr_node srv = {
234 		.addr = bt_mesh_shell_target_ctx.dst,
235 		.net_idx = bt_mesh_shell_target_ctx.net_idx,
236 		.ttl = BT_MESH_TTL_DEFAULT,
237 	};
238 	int err;
239 
240 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) {
241 		return -ENODEV;
242 	}
243 
244 	err = bt_mesh_rpr_scan_get((struct bt_mesh_rpr_cli *)mod->rt->user_data, &srv, &rsp);
245 	if (err) {
246 		shell_print(sh, "Scan get failed: %d", err);
247 		return err;
248 	}
249 
250 	shell_print(sh, "Remote Provisioning scan on 0x%04x:", bt_mesh_shell_target_ctx.dst);
251 	shell_print(sh, "\tStatus:         %u", rsp.status);
252 	shell_print(sh, "\tScan type:      %u", rsp.scan);
253 	shell_print(sh, "\tMax devices:    %u", rsp.max_devs);
254 	shell_print(sh, "\tRemaining time: %u", rsp.timeout);
255 	return 0;
256 }
257 
cmd_scan_stop(const struct shell * sh,size_t argc,char * argv[])258 static int cmd_scan_stop(const struct shell *sh, size_t argc, char *argv[])
259 {
260 	struct bt_mesh_rpr_scan_status rsp;
261 	const struct bt_mesh_rpr_node srv = {
262 		.addr = bt_mesh_shell_target_ctx.dst,
263 		.net_idx = bt_mesh_shell_target_ctx.net_idx,
264 		.ttl = BT_MESH_TTL_DEFAULT,
265 	};
266 	int err;
267 
268 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) {
269 		return -ENODEV;
270 	}
271 
272 	err = bt_mesh_rpr_scan_stop((struct bt_mesh_rpr_cli *)mod->rt->user_data, &srv, &rsp);
273 	if (err || rsp.status) {
274 		shell_print(sh, "Scan stop failed: %d %u", err, rsp.status);
275 		return err;
276 	}
277 
278 	shell_print(sh, "Remote Provisioning scan on 0x%04x stopped.",
279 		    bt_mesh_shell_target_ctx.dst);
280 	return 0;
281 }
282 
cmd_link_get(const struct shell * sh,size_t argc,char * argv[])283 static int cmd_link_get(const struct shell *sh, size_t argc, char *argv[])
284 {
285 	struct bt_mesh_rpr_link rsp;
286 	const struct bt_mesh_rpr_node srv = {
287 		.addr = bt_mesh_shell_target_ctx.dst,
288 		.net_idx = bt_mesh_shell_target_ctx.net_idx,
289 		.ttl = BT_MESH_TTL_DEFAULT,
290 	};
291 	int err;
292 
293 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) {
294 		return -ENODEV;
295 	}
296 
297 	err = bt_mesh_rpr_link_get((struct bt_mesh_rpr_cli *)mod->rt->user_data, &srv, &rsp);
298 	if (err) {
299 		shell_print(sh, "Link get failed: %d %u", err, rsp.status);
300 		return err;
301 	}
302 
303 	shell_print(sh, "Remote Provisioning Link on 0x%04x:", bt_mesh_shell_target_ctx.dst);
304 	shell_print(sh, "\tStatus: %u", rsp.status);
305 	shell_print(sh, "\tState:  %u", rsp.state);
306 	return 0;
307 }
308 
cmd_link_close(const struct shell * sh,size_t argc,char * argv[])309 static int cmd_link_close(const struct shell *sh, size_t argc, char *argv[])
310 {
311 	struct bt_mesh_rpr_link rsp;
312 	const struct bt_mesh_rpr_node srv = {
313 		.addr = bt_mesh_shell_target_ctx.dst,
314 		.net_idx = bt_mesh_shell_target_ctx.net_idx,
315 		.ttl = BT_MESH_TTL_DEFAULT,
316 	};
317 	int err;
318 
319 	err = bt_mesh_rpr_link_close((struct bt_mesh_rpr_cli *)mod->rt->user_data, &srv, &rsp);
320 	if (err) {
321 		shell_print(sh, "Link close failed: %d %u", err, rsp.status);
322 		return err;
323 	}
324 
325 	shell_print(sh, "Remote Provisioning Link on 0x%04x:", bt_mesh_shell_target_ctx.dst);
326 	shell_print(sh, "\tStatus: %u", rsp.status);
327 	shell_print(sh, "\tState:  %u", rsp.state);
328 	return 0;
329 }
330 
cmd_provision_remote(const struct shell * sh,size_t argc,char * argv[])331 static int cmd_provision_remote(const struct shell *sh, size_t argc, char *argv[])
332 {
333 	struct bt_mesh_rpr_node srv = {
334 		.addr = bt_mesh_shell_target_ctx.dst,
335 		.net_idx = bt_mesh_shell_target_ctx.net_idx,
336 		.ttl = BT_MESH_TTL_DEFAULT,
337 	};
338 	uint8_t uuid[16];
339 	size_t len;
340 	uint16_t net_idx;
341 	uint16_t addr;
342 	int err = 0;
343 
344 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) {
345 		return -ENODEV;
346 	}
347 
348 	len = hex2bin(argv[1], strlen(argv[1]), uuid, sizeof(uuid));
349 	(void)memset(uuid + len, 0, sizeof(uuid) - len);
350 
351 	net_idx = shell_strtoul(argv[2], 0, &err);
352 	addr = shell_strtoul(argv[3], 0, &err);
353 	if (err) {
354 		shell_warn(sh, "Unable to parse input string argument");
355 		return err;
356 	}
357 
358 	err = bt_mesh_provision_remote((struct bt_mesh_rpr_cli *)mod->rt->user_data,
359 				       &srv, uuid, net_idx, addr);
360 	if (err) {
361 		shell_print(sh, "Prov remote start failed: %d", err);
362 	}
363 
364 	return err;
365 }
366 
cmd_reprovision_remote(const struct shell * sh,size_t argc,char * argv[])367 static int cmd_reprovision_remote(const struct shell *sh, size_t argc, char *argv[])
368 {
369 	struct bt_mesh_rpr_node srv = {
370 		.addr = bt_mesh_shell_target_ctx.dst,
371 		.net_idx = bt_mesh_shell_target_ctx.net_idx,
372 		.ttl = BT_MESH_TTL_DEFAULT,
373 	};
374 	bool composition_changed;
375 	uint16_t addr;
376 	int err = 0;
377 
378 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) {
379 		return -ENODEV;
380 	}
381 
382 	addr = shell_strtoul(argv[1], 0, &err);
383 	if (err) {
384 		shell_warn(sh, "Unable to parse input string argument");
385 		return err;
386 	}
387 
388 	if (!BT_MESH_ADDR_IS_UNICAST(addr)) {
389 		shell_print(sh, "Must be a valid unicast address");
390 		return -EINVAL;
391 	}
392 
393 	composition_changed = (argc > 2 && shell_strtobool(argv[2], 0, &err));
394 	if (err) {
395 		shell_warn(sh, "Unable to parse input string argument");
396 		return err;
397 	}
398 
399 	err = bt_mesh_reprovision_remote((struct bt_mesh_rpr_cli *)mod->rt->user_data,
400 					 &srv, addr, composition_changed);
401 	if (err) {
402 		shell_print(sh, "Reprovisioning failed: %d", err);
403 	}
404 
405 	return 0;
406 }
407 
408 BT_MESH_SHELL_MDL_INSTANCE_CMDS(instance_cmds, BT_MESH_MODEL_ID_REMOTE_PROV_CLI, mod);
409 
410 SHELL_STATIC_SUBCMD_SET_CREATE(
411 	rpr_cli_cmds,
412 	SHELL_CMD_ARG(scan, NULL, "<Timeout(s)> [<UUID(1-16 hex)>]", cmd_scan, 2, 1),
413 	SHELL_CMD_ARG(scan-ext, NULL, "<Timeout(s)> <UUID(1-16 hex)> [<ADType> ... ]",
414 		      cmd_scan_ext, 3, CONFIG_BT_MESH_RPR_AD_TYPES_MAX),
415 	SHELL_CMD_ARG(scan-srv, NULL, "[<ADType> ... ]", cmd_scan_srv, 1,
416 		      CONFIG_BT_MESH_RPR_AD_TYPES_MAX),
417 	SHELL_CMD_ARG(scan-caps, NULL, NULL, cmd_scan_caps, 1, 0),
418 	SHELL_CMD_ARG(scan-get, NULL, NULL, cmd_scan_get, 1, 0),
419 	SHELL_CMD_ARG(scan-stop, NULL, NULL, cmd_scan_stop, 1, 0),
420 	SHELL_CMD_ARG(link-get, NULL, NULL, cmd_link_get, 1, 0),
421 	SHELL_CMD_ARG(link-close, NULL, NULL, cmd_link_close, 1, 0),
422 	SHELL_CMD_ARG(provision-remote, NULL, "<UUID(1-16 hex)> <NetKeyIdx> <Addr>",
423 		      cmd_provision_remote, 4, 0),
424 	SHELL_CMD_ARG(reprovision-remote, NULL, "<Addr> [<CompChanged(false, true)>]",
425 		      cmd_reprovision_remote, 2, 1),
426 	SHELL_CMD(instance, &instance_cmds, "Instance commands", bt_mesh_shell_mdl_cmds_help),
427 	SHELL_SUBCMD_SET_END);
428 
429 SHELL_SUBCMD_ADD((mesh, models), rpr, &rpr_cli_cmds, "Remote Provisioning Cli commands",
430 		 bt_mesh_shell_mdl_cmds_help, 1, 1);
431