1212 lines
29 KiB
C
1212 lines
29 KiB
C
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2017 Intel Corporation. All rights reserved.
|
|
*
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <inttypes.h>
|
|
#include <stdbool.h>
|
|
#include <sys/uio.h>
|
|
#include <wordexp.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#include "src/shared/shell.h"
|
|
#include "src/shared/util.h"
|
|
#include "mesh/mesh-net.h"
|
|
#include "mesh/keys.h"
|
|
#include "mesh/net.h"
|
|
#include "mesh/node.h"
|
|
#include "mesh/prov-db.h"
|
|
#include "mesh/util.h"
|
|
#include "mesh/config-model.h"
|
|
|
|
#define MIN_COMPOSITION_LEN 16
|
|
|
|
static uint32_t print_mod_id(uint8_t *data, bool vid)
|
|
{
|
|
uint32_t mod_id;
|
|
|
|
if (!vid) {
|
|
mod_id = get_le16(data);
|
|
bt_shell_printf("Model Id\t%4.4x\n", mod_id);
|
|
mod_id = 0xffff0000 | mod_id;
|
|
} else {
|
|
mod_id = get_le16(data + 2);
|
|
bt_shell_printf("Model Id\t%4.4x %4.4x\n",
|
|
get_le16(data), mod_id);
|
|
mod_id = get_le16(data) << 16 | mod_id;
|
|
}
|
|
return mod_id;
|
|
}
|
|
|
|
static bool client_msg_recvd(uint16_t src, uint8_t *data,
|
|
uint16_t len, void *user_data)
|
|
{
|
|
uint32_t opcode;
|
|
struct mesh_node *node;
|
|
uint16_t app_idx, net_idx, addr;
|
|
uint32_t mod_id;
|
|
uint16_t primary;
|
|
uint16_t ele_addr;
|
|
uint8_t ele_idx;
|
|
struct mesh_publication pub;
|
|
int n;
|
|
uint16_t i;
|
|
|
|
if (mesh_opcode_get(data, len, &opcode, &n)) {
|
|
len -= n;
|
|
data += n;
|
|
} else
|
|
return false;
|
|
|
|
if (IS_UNICAST(src)) {
|
|
node = node_find_by_addr(src);
|
|
} else
|
|
node = NULL;
|
|
|
|
if (!node)
|
|
return false;
|
|
|
|
primary = node_get_primary(node);
|
|
if (primary != src)
|
|
return false;
|
|
|
|
switch (opcode & ~OP_UNRELIABLE) {
|
|
default:
|
|
return false;
|
|
|
|
case OP_DEV_COMP_STATUS:
|
|
if (len < MIN_COMPOSITION_LEN || !node)
|
|
break;
|
|
if (node_parse_composition(node, data, len)) {
|
|
if (!prov_db_add_node_composition(node, data, len))
|
|
break;
|
|
}
|
|
|
|
if (node_get_composition(node))
|
|
prov_db_print_node_composition(node);
|
|
break;
|
|
|
|
case OP_APPKEY_STATUS:
|
|
if (len != 4)
|
|
break;
|
|
|
|
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%3.3x\n", net_idx);
|
|
bt_shell_printf("AppKey\t%3.3x\n", app_idx);
|
|
|
|
if (data[0] != MESH_STATUS_SUCCESS &&
|
|
data[0] != MESH_STATUS_IDX_ALREADY_STORED &&
|
|
node_app_key_delete(node, net_idx, app_idx))
|
|
prov_db_node_keys(node, node_get_app_keys(node),
|
|
"appKeys");
|
|
break;
|
|
|
|
case OP_NETKEY_STATUS:
|
|
if (len != 3)
|
|
break;
|
|
|
|
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 %3.3x\n", net_idx);
|
|
|
|
if (data[0] != MESH_STATUS_SUCCESS &&
|
|
data[0] != MESH_STATUS_IDX_ALREADY_STORED &&
|
|
node_net_key_delete(node, net_idx))
|
|
prov_db_node_keys(node, node_get_net_keys(node),
|
|
"netKeys");
|
|
break;
|
|
|
|
case OP_MODEL_APP_STATUS:
|
|
if (len != 7 && len != 9)
|
|
break;
|
|
|
|
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) ? true : false);
|
|
|
|
bt_shell_printf("AppIdx\t\t%3.3x\n ", app_idx);
|
|
|
|
if (data[0] == MESH_STATUS_SUCCESS &&
|
|
node_add_binding(node, addr - src, mod_id, app_idx))
|
|
prov_db_add_binding(node, addr - src, mod_id, app_idx);
|
|
break;
|
|
|
|
case OP_NODE_IDENTITY_STATUS:
|
|
if (len != 4)
|
|
return true;
|
|
bt_shell_printf("Network index 0x%04x "
|
|
"Node Identity state 0x%02x status %s\n",
|
|
get_le16(data + 1), data[3],
|
|
mesh_status_str(data[0]));
|
|
break;
|
|
|
|
case OP_CONFIG_RELAY_STATUS:
|
|
if (len != 2)
|
|
return true;
|
|
bt_shell_printf("Node %4.4x Relay state 0x%02x"
|
|
" count %d steps %d\n",
|
|
src, data[0], data[1]>>5, data[1] & 0x1f);
|
|
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]);
|
|
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]);
|
|
if (node_set_default_ttl (node, data[0]))
|
|
prov_db_node_set_ttl(node, 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);
|
|
|
|
bt_shell_printf("Element Addr\t%04x\n", ele_addr);
|
|
|
|
mod_id = print_mod_id(data + 10, (len == 14) ? true : false);
|
|
|
|
pub.u.addr16 = get_le16(data + 3);
|
|
pub.app_idx = get_le16(data + 5);
|
|
pub.ttl = data[7];
|
|
pub.period = data[8];
|
|
n = (data[8] & 0x3f);
|
|
bt_shell_printf("Pub Addr\t%04x\n", pub.u.addr16);
|
|
switch (data[8] >> 6) {
|
|
case 0:
|
|
bt_shell_printf("Period\t\t%d ms\n", n * 100);
|
|
break;
|
|
case 2:
|
|
n *= 10;
|
|
/* fall through */
|
|
case 1:
|
|
bt_shell_printf("Period\t\t%d sec\n", n);
|
|
break;
|
|
case 3:
|
|
bt_shell_printf("Period\t\t%d min\n", n * 10);
|
|
break;
|
|
}
|
|
|
|
pub.retransmit = data[9];
|
|
bt_shell_printf("Rexmit count\t%d\n", data[9] >> 5);
|
|
bt_shell_printf("Rexmit steps\t%d\n", data[9] & 0x1f);
|
|
|
|
ele_idx = ele_addr - node_get_primary(node);
|
|
|
|
/* Local configuration is saved by server */
|
|
if (node == node_get_local_node())
|
|
break;
|
|
|
|
if (node_model_pub_set(node, ele_idx, mod_id, &pub))
|
|
prov_db_node_set_model_pub(node, ele_idx, mod_id,
|
|
node_model_pub_get(node, ele_idx, mod_id));
|
|
break;
|
|
|
|
/* Per Mesh Profile 4.3.2.19 */
|
|
case OP_CONFIG_MODEL_SUB_STATUS:
|
|
bt_shell_printf("\nNode %4.4x Subscription status %s\n",
|
|
src, mesh_status_str(data[0]));
|
|
|
|
if (data[0] != MESH_STATUS_SUCCESS)
|
|
return true;
|
|
|
|
ele_addr = get_le16(data + 1);
|
|
addr = get_le16(data + 3);
|
|
ele_idx = ele_addr - node_get_primary(node);
|
|
|
|
bt_shell_printf("Element Addr\t%4.4x\n", ele_addr);
|
|
|
|
mod_id = print_mod_id(data + 5, (len == 9) ? true : false);
|
|
|
|
bt_shell_printf("Subscr Addr\t%4.4x\n", addr);
|
|
|
|
/* Save subscriptions in node and database */
|
|
if (node_add_subscription(node, ele_idx, mod_id, addr))
|
|
prov_db_add_subscription(node, ele_idx, mod_id, addr);
|
|
break;
|
|
|
|
/* Per Mesh Profile 4.3.2.27 */
|
|
case OP_CONFIG_MODEL_SUB_LIST:
|
|
|
|
bt_shell_printf("\nNode %4.4x Subscription List 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));
|
|
bt_shell_printf("Model ID\t%4.4x\n", get_le16(data + 3));
|
|
|
|
for (i = 5; i < len; i += 2)
|
|
bt_shell_printf("Subscr Addr\t%4.4x\n",
|
|
get_le16(data + i));
|
|
break;
|
|
|
|
/* Per Mesh Profile 4.3.2.50 */
|
|
case OP_MODEL_APP_LIST:
|
|
bt_shell_printf("\nNode %4.4x 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));
|
|
bt_shell_printf("Model ID\t%4.4x\n", get_le16(data + 3));
|
|
|
|
for (i = 5; i < len; i += 2)
|
|
bt_shell_printf("Model AppIdx\t%4.4x\n",
|
|
get_le16(data + i));
|
|
break;
|
|
|
|
/* Per Mesh Profile 4.3.2.63 */
|
|
case OP_CONFIG_HEARTBEAT_PUB_STATUS:
|
|
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;
|
|
|
|
bt_shell_printf("Destination\t%4.4x\n", get_le16(data + 1));
|
|
bt_shell_printf("Count\t\t%2.2x\n", data[3]);
|
|
bt_shell_printf("Period\t\t%2.2x\n", data[4]);
|
|
bt_shell_printf("TTL\t\t%2.2x\n", data[5]);
|
|
bt_shell_printf("Features\t%4.4x\n", get_le16(data + 6));
|
|
bt_shell_printf("Net_Idx\t%4.4x\n", get_le16(data + 8));
|
|
break;
|
|
|
|
/* Per Mesh Profile 4.3.2.66 */
|
|
case OP_CONFIG_HEARTBEAT_SUB_STATUS:
|
|
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;
|
|
|
|
bt_shell_printf("Source\t\t%4.4x\n", get_le16(data + 1));
|
|
bt_shell_printf("Destination\t%4.4x\n", get_le16(data + 3));
|
|
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]);
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static uint32_t target;
|
|
static uint32_t parms[8];
|
|
|
|
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;
|
|
|
|
memset(parms, 0xff, sizeof(parms));
|
|
|
|
for (i = 0; i < sizeof(parms)/sizeof(parms[0]) && i < (unsigned) argc;
|
|
i++) {
|
|
sscanf(argv[i], "%x", &parms[i]);
|
|
if (parms[i] == 0xffffffff)
|
|
break;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
static void cmd_node_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);
|
|
} else {
|
|
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)
|
|
{
|
|
struct mesh_node *node = node_get_local_node();
|
|
uint16_t primary;
|
|
|
|
if(!node)
|
|
return false;
|
|
|
|
primary = node_get_primary(node);
|
|
if (target != primary)
|
|
return net_access_layer_send(DEFAULT_TTL, primary,
|
|
target, APP_IDX_DEV, buf, len);
|
|
|
|
node_local_data_handler(primary, target, node_get_iv_index(node),
|
|
node_get_sequence_number(node), APP_IDX_DEV,
|
|
buf, len);
|
|
return true;
|
|
|
|
}
|
|
|
|
static void cmd_default(uint32_t opcode)
|
|
{
|
|
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(opcode, msg);
|
|
|
|
if (!config_send(msg, n)) {
|
|
bt_shell_printf("Failed to send command (opcode 0x%x)\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];
|
|
struct mesh_node *node;
|
|
|
|
if (IS_UNASSIGNED(target)) {
|
|
bt_shell_printf("Destination not set\n");
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
node = node_find_by_addr(target);
|
|
|
|
if (!node)
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
|
|
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)) {
|
|
bt_shell_printf("Failed to send \"GET NODE COMPOSITION\"\n");
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
|
|
}
|
|
|
|
static void cmd_net_key(int argc, char *argv[], uint32_t opcode)
|
|
{
|
|
uint16_t n;
|
|
uint8_t msg[32];
|
|
uint16_t net_idx;
|
|
uint8_t *key;
|
|
struct mesh_node *node;
|
|
|
|
if (IS_UNASSIGNED(target)) {
|
|
bt_shell_printf("Destination not set\n");
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
n = mesh_opcode_set(opcode, msg);
|
|
|
|
if (read_input_parameters(argc, argv) != 1) {
|
|
bt_shell_printf("Bad arguments %s\n", argv[1]);
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
node = node_find_by_addr(target);
|
|
if (!node) {
|
|
bt_shell_printf("Node %4.4x\n not found", target);
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
net_idx = parms[0];
|
|
|
|
if (opcode != OP_NETKEY_DELETE) {
|
|
|
|
key = keys_net_key_get(net_idx, true);
|
|
if (!key) {
|
|
bt_shell_printf("NetKey with index %4.4x not found\n",
|
|
net_idx);
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
put_le16(net_idx, &msg[n]);
|
|
n += 2;
|
|
|
|
memcpy(msg + n, key, 16);
|
|
n += 16;
|
|
}
|
|
|
|
if (!config_send(msg, n)) {
|
|
bt_shell_printf("Failed to send \"%s NET KEY\"\n",
|
|
opcode == OP_NETKEY_ADD ? "ADD" : "DEL");
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (opcode != OP_NETKEY_DELETE) {
|
|
if (node_net_key_add(node, net_idx))
|
|
prov_db_node_keys(node, node_get_net_keys(node),
|
|
"netKeys");
|
|
} else {
|
|
if (node_net_key_delete(node, net_idx))
|
|
prov_db_node_keys(node, node_get_net_keys(node),
|
|
"netKeys");
|
|
}
|
|
|
|
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
|
|
}
|
|
|
|
static void cmd_netkey_add(int argc, char *argv[])
|
|
{
|
|
cmd_net_key(argc, argv, OP_NETKEY_ADD);
|
|
}
|
|
|
|
static void cmd_netkey_del(int argc, char *argv[])
|
|
{
|
|
cmd_net_key(argc, argv, OP_NETKEY_DELETE);
|
|
}
|
|
|
|
static void cmd_app_key(int argc, char *argv[], uint32_t opcode)
|
|
{
|
|
uint16_t n;
|
|
uint8_t msg[32];
|
|
uint16_t net_idx;
|
|
uint16_t app_idx;
|
|
uint8_t *key;
|
|
struct mesh_node *node;
|
|
|
|
if (IS_UNASSIGNED(target)) {
|
|
bt_shell_printf("Destination not set\n");
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (read_input_parameters(argc, argv) != 1) {
|
|
bt_shell_printf("Bad arguments %s\n", argv[1]);
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
node = node_find_by_addr(target);
|
|
if (!node) {
|
|
bt_shell_printf("Node %4.4x\n not found", target);
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
n = mesh_opcode_set(opcode, msg);
|
|
|
|
app_idx = parms[0];
|
|
net_idx = keys_app_key_get_bound(app_idx);
|
|
if (net_idx == NET_IDX_INVALID) {
|
|
bt_shell_printf("AppKey with index %4.4x not found\n", app_idx);
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
msg[n++] = net_idx & 0xf;
|
|
msg[n++] = ((net_idx >> 8) & 0xf) |
|
|
((app_idx << 4) & 0xf0);
|
|
msg[n++] = app_idx >> 4;
|
|
|
|
if (opcode != OP_APPKEY_DELETE) {
|
|
key = keys_app_key_get(app_idx, true);
|
|
if (!key) {
|
|
bt_shell_printf("AppKey %4.4x not found\n", net_idx);
|
|
return;
|
|
}
|
|
|
|
memcpy(msg + n, key, 16);
|
|
n += 16;
|
|
}
|
|
|
|
if (!config_send(msg, n)) {
|
|
bt_shell_printf("Failed to send \"ADD %s KEY\"\n",
|
|
opcode == OP_APPKEY_ADD ? "ADD" : "DEL");
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (opcode != OP_APPKEY_DELETE) {
|
|
if (node_app_key_add(node, app_idx))
|
|
prov_db_node_keys(node, node_get_app_keys(node),
|
|
"appKeys");
|
|
} else {
|
|
if (node_app_key_delete(node, net_idx, app_idx))
|
|
prov_db_node_keys(node, node_get_app_keys(node),
|
|
"appKeys");
|
|
}
|
|
|
|
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
|
|
}
|
|
|
|
static void cmd_appkey_add(int argc, char *argv[])
|
|
{
|
|
cmd_app_key(argc, argv, OP_APPKEY_ADD);
|
|
}
|
|
|
|
static void cmd_appkey_del(int argc, char *argv[])
|
|
{
|
|
cmd_app_key(argc, argv, OP_APPKEY_DELETE);
|
|
}
|
|
|
|
static bool verify_config_target(uint32_t dst)
|
|
{
|
|
struct mesh_node *node;
|
|
|
|
if (IS_UNASSIGNED(dst)) {
|
|
bt_shell_printf("Destination not set\n");
|
|
return false;
|
|
}
|
|
|
|
node = node_find_by_addr(dst);
|
|
if (!node) {
|
|
bt_shell_printf("Node with unicast address %4.4x unknown\n",
|
|
dst);
|
|
return false;
|
|
}
|
|
|
|
if (!node_get_composition(node)) {
|
|
bt_shell_printf("Node composition for %4.4x unknown\n", dst);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void cmd_bind(int argc, char *argv[])
|
|
{
|
|
uint16_t n;
|
|
uint8_t msg[32];
|
|
int parm_cnt;
|
|
|
|
if (!verify_config_target(target))
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
|
|
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);
|
|
}
|
|
|
|
n = mesh_opcode_set(OP_MODEL_APP_BIND, msg);
|
|
|
|
put_le16(target + parms[0], msg + n);
|
|
n += 2;
|
|
put_le16(parms[1], msg + n);
|
|
n += 2;
|
|
if (parm_cnt == 4) {
|
|
put_le16(parms[3], msg + n);
|
|
put_le16(parms[2], msg + n + 2);
|
|
n += 4;
|
|
} else {
|
|
put_le16(parms[2], msg + n);
|
|
n += 2;
|
|
}
|
|
|
|
if (!config_send(msg, n)) {
|
|
bt_shell_printf("Failed to send \"MODEL APP BIND\"\n");
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
|
|
}
|
|
|
|
static void cmd_ident_set(int argc, char *argv[])
|
|
{
|
|
uint16_t n;
|
|
uint8_t msg[2 + 3 + 4];
|
|
int parm_cnt;
|
|
|
|
if (!verify_config_target(target))
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
|
|
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)) {
|
|
bt_shell_printf("Failed to send \"SET IDENTITY\"\n");
|
|
return;
|
|
}
|
|
|
|
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];
|
|
int parm_cnt;
|
|
|
|
if (!verify_config_target(target))
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
|
|
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)) {
|
|
bt_shell_printf("Failed to send \"GET IDENTITY\"\n");
|
|
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 + 4];
|
|
int parm_cnt;
|
|
|
|
if (!verify_config_target(target))
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
|
|
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];
|
|
msg[n++] = parms[1];
|
|
|
|
if (!config_send(msg, n)) {
|
|
bt_shell_printf("Failed to send \"SET PROXY\"\n");
|
|
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];
|
|
int parm_cnt;
|
|
|
|
if (!verify_config_target(target))
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
|
|
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] << 5) | parms[2];
|
|
|
|
if (!config_send(msg, n)) {
|
|
bt_shell_printf("Failed to send \"SET RELAY\"\n");
|
|
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];
|
|
int parm_cnt;
|
|
uint8_t ttl;
|
|
|
|
if (IS_UNASSIGNED(target)) {
|
|
bt_shell_printf("Destination not set\n");
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
n = mesh_opcode_set(OP_CONFIG_DEFAULT_TTL_SET, msg);
|
|
|
|
parm_cnt = read_input_parameters(argc, argv);
|
|
if (parm_cnt) {
|
|
ttl = parms[0] & TTL_MASK;
|
|
} else
|
|
ttl = node_get_default_ttl(node_get_local_node());
|
|
|
|
msg[n++] = ttl;
|
|
|
|
if (!config_send(msg, n)) {
|
|
bt_shell_printf("Failed to send \"SET_DEFAULT TTL\"\n");
|
|
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[32];
|
|
int parm_cnt;
|
|
|
|
if (!verify_config_target(target))
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
|
|
n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_SET, msg);
|
|
|
|
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);
|
|
}
|
|
|
|
put_le16(parms[0], msg + n);
|
|
n += 2;
|
|
/* Publish address */
|
|
put_le16(parms[1], msg + n);
|
|
n += 2;
|
|
/* 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 */
|
|
if (parm_cnt == 7) {
|
|
put_le16(parms[6], msg + n);
|
|
put_le16(parms[5], msg + n + 2);
|
|
n += 4;
|
|
} else {
|
|
put_le16(parms[5], msg + n);
|
|
n += 2;
|
|
}
|
|
|
|
if (!config_send(msg, n)) {
|
|
bt_shell_printf("Failed to send \"SET MODEL PUBLICATION\"\n");
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
|
|
}
|
|
|
|
static void cmd_pub_get(int argc, char *argv[])
|
|
{
|
|
uint16_t n;
|
|
uint8_t msg[32];
|
|
int 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_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 */
|
|
if (parm_cnt == 3) {
|
|
put_le16(parms[2], msg + n);
|
|
put_le16(parms[1], msg + n + 2);
|
|
n += 4;
|
|
} else {
|
|
put_le16(parms[1], msg + n);
|
|
n += 2;
|
|
}
|
|
|
|
if (!config_send(msg, n)) {
|
|
bt_shell_printf("Failed to send \"GET MODEL PUBLICATION\"\n");
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
|
|
}
|
|
|
|
static void cmd_sub_add(int argc, char *argv[])
|
|
{
|
|
uint16_t n;
|
|
uint8_t msg[32];
|
|
int 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_MODEL_SUB_ADD, 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);
|
|
}
|
|
|
|
/* Per Mesh Profile 4.3.2.19 */
|
|
/* Element Address */
|
|
put_le16(parms[0], msg + n);
|
|
n += 2;
|
|
/* Subscription Address */
|
|
put_le16(parms[1], msg + n);
|
|
n += 2;
|
|
/* SIG Model ID */
|
|
put_le16(parms[2], msg + n);
|
|
n += 2;
|
|
|
|
if (!config_send(msg, n)) {
|
|
bt_shell_printf("Failed to send \"ADD SUBSCRIPTION\"\n");
|
|
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];
|
|
int 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_MODEL_SUB_GET, msg);
|
|
|
|
parm_cnt = read_input_parameters(argc, argv);
|
|
if (parm_cnt != 2) {
|
|
bt_shell_printf("Bad arguments: %s\n", argv[1]);
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* Per Mesh Profile 4.3.2.27 */
|
|
/* Element Address */
|
|
put_le16(parms[0], msg + n);
|
|
n += 2;
|
|
/* Model ID */
|
|
put_le16(parms[1], msg + n);
|
|
n += 2;
|
|
|
|
if (!config_send(msg, n)) {
|
|
bt_shell_printf("Failed to send \"GET SUB GET\"\n");
|
|
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];
|
|
int 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_MODEL_APP_GET, msg);
|
|
|
|
parm_cnt = read_input_parameters(argc, argv);
|
|
if (parm_cnt != 2) {
|
|
bt_shell_printf("Bad arguments: %s\n", argv[1]);
|
|
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* Per Mesh Profile 4.3.2.49 */
|
|
/* Element Address */
|
|
put_le16(parms[0], msg + n);
|
|
n += 2;
|
|
/* Model ID */
|
|
put_le16(parms[1], msg + n);
|
|
n += 2;
|
|
|
|
if (!config_send(msg, n)) {
|
|
bt_shell_printf("Failed to send \"GET APP GET\"\n");
|
|
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];
|
|
int 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_PUB_SET, msg);
|
|
|
|
parm_cnt = read_input_parameters(argc, argv);
|
|
if (parm_cnt != 5) {
|
|
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++] = DEFAULT_TTL;
|
|
/* Features */
|
|
put_le16(parms[3], msg + n);
|
|
n += 2;
|
|
/* NetKey Index */
|
|
put_le16(parms[4], msg + n);
|
|
n += 2;
|
|
|
|
if (!config_send(msg, n)) {
|
|
bt_shell_printf("Failed to send \"SET HEARTBEAT PUBLISH\"\n");
|
|
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];
|
|
int 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);
|
|
}
|
|
|
|
/* 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)) {
|
|
bt_shell_printf("Failed to send \"SET HEARTBEAT SUBSCRIBE\"\n");
|
|
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 const struct bt_shell_menu cfg_menu = {
|
|
.name = "config",
|
|
.desc = "Configuration Model Submenu",
|
|
.entries = {
|
|
{"target", "<unicast>", cmd_node_set,
|
|
"Set target node to configure"},
|
|
{"composition-get", "[page_num]", cmd_composition_get,
|
|
"Get composition data"},
|
|
{"netkey-add", "<net_idx>", cmd_netkey_add,
|
|
"Add network key"},
|
|
{"netkey-del", "<net_idx>", cmd_netkey_del,
|
|
"Delete network key"},
|
|
{"appkey-add", "<app_idx>", cmd_appkey_add,
|
|
"Add application key"},
|
|
{"appkey-del", "<app_idx>", cmd_appkey_del,
|
|
"Delete application key"},
|
|
{"bind", "<ele_idx> <app_idx> <mod_id> [cid]",
|
|
cmd_bind, "Bind app key to a model"},
|
|
{"mod-appidx-get", "<ele_addr> <model 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)> <mod id> "
|
|
"[cid]",
|
|
cmd_pub_set, "\n\t\t\t\t\t\t Set publication"},
|
|
{"pub-get", "<ele_addr> <model>", 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"},
|
|
{"relay-set", "<relay> <rexmt count> <rexmt steps>",
|
|
cmd_relay_set,
|
|
"Set relay"},
|
|
{"relay-get", NULL, cmd_relay_get,
|
|
"Get relay"},
|
|
{"hb-pub-set", "<pub_addr> <count> <period> <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"},
|
|
{"sub-add", "<ele_addr> <sub_addr> <model id>",
|
|
cmd_sub_add, "Add subscription"},
|
|
{"sub-get", "<ele_addr> <model id>",
|
|
cmd_sub_get, "Get subscription"},
|
|
{} },
|
|
};
|
|
|
|
void config_client_get_composition(uint32_t dst)
|
|
{
|
|
uint32_t tmp = target;
|
|
|
|
target = dst;
|
|
cmd_composition_get(0, NULL);
|
|
target = tmp;
|
|
}
|
|
|
|
static struct mesh_model_ops client_cbs = {
|
|
client_msg_recvd,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
bool config_client_init(void)
|
|
{
|
|
if (!node_local_model_register(PRIMARY_ELEMENT_IDX,
|
|
CONFIG_CLIENT_MODEL_ID,
|
|
&client_cbs, NULL))
|
|
return false;
|
|
|
|
bt_shell_add_submenu(&cfg_menu);
|
|
|
|
return true;
|
|
}
|