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