bluez/tools/mesh/cfgcli.c
Inga Stotland 9761d7b1e4 tools/mesh-cfgclient: Fix config menu help message
An info message suggesting to request a remote node composition
should reference the correct menu command: "composition-get".
2022-01-12 14:34:55 -08:00

2131 lines
52 KiB
C

// SPDX-License-Identifier: LGPL-2.1-or-later
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2019-2020 Intel Corporation. All rights reserved.
*
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdbool.h>
#include <ell/ell.h>
#include "src/shared/shell.h"
#include "src/shared/util.h"
#include "mesh/mesh-defs.h"
#include "mesh/util.h"
#include "mesh/crypto.h"
#include "tools/mesh/util.h"
#include "tools/mesh/model.h"
#include "tools/mesh/keys.h"
#include "tools/mesh/mesh-db.h"
#include "tools/mesh/remote.h"
#include "tools/mesh/config-model.h"
#include "tools/mesh/cfgcli.h"
#define MIN_COMPOSITION_LEN 16
#define NO_RESPONSE 0xFFFFFFFF
/* Default timeout for getting a response to a sent config command (seconds) */
#define DEFAULT_TIMEOUT 2
struct cfg_cmd {
uint32_t opcode;
uint32_t rsp;
const char *desc;
};
struct pending_req {
struct l_timeout *timer;
const struct cfg_cmd *cmd;
uint16_t addr;
};
static struct l_queue *requests;
static struct l_queue *groups;
static void *send_data;
static model_send_msg_func_t send_msg;
static delete_remote_func_t mgr_del_remote;
static void *key_data;
static key_send_func_t send_key_msg;
static uint32_t rsp_timeout = DEFAULT_TIMEOUT;
static uint16_t target = UNASSIGNED_ADDRESS;
static uint32_t parms[8];
static struct cfg_cmd cmds[] = {
{ OP_APPKEY_ADD, OP_APPKEY_STATUS, "AppKeyAdd" },
{ OP_APPKEY_DELETE, OP_APPKEY_STATUS, "AppKeyDelete" },
{ OP_APPKEY_GET, OP_APPKEY_LIST, "AppKeyGet" },
{ OP_APPKEY_LIST, NO_RESPONSE, "AppKeyList" },
{ OP_APPKEY_STATUS, NO_RESPONSE, "AppKeyStatus" },
{ OP_APPKEY_UPDATE, OP_APPKEY_STATUS, "AppKeyUpdate" },
{ OP_DEV_COMP_GET, OP_DEV_COMP_STATUS, "DeviceCompositionGet" },
{ OP_DEV_COMP_STATUS, NO_RESPONSE, "DeviceCompositionStatus" },
{ OP_CONFIG_BEACON_GET, OP_CONFIG_BEACON_STATUS, "BeaconGet" },
{ OP_CONFIG_BEACON_SET, OP_CONFIG_BEACON_STATUS, "BeaconSet" },
{ OP_CONFIG_BEACON_STATUS, NO_RESPONSE, "BeaconStatus" },
{ OP_CONFIG_DEFAULT_TTL_GET, OP_CONFIG_DEFAULT_TTL_STATUS,
"DefaultTTLGet" },
{ OP_CONFIG_DEFAULT_TTL_SET, OP_CONFIG_DEFAULT_TTL_STATUS,
"DefaultTTLSet" },
{ OP_CONFIG_DEFAULT_TTL_STATUS, NO_RESPONSE, "DefaultTTLStatus" },
{ OP_CONFIG_FRIEND_GET, OP_CONFIG_FRIEND_STATUS, "FriendGet" },
{ OP_CONFIG_FRIEND_SET, OP_CONFIG_FRIEND_STATUS, "FrienSet" },
{ OP_CONFIG_FRIEND_STATUS, NO_RESPONSE, "FriendStatus" },
{ OP_CONFIG_PROXY_GET, OP_CONFIG_PROXY_STATUS, "ProxyGet" },
{ OP_CONFIG_PROXY_SET, OP_CONFIG_PROXY_STATUS, "ProxySet" },
{ OP_CONFIG_PROXY_STATUS, NO_RESPONSE, "ProxyStatus" },
{ OP_CONFIG_KEY_REFRESH_PHASE_GET, OP_CONFIG_KEY_REFRESH_PHASE_STATUS,
"KeyRefreshPhaseGet" },
{ OP_CONFIG_KEY_REFRESH_PHASE_SET, OP_CONFIG_KEY_REFRESH_PHASE_STATUS,
"KeyRefreshPhaseSet" },
{ OP_CONFIG_KEY_REFRESH_PHASE_STATUS, NO_RESPONSE,
"KeyRefreshPhaseStatus" },
{ OP_CONFIG_MODEL_PUB_GET, OP_CONFIG_MODEL_PUB_STATUS, "ModelPubGet" },
{ OP_CONFIG_MODEL_PUB_SET, OP_CONFIG_MODEL_PUB_STATUS, "ModelPubSet" },
{ OP_CONFIG_MODEL_PUB_STATUS, NO_RESPONSE, "ModelPubStatus" },
{ OP_CONFIG_MODEL_PUB_VIRT_SET, OP_CONFIG_MODEL_PUB_STATUS,
"ModelPubVirtualSet" },
{ OP_CONFIG_MODEL_SUB_ADD, OP_CONFIG_MODEL_SUB_STATUS, "ModelSubAdd" },
{ OP_CONFIG_MODEL_SUB_DELETE, OP_CONFIG_MODEL_SUB_STATUS,
"ModelSubDelete" },
{ OP_CONFIG_MODEL_SUB_DELETE_ALL, OP_CONFIG_MODEL_SUB_STATUS,
"ModelSubDeleteAll" },
{ OP_CONFIG_MODEL_SUB_OVERWRITE, OP_CONFIG_MODEL_SUB_STATUS,
"ModelSubOverwrite" },
{ OP_CONFIG_MODEL_SUB_STATUS, NO_RESPONSE, "ModelSubStatus" },
{ OP_CONFIG_MODEL_SUB_VIRT_ADD, OP_CONFIG_MODEL_SUB_STATUS,
"ModelSubVirtAdd" },
{ OP_CONFIG_MODEL_SUB_VIRT_DELETE, OP_CONFIG_MODEL_SUB_STATUS,
"ModelSubVirtDelete" },
{ OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE, OP_CONFIG_MODEL_SUB_STATUS,
"ModelSubVirtOverwrite" },
{ OP_CONFIG_NETWORK_TRANSMIT_GET, OP_CONFIG_NETWORK_TRANSMIT_STATUS,
"NetworkTransmitGet" },
{ OP_CONFIG_NETWORK_TRANSMIT_SET, OP_CONFIG_NETWORK_TRANSMIT_STATUS,
"NetworkTransmitSet" },
{ OP_CONFIG_NETWORK_TRANSMIT_STATUS, NO_RESPONSE,
"NetworkTransmitStatus" },
{ OP_CONFIG_RELAY_GET, OP_CONFIG_RELAY_STATUS, "RelayGet" },
{ OP_CONFIG_RELAY_SET, OP_CONFIG_RELAY_STATUS, "RelaySet" },
{ OP_CONFIG_RELAY_STATUS, NO_RESPONSE, "RelayStatus" },
{ OP_CONFIG_MODEL_SUB_GET, OP_CONFIG_MODEL_SUB_LIST, "ModelSubGet" },
{ OP_CONFIG_MODEL_SUB_LIST, NO_RESPONSE, "ModelSubList" },
{ OP_CONFIG_VEND_MODEL_SUB_GET, OP_CONFIG_VEND_MODEL_SUB_LIST,
"VendorModelSubGet" },
{ OP_CONFIG_VEND_MODEL_SUB_LIST, NO_RESPONSE, "VendorModelSubList" },
{ OP_CONFIG_POLL_TIMEOUT_LIST, OP_CONFIG_POLL_TIMEOUT_STATUS,
"PollTimeoutList" },
{ OP_CONFIG_POLL_TIMEOUT_STATUS, NO_RESPONSE, "PollTimeoutStatus" },
{ OP_CONFIG_HEARTBEAT_PUB_GET, OP_CONFIG_HEARTBEAT_PUB_STATUS,
"HeartbeatPubGet" },
{ OP_CONFIG_HEARTBEAT_PUB_SET, OP_CONFIG_HEARTBEAT_PUB_STATUS,
"HeartbeatPubSet" },
{ OP_CONFIG_HEARTBEAT_PUB_STATUS, NO_RESPONSE, "HeartbeatPubStatus" },
{ OP_CONFIG_HEARTBEAT_SUB_GET, OP_CONFIG_HEARTBEAT_SUB_STATUS,
"HeartbeatSubGet" },
{ OP_CONFIG_HEARTBEAT_SUB_SET, OP_CONFIG_HEARTBEAT_SUB_STATUS,
"HeartbeatSubSet" },
{ OP_CONFIG_HEARTBEAT_SUB_STATUS, NO_RESPONSE, "HeartbeatSubStatus" },
{ OP_MODEL_APP_BIND, OP_MODEL_APP_STATUS, "ModelAppBind" },
{ OP_MODEL_APP_STATUS, NO_RESPONSE, "ModelAppStatus" },
{ OP_MODEL_APP_UNBIND, OP_MODEL_APP_STATUS, "ModelAppUnbind" },
{ OP_NETKEY_ADD, OP_NETKEY_STATUS, "NetKeyAdd" },
{ OP_NETKEY_DELETE, OP_NETKEY_STATUS, "NetKeyDelete" },
{ OP_NETKEY_GET, OP_NETKEY_LIST, "NetKeyGet" },
{ OP_NETKEY_LIST, NO_RESPONSE, "NetKeyList" },
{ OP_NETKEY_STATUS, NO_RESPONSE, "NetKeyStatus" },
{ OP_NETKEY_UPDATE, OP_NETKEY_STATUS, "NetKeyUpdate" },
{ OP_NODE_IDENTITY_GET, OP_NODE_IDENTITY_STATUS, "NodeIdentityGet" },
{ OP_NODE_IDENTITY_SET, OP_NODE_IDENTITY_STATUS, "NodeIdentitySet" },
{ OP_NODE_IDENTITY_STATUS, NO_RESPONSE, "NodeIdentityStatus" },
{ OP_NODE_RESET, OP_NODE_RESET_STATUS, "NodeReset" },
{ OP_NODE_RESET_STATUS, NO_RESPONSE, "NodeResetStatus" },
{ OP_MODEL_APP_GET, OP_MODEL_APP_LIST, "ModelAppGet" },
{ OP_MODEL_APP_LIST, NO_RESPONSE, "ModelAppList" },
{ OP_VEND_MODEL_APP_GET, OP_VEND_MODEL_APP_LIST, "VendorModelAppGet" },
{ OP_VEND_MODEL_APP_LIST, NO_RESPONSE, "VendorModelAppList" }
};
static const struct cfg_cmd *get_cmd(uint32_t opcode)
{
uint32_t n;
for (n = 0; n < L_ARRAY_SIZE(cmds); n++) {
if (opcode == cmds[n].opcode)
return &cmds[n];
}
return NULL;
}
static const char *opcode_str(uint32_t opcode)
{
const struct cfg_cmd *cmd;
cmd = get_cmd(opcode);
if (!cmd)
return "Unknown";
return cmd->desc;
}
static void reset_remote_node(uint16_t addr)
{
uint8_t ele_cnt = remote_del_node(addr);
bt_shell_printf("Remote removed (primary %4.4x)\n", addr);
if (ele_cnt && mgr_del_remote)
mgr_del_remote(addr, ele_cnt);
}
static void free_request(void *a)
{
struct pending_req *req = a;
l_timeout_remove(req->timer);
l_free(req);
}
static struct pending_req *get_req_by_rsp(uint16_t addr, uint32_t rsp)
{
const struct l_queue_entry *entry;
entry = l_queue_get_entries(requests);
for (; entry; entry = entry->next) {
struct pending_req *req = entry->data;
if (req->addr == addr && req->cmd->rsp == rsp)
return req;
}
return NULL;
}
static void wait_rsp_timeout(struct l_timeout *timeout, void *user_data)
{
struct pending_req *req = user_data;
bt_shell_printf("No response for \"%s\" from %4.4x\n",
req->cmd->desc, req->addr);
/* Node reset case: delete the remote even if there is no response */
if (req->cmd->opcode == OP_NODE_RESET)
reset_remote_node(req->addr);
l_queue_remove(requests, req);
free_request(req);
}
static void add_request(uint32_t opcode)
{
struct pending_req *req;
const struct cfg_cmd *cmd;
cmd = get_cmd(opcode);
if (!cmd)
return;
req = l_new(struct pending_req, 1);
req->cmd = cmd;
req->addr = target;
req->timer = l_timeout_create(rsp_timeout,
wait_rsp_timeout, req, NULL);
l_queue_push_tail(requests, req);
}
static uint32_t print_mod_id(uint8_t *data, bool vendor, const char *offset)
{
uint32_t mod_id;
if (!vendor) {
mod_id = get_le16(data);
bt_shell_printf("%sModel ID\t%4.4x \"%s\"\n",
offset, mod_id, sig_model_string(mod_id));
mod_id = VENDOR_ID_MASK | mod_id;
} else {
mod_id = get_le16(data + 2);
bt_shell_printf("%sModel ID\t%4.4x %4.4x\n", offset,
get_le16(data), mod_id);
mod_id = get_le16(data) << 16 | mod_id;
}
return mod_id;
}
static void print_composition(uint8_t *data, uint16_t len)
{
uint16_t features;
int i = 0;
bt_shell_printf("Received composion:\n");
/* skip page -- We only support Page Zero */
data++;
len--;
bt_shell_printf("\tCID: %4.4x", get_le16(&data[0]));
bt_shell_printf("\tPID: %4.4x", get_le16(&data[2]));
bt_shell_printf("\tVID: %4.4x", get_le16(&data[4]));
bt_shell_printf("\tCRPL: %4.4x", get_le16(&data[6]));
features = get_le16(&data[8]);
data += 10;
len -= 10;
bt_shell_printf("\tFeature support:\n");
bt_shell_printf("\t\trelay: %s\n", (features & FEATURE_RELAY) ?
"yes" : "no");
bt_shell_printf("\t\tproxy: %s\n", (features & FEATURE_PROXY) ?
"yes" : "no");
bt_shell_printf("\t\tfriend: %s\n", (features & FEATURE_FRIEND) ?
"yes" : "no");
bt_shell_printf("\t\tlpn: %s\n", (features & FEATURE_LPN) ?
"yes" : "no");
while (len) {
uint8_t m, v;
bt_shell_printf("\t Element %d:\n", i);
bt_shell_printf("\t\tlocation: %4.4x\n", get_le16(data));
data += 2;
len -= 2;
m = *data++;
v = *data++;
len -= 2;
if (m)
bt_shell_printf("\t\tSIG defined models:\n");
while (len >= 2 && m--) {
print_mod_id(data, false, "\t\t ");
data += 2;
len -= 2;
}
if (v)
bt_shell_printf("\t\t Vendor defined models:\n");
while (len >= 4 && v--) {
print_mod_id(data, true, "\t\t ");
data += 4;
len -= 4;
}
i++;
}
}
static void print_pub(uint16_t ele_addr, uint32_t mod_id,
struct model_pub *pub)
{
bt_shell_printf("\tElement: %4.4x\n", ele_addr);
bt_shell_printf("\tPub Addr: %4.4x\n", pub->u.addr);
if (mod_id < VENDOR_ID_MASK)
bt_shell_printf("\tModel: %8.8x\n", mod_id);
else
bt_shell_printf("\tModel: %4.4x\n",
(uint16_t) (mod_id & ~VENDOR_ID_MASK));
bt_shell_printf("\tApp Key Idx: %u (0x%3.3x)\n", pub->app_idx,
pub->app_idx);
bt_shell_printf("\tTTL: %2.2x\n", pub->ttl);
}
static void print_sub_list(uint16_t addr, bool is_vendor, uint8_t *data,
uint16_t len)
{
uint16_t i;
bt_shell_printf("\nNode %4.4x Subscription List status %s\n",
addr, mesh_status_str(data[0]));
bt_shell_printf("Element Addr\t%4.4x\n", get_le16(data + 1));
print_mod_id(data + 3, is_vendor, "");
i = (is_vendor ? 7 : 5);
bt_shell_printf("Subscriptions:\n");
for (; i < len; i += 2)
bt_shell_printf("\t\t%4.4x\n ", get_le16(data + i));
}
static void print_appkey_list(uint16_t len, uint8_t *data)
{
uint16_t app_idx;
bt_shell_printf("AppKeys:\n");
while (len >= 3) {
app_idx = l_get_le16(data) & 0xfff;
bt_shell_printf("\t%u (0x%3.3x)\n", app_idx, app_idx);
app_idx = l_get_le16(data + 1) >> 4;
bt_shell_printf("\t%u (0x%3.3x)\n", app_idx, app_idx);
data += 3;
len -= 3;
}
if (len == 2) {
app_idx = l_get_le16(data) & 0xfff;
bt_shell_printf("\t %u (0x%3.3x)\n", app_idx, app_idx);
}
}
static bool match_group_addr(const void *a, const void *b)
{
const struct mesh_group *grp = a;
uint16_t addr = L_PTR_TO_UINT(b);
return grp->addr == addr;
}
static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
uint16_t len)
{
uint32_t opcode, mod_id;
const struct cfg_cmd *cmd;
uint16_t app_idx, net_idx, addr, ele_addr, features;
struct mesh_group *grp;
struct model_pub pub;
int n;
struct pending_req *req;
bool saved = false;
if (mesh_opcode_get(data, len, &opcode, &n)) {
len -= n;
data += n;
} else
return false;
bt_shell_printf("Received %s (len %u)\n", opcode_str(opcode), len);
req = get_req_by_rsp(src, opcode);
if (req) {
cmd = req->cmd;
l_queue_remove(requests, req);
free_request(req);
} else
cmd = NULL;
switch (opcode) {
default:
return false;
case OP_DEV_COMP_STATUS:
if (len < MIN_COMPOSITION_LEN)
return true;
print_composition(data, len);
saved = mesh_db_node_set_composition(src, data, len);
if (saved)
remote_set_composition(src, true);
break;
case OP_APPKEY_STATUS:
if (len != 4)
return true;
bt_shell_printf("Node %4.4x AppKey status %s\n", src,
mesh_status_str(data[0]));
net_idx = get_le16(data + 1) & 0xfff;
app_idx = get_le16(data + 2) >> 4;
bt_shell_printf("NetKey\t%u (0x%3.3x)\n", net_idx, net_idx);
bt_shell_printf("AppKey\t%u (0x%3.3x)\n", app_idx, app_idx);
if (data[0] != MESH_STATUS_SUCCESS)
return true;
if (!cmd)
return true;
if (cmd->opcode == OP_APPKEY_ADD)
saved = remote_add_app_key(src, app_idx, true);
else if (cmd->opcode == OP_APPKEY_DELETE)
saved = remote_del_app_key(src, app_idx);
else if (cmd->opcode == OP_APPKEY_UPDATE)
saved = remote_update_app_key(src, app_idx, true, true);
break;
case OP_APPKEY_LIST:
if (len < 3)
return true;
bt_shell_printf("AppKey List (node %4.4x) Status %s\n",
src, mesh_status_str(data[0]));
net_idx = l_get_le16(&data[1]);
bt_shell_printf("NetKey %u (0x%3.3x)\n", net_idx, net_idx);
len -= 3;
if (data[0] != MESH_STATUS_SUCCESS)
return true;
data += 3;
print_appkey_list(len, data);
return true;
case OP_NETKEY_STATUS:
if (len != 3)
return true;
bt_shell_printf("Node %4.4x NetKey status %s\n", src,
mesh_status_str(data[0]));
net_idx = get_le16(data + 1) & 0xfff;
bt_shell_printf("\tNetKey %u (0x%3.3x)\n", net_idx, net_idx);
if (data[0] != MESH_STATUS_SUCCESS)
return true;
if (!cmd)
return true;
if (cmd->opcode == OP_NETKEY_ADD)
saved = remote_add_net_key(src, net_idx, true);
else if (cmd->opcode == OP_NETKEY_DELETE)
saved = remote_del_net_key(src, net_idx);
else if (cmd->opcode == OP_NETKEY_UPDATE)
saved = remote_update_net_key(src, net_idx, true, true);
break;
case OP_NETKEY_LIST:
if (len < 2)
return true;
bt_shell_printf("NetKey List (node %4.4x):\n", src);
while (len >= 3) {
net_idx = l_get_le16(data) & 0xfff;
bt_shell_printf("\t%u (0x%3.3x)\n", net_idx, net_idx);
net_idx = l_get_le16(data + 1) >> 4;
bt_shell_printf("\t%u (0x%3.3x)\n", net_idx, net_idx);
data += 3;
len -= 3;
}
if (len == 2) {
net_idx = l_get_le16(data) & 0xfff;
bt_shell_printf("\t %u (0x%3.3x)\n", net_idx, net_idx);
}
return true;
case OP_CONFIG_KEY_REFRESH_PHASE_STATUS:
if (len != 4)
return true;
bt_shell_printf("Node %4.4x Key Refresh Phase status %s\n", src,
mesh_status_str(data[0]));
net_idx = get_le16(data + 1) & 0xfff;
bt_shell_printf("\tNetKey %u (0x%3.3x)\n", net_idx, net_idx);
bt_shell_printf("\tKR Phase %2.2x\n", data[3]);
if (data[0] != MESH_STATUS_SUCCESS)
return true;
if (data[3] != KEY_REFRESH_PHASE_NONE)
return true;
saved = remote_finish_key_refresh(src, net_idx);
break;
case OP_MODEL_APP_STATUS:
if (len != 7 && len != 9)
return true;
bt_shell_printf("Node %4.4x: Model App status %s\n", src,
mesh_status_str(data[0]));
addr = get_le16(data + 1);
app_idx = get_le16(data + 3);
bt_shell_printf("Element Addr\t%4.4x\n", addr);
mod_id = print_mod_id(data + 5, len == 9, "");
bt_shell_printf("AppIdx\t\t%u (0x%3.3x)\n ", app_idx, app_idx);
if (data[0] != MESH_STATUS_SUCCESS || !cmd)
return true;
if (cmd->opcode == OP_MODEL_APP_BIND)
saved = mesh_db_node_model_bind(src, addr, len == 9,
mod_id, app_idx);
else
saved = mesh_db_node_model_unbind(src, addr, len == 9,
mod_id, app_idx);
break;
case OP_NODE_IDENTITY_STATUS:
if (len != 4)
return true;
bt_shell_printf("NetIdx %4.4x, NodeIdState 0x%02x, status %s\n",
get_le16(data + 1), data[3],
mesh_status_str(data[0]));
return true;
case OP_CONFIG_BEACON_STATUS:
if (len != 1)
return true;
bt_shell_printf("Node %4.4x: Config Beacon Status 0x%02x\n",
src, data[0]);
saved = mesh_db_node_set_beacon(src, data[0] != 0);
break;
case OP_CONFIG_RELAY_STATUS:
if (len != 2)
return true;
bt_shell_printf("Node %4.4x: Relay 0x%02x, cnt %d, steps %d\n",
src, data[0], data[1] & 0x7, data[1] >> 3);
saved = mesh_db_node_set_relay(src, data[0], data[1] & 7,
((data[1] >> 3) + 1) * 10);
break;
case OP_CONFIG_PROXY_STATUS:
if (len != 1)
return true;
bt_shell_printf("Node %4.4x Proxy state 0x%02x\n",
src, data[0]);
saved = mesh_db_node_set_proxy(src, data[0]);
break;
case OP_CONFIG_DEFAULT_TTL_STATUS:
if (len != 1)
return true;
bt_shell_printf("Node %4.4x Default TTL %d\n", src, data[0]);
saved = mesh_db_node_set_ttl(src, data[0]);
break;
case OP_CONFIG_MODEL_PUB_STATUS:
if (len != 12 && len != 14)
return true;
bt_shell_printf("\nNode %4.4x Publication status %s\n",
src, mesh_status_str(data[0]));
if (data[0] != MESH_STATUS_SUCCESS)
return true;
ele_addr = get_le16(data + 1);
mod_id = print_mod_id(data + 10, len == 14, "");
pub.u.addr = get_le16(data + 3);
pub.app_idx = get_le16(data + 5);
pub.cred = ((pub.app_idx & 0x1000) != 0);
pub.app_idx &= 0x3ff;
pub.ttl = data[7];
pub.prd_steps = (data[8] & 0x3f);
print_pub(ele_addr, mod_id, &pub);
switch (data[8] >> 6) {
case 0:
pub.prd_res = 100;
break;
case 2:
pub.prd_res = 10;
break;
case 1:
pub.prd_res = 10000;
break;
case 3:
pub.prd_res = 600000;
break;
}
pub.rtx_cnt = data[9] & 0x7;
pub.rtx_interval = ((data[9] >> 3) + 1) * 50;
bt_shell_printf("Rexmit count\t%d\n", pub.rtx_cnt);
bt_shell_printf("Rexmit steps\t%d\n", pub.rtx_interval);
if (IS_VIRTUAL(pub.u.addr)) {
grp = l_queue_find(groups, match_group_addr,
L_UINT_TO_PTR(pub.u.addr));
if (!grp) {
bt_shell_printf("Unknown virtual group\n");
return true;
}
memcpy(pub.u.label, grp->label, sizeof(pub.u.label));
}
saved = mesh_db_node_model_set_pub(src, ele_addr, len == 14,
mod_id, &pub,
IS_VIRTUAL(pub.u.addr));
break;
/* Per Mesh Profile 4.3.2.19 */
case OP_CONFIG_MODEL_SUB_STATUS:
if (len != 7 && len != 9)
return true;
bt_shell_printf("\nNode %4.4x Subscription status %s\n",
src, mesh_status_str(data[0]));
ele_addr = get_le16(data + 1);
addr = get_le16(data + 3);
bt_shell_printf("Element Addr\t%4.4x\n", ele_addr);
mod_id = print_mod_id(data + 5, len == 9, "");
bt_shell_printf("Subscr Addr\t%4.4x\n", addr);
grp = l_queue_find(groups, match_group_addr,
L_UINT_TO_PTR(addr));
if (data[0] != MESH_STATUS_SUCCESS || !cmd)
return true;
switch (cmd->opcode) {
default:
return true;
case OP_CONFIG_MODEL_SUB_ADD:
saved = mesh_db_node_model_add_sub(src, ele_addr,
len == 9, mod_id, addr);
break;
case OP_CONFIG_MODEL_SUB_DELETE:
saved = mesh_db_node_model_del_sub(src, ele_addr,
len == 9, mod_id, addr);
break;
case OP_CONFIG_MODEL_SUB_OVERWRITE:
saved = mesh_db_node_model_overwrt_sub(src, ele_addr,
len == 9, mod_id, addr);
break;
case OP_CONFIG_MODEL_SUB_DELETE_ALL:
saved = mesh_db_node_model_del_sub_all(src, ele_addr,
len == 9, mod_id);
break;
case OP_CONFIG_MODEL_SUB_VIRT_ADD:
if (grp)
saved = mesh_db_node_model_add_sub_virt(src,
ele_addr, len == 9,
mod_id, grp->label);
break;
case OP_CONFIG_MODEL_SUB_VIRT_DELETE:
if (grp)
saved = mesh_db_node_model_del_sub_virt(src,
ele_addr, len == 9,
mod_id, grp->label);
break;
case OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE:
if (grp)
saved = mesh_db_node_model_overwrt_sub_virt(src,
ele_addr, len == 9,
mod_id, grp->label);
break;
}
break;
/* Per Mesh Profile 4.3.2.27 */
case OP_CONFIG_MODEL_SUB_LIST:
if (len < 5)
return true;
print_sub_list(src, false, data, len);
return true;
case OP_CONFIG_VEND_MODEL_SUB_LIST:
if (len < 7)
return true;
print_sub_list(src, true, data, len);
return true;
/* Per Mesh Profile 4.3.2.50 */
case OP_MODEL_APP_LIST:
if (len < 5)
return true;
bt_shell_printf("\nNode %4.4x Model AppIdx status %s\n",
src, mesh_status_str(data[0]));
bt_shell_printf("Element Addr\t%4.4x\n", get_le16(data + 1));
bt_shell_printf("Model ID\t%4.4x\n", get_le16(data + 3));
data += 5;
len -= 5;
print_appkey_list(len, data);
return true;
case OP_VEND_MODEL_APP_LIST:
if (len < 7)
return true;
bt_shell_printf("\nNode %4.4x Vendor Model AppIdx status %s\n",
src, mesh_status_str(data[0]));
if (data[0] != MESH_STATUS_SUCCESS)
return true;
bt_shell_printf("Element Addr\t%4.4x\n", get_le16(data + 1));
print_mod_id(data + 3, true, "");
data += 7;
len -= 7;
print_appkey_list(len, data);
return true;
/* Per Mesh Profile 4.3.2.63 */
case OP_CONFIG_HEARTBEAT_PUB_STATUS:
if (len != 10)
return true;
bt_shell_printf("\nNode %4.4x Heartbeat publish status %s\n",
src, mesh_status_str(data[0]));
if (data[0] != MESH_STATUS_SUCCESS)
return true;
addr = get_le16(data + 1);
bt_shell_printf("Destination\t%4.4x\n", addr);
bt_shell_printf("CountLog\t\t%2.2x\n", data[3]);
bt_shell_printf("PeriodLog\t\t%2.2x\n", data[4]);
bt_shell_printf("TTL\t\t%2.2x\n", data[5]);
features = get_le16(data + 6);
bt_shell_printf("Features\t%4.4x\n", features);
net_idx = get_le16(data + 8);
bt_shell_printf("Net_Idx\t%u (0x%3.3x)\n", net_idx, net_idx);
saved = mesh_db_node_set_hb_pub(src, addr, net_idx, data[4],
data[5], features);
break;
/* Per Mesh Profile 4.3.2.66 */
case OP_CONFIG_HEARTBEAT_SUB_STATUS:
if (len != 9)
return true;
bt_shell_printf("\nNode %4.4x Heartbeat subscribe status %s\n",
src, mesh_status_str(data[0]));
if (data[0] != MESH_STATUS_SUCCESS)
return true;
ele_addr = get_le16(data + 1);
bt_shell_printf("Source\t\t%4.4x\n", ele_addr);
addr = get_le16(data + 3);
bt_shell_printf("Destination\t%4.4x\n", addr);
bt_shell_printf("Period\t\t%2.2x\n", data[5]);
bt_shell_printf("Count\t\t%2.2x\n", data[6]);
bt_shell_printf("Min Hops\t%2.2x\n", data[7]);
bt_shell_printf("Max Hops\t%2.2x\n", data[8]);
saved = mesh_db_node_set_hb_sub(src, ele_addr, addr);
break;
/* Per Mesh Profile 4.3.2.71 */
case OP_CONFIG_NETWORK_TRANSMIT_STATUS:
if (len != 1)
return true;
bt_shell_printf("Node %4.4x: Net transmit cnt %d, steps %d\n",
src, data[0] & 7, data[0] >> 3);
saved = mesh_db_node_set_net_transmit(src, data[0] & 7,
((data[0] >> 3) + 1) * 10);
break;
/* Per Mesh Profile 4.3.2.54 */
case OP_NODE_RESET_STATUS:
bt_shell_printf("Node %4.4x is reset\n", src);
reset_remote_node(src);
return true;
/* Per Mesh Profile 4.3.2.57 */
case OP_CONFIG_FRIEND_STATUS:
if (len != 1)
return true;
bt_shell_printf("Node %4.4x Friend state 0x%02x\n",
src, data[0]);
saved = mesh_db_node_set_friend(src, data[0]);
break;
}
if (!saved)
bt_shell_printf("Warning: Configuration not updated\n");
return true;
}
static uint16_t put_model_id(uint8_t *buf, uint32_t *args, bool vendor)
{
uint16_t n = 2;
if (vendor) {
put_le16(args[1], buf);
buf += 2;
n = 4;
}
put_le16(args[0], buf);
return n;
}
static uint32_t read_input_parameters(int argc, char *argv[])
{
uint32_t i;
if (!argc)
return 0;
--argc;
++argv;
if (!argc || argv[0][0] == '\0')
return 0;
for (i = 0; i < L_ARRAY_SIZE(parms) && i < (uint32_t) argc; i++) {
if (sscanf(argv[i], "%x", &parms[i]) != 1)
break;
}
return i;
}
static int compare_group_addr(const void *a, const void *b, void *user_data)
{
const struct mesh_group *grp0 = a;
const struct mesh_group *grp1 = b;
if (grp0->addr < grp1->addr)
return -1;
if (grp0->addr > grp1->addr)
return 1;
return 0;
}
static void print_virtual_not_found(uint16_t addr)
{
bt_shell_printf("Virtual group with hash %4.4x not found\n", addr);
bt_shell_printf("To see available, use \"group-list\"\n");
bt_shell_printf("To create new, use \"virt-add\"\n");
}
static struct mesh_group *add_group(uint16_t addr)
{
struct mesh_group *grp;
if (!IS_GROUP(addr) || addr >= FIXED_GROUP_LOW)
return NULL;
grp = l_queue_find(groups, match_group_addr, L_UINT_TO_PTR(addr));
if (grp)
return grp;
grp = l_new(struct mesh_group, 1);
grp->addr = addr;
l_queue_insert(groups, grp, compare_group_addr, NULL);
mesh_db_add_group(grp);
return grp;
}
static void cmd_timeout_set(int argc, char *argv[])
{
if (read_input_parameters(argc, argv) != 1)
return bt_shell_noninteractive_quit(EXIT_FAILURE);
rsp_timeout = parms[0];
bt_shell_printf("Timeout to wait for remote node's response: %d secs\n",
rsp_timeout);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_dst_set(int argc, char *argv[])
{
uint32_t dst;
char *end;
dst = strtol(argv[1], &end, 16);
if (end != (argv[1] + 4)) {
bt_shell_printf("Bad unicast address %s: "
"expected format 4 digit hex\n", argv[1]);
target = UNASSIGNED_ADDRESS;
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
bt_shell_printf("Configuring node %4.4x\n", dst);
target = dst;
set_menu_prompt("config", argv[1]);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static bool config_send(uint8_t *buf, uint16_t len, uint32_t opcode)
{
const struct cfg_cmd *cmd;
bool res;
if (IS_UNASSIGNED(target)) {
bt_shell_printf("Destination not set\n");
return false;
}
cmd = get_cmd(opcode);
if (!cmd)
return false;
if (get_req_by_rsp(target, cmd->rsp)) {
bt_shell_printf("Another command is pending\n");
return false;
}
res = send_msg(send_data, target, APP_IDX_DEV_REMOTE, buf, len);
if (!res)
bt_shell_printf("Failed to send \"%s\"\n", opcode_str(opcode));
if (cmd->rsp != NO_RESPONSE)
add_request(opcode);
return res;
}
static void cmd_default(uint32_t opcode)
{
uint16_t n;
uint8_t msg[32];
n = mesh_opcode_set(opcode, msg);
if (!config_send(msg, n, opcode))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_composition_get(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
n = mesh_opcode_set(OP_DEV_COMP_GET, msg);
/* By default, use page 0 */
msg[n++] = (read_input_parameters(argc, argv) == 1) ? parms[0] : 0;
if (!config_send(msg, n, OP_DEV_COMP_GET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_netkey_del(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
if (IS_UNASSIGNED(target)) {
bt_shell_printf("Destination not set\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
n = mesh_opcode_set(OP_NETKEY_DELETE, msg);
if (read_input_parameters(argc, argv) != 1) {
bt_shell_printf("Bad arguments %s\n", argv[1]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
put_le16(parms[0], msg + n);
n += 2;
if (!config_send(msg, n, OP_NETKEY_DELETE))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_kr_phase_get(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
if (IS_UNASSIGNED(target)) {
bt_shell_printf("Destination not set\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
n = mesh_opcode_set(OP_CONFIG_KEY_REFRESH_PHASE_GET, msg);
if (read_input_parameters(argc, argv) != 1)
return bt_shell_noninteractive_quit(EXIT_FAILURE);
put_le16(parms[0], msg + n);
n += 2;
if (!config_send(msg, n, OP_CONFIG_KEY_REFRESH_PHASE_GET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_kr_phase_set(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
uint8_t phase;
if (IS_UNASSIGNED(target)) {
bt_shell_printf("Destination not set\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
n = mesh_opcode_set(OP_CONFIG_KEY_REFRESH_PHASE_SET, msg);
if (read_input_parameters(argc, argv) != 2)
return bt_shell_noninteractive_quit(EXIT_FAILURE);
if (parms[1] != KEY_REFRESH_PHASE_TWO &&
parms[1] != KEY_REFRESH_PHASE_THREE) {
bt_shell_printf("Invalid KR transition value %u\n", parms[1]);
bt_shell_printf("Allowed values: 2 or 3\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
if (!keys_get_net_key_phase((uint16_t) parms[0], &phase)) {
bt_shell_printf("Subnet KR state not found\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
if (phase != (parms[1] % KEY_REFRESH_PHASE_THREE)) {
bt_shell_printf("Subnet's phase must be updated first!\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
put_le16(parms[0], msg + n);
n += 2;
msg[n++] = parms[1];
if (!config_send(msg, n, OP_CONFIG_KEY_REFRESH_PHASE_SET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_appkey_del(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
uint16_t app_idx, net_idx;
if (IS_UNASSIGNED(target)) {
bt_shell_printf("Destination not set\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
n = mesh_opcode_set(OP_APPKEY_DELETE, msg);
if (read_input_parameters(argc, argv) != 1) {
bt_shell_printf("Bad arguments %s\n", argv[1]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
app_idx = (uint16_t) parms[0];
net_idx = keys_get_bound_key(app_idx);
/* Pack bound NetKey and AppKey into 3 octets */
msg[n] = net_idx;
msg[n + 1] = ((net_idx >> 8) & 0xf) | ((app_idx << 4) & 0xf0);
msg[n + 2] = app_idx >> 4;
n += 3;
if (!config_send(msg, n, OP_APPKEY_DELETE))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_appkey_get(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
if (IS_UNASSIGNED(target)) {
bt_shell_printf("Destination not set\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
n = mesh_opcode_set(OP_APPKEY_GET, msg);
if (read_input_parameters(argc, argv) != 1) {
bt_shell_printf("Bad arguments %s\n", argv[1]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
put_le16(parms[0], msg + n);
n += 2;
if (!config_send(msg, n, OP_APPKEY_GET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_key_add(uint32_t opcode, int argc, char *argv[])
{
uint16_t key_idx;
bool is_appkey, update;
const struct cfg_cmd *cmd;
if (IS_UNASSIGNED(target)) {
bt_shell_printf("Destination not set\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
if (!send_key_msg) {
bt_shell_printf("Send key callback not set\n");
return;
}
if (read_input_parameters(argc, argv) != 1) {
bt_shell_printf("Bad arguments %s\n", argv[1]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
cmd = get_cmd(opcode);
if (!cmd)
return bt_shell_noninteractive_quit(EXIT_FAILURE);
if (get_req_by_rsp(target, cmd->rsp)) {
bt_shell_printf("Another key command is pending\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
key_idx = (uint16_t) parms[0];
update = (opcode == OP_NETKEY_UPDATE || opcode == OP_APPKEY_UPDATE);
is_appkey = (opcode == OP_APPKEY_ADD || opcode == OP_APPKEY_UPDATE);
if (!send_key_msg(key_data, target, key_idx, is_appkey, update))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
add_request(opcode);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_netkey_add(int argc, char *argv[])
{
cmd_key_add(OP_NETKEY_ADD, argc, argv);
}
static void cmd_netkey_update(int argc, char *argv[])
{
cmd_key_add(OP_NETKEY_UPDATE, argc, argv);
}
static void cmd_appkey_add(int argc, char *argv[])
{
cmd_key_add(OP_APPKEY_ADD, argc, argv);
}
static void cmd_appkey_update(int argc, char *argv[])
{
cmd_key_add(OP_APPKEY_UPDATE, argc, argv);
}
static void cmd_bind(uint32_t opcode, int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
uint32_t parm_cnt;
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 3 && parm_cnt != 4) {
bt_shell_printf("Bad arguments\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
if (!remote_has_composition(target)) {
bt_shell_printf("Node composition is unknown\n");
bt_shell_printf("Call \"composition-get\" first\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
n = mesh_opcode_set(opcode, msg);
put_le16(parms[0], msg + n);
n += 2;
put_le16(parms[1], msg + n);
n += 2;
n += put_model_id(msg + n, &parms[2], parm_cnt == 4);
if (!config_send(msg, n, opcode))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_add_binding(int argc, char *argv[])
{
cmd_bind(OP_MODEL_APP_BIND, argc, argv);
}
static void cmd_del_binding(int argc, char *argv[])
{
cmd_bind(OP_MODEL_APP_UNBIND, argc, argv);
}
static void cmd_beacon_set(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[2 + 1];
uint32_t parm_cnt;
n = mesh_opcode_set(OP_CONFIG_BEACON_SET, msg);
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 1) {
bt_shell_printf("bad arguments\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
msg[n++] = parms[0];
if (!config_send(msg, n, OP_CONFIG_BEACON_SET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_beacon_get(int argc, char *argv[])
{
cmd_default(OP_CONFIG_BEACON_GET);
}
static void cmd_ident_set(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[2 + 3 + 4];
uint32_t parm_cnt;
n = mesh_opcode_set(OP_NODE_IDENTITY_SET, msg);
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 2) {
bt_shell_printf("bad arguments\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
put_le16(parms[0], msg + n);
n += 2;
msg[n++] = parms[1];
if (!config_send(msg, n, OP_NODE_IDENTITY_SET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_ident_get(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[2 + 2 + 4];
uint32_t parm_cnt;
n = mesh_opcode_set(OP_NODE_IDENTITY_GET, msg);
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 1) {
bt_shell_printf("bad arguments\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
put_le16(parms[0], msg + n);
n += 2;
if (!config_send(msg, n, OP_NODE_IDENTITY_GET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_proxy_set(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[2 + 1];
uint32_t parm_cnt;
n = mesh_opcode_set(OP_CONFIG_PROXY_SET, msg);
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 1) {
bt_shell_printf("bad arguments");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
msg[n++] = parms[0];
if (!config_send(msg, n, OP_CONFIG_PROXY_SET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_proxy_get(int argc, char *argv[])
{
cmd_default(OP_CONFIG_PROXY_GET);
}
static void cmd_relay_set(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[2 + 2 + 4];
uint32_t parm_cnt;
n = mesh_opcode_set(OP_CONFIG_RELAY_SET, msg);
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 3) {
bt_shell_printf("bad arguments\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
msg[n++] = parms[0];
msg[n++] = parms[1] | (parms[2] << 3);
if (!config_send(msg, n, OP_CONFIG_RELAY_SET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_relay_get(int argc, char *argv[])
{
cmd_default(OP_CONFIG_RELAY_GET);
}
static void cmd_ttl_set(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
uint32_t parm_cnt;
parm_cnt = read_input_parameters(argc, argv);
if (!parm_cnt || parms[0] > TTL_MASK) {
bt_shell_printf("Bad TTL value\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
n = mesh_opcode_set(OP_CONFIG_DEFAULT_TTL_SET, msg);
msg[n++] = parms[0];
if (!config_send(msg, n, OP_CONFIG_DEFAULT_TTL_SET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_pub_set(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[48];
uint32_t parm_cnt;
struct mesh_group *grp;
uint32_t opcode;
uint16_t pub_addr;
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 6 && parm_cnt != 7) {
bt_shell_printf("Bad arguments\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
if (parms[1] > ALL_NODES_ADDRESS) {
bt_shell_printf("Bad publication address %x\n", parms[1]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
if (!remote_has_composition(target)) {
bt_shell_printf("Node composition is unknown\n");
bt_shell_printf("Call \"composition-get\" first\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
pub_addr = parms[1];
grp = l_queue_find(groups, match_group_addr, L_UINT_TO_PTR(pub_addr));
if (!grp)
grp = add_group(pub_addr);
if (!grp && IS_VIRTUAL(pub_addr)) {
print_virtual_not_found(pub_addr);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
opcode = (!IS_VIRTUAL(pub_addr)) ? OP_CONFIG_MODEL_PUB_SET :
OP_CONFIG_MODEL_PUB_VIRT_SET;
n = mesh_opcode_set(opcode, msg);
put_le16(parms[0], msg + n);
n += 2;
/* Publish address */
if (!IS_VIRTUAL(pub_addr)) {
put_le16(pub_addr, msg + n);
n += 2;
} else {
memcpy(msg + n, grp->label, 16);
n += 16;
}
/* AppKey index + credential (set to 0) */
put_le16(parms[2], msg + n);
n += 2;
/* TTL */
msg[n++] = DEFAULT_TTL;
/* Publish period step count and step resolution */
msg[n++] = parms[3];
/* Publish retransmit count & interval steps */
msg[n++] = parms[4];
/* Model Id */
n += put_model_id(msg + n, &parms[5], parm_cnt == 7);
if (!config_send(msg, n, opcode))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_pub_get(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
uint32_t parm_cnt;
n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_GET, msg);
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 2 && parm_cnt != 3) {
bt_shell_printf("Bad arguments: %s\n", argv[1]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
/* Element Address */
put_le16(parms[0], msg + n);
n += 2;
/* Model Id */
n += put_model_id(msg + n, &parms[1], parm_cnt == 3);
if (!config_send(msg, n, OP_CONFIG_MODEL_PUB_GET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void subscription_cmd(int argc, char *argv[], uint32_t opcode)
{
uint16_t n;
uint8_t msg[32];
uint32_t parm_cnt;
struct mesh_group *grp;
uint16_t sub_addr;
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 3 && parm_cnt != 4) {
bt_shell_printf("Bad arguments: %s\n", argv[1]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
if ((!IS_GROUP(parms[1]) || IS_ALL_NODES(parms[1])) &&
!IS_VIRTUAL(parms[1])) {
bt_shell_printf("Bad subscription address %x\n", parms[1]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
if (!remote_has_composition(target)) {
bt_shell_printf("Node composition is unknown\n");
bt_shell_printf("Call \"composition-get\" first\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
sub_addr = parms[1];
grp = l_queue_find(groups, match_group_addr, L_UINT_TO_PTR(sub_addr));
if (!grp && IS_VIRTUAL(sub_addr)) {
print_virtual_not_found(sub_addr);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
if (!grp && opcode != OP_CONFIG_MODEL_SUB_DELETE)
grp = add_group(sub_addr);
if (IS_VIRTUAL(sub_addr)) {
if (opcode == OP_CONFIG_MODEL_SUB_ADD)
opcode = OP_CONFIG_MODEL_SUB_VIRT_ADD;
else if (opcode == OP_CONFIG_MODEL_SUB_DELETE)
opcode = OP_CONFIG_MODEL_SUB_VIRT_DELETE;
else if (opcode == OP_CONFIG_MODEL_SUB_OVERWRITE)
opcode = OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE;
}
n = mesh_opcode_set(opcode, msg);
/* Element Address */
put_le16(parms[0], msg + n);
n += 2;
/* Subscription Address */
if (!IS_VIRTUAL(sub_addr)) {
put_le16(sub_addr, msg + n);
n += 2;
} else {
memcpy(msg + n, grp->label, 16);
n += 16;
}
/* Model ID */
n += put_model_id(msg + n, &parms[2], parm_cnt == 4);
if (!config_send(msg, n, opcode))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_sub_add(int argc, char *argv[])
{
subscription_cmd(argc, argv, OP_CONFIG_MODEL_SUB_ADD);
}
static void cmd_sub_del(int argc, char *argv[])
{
subscription_cmd(argc, argv, OP_CONFIG_MODEL_SUB_DELETE);
}
static void cmd_sub_ovwrt(int argc, char *argv[])
{
subscription_cmd(argc, argv, OP_CONFIG_MODEL_SUB_OVERWRITE);
}
static void cmd_sub_del_all(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
uint32_t parm_cnt;
n = mesh_opcode_set(OP_CONFIG_MODEL_SUB_DELETE_ALL, msg);
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 2 && parm_cnt != 3) {
bt_shell_printf("Bad arguments: %s\n", argv[1]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
/* Element Address */
put_le16(parms[0], msg + n);
n += 2;
/* Model ID */
n += put_model_id(msg + n, &parms[1], parm_cnt == 3);
if (!config_send(msg, n, OP_CONFIG_MODEL_SUB_DELETE_ALL))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_sub_get(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
uint32_t parm_cnt;
bool vendor;
uint32_t opcode;
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 2 && parm_cnt != 3) {
bt_shell_printf("Bad arguments: %s\n", argv[1]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
vendor = (parm_cnt == 3);
opcode = !vendor ? OP_CONFIG_MODEL_SUB_GET :
OP_CONFIG_VEND_MODEL_SUB_GET;
n = mesh_opcode_set(opcode, msg);
/* Element Address */
put_le16(parms[0], msg + n);
n += 2;
/* Model ID */
n += put_model_id(msg + n, &parms[1], vendor);
if (!config_send(msg, n, opcode))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_mod_appidx_get(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
uint32_t parm_cnt;
bool vendor;
uint32_t opcode;
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 2 && parm_cnt != 3) {
bt_shell_printf("Bad arguments: %s\n", argv[1]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
vendor = (parm_cnt == 3);
opcode = !vendor ? OP_MODEL_APP_GET : OP_VEND_MODEL_APP_GET;
n = mesh_opcode_set(opcode, msg);
/* Element Address */
put_le16(parms[0], msg + n);
n += 2;
/* Model ID */
n += put_model_id(msg + n, &parms[1], vendor);
if (!config_send(msg, n, opcode))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_hb_pub_set(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
uint32_t parm_cnt;
n = mesh_opcode_set(OP_CONFIG_HEARTBEAT_PUB_SET, msg);
if (!l_queue_find(groups, match_group_addr, L_UINT_TO_PTR(parms[1])))
add_group(parms[1]);
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 6) {
bt_shell_printf("Bad arguments: %s\n", argv[1]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
/* Per Mesh Profile 4.3.2.62 */
/* Publish address */
put_le16(parms[0], msg + n);
n += 2;
/* Count Log */
msg[n++] = parms[1];
/* Period Log */
msg[n++] = parms[2];
/* Heartbeat TTL */
msg[n++] = parms[3];
/* Features */
put_le16(parms[4], msg + n);
n += 2;
/* NetKey Index */
put_le16(parms[5], msg + n);
n += 2;
if (!config_send(msg, n, OP_CONFIG_HEARTBEAT_PUB_SET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_hb_pub_get(int argc, char *argv[])
{
cmd_default(OP_CONFIG_HEARTBEAT_PUB_GET);
}
static void cmd_hb_sub_set(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
uint32_t parm_cnt;
if (IS_UNASSIGNED(target)) {
bt_shell_printf("Destination not set\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
n = mesh_opcode_set(OP_CONFIG_HEARTBEAT_SUB_SET, msg);
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 3) {
bt_shell_printf("Bad arguments: %s\n", argv[1]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
if (!l_queue_find(groups, match_group_addr, L_UINT_TO_PTR(parms[1])))
add_group(parms[1]);
/* Per Mesh Profile 4.3.2.65 */
/* Source address */
put_le16(parms[0], msg + n);
n += 2;
/* Destination address */
put_le16(parms[1], msg + n);
n += 2;
/* Period log */
msg[n++] = parms[2];
if (!config_send(msg, n, OP_CONFIG_HEARTBEAT_SUB_SET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_hb_sub_get(int argc, char *argv[])
{
cmd_default(OP_CONFIG_HEARTBEAT_SUB_GET);
}
static void cmd_ttl_get(int argc, char *argv[])
{
cmd_default(OP_CONFIG_DEFAULT_TTL_GET);
}
static void cmd_network_transmit_get(int argc, char *argv[])
{
cmd_default(OP_CONFIG_NETWORK_TRANSMIT_GET);
}
static void cmd_network_transmit_set(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[2 + 1];
uint32_t parm_cnt;
n = mesh_opcode_set(OP_CONFIG_NETWORK_TRANSMIT_SET, msg);
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 2) {
bt_shell_printf("bad arguments\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
msg[n++] = parms[0] | (parms[1] << 3);
if (!config_send(msg, n, OP_CONFIG_NETWORK_TRANSMIT_SET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_friend_set(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[2 + 1];
uint32_t parm_cnt;
n = mesh_opcode_set(OP_CONFIG_FRIEND_SET, msg);
parm_cnt = read_input_parameters(argc, argv);
if (parm_cnt != 1) {
bt_shell_printf("bad arguments");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
msg[n++] = parms[0];
if (!config_send(msg, n, OP_CONFIG_FRIEND_SET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_friend_get(int argc, char *argv[])
{
cmd_default(OP_CONFIG_FRIEND_GET);
}
static void cmd_node_reset(int argc, char *argv[])
{
uint16_t n, i;
uint8_t msg[8];
struct pending_req *req;
if (IS_UNASSIGNED(target)) {
bt_shell_printf("Destination not set\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
/* Cannot remet self */
if (target == 0x0001) {
bt_shell_printf("Resetting self not allowed\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
n = mesh_opcode_set(OP_NODE_RESET, msg);
req = l_new(struct pending_req, 1);
req->addr = target;
req->cmd = get_cmd(OP_NODE_RESET);
/*
* As a courtesy to the remote node, send the reset command
* several times. Treat this as a single request with a longer
* response timeout.
*/
req->timer = l_timeout_create(rsp_timeout * 2,
wait_rsp_timeout, req, NULL);
l_queue_push_tail(requests, req);
for (i = 0; i < 5; i++)
send_msg(send_data, target, APP_IDX_DEV_REMOTE, msg, n);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_netkey_get(int argc, char *argv[])
{
cmd_default(OP_NETKEY_GET);
}
static void print_group(void *a, void *b)
{
struct mesh_group *grp = a;
char buf[33];
if (!IS_VIRTUAL(grp->addr)) {
bt_shell_printf("\tGroup addr: %4.4x\n", grp->addr);
return;
}
hex2str(grp->label, 16, buf, sizeof(buf));
bt_shell_printf("\tVirtual addr: %4.4x, label: %s\n", grp->addr, buf);
}
static void cmd_add_virt(int argc, char *argv[])
{
struct mesh_group *grp, *tmp;
uint8_t max_tries = 3;
grp = l_new(struct mesh_group, 1);
retry:
l_getrandom(grp->label, 16);
mesh_crypto_virtual_addr(grp->label, &grp->addr);
/* For simplicity sake, avoid labels that map to the same hash */
tmp = l_queue_find(groups, match_group_addr, L_UINT_TO_PTR(grp->addr));
if (!tmp) {
l_queue_insert(groups, grp, compare_group_addr, NULL);
print_group(grp, NULL);
mesh_db_add_group(grp);
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
max_tries--;
if (max_tries)
goto retry;
l_free(grp);
bt_shell_printf("Failed to generate unique label. Try again.");
bt_shell_noninteractive_quit(EXIT_FAILURE);
}
static void cmd_list_groups(int argc, char *argv[])
{
l_queue_foreach(groups, print_group, NULL);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
static bool tx_setup(model_send_msg_func_t send_func, void *user_data)
{
if (!send_func)
return false;
send_msg = send_func;
send_data = user_data;
return true;
}
static const struct bt_shell_menu cfg_menu = {
.name = "config",
.desc = "Configuration Model Submenu",
.entries = {
{"target", "<unicast>", cmd_dst_set,
"Set target node to configure"},
{"timeout", "<seconds>", cmd_timeout_set,
"Set response timeout (seconds)"},
{"composition-get", "[page_num]", cmd_composition_get,
"Get composition data"},
{"netkey-add", "<net_idx>", cmd_netkey_add,
"Add NetKey"},
{"netkey-update", "<net_idx>", cmd_netkey_update,
"Update NetKey"},
{"netkey-del", "<net_idx>", cmd_netkey_del,
"Delete NetKey"},
{"netkey-get", NULL, cmd_netkey_get,
"List NetKeys known to the node"},
{"kr-phase-get", "<net_idx>", cmd_kr_phase_get,
"Get Key Refresh phase of a NetKey"},
{"kr-phase-set", "<net_idx> <phase>", cmd_kr_phase_set,
"Set Key Refresh phase transition of a NetKey"},
{"appkey-add", "<app_idx>", cmd_appkey_add,
"Add AppKey"},
{"appkey-update", "<app_idx>", cmd_appkey_update,
"Add AppKey"},
{"appkey-del", "<app_idx>", cmd_appkey_del,
"Delete AppKey"},
{"appkey-get", "<net_idx>", cmd_appkey_get,
"List AppKeys bound to the NetKey"},
{"bind", "<ele_addr> <app_idx> <model_id> [vendor_id]", cmd_add_binding,
"Bind AppKey to a model"},
{"unbind", "<ele_addr> <app_idx> <model_id> [vendor_id]",
cmd_del_binding,
"Remove AppKey from a model"},
{"mod-appidx-get", "<ele_addr> <model_id> [vendor_id]",
cmd_mod_appidx_get,
"Get model app_idx"},
{"ttl-set", "<ttl>", cmd_ttl_set,
"Set default TTL"},
{"ttl-get", NULL, cmd_ttl_get,
"Get default TTL"},
{"pub-set", "<ele_addr> <pub_addr> <app_idx> <per (step|res)> "
"<re-xmt (cnt|per)> <model_id> [vendor_id]",
cmd_pub_set,
"Set publication"},
{"pub-get", "<ele_addr> <model_id> [vendor_id]", cmd_pub_get,
"Get publication"},
{"proxy-set", "<proxy>", cmd_proxy_set,
"Set proxy state"},
{"proxy-get", NULL, cmd_proxy_get,
"Get proxy state"},
{"ident-set", "<net_idx> <state>", cmd_ident_set,
"Set node identity state"},
{"ident-get", "<net_idx>", cmd_ident_get,
"Get node identity state"},
{"beacon-set", "<state>", cmd_beacon_set,
"Set node identity state"},
{"beacon-get", NULL, cmd_beacon_get,
"Get node beacon state"},
{"relay-set", "<relay> <rexmt count> <rexmt steps>", cmd_relay_set,
"Set relay"},
{"relay-get", NULL, cmd_relay_get,
"Get relay"},
{"friend-set", "<state>", cmd_friend_set,
"Set friend state"},
{"friend-get", NULL, cmd_friend_get,
"Get friend state"},
{"network-transmit-get", NULL, cmd_network_transmit_get,
"Get network transmit state"},
{"network-transmit-set", "<count> <steps>", cmd_network_transmit_set,
"Set network transmit state"},
{"hb-pub-set", "<pub_addr> <count> <period> <ttl> <features> <net_idx>",
cmd_hb_pub_set,
"Set heartbeat publish"},
{"hb-pub-get", NULL, cmd_hb_pub_get,
"Get heartbeat publish"},
{"hb-sub-set", "<src_addr> <dst_addr> <period>", cmd_hb_sub_set,
"Set heartbeat subscribe"},
{"hb-sub-get", NULL, cmd_hb_sub_get,
"Get heartbeat subscribe"},
{"virt-add", NULL, cmd_add_virt, "Generate and add a virtual label"},
{"group-list", NULL, cmd_list_groups,
"Display existing group addresses and virtual labels"},
{"sub-add", "<ele_addr> <sub_addr> <model_id> [vendor]",
cmd_sub_add, "Add subscription"},
{"sub-del", "<ele_addr> <sub_addr> <model_id> [vendor]",
cmd_sub_del, "Delete subscription"},
{"sub-wrt", "<ele_addr> <sub_addr> <model_id> [vendor]",
cmd_sub_ovwrt, "Overwrite subscription"},
{"sub-del-all", "<ele_addr> <model_id> [vendor]", cmd_sub_del_all,
"Delete subscription"},
{"sub-get", "<ele_addr> <model_id> [vendor]", cmd_sub_get,
"Get subscription"},
{"node-reset", NULL, cmd_node_reset,
"Reset a node and remove it from network"},
{} },
};
static struct model_info cli_info = {
.ops = {
.set_send_func = tx_setup,
.set_pub_func = NULL,
.recv = msg_recvd,
.bind = NULL,
.pub = NULL
},
.mod_id = CONFIG_CLIENT_MODEL_ID,
.vendor_id = VENDOR_ID_INVALID
};
struct model_info *cfgcli_init(key_send_func_t key_send,
delete_remote_func_t del_node, void *user_data)
{
if (!key_send)
return NULL;
send_key_msg = key_send;
key_data = user_data;
mgr_del_remote = del_node;
requests = l_queue_new();
groups = mesh_db_load_groups();
bt_shell_add_submenu(&cfg_menu);
return &cli_info;
}
void cfgcli_cleanup(void)
{
l_queue_destroy(requests, free_request);
l_queue_destroy(groups, l_free);
}