370 lines
11 KiB
C
370 lines
11 KiB
C
/*
|
|
* Copyright (c) 2024 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/bluetooth/mesh.h>
|
|
#include "access.h"
|
|
#include "foundation.h"
|
|
#include "msg.h"
|
|
|
|
#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(bt_mesh_brg_cfg_cli);
|
|
|
|
static int32_t msg_timeout;
|
|
|
|
static struct bt_mesh_brg_cfg_cli *cli;
|
|
|
|
static int bridge_status(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
enum bt_mesh_brg_cfg_state status = (enum bt_mesh_brg_cfg_state)net_buf_simple_pull_u8(buf);
|
|
enum bt_mesh_brg_cfg_state *rsp;
|
|
|
|
if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_SUBNET_BRIDGE_STATUS, ctx->addr,
|
|
(void **)&rsp)) {
|
|
*rsp = status;
|
|
bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
|
|
}
|
|
|
|
if (cli->cb && cli->cb->bridge_status) {
|
|
cli->cb->bridge_status(cli, ctx->addr, status);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int table_status(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
struct bt_mesh_brg_cfg_table_status table_status;
|
|
struct bt_mesh_brg_cfg_table_status *rsp;
|
|
|
|
table_status.status = net_buf_simple_pull_u8(buf);
|
|
table_status.entry.directions = net_buf_simple_pull_u8(buf);
|
|
key_idx_unpack_pair(buf, &table_status.entry.net_idx1, &table_status.entry.net_idx2);
|
|
table_status.entry.addr1 = net_buf_simple_pull_le16(buf);
|
|
table_status.entry.addr2 = net_buf_simple_pull_le16(buf);
|
|
|
|
if (!(table_status.entry.addr1 == BT_MESH_ADDR_UNASSIGNED ||
|
|
BT_MESH_ADDR_IS_UNICAST(table_status.entry.addr1))) {
|
|
LOG_ERR("addr1 shall be a unicast address or unassigned.");
|
|
return -EINVAL;
|
|
} else if (table_status.entry.addr2 == BT_MESH_ADDR_ALL_NODES) {
|
|
LOG_ERR("addr2 shall not be the all-nodes fixed group address.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_BRIDGING_TABLE_STATUS, ctx->addr,
|
|
(void **)&rsp)) {
|
|
*rsp = table_status;
|
|
bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
|
|
}
|
|
|
|
if (cli->cb && cli->cb->table_status) {
|
|
cli->cb->table_status(cli, ctx->addr, &table_status);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int subnets_list(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
struct bt_mesh_brg_cfg_subnets_list subnets_list;
|
|
struct bt_mesh_brg_cfg_subnets_list *rsp;
|
|
uint16_t net_idx_filter;
|
|
|
|
net_idx_filter = net_buf_simple_pull_le16(buf);
|
|
subnets_list.net_idx_filter.filter = net_idx_filter & BIT_MASK(2);
|
|
subnets_list.net_idx_filter.net_idx = (net_idx_filter >> 4) & BIT_MASK(12);
|
|
subnets_list.start_idx = net_buf_simple_pull_u8(buf);
|
|
|
|
if (buf->len && !(buf->len % 3)) {
|
|
subnets_list.list = buf;
|
|
} else {
|
|
subnets_list.list = NULL;
|
|
}
|
|
|
|
if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_BRIDGED_SUBNETS_LIST, ctx->addr,
|
|
(void **)&rsp)) {
|
|
rsp->net_idx_filter = subnets_list.net_idx_filter;
|
|
rsp->start_idx = subnets_list.start_idx;
|
|
|
|
if (rsp->list) {
|
|
size_t to_copy;
|
|
|
|
to_copy = MIN(net_buf_simple_tailroom(rsp->list), buf->len);
|
|
net_buf_simple_add_mem(rsp->list, buf->data, to_copy);
|
|
}
|
|
|
|
bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
|
|
}
|
|
|
|
if (cli->cb && cli->cb->subnets_list) {
|
|
cli->cb->subnets_list(cli, ctx->addr, &subnets_list);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int table_list(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
struct bt_mesh_brg_cfg_table_list table_list;
|
|
struct bt_mesh_brg_cfg_table_list *rsp;
|
|
|
|
table_list.status = net_buf_simple_pull_u8(buf);
|
|
key_idx_unpack_pair(buf, &table_list.net_idx1, &table_list.net_idx2);
|
|
table_list.start_idx = net_buf_simple_pull_le16(buf);
|
|
|
|
if ((table_list.status == STATUS_SUCCESS) && buf->len && !(buf->len % 5)) {
|
|
table_list.list = buf;
|
|
} else {
|
|
table_list.list = NULL;
|
|
}
|
|
|
|
if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_BRIDGING_TABLE_LIST, ctx->addr,
|
|
(void **)&rsp)) {
|
|
rsp->status = table_list.status;
|
|
rsp->net_idx1 = table_list.net_idx1;
|
|
rsp->net_idx2 = table_list.net_idx2;
|
|
rsp->start_idx = table_list.start_idx;
|
|
|
|
if (rsp->list) {
|
|
size_t to_copy;
|
|
|
|
to_copy = MIN(net_buf_simple_tailroom(rsp->list), (buf->len / 5) * 5);
|
|
net_buf_simple_add_mem(rsp->list, buf->data, to_copy);
|
|
}
|
|
|
|
bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
|
|
}
|
|
|
|
if (cli->cb && cli->cb->table_list) {
|
|
cli->cb->table_list(cli, ctx->addr, &table_list);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int table_size_status(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
uint16_t size = net_buf_simple_pull_le16(buf);
|
|
uint16_t *rsp;
|
|
|
|
if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_BRIDGING_TABLE_SIZE_STATUS, ctx->addr,
|
|
(void **)&rsp)) {
|
|
*rsp = size;
|
|
bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
|
|
}
|
|
|
|
if (cli->cb && cli->cb->table_size_status) {
|
|
cli->cb->table_size_status(cli, ctx->addr, size);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const struct bt_mesh_model_op _bt_mesh_brg_cfg_cli_op[] = {
|
|
{OP_SUBNET_BRIDGE_STATUS, BT_MESH_LEN_EXACT(1), bridge_status},
|
|
{OP_BRIDGING_TABLE_STATUS, BT_MESH_LEN_EXACT(9), table_status},
|
|
{OP_BRIDGED_SUBNETS_LIST, BT_MESH_LEN_MIN(3), subnets_list},
|
|
{OP_BRIDGING_TABLE_LIST, BT_MESH_LEN_MIN(6), table_list},
|
|
{OP_BRIDGING_TABLE_SIZE_STATUS, BT_MESH_LEN_EXACT(2), table_size_status},
|
|
BT_MESH_MODEL_OP_END,
|
|
};
|
|
|
|
static int brg_cfg_cli_init(const struct bt_mesh_model *model)
|
|
{
|
|
if (!bt_mesh_model_in_primary(model)) {
|
|
LOG_ERR("Bridge Configuration Client only allowed in primary element");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!model->rt->user_data) {
|
|
LOG_ERR("No Bridge Configuration Client context provided");
|
|
return -EINVAL;
|
|
}
|
|
|
|
cli = model->rt->user_data;
|
|
cli->model = model;
|
|
msg_timeout = CONFIG_BT_MESH_BRG_CFG_CLI_TIMEOUT;
|
|
|
|
model->keys[0] = BT_MESH_KEY_DEV_ANY;
|
|
model->rt->flags |= BT_MESH_MOD_DEVKEY_ONLY;
|
|
|
|
bt_mesh_msg_ack_ctx_init(&cli->ack_ctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
const struct bt_mesh_model_cb _bt_mesh_brg_cfg_cli_cb = {
|
|
.init = brg_cfg_cli_init,
|
|
};
|
|
|
|
int bt_mesh_brg_cfg_cli_get(uint16_t net_idx, uint16_t addr, enum bt_mesh_brg_cfg_state *status)
|
|
{
|
|
BT_MESH_MODEL_BUF_DEFINE(msg, OP_SUBNET_BRIDGE_GET, 0);
|
|
struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
|
|
const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
|
|
.ack = &cli->ack_ctx,
|
|
.op = OP_SUBNET_BRIDGE_STATUS,
|
|
.user_data = status,
|
|
.timeout = msg_timeout,
|
|
};
|
|
|
|
bt_mesh_model_msg_init(&msg, OP_SUBNET_BRIDGE_GET);
|
|
|
|
return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !status ? NULL : &rsp_ctx);
|
|
}
|
|
|
|
int bt_mesh_brg_cfg_cli_set(uint16_t net_idx, uint16_t addr, enum bt_mesh_brg_cfg_state val,
|
|
enum bt_mesh_brg_cfg_state *status)
|
|
{
|
|
BT_MESH_MODEL_BUF_DEFINE(msg, OP_SUBNET_BRIDGE_SET, 1);
|
|
struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
|
|
const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
|
|
.ack = &cli->ack_ctx,
|
|
.op = OP_SUBNET_BRIDGE_STATUS,
|
|
.user_data = status,
|
|
.timeout = msg_timeout,
|
|
};
|
|
|
|
bt_mesh_model_msg_init(&msg, OP_SUBNET_BRIDGE_SET);
|
|
net_buf_simple_add_u8(&msg, (uint8_t)val);
|
|
|
|
return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !status ? NULL : &rsp_ctx);
|
|
}
|
|
|
|
int bt_mesh_brg_cfg_cli_table_size_get(uint16_t net_idx, uint16_t addr, uint16_t *size)
|
|
{
|
|
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_SIZE_GET, 0);
|
|
struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
|
|
const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
|
|
.ack = &cli->ack_ctx,
|
|
.op = OP_BRIDGING_TABLE_SIZE_STATUS,
|
|
.user_data = size,
|
|
.timeout = msg_timeout,
|
|
};
|
|
|
|
bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_SIZE_GET);
|
|
|
|
return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !size ? NULL : &rsp_ctx);
|
|
}
|
|
|
|
int bt_mesh_brg_cfg_cli_table_add(uint16_t net_idx, uint16_t addr,
|
|
struct bt_mesh_brg_cfg_table_entry *entry,
|
|
struct bt_mesh_brg_cfg_table_status *rsp)
|
|
{
|
|
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_ADD, 8);
|
|
struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
|
|
const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
|
|
.ack = &cli->ack_ctx,
|
|
.op = OP_BRIDGING_TABLE_STATUS,
|
|
.user_data = rsp,
|
|
.timeout = msg_timeout,
|
|
};
|
|
|
|
if (entry->addr1 == entry->addr2) {
|
|
LOG_ERR("addr1 and addr2 shall have different values.");
|
|
return -EINVAL;
|
|
} else if (!BT_MESH_ADDR_IS_UNICAST(entry->addr1)) {
|
|
LOG_ERR("addr1 shall be a unicast address.");
|
|
return -EINVAL;
|
|
} else if (entry->directions == 0x01 && (entry->addr2 == BT_MESH_ADDR_UNASSIGNED ||
|
|
entry->addr2 == BT_MESH_ADDR_ALL_NODES)) {
|
|
LOG_ERR("For direction 0x01: addr2 shall not be unassigned or the all-nodes fixed "
|
|
"group address.");
|
|
return -EINVAL;
|
|
} else if (entry->directions == 0x02 && !BT_MESH_ADDR_IS_UNICAST(entry->addr2)) {
|
|
LOG_ERR("For direction 0x02: addr2 shall be a unicast address.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_ADD);
|
|
net_buf_simple_add_u8(&msg, entry->directions);
|
|
key_idx_pack_pair(&msg, entry->net_idx1, entry->net_idx2);
|
|
net_buf_simple_add_le16(&msg, entry->addr1);
|
|
net_buf_simple_add_le16(&msg, entry->addr2);
|
|
|
|
return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !rsp ? NULL : &rsp_ctx);
|
|
}
|
|
|
|
int bt_mesh_brg_cfg_cli_table_remove(uint16_t net_idx, uint16_t addr, uint16_t net_idx1,
|
|
uint16_t net_idx2, uint16_t addr1, uint16_t addr2,
|
|
struct bt_mesh_brg_cfg_table_status *rsp)
|
|
{
|
|
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_REMOVE, 7);
|
|
struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
|
|
const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
|
|
.ack = &cli->ack_ctx,
|
|
.op = OP_BRIDGING_TABLE_STATUS,
|
|
.user_data = rsp,
|
|
.timeout = msg_timeout,
|
|
};
|
|
|
|
if (!(addr1 == BT_MESH_ADDR_UNASSIGNED || BT_MESH_ADDR_IS_UNICAST(addr1))) {
|
|
LOG_ERR("addr1 shall be a unicast address or unassigned.");
|
|
return -EINVAL;
|
|
} else if (addr2 == BT_MESH_ADDR_ALL_NODES) {
|
|
LOG_ERR("addr2 shall not be the all-nodes fixed group address.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_REMOVE);
|
|
key_idx_pack_pair(&msg, net_idx1, net_idx2);
|
|
net_buf_simple_add_le16(&msg, addr1);
|
|
net_buf_simple_add_le16(&msg, addr2);
|
|
|
|
return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !rsp ? NULL : &rsp_ctx);
|
|
}
|
|
|
|
int bt_mesh_brg_cfg_cli_subnets_get(uint16_t net_idx, uint16_t addr,
|
|
struct bt_mesh_brg_cfg_filter_netkey filter_net_idx,
|
|
uint8_t start_idx, struct bt_mesh_brg_cfg_subnets_list *rsp)
|
|
{
|
|
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGED_SUBNETS_GET, 3);
|
|
struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
|
|
const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
|
|
.ack = &cli->ack_ctx,
|
|
.op = OP_BRIDGED_SUBNETS_LIST,
|
|
.user_data = rsp,
|
|
.timeout = msg_timeout,
|
|
};
|
|
|
|
bt_mesh_model_msg_init(&msg, OP_BRIDGED_SUBNETS_GET);
|
|
net_buf_simple_add_le16(&msg, (filter_net_idx.filter | filter_net_idx.net_idx << 4));
|
|
net_buf_simple_add_u8(&msg, start_idx);
|
|
|
|
return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !rsp ? NULL : &rsp_ctx);
|
|
}
|
|
|
|
int bt_mesh_brg_cfg_cli_table_get(uint16_t net_idx, uint16_t addr, uint16_t net_idx1,
|
|
uint16_t net_idx2, uint16_t start_idx,
|
|
struct bt_mesh_brg_cfg_table_list *rsp)
|
|
{
|
|
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_GET, 5);
|
|
struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
|
|
const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
|
|
.ack = &cli->ack_ctx,
|
|
.op = OP_BRIDGING_TABLE_LIST,
|
|
.user_data = rsp,
|
|
.timeout = msg_timeout,
|
|
};
|
|
|
|
bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_GET);
|
|
key_idx_pack_pair(&msg, net_idx1, net_idx2);
|
|
net_buf_simple_add_le16(&msg, start_idx);
|
|
|
|
return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !rsp ? NULL : &rsp_ctx);
|
|
}
|
|
|
|
int32_t bt_mesh_brg_cfg_cli_timeout_get(void)
|
|
{
|
|
return msg_timeout;
|
|
}
|
|
|
|
void bt_mesh_brg_cfg_cli_timeout_set(int32_t timeout)
|
|
{
|
|
msg_timeout = timeout;
|
|
}
|