431 lines
12 KiB
C
431 lines
12 KiB
C
/*
|
|
* 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);
|