| /* |
| * 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; |
| } |