| /* |
| * Copyright (c) 2022 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <stdlib.h> |
| #include <zephyr/shell/shell.h> |
| #include <zephyr/bluetooth/mesh.h> |
| #include <zephyr/bluetooth/mesh/shell.h> |
| |
| #include "utils.h" |
| |
| static const struct bt_mesh_model *mod; |
| |
| /*************************************************************************************************** |
| * Implementation of the model's instance |
| **************************************************************************************************/ |
| |
| extern const struct shell *bt_mesh_shell_ctx_shell; |
| |
| static void 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) |
| { |
| char uuid_hex_str[32 + 1]; |
| |
| bin2hex(unprov->uuid, 16, uuid_hex_str, sizeof(uuid_hex_str)); |
| |
| shell_print(bt_mesh_shell_ctx_shell, |
| "Server 0x%04x:\n" |
| "\tuuid: %s\n" |
| "\tOOB: 0x%04x", |
| srv->addr, uuid_hex_str, unprov->oob); |
| |
| while (adv_data && adv_data->len > 2) { |
| uint8_t len, type; |
| uint8_t data[31]; |
| |
| len = net_buf_simple_pull_u8(adv_data); |
| if (len == 0) { |
| /* No data in this AD Structure. */ |
| continue; |
| } |
| |
| if (len > adv_data->len) { |
| /* Malformed AD Structure. */ |
| break; |
| } |
| |
| type = net_buf_simple_pull_u8(adv_data); |
| if ((--len) > 0) { |
| uint8_t dlen; |
| |
| /* Pull all length, but print only what fits into `data` array. */ |
| dlen = MIN(len, sizeof(data) - 1); |
| memcpy(data, net_buf_simple_pull_mem(adv_data, len), dlen); |
| len = dlen; |
| } |
| data[len] = '\0'; |
| |
| if (type == BT_DATA_URI) { |
| shell_print(bt_mesh_shell_ctx_shell, "\tURI: \"\\x%02x%s\"", |
| data[0], &data[1]); |
| } else if (type == BT_DATA_NAME_COMPLETE) { |
| shell_print(bt_mesh_shell_ctx_shell, "\tName: \"%s\"", data); |
| } else { |
| char string[64 + 1]; |
| |
| bin2hex(data, len, string, sizeof(string)); |
| shell_print(bt_mesh_shell_ctx_shell, "\t0x%02x: %s", type, string); |
| } |
| } |
| } |
| |
| struct bt_mesh_rpr_cli bt_mesh_shell_rpr_cli = { |
| .scan_report = rpr_scan_report, |
| }; |
| |
| /*************************************************************************************************** |
| * Shell Commands |
| **************************************************************************************************/ |
| |
| static int cmd_scan(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| struct bt_mesh_rpr_scan_status rsp; |
| const struct bt_mesh_rpr_node srv = { |
| .addr = bt_mesh_shell_target_ctx.dst, |
| .net_idx = bt_mesh_shell_target_ctx.net_idx, |
| .ttl = BT_MESH_TTL_DEFAULT, |
| }; |
| uint8_t uuid[16] = {0}; |
| uint8_t timeout; |
| int err = 0; |
| |
| if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) { |
| return -ENODEV; |
| } |
| |
| timeout = shell_strtoul(argv[1], 0, &err); |
| if (err) { |
| shell_warn(sh, "Unable to parse input string argument"); |
| return err; |
| } |
| |
| if (argc > 2) { |
| hex2bin(argv[2], strlen(argv[2]), uuid, 16); |
| } |
| |
| err = bt_mesh_rpr_scan_start((struct bt_mesh_rpr_cli *)mod->rt->user_data, |
| &srv, argc > 2 ? uuid : NULL, timeout, |
| BT_MESH_RPR_SCAN_MAX_DEVS_ANY, &rsp); |
| if (err) { |
| shell_print(sh, "Scan start failed: %d", err); |
| return err; |
| } |
| |
| if (rsp.status == BT_MESH_RPR_SUCCESS) { |
| shell_print(sh, "Scan started."); |
| } else { |
| shell_print(sh, "Scan start response: %d", rsp.status); |
| } |
| |
| return 0; |
| } |
| |
| static int cmd_scan_ext(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| const struct bt_mesh_rpr_node srv = { |
| .addr = bt_mesh_shell_target_ctx.dst, |
| .net_idx = bt_mesh_shell_target_ctx.net_idx, |
| .ttl = BT_MESH_TTL_DEFAULT, |
| }; |
| uint8_t ad_types[CONFIG_BT_MESH_RPR_AD_TYPES_MAX]; |
| uint8_t uuid[16] = {0}; |
| uint8_t timeout; |
| int i, err = 0; |
| |
| if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) { |
| return -ENODEV; |
| } |
| |
| hex2bin(argv[2], strlen(argv[2]), uuid, 16); |
| |
| for (i = 0; i < argc - 3; i++) { |
| ad_types[i] = shell_strtoul(argv[3 + i], 0, &err); |
| } |
| |
| timeout = shell_strtoul(argv[1], 0, &err); |
| if (err) { |
| shell_warn(sh, "Unable to parse input string argument"); |
| return err; |
| } |
| |
| err = bt_mesh_rpr_scan_start_ext((struct bt_mesh_rpr_cli *)mod->rt->user_data, |
| &srv, uuid, timeout, ad_types, |
| (argc - 3)); |
| if (err) { |
| shell_print(sh, "Scan start failed: %d", err); |
| return err; |
| } |
| |
| shell_print(sh, "Extended scan started."); |
| |
| return 0; |
| } |
| |
| static int cmd_scan_srv(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| const struct bt_mesh_rpr_node srv = { |
| .addr = bt_mesh_shell_target_ctx.dst, |
| .net_idx = bt_mesh_shell_target_ctx.net_idx, |
| .ttl = BT_MESH_TTL_DEFAULT, |
| }; |
| uint8_t ad_types[CONFIG_BT_MESH_RPR_AD_TYPES_MAX]; |
| int i, err = 0; |
| |
| if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) { |
| return -ENODEV; |
| } |
| |
| for (i = 0; i < argc - 1; i++) { |
| ad_types[i] = shell_strtoul(argv[1 + i], 0, &err); |
| } |
| |
| if (err) { |
| shell_warn(sh, "Unable to parse input string argument"); |
| return err; |
| } |
| |
| err = bt_mesh_rpr_scan_start_ext((struct bt_mesh_rpr_cli *)mod->rt->user_data, |
| &srv, NULL, 0, ad_types, (argc - 1)); |
| if (err) { |
| shell_print(sh, "Scan start failed: %d", err); |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| static int cmd_scan_caps(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| struct bt_mesh_rpr_caps caps; |
| const struct bt_mesh_rpr_node srv = { |
| .addr = bt_mesh_shell_target_ctx.dst, |
| .net_idx = bt_mesh_shell_target_ctx.net_idx, |
| .ttl = BT_MESH_TTL_DEFAULT, |
| }; |
| int err; |
| |
| if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) { |
| return -ENODEV; |
| } |
| |
| err = bt_mesh_rpr_scan_caps_get((struct bt_mesh_rpr_cli *)mod->rt->user_data, &srv, &caps); |
| if (err) { |
| shell_print(sh, "Scan capabilities get failed: %d", err); |
| return err; |
| } |
| |
| shell_print(sh, "Remote Provisioning scan capabilities of 0x%04x:", |
| bt_mesh_shell_target_ctx.dst); |
| shell_print(sh, "\tMax devices: %u", caps.max_devs); |
| shell_print(sh, "\tActive scanning: %s", |
| caps.active_scan ? "true" : "false"); |
| return 0; |
| } |
| |
| static int cmd_scan_get(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| struct bt_mesh_rpr_scan_status rsp; |
| const struct bt_mesh_rpr_node srv = { |
| .addr = bt_mesh_shell_target_ctx.dst, |
| .net_idx = bt_mesh_shell_target_ctx.net_idx, |
| .ttl = BT_MESH_TTL_DEFAULT, |
| }; |
| int err; |
| |
| if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) { |
| return -ENODEV; |
| } |
| |
| err = bt_mesh_rpr_scan_get((struct bt_mesh_rpr_cli *)mod->rt->user_data, &srv, &rsp); |
| if (err) { |
| shell_print(sh, "Scan get failed: %d", err); |
| return err; |
| } |
| |
| shell_print(sh, "Remote Provisioning scan on 0x%04x:", bt_mesh_shell_target_ctx.dst); |
| shell_print(sh, "\tStatus: %u", rsp.status); |
| shell_print(sh, "\tScan type: %u", rsp.scan); |
| shell_print(sh, "\tMax devices: %u", rsp.max_devs); |
| shell_print(sh, "\tRemaining time: %u", rsp.timeout); |
| return 0; |
| } |
| |
| static int cmd_scan_stop(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| struct bt_mesh_rpr_scan_status rsp; |
| const struct bt_mesh_rpr_node srv = { |
| .addr = bt_mesh_shell_target_ctx.dst, |
| .net_idx = bt_mesh_shell_target_ctx.net_idx, |
| .ttl = BT_MESH_TTL_DEFAULT, |
| }; |
| int err; |
| |
| if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) { |
| return -ENODEV; |
| } |
| |
| err = bt_mesh_rpr_scan_stop((struct bt_mesh_rpr_cli *)mod->rt->user_data, &srv, &rsp); |
| if (err || rsp.status) { |
| shell_print(sh, "Scan stop failed: %d %u", err, rsp.status); |
| return err; |
| } |
| |
| shell_print(sh, "Remote Provisioning scan on 0x%04x stopped.", |
| bt_mesh_shell_target_ctx.dst); |
| return 0; |
| } |
| |
| static int cmd_link_get(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| struct bt_mesh_rpr_link rsp; |
| const struct bt_mesh_rpr_node srv = { |
| .addr = bt_mesh_shell_target_ctx.dst, |
| .net_idx = bt_mesh_shell_target_ctx.net_idx, |
| .ttl = BT_MESH_TTL_DEFAULT, |
| }; |
| int err; |
| |
| if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) { |
| return -ENODEV; |
| } |
| |
| err = bt_mesh_rpr_link_get((struct bt_mesh_rpr_cli *)mod->rt->user_data, &srv, &rsp); |
| if (err) { |
| shell_print(sh, "Link get failed: %d %u", err, rsp.status); |
| return err; |
| } |
| |
| shell_print(sh, "Remote Provisioning Link on 0x%04x:", bt_mesh_shell_target_ctx.dst); |
| shell_print(sh, "\tStatus: %u", rsp.status); |
| shell_print(sh, "\tState: %u", rsp.state); |
| return 0; |
| } |
| |
| static int cmd_link_close(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| struct bt_mesh_rpr_link rsp; |
| const struct bt_mesh_rpr_node srv = { |
| .addr = bt_mesh_shell_target_ctx.dst, |
| .net_idx = bt_mesh_shell_target_ctx.net_idx, |
| .ttl = BT_MESH_TTL_DEFAULT, |
| }; |
| int err; |
| |
| err = bt_mesh_rpr_link_close((struct bt_mesh_rpr_cli *)mod->rt->user_data, &srv, &rsp); |
| if (err) { |
| shell_print(sh, "Link close failed: %d %u", err, rsp.status); |
| return err; |
| } |
| |
| shell_print(sh, "Remote Provisioning Link on 0x%04x:", bt_mesh_shell_target_ctx.dst); |
| shell_print(sh, "\tStatus: %u", rsp.status); |
| shell_print(sh, "\tState: %u", rsp.state); |
| return 0; |
| } |
| |
| static int cmd_provision_remote(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| struct bt_mesh_rpr_node srv = { |
| .addr = bt_mesh_shell_target_ctx.dst, |
| .net_idx = bt_mesh_shell_target_ctx.net_idx, |
| .ttl = BT_MESH_TTL_DEFAULT, |
| }; |
| uint8_t uuid[16]; |
| size_t len; |
| uint16_t net_idx; |
| uint16_t addr; |
| int err = 0; |
| |
| if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) { |
| return -ENODEV; |
| } |
| |
| len = hex2bin(argv[1], strlen(argv[1]), uuid, sizeof(uuid)); |
| (void)memset(uuid + len, 0, sizeof(uuid) - len); |
| |
| net_idx = shell_strtoul(argv[2], 0, &err); |
| addr = shell_strtoul(argv[3], 0, &err); |
| if (err) { |
| shell_warn(sh, "Unable to parse input string argument"); |
| return err; |
| } |
| |
| err = bt_mesh_provision_remote((struct bt_mesh_rpr_cli *)mod->rt->user_data, |
| &srv, uuid, net_idx, addr); |
| if (err) { |
| shell_print(sh, "Prov remote start failed: %d", err); |
| } |
| |
| return err; |
| } |
| |
| static int cmd_reprovision_remote(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| struct bt_mesh_rpr_node srv = { |
| .addr = bt_mesh_shell_target_ctx.dst, |
| .net_idx = bt_mesh_shell_target_ctx.net_idx, |
| .ttl = BT_MESH_TTL_DEFAULT, |
| }; |
| bool composition_changed; |
| uint16_t addr; |
| int err = 0; |
| |
| if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_REMOTE_PROV_CLI, &mod)) { |
| return -ENODEV; |
| } |
| |
| addr = shell_strtoul(argv[1], 0, &err); |
| if (err) { |
| shell_warn(sh, "Unable to parse input string argument"); |
| return err; |
| } |
| |
| if (!BT_MESH_ADDR_IS_UNICAST(addr)) { |
| shell_print(sh, "Must be a valid unicast address"); |
| return -EINVAL; |
| } |
| |
| composition_changed = (argc > 2 && shell_strtobool(argv[2], 0, &err)); |
| if (err) { |
| shell_warn(sh, "Unable to parse input string argument"); |
| return err; |
| } |
| |
| err = bt_mesh_reprovision_remote((struct bt_mesh_rpr_cli *)mod->rt->user_data, |
| &srv, addr, composition_changed); |
| if (err) { |
| shell_print(sh, "Reprovisioning failed: %d", err); |
| } |
| |
| return 0; |
| } |
| |
| BT_MESH_SHELL_MDL_INSTANCE_CMDS(instance_cmds, BT_MESH_MODEL_ID_REMOTE_PROV_CLI, mod); |
| |
| SHELL_STATIC_SUBCMD_SET_CREATE( |
| rpr_cli_cmds, |
| SHELL_CMD_ARG(scan, NULL, "<Timeout(s)> [<UUID(1-16 hex)>]", cmd_scan, 2, 1), |
| SHELL_CMD_ARG(scan-ext, NULL, "<Timeout(s)> <UUID(1-16 hex)> [<ADType> ... ]", |
| cmd_scan_ext, 3, CONFIG_BT_MESH_RPR_AD_TYPES_MAX), |
| SHELL_CMD_ARG(scan-srv, NULL, "[<ADType> ... ]", cmd_scan_srv, 1, |
| CONFIG_BT_MESH_RPR_AD_TYPES_MAX), |
| SHELL_CMD_ARG(scan-caps, NULL, NULL, cmd_scan_caps, 1, 0), |
| SHELL_CMD_ARG(scan-get, NULL, NULL, cmd_scan_get, 1, 0), |
| SHELL_CMD_ARG(scan-stop, NULL, NULL, cmd_scan_stop, 1, 0), |
| SHELL_CMD_ARG(link-get, NULL, NULL, cmd_link_get, 1, 0), |
| SHELL_CMD_ARG(link-close, NULL, NULL, cmd_link_close, 1, 0), |
| SHELL_CMD_ARG(provision-remote, NULL, "<UUID(1-16 hex)> <NetKeyIdx> <Addr>", |
| cmd_provision_remote, 4, 0), |
| SHELL_CMD_ARG(reprovision-remote, NULL, "<Addr> [<CompChanged(false, true)>]", |
| cmd_reprovision_remote, 2, 1), |
| SHELL_CMD(instance, &instance_cmds, "Instance commands", bt_mesh_shell_mdl_cmds_help), |
| SHELL_SUBCMD_SET_END); |
| |
| SHELL_SUBCMD_ADD((mesh, models), rpr, &rpr_cli_cmds, "Remote Provisioning Cli commands", |
| bt_mesh_shell_mdl_cmds_help, 1, 1); |