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