zephyr/subsys/bluetooth/mesh/cfg_srv.c

2559 lines
64 KiB
C
Raw Normal View History

/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <zephyr/types.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/mesh.h>
#include "common/bt_str.h"
#include "testing.h"
#include "mesh.h"
#include "net.h"
#include "rpl.h"
#include "lpn.h"
#include "transport.h"
#include "heartbeat.h"
#include "crypto.h"
#include "access.h"
#include "beacon.h"
#include "proxy.h"
#include "foundation.h"
#include "friend.h"
#include "settings.h"
#include "cfg.h"
#include "va.h"
#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_cfg_srv);
static void node_reset_pending_handler(struct k_work *work)
{
bt_mesh_reset();
}
static K_WORK_DEFINE(node_reset_pending, node_reset_pending_handler);
static int dev_comp_data_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
NET_BUF_SIMPLE_DEFINE(sdu, BT_MESH_TX_SDU_MAX);
uint8_t page;
int err;
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
page = bt_mesh_comp_parse_page(buf);
LOG_DBG("Preparing Composition data page %d", page);
bt_mesh_model_msg_init(&sdu, OP_DEV_COMP_DATA_STATUS);
net_buf_simple_add_u8(&sdu, page);
if (atomic_test_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY) && page < 128) {
sdu.size -= BT_MESH_MIC_SHORT;
err = bt_mesh_comp_read(&sdu, page);
sdu.size += BT_MESH_MIC_SHORT;
} else {
err = bt_mesh_comp_data_get_page(&sdu, page, 0);
}
if (err) {
LOG_ERR("Failed to get CDP%d, err:%d", page, err);
return err;
}
if (bt_mesh_model_send(model, ctx, &sdu, NULL, NULL)) {
LOG_ERR("Unable to send Device Composition Status response");
}
return err;
}
static const struct bt_mesh_model *get_model(const struct bt_mesh_elem *elem,
struct net_buf_simple *buf, bool *vnd)
{
if (buf->len < 4) {
uint16_t id;
id = net_buf_simple_pull_le16(buf);
LOG_DBG("ID 0x%04x addr 0x%04x", id, elem->rt->addr);
*vnd = false;
return bt_mesh_model_find(elem, id);
} else {
uint16_t company, id;
company = net_buf_simple_pull_le16(buf);
id = net_buf_simple_pull_le16(buf);
LOG_DBG("Company 0x%04x ID 0x%04x addr 0x%04x", company, id, elem->rt->addr);
*vnd = true;
return bt_mesh_model_find_vnd(elem, company, id);
}
}
static uint8_t _mod_pub_set(const struct bt_mesh_model *model, uint16_t pub_addr,
const uint8_t *uuid, uint16_t app_idx, uint8_t cred_flag,
uint8_t ttl, uint8_t period, uint8_t retransmit, bool store)
{
if (!model->pub) {
return STATUS_NVAL_PUB_PARAM;
}
if (!IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) && cred_flag) {
return STATUS_FEAT_NOT_SUPP;
}
if (!model->pub->update && period) {
return STATUS_NVAL_PUB_PARAM;
}
if (pub_addr == BT_MESH_ADDR_UNASSIGNED) {
if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
return STATUS_SUCCESS;
}
model->pub->addr = BT_MESH_ADDR_UNASSIGNED;
model->pub->key = 0U;
model->pub->cred = 0U;
model->pub->ttl = 0U;
model->pub->period = 0U;
model->pub->retransmit = 0U;
model->pub->count = 0U;
model->pub->uuid = NULL;
if (model->pub->update) {
/* If this fails, the timer will check pub->addr and
* exit without transmitting.
*/
(void)k_work_cancel_delayable(&model->pub->timer);
}
if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
bt_mesh_model_pub_store(model);
}
return STATUS_SUCCESS;
}
if (!bt_mesh_app_key_exists(app_idx) || !bt_mesh_model_has_key(model, app_idx)) {
return STATUS_INVALID_APPKEY;
}
#if CONFIG_BT_MESH_LABEL_COUNT > 0
if (BT_MESH_ADDR_IS_VIRTUAL(model->pub->addr)) {
(void)bt_mesh_va_del(model->pub->uuid);
}
#endif
model->pub->addr = pub_addr;
model->pub->key = app_idx;
model->pub->cred = cred_flag;
model->pub->ttl = ttl;
model->pub->period = period;
model->pub->retransmit = retransmit;
model->pub->uuid = uuid;
if (model->pub->update) {
int32_t period_ms;
period_ms = bt_mesh_model_pub_period_get(model);
LOG_DBG("period %u ms", period_ms);
if (period_ms > 0) {
k_work_reschedule(&model->pub->timer,
K_MSEC(period_ms));
} else {
/* If this fails, publication will stop after the
* ongoing set of retransmits.
*/
(void)k_work_cancel_delayable(&model->pub->timer);
}
}
if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
bt_mesh_model_pub_store(model);
}
return STATUS_SUCCESS;
}
static uint8_t mod_bind(const struct bt_mesh_model *model, uint16_t key_idx)
{
int i;
LOG_DBG("model %p key_idx 0x%03x", model, key_idx);
if (!bt_mesh_app_key_exists(key_idx)) {
return STATUS_INVALID_APPKEY;
}
for (i = 0; i < model->keys_cnt; i++) {
LOG_DBG("model %p id 0x%04x i %d key 0x%03x", model, model->id, i, model->keys[i]);
/* Treat existing binding as success */
if (model->keys[i] == key_idx) {
return STATUS_SUCCESS;
}
}
for (i = 0; i < model->keys_cnt; i++) {
if (model->keys[i] == BT_MESH_KEY_UNUSED) {
model->keys[i] = key_idx;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_model_bind_store(model);
}
return STATUS_SUCCESS;
}
}
return STATUS_INSUFF_RESOURCES;
}
static uint8_t mod_unbind(const struct bt_mesh_model *model, uint16_t key_idx, bool store)
{
int i;
LOG_DBG("model %p key_idx 0x%03x store %u", model, key_idx, store);
if (!bt_mesh_app_key_exists(key_idx)) {
return STATUS_INVALID_APPKEY;
}
for (i = 0; i < model->keys_cnt; i++) {
if (model->keys[i] != key_idx) {
continue;
}
model->keys[i] = BT_MESH_KEY_UNUSED;
if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
bt_mesh_model_bind_store(model);
}
if (model->pub && model->pub->key == key_idx) {
_mod_pub_set(model, BT_MESH_ADDR_UNASSIGNED, NULL,
0, 0, 0, 0, 0, store);
}
}
return STATUS_SUCCESS;
}
static void key_idx_pack_list(struct net_buf_simple *buf, uint16_t *arr, size_t cnt)
{
uint16_t *idx = NULL;
for (int i = 0; i < cnt; i++) {
if (arr[i] != BT_MESH_KEY_UNUSED) {
if (!idx) {
idx = &arr[i];
continue;
}
key_idx_pack_pair(buf, *idx, arr[i]);
idx = NULL;
}
}
if (idx) {
net_buf_simple_add_le16(buf, *idx);
}
}
static int send_app_key_status(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
uint8_t status,
uint16_t app_idx, uint16_t net_idx)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_APP_KEY_STATUS, 4);
bt_mesh_model_msg_init(&msg, OP_APP_KEY_STATUS);
net_buf_simple_add_u8(&msg, status);
key_idx_pack_pair(&msg, net_idx, app_idx);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send App Key Status response");
}
return 0;
}
static int app_key_add(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint16_t key_net_idx, key_app_idx;
uint8_t status;
key_idx_unpack_pair(buf, &key_net_idx, &key_app_idx);
LOG_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx);
status = bt_mesh_app_key_add(key_app_idx, key_net_idx, buf->data);
return send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
}
static int app_key_update(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint16_t key_net_idx, key_app_idx;
uint8_t status;
key_idx_unpack_pair(buf, &key_net_idx, &key_app_idx);
LOG_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx);
status = bt_mesh_app_key_update(key_app_idx, key_net_idx, buf->data);
LOG_DBG("status 0x%02x", status);
return send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
}
static void mod_app_key_del(const struct bt_mesh_model *mod,
const struct bt_mesh_elem *elem, bool vnd, bool primary,
void *user_data)
{
uint16_t *app_idx = user_data;
mod_unbind(mod, *app_idx, true);
}
static void app_key_evt(uint16_t app_idx, uint16_t net_idx,
enum bt_mesh_key_evt evt)
{
if (evt == BT_MESH_KEY_DELETED) {
bt_mesh_model_foreach(&mod_app_key_del, &app_idx);
}
}
BT_MESH_APP_KEY_CB_DEFINE(app_key_evt);
static int app_key_del(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint16_t key_net_idx, key_app_idx;
uint8_t status;
key_idx_unpack_pair(buf, &key_net_idx, &key_app_idx);
LOG_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx);
status = bt_mesh_app_key_del(key_app_idx, key_net_idx);
return send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
}
/* Index list length: 3 bytes for every pair and 2 bytes for an odd idx */
#define IDX_LEN(num) (((num) / 2) * 3 + ((num) % 2) * 2)
static int app_key_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_APP_KEY_LIST,
3 + IDX_LEN(CONFIG_BT_MESH_APP_KEY_COUNT));
uint16_t app_idx[CONFIG_BT_MESH_APP_KEY_COUNT];
uint16_t get_idx;
uint8_t status;
ssize_t count;
get_idx = net_buf_simple_pull_le16(buf);
if (get_idx > 0xfff) {
LOG_ERR("Invalid NetKeyIndex 0x%04x", get_idx);
return -EINVAL;
}
LOG_DBG("idx 0x%04x", get_idx);
bt_mesh_model_msg_init(&msg, OP_APP_KEY_LIST);
if (!bt_mesh_subnet_exists(get_idx)) {
status = STATUS_INVALID_NETKEY;
} else {
status = STATUS_SUCCESS;
}
net_buf_simple_add_u8(&msg, status);
net_buf_simple_add_le16(&msg, get_idx);
if (status != STATUS_SUCCESS) {
goto send_status;
}
count = bt_mesh_app_keys_get(get_idx, app_idx, ARRAY_SIZE(app_idx), 0);
if (count < 0 || count > ARRAY_SIZE(app_idx)) {
count = ARRAY_SIZE(app_idx);
}
key_idx_pack_list(&msg, app_idx, count);
send_status:
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send AppKey List");
}
return 0;
}
static int beacon_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BEACON_STATUS, 1);
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
bt_mesh_model_msg_init(&msg, OP_BEACON_STATUS);
net_buf_simple_add_u8(&msg, bt_mesh_beacon_enabled());
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Config Beacon Status response");
}
return 0;
}
static int beacon_set(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BEACON_STATUS, 1);
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
if (buf->data[0] != 0x00 && buf->data[0] != 0x01) {
LOG_WRN("Invalid Config Beacon value 0x%02x", buf->data[0]);
return -EINVAL;
}
bt_mesh_beacon_set(buf->data[0]);
bt_mesh_model_msg_init(&msg, OP_BEACON_STATUS);
net_buf_simple_add_u8(&msg, buf->data[0]);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Config Beacon Status response");
}
return 0;
}
static int default_ttl_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_DEFAULT_TTL_STATUS, 1);
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
bt_mesh_model_msg_init(&msg, OP_DEFAULT_TTL_STATUS);
net_buf_simple_add_u8(&msg, bt_mesh_default_ttl_get());
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Default TTL Status response");
}
return 0;
}
static int default_ttl_set(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_DEFAULT_TTL_STATUS, 1);
int err;
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
err = bt_mesh_default_ttl_set(buf->data[0]);
if (err) {
LOG_WRN("Prohibited Default TTL value 0x%02x", buf->data[0]);
return err;
}
bt_mesh_model_msg_init(&msg, OP_DEFAULT_TTL_STATUS);
net_buf_simple_add_u8(&msg, buf->data[0]);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Default TTL Status response");
}
return 0;
}
static int send_gatt_proxy_status(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_GATT_PROXY_STATUS, 1);
bt_mesh_model_msg_init(&msg, OP_GATT_PROXY_STATUS);
net_buf_simple_add_u8(&msg, bt_mesh_gatt_proxy_get());
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send GATT Proxy Status");
}
return 0;
}
static int gatt_proxy_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
return send_gatt_proxy_status(model, ctx);
}
static int gatt_proxy_set(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
if (buf->data[0] != 0x00 && buf->data[0] != 0x01) {
LOG_WRN("Invalid GATT Proxy value 0x%02x", buf->data[0]);
return -EINVAL;
}
(void)bt_mesh_gatt_proxy_set(buf->data[0]);
/** 4.2.46.1: If the value of the Node Identity state of the node for any subnet is 0x01,
* then the value of the Private Node Identity state shall be Disable (0x00).
*/
if (IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS) && buf->data[0]) {
(void)bt_mesh_priv_gatt_proxy_set(BT_MESH_FEATURE_DISABLED);
}
return send_gatt_proxy_status(model, ctx);
}
static int net_transmit_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_NET_TRANSMIT_STATUS, 1);
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
bt_mesh_model_msg_init(&msg, OP_NET_TRANSMIT_STATUS);
net_buf_simple_add_u8(&msg, bt_mesh_net_transmit_get());
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Config Network Transmit Status");
}
return 0;
}
static int net_transmit_set(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_NET_TRANSMIT_STATUS, 1);
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
LOG_DBG("Transmit 0x%02x (count %u interval %ums)", buf->data[0],
BT_MESH_TRANSMIT_COUNT(buf->data[0]), BT_MESH_TRANSMIT_INT(buf->data[0]));
bt_mesh_net_transmit_set(buf->data[0]);
bt_mesh_model_msg_init(&msg, OP_NET_TRANSMIT_STATUS);
net_buf_simple_add_u8(&msg, buf->data[0]);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Network Transmit Status");
}
return 0;
}
static int relay_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_RELAY_STATUS, 2);
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
bt_mesh_model_msg_init(&msg, OP_RELAY_STATUS);
net_buf_simple_add_u8(&msg, bt_mesh_relay_get());
net_buf_simple_add_u8(&msg, bt_mesh_relay_retransmit_get());
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Config Relay Status response");
}
return 0;
}
static int relay_set(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_RELAY_STATUS, 2);
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
if (buf->data[0] != 0x00 && buf->data[0] != 0x01) {
LOG_WRN("Invalid Relay value 0x%02x", buf->data[0]);
return -EINVAL;
}
(void)bt_mesh_relay_set(buf->data[0], buf->data[1]);
bt_mesh_model_msg_init(&msg, OP_RELAY_STATUS);
net_buf_simple_add_u8(&msg, bt_mesh_relay_get());
net_buf_simple_add_u8(&msg, bt_mesh_relay_retransmit_get());
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Relay Status response");
}
return 0;
}
static int send_mod_pub_status(const struct bt_mesh_model *cfg_mod,
struct bt_mesh_msg_ctx *ctx, uint16_t elem_addr,
uint16_t pub_addr, bool vnd,
const struct bt_mesh_model *mod, uint8_t status,
uint8_t *mod_id)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_MOD_PUB_STATUS, 14);
bt_mesh_model_msg_init(&msg, OP_MOD_PUB_STATUS);
net_buf_simple_add_u8(&msg, status);
net_buf_simple_add_le16(&msg, elem_addr);
if (status != STATUS_SUCCESS) {
(void)memset(net_buf_simple_add(&msg, 7), 0, 7);
} else {
uint16_t idx_cred;
net_buf_simple_add_le16(&msg, pub_addr);
idx_cred = mod->pub->key | (uint16_t)mod->pub->cred << 12;
net_buf_simple_add_le16(&msg, idx_cred);
net_buf_simple_add_u8(&msg, mod->pub->ttl);
net_buf_simple_add_u8(&msg, mod->pub->period);
net_buf_simple_add_u8(&msg, mod->pub->retransmit);
}
if (vnd) {
memcpy(net_buf_simple_add(&msg, 4), mod_id, 4);
} else {
memcpy(net_buf_simple_add(&msg, 2), mod_id, 2);
}
if (bt_mesh_model_send(cfg_mod, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Model Publication Status");
}
return 0;
}
static int mod_pub_get(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint16_t elem_addr, pub_addr = 0U;
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
uint8_t *mod_id, status;
bool vnd;
if ((buf->len != 4U) && (buf->len != 6U)) {
LOG_ERR("The message size for the application opcode is incorrect.");
return -EMSGSIZE;
}
elem_addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
mod_id = buf->data;
LOG_DBG("elem_addr 0x%04x", elem_addr);
elem = bt_mesh_elem_find(elem_addr);
if (!elem) {
mod = NULL;
vnd = (buf->len == 4U);
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
mod = get_model(elem, buf, &vnd);
if (!mod) {
status = STATUS_INVALID_MODEL;
goto send_status;
}
if (!mod->pub) {
status = STATUS_NVAL_PUB_PARAM;
goto send_status;
}
pub_addr = mod->pub->addr;
status = STATUS_SUCCESS;
send_status:
return send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod,
status, mod_id);
}
static int mod_pub_set(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint8_t retransmit, status, pub_ttl, pub_period, cred_flag;
uint16_t elem_addr, pub_addr, pub_app_idx;
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
uint8_t *mod_id;
bool vnd;
if ((buf->len != 11U) && (buf->len != 13U)) {
LOG_ERR("The message size for the application opcode is incorrect.");
return -EMSGSIZE;
}
elem_addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
pub_addr = net_buf_simple_pull_le16(buf);
pub_app_idx = net_buf_simple_pull_le16(buf);
cred_flag = ((pub_app_idx >> 12) & BIT_MASK(1));
pub_app_idx &= BIT_MASK(12);
pub_ttl = net_buf_simple_pull_u8(buf);
if (pub_ttl > BT_MESH_TTL_MAX && pub_ttl != BT_MESH_TTL_DEFAULT) {
LOG_ERR("Invalid TTL value 0x%02x", pub_ttl);
return -EINVAL;
}
pub_period = net_buf_simple_pull_u8(buf);
retransmit = net_buf_simple_pull_u8(buf);
mod_id = buf->data;
LOG_DBG("elem_addr 0x%04x pub_addr 0x%04x cred_flag %u", elem_addr, pub_addr, cred_flag);
LOG_DBG("pub_app_idx 0x%03x, pub_ttl %u pub_period 0x%02x", pub_app_idx, pub_ttl,
pub_period);
LOG_DBG("retransmit 0x%02x (count %u interval %ums)", retransmit,
BT_MESH_PUB_TRANSMIT_COUNT(retransmit), BT_MESH_PUB_TRANSMIT_INT(retransmit));
elem = bt_mesh_elem_find(elem_addr);
if (!elem) {
mod = NULL;
vnd = (buf->len == 4U);
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
mod = get_model(elem, buf, &vnd);
if (!mod) {
status = STATUS_INVALID_MODEL;
goto send_status;
}
status = _mod_pub_set(mod, pub_addr, NULL, pub_app_idx, cred_flag, pub_ttl,
pub_period, retransmit, true);
send_status:
return send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod,
status, mod_id);
}
static size_t mod_sub_list_clear(const struct bt_mesh_model *mod)
{
size_t clear_count;
int i;
for (i = 0, clear_count = 0; i < mod->groups_cnt; i++) {
/* mod->groups contains both, group and virtual addrs. Virtual addrs deletion will
* be handled separately.
*/
if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) {
mod->groups[i] = BT_MESH_ADDR_UNASSIGNED;
clear_count++;
}
}
#if CONFIG_BT_MESH_LABEL_COUNT > 0
/* Unref stored labels related to this model */
for (i = 0; i < CONFIG_BT_MESH_LABEL_COUNT; i++) {
if (mod->uuids[i] == NULL) {
continue;
}
(void)bt_mesh_va_del(mod->uuids[i]);
mod->uuids[i] = NULL;
/* No need to increment `clear_count` as `groups` contains virtual addresses. */
}
#endif
return clear_count;
}
static int mod_pub_va_set(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
const struct bt_mesh_va *va;
uint8_t retransmit, status, pub_ttl, pub_period, cred_flag;
uint16_t elem_addr, pub_app_idx;
uint16_t pub_addr = 0U;
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
const uint8_t *uuid;
uint8_t *mod_id;
bool vnd;
if ((buf->len != 25U) && (buf->len != 27U)) {
LOG_ERR("The message size for the application opcode is incorrect.");
return -EMSGSIZE;
}
elem_addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
uuid = net_buf_simple_pull_mem(buf, 16);
pub_app_idx = net_buf_simple_pull_le16(buf);
cred_flag = ((pub_app_idx >> 12) & BIT_MASK(1));
pub_app_idx &= BIT_MASK(12);
pub_ttl = net_buf_simple_pull_u8(buf);
if (pub_ttl > BT_MESH_TTL_MAX && pub_ttl != BT_MESH_TTL_DEFAULT) {
LOG_ERR("Invalid TTL value 0x%02x", pub_ttl);
return -EINVAL;
}
pub_period = net_buf_simple_pull_u8(buf);
retransmit = net_buf_simple_pull_u8(buf);
mod_id = buf->data;
LOG_DBG("elem_addr 0x%04x cred_flag %u", elem_addr, cred_flag);
LOG_DBG("pub_app_idx 0x%03x, pub_ttl %u pub_period 0x%02x", pub_app_idx, pub_ttl,
pub_period);
LOG_DBG("retransmit 0x%02x (count %u interval %ums)", retransmit,
BT_MESH_PUB_TRANSMIT_COUNT(retransmit), BT_MESH_PUB_TRANSMIT_INT(retransmit));
elem = bt_mesh_elem_find(elem_addr);
if (!elem) {
mod = NULL;
vnd = (buf->len == 4U);
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
mod = get_model(elem, buf, &vnd);
if (!mod) {
status = STATUS_INVALID_MODEL;
goto send_status;
}
status = bt_mesh_va_add(uuid, &va);
if (status != STATUS_SUCCESS) {
goto send_status;
}
pub_addr = va->addr;
status = _mod_pub_set(mod, pub_addr, va->uuid, pub_app_idx, cred_flag, pub_ttl,
pub_period, retransmit, true);
if (status != STATUS_SUCCESS) {
(void)bt_mesh_va_del(va->uuid);
}
send_status:
return send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod,
status, mod_id);
}
static int send_mod_sub_status(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx, uint8_t status,
uint16_t elem_addr, uint16_t sub_addr, uint8_t *mod_id,
bool vnd)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_MOD_SUB_STATUS, 9);
LOG_DBG("status 0x%02x elem_addr 0x%04x sub_addr 0x%04x", status, elem_addr, sub_addr);
bt_mesh_model_msg_init(&msg, OP_MOD_SUB_STATUS);
net_buf_simple_add_u8(&msg, status);
net_buf_simple_add_le16(&msg, elem_addr);
net_buf_simple_add_le16(&msg, sub_addr);
if (vnd) {
memcpy(net_buf_simple_add(&msg, 4), mod_id, 4);
} else {
memcpy(net_buf_simple_add(&msg, 2), mod_id, 2);
}
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Model Subscription Status");
}
return 0;
}
static int mod_sub_add(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint16_t elem_addr, sub_addr;
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
uint8_t *mod_id;
uint8_t status;
uint16_t *entry;
bool vnd;
if ((buf->len != 6U) && (buf->len != 8U)) {
LOG_ERR("The message size for the application opcode is incorrect.");
return -EMSGSIZE;
}
elem_addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
sub_addr = net_buf_simple_pull_le16(buf);
LOG_DBG("elem_addr 0x%04x, sub_addr 0x%04x", elem_addr, sub_addr);
mod_id = buf->data;
elem = bt_mesh_elem_find(elem_addr);
if (!elem) {
mod = NULL;
vnd = (buf->len == 4U);
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
mod = get_model(elem, buf, &vnd);
if (!mod) {
status = STATUS_INVALID_MODEL;
goto send_status;
}
if (!BT_MESH_ADDR_IS_GROUP(sub_addr) && !BT_MESH_ADDR_IS_FIXED_GROUP(sub_addr)) {
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
if (bt_mesh_model_find_group(&mod, sub_addr)) {
/* Tried to add existing subscription */
LOG_DBG("found existing subscription");
status = STATUS_SUCCESS;
goto send_status;
}
entry = bt_mesh_model_find_group(&mod, BT_MESH_ADDR_UNASSIGNED);
if (!entry) {
status = STATUS_INSUFF_RESOURCES;
goto send_status;
}
*entry = sub_addr;
status = STATUS_SUCCESS;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_model_sub_store(mod);
}
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
bt_mesh_lpn_group_add(sub_addr);
}
send_status:
return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
mod_id, vnd);
}
static int mod_sub_del(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint16_t elem_addr, sub_addr;
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
uint8_t *mod_id;
uint16_t *match;
uint8_t status;
bool vnd;
if ((buf->len != 6U) && (buf->len != 8U)) {
LOG_ERR("The message size for the application opcode is incorrect.");
return -EMSGSIZE;
}
elem_addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
sub_addr = net_buf_simple_pull_le16(buf);
LOG_DBG("elem_addr 0x%04x sub_addr 0x%04x", elem_addr, sub_addr);
mod_id = buf->data;
elem = bt_mesh_elem_find(elem_addr);
if (!elem) {
mod = NULL;
vnd = (buf->len == 4U);
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
mod = get_model(elem, buf, &vnd);
if (!mod) {
status = STATUS_INVALID_MODEL;
goto send_status;
}
if (!BT_MESH_ADDR_IS_GROUP(sub_addr) && !BT_MESH_ADDR_IS_FIXED_GROUP(sub_addr)) {
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
/* An attempt to remove a non-existing address shall be treated
* as a success.
*/
status = STATUS_SUCCESS;
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
bt_mesh_lpn_group_del(&sub_addr, 1);
}
match = bt_mesh_model_find_group(&mod, sub_addr);
if (match) {
*match = BT_MESH_ADDR_UNASSIGNED;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_model_sub_store(mod);
}
}
send_status:
return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
mod_id, vnd);
}
static enum bt_mesh_walk mod_sub_clear_visitor(const struct bt_mesh_model *mod, void *user_data)
{
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
bt_mesh_lpn_group_del(mod->groups, mod->groups_cnt);
}
mod_sub_list_clear(mod);
return BT_MESH_WALK_CONTINUE;
}
static int mod_sub_overwrite(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint16_t elem_addr, sub_addr;
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
uint8_t *mod_id;
uint8_t status;
bool vnd;
if ((buf->len != 6U) && (buf->len != 8U)) {
LOG_ERR("The message size for the application opcode is incorrect.");
return -EMSGSIZE;
}
elem_addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
sub_addr = net_buf_simple_pull_le16(buf);
LOG_DBG("elem_addr 0x%04x sub_addr 0x%04x", elem_addr, sub_addr);
mod_id = buf->data;
elem = bt_mesh_elem_find(elem_addr);
if (!elem) {
mod = NULL;
vnd = (buf->len == 4U);
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
mod = get_model(elem, buf, &vnd);
if (!mod) {
status = STATUS_INVALID_MODEL;
goto send_status;
}
if (!BT_MESH_ADDR_IS_GROUP(sub_addr) && !BT_MESH_ADDR_IS_FIXED_GROUP(sub_addr)) {
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
if (mod->groups_cnt > 0) {
bt_mesh_model_extensions_walk(mod, mod_sub_clear_visitor, NULL);
mod->groups[0] = sub_addr;
status = STATUS_SUCCESS;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_model_sub_store(mod);
}
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
bt_mesh_lpn_group_add(sub_addr);
}
} else {
status = STATUS_INSUFF_RESOURCES;
}
send_status:
return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
mod_id, vnd);
}
static int mod_sub_del_all(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
uint16_t elem_addr;
uint8_t *mod_id;
uint8_t status;
bool vnd;
if ((buf->len != 4U) && (buf->len != 6U)) {
LOG_ERR("The message size for the application opcode is incorrect.");
return -EMSGSIZE;
}
elem_addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
LOG_DBG("elem_addr 0x%04x", elem_addr);
mod_id = buf->data;
elem = bt_mesh_elem_find(elem_addr);
if (!elem) {
mod = NULL;
vnd = (buf->len == 4U);
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
mod = get_model(elem, buf, &vnd);
if (!mod) {
status = STATUS_INVALID_MODEL;
goto send_status;
}
bt_mesh_model_extensions_walk(mod, mod_sub_clear_visitor, NULL);
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_model_sub_store(mod);
}
status = STATUS_SUCCESS;
send_status:
return send_mod_sub_status(model, ctx, status, elem_addr,
BT_MESH_ADDR_UNASSIGNED, mod_id, vnd);
}
struct mod_sub_list_ctx {
uint16_t elem_idx;
struct net_buf_simple *msg;
};
static enum bt_mesh_walk mod_sub_list_visitor(const struct bt_mesh_model *mod, void *ctx)
{
struct mod_sub_list_ctx *visit = ctx;
int count = 0;
int i;
if (mod->rt->elem_idx != visit->elem_idx) {
return BT_MESH_WALK_CONTINUE;
}
for (i = 0; i < mod->groups_cnt; i++) {
if (mod->groups[i] == BT_MESH_ADDR_UNASSIGNED) {
continue;
}
if (net_buf_simple_tailroom(visit->msg) <
2 + BT_MESH_MIC_SHORT) {
LOG_WRN("No room for all groups");
return BT_MESH_WALK_STOP;
}
net_buf_simple_add_le16(visit->msg, mod->groups[i]);
count++;
}
LOG_DBG("sublist: model %u:%x: %u groups", mod->rt->elem_idx, mod->id, count);
return BT_MESH_WALK_CONTINUE;
}
static int mod_sub_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
NET_BUF_SIMPLE_DEFINE(msg, BT_MESH_TX_SDU_MAX);
struct mod_sub_list_ctx visit_ctx;
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
uint16_t addr, id;
addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
id = net_buf_simple_pull_le16(buf);
LOG_DBG("addr 0x%04x id 0x%04x", addr, id);
bt_mesh_model_msg_init(&msg, OP_MOD_SUB_LIST);
elem = bt_mesh_elem_find(addr);
if (!elem) {
net_buf_simple_add_u8(&msg, STATUS_INVALID_ADDRESS);
net_buf_simple_add_le16(&msg, addr);
net_buf_simple_add_le16(&msg, id);
goto send_list;
}
mod = bt_mesh_model_find(elem, id);
if (!mod) {
net_buf_simple_add_u8(&msg, STATUS_INVALID_MODEL);
net_buf_simple_add_le16(&msg, addr);
net_buf_simple_add_le16(&msg, id);
goto send_list;
}
net_buf_simple_add_u8(&msg, STATUS_SUCCESS);
net_buf_simple_add_le16(&msg, addr);
net_buf_simple_add_le16(&msg, id);
visit_ctx.msg = &msg;
visit_ctx.elem_idx = mod->rt->elem_idx;
bt_mesh_model_extensions_walk(mod, mod_sub_list_visitor, &visit_ctx);
send_list:
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Model Subscription List");
}
return 0;
}
static int mod_sub_get_vnd(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
NET_BUF_SIMPLE_DEFINE(msg, BT_MESH_TX_SDU_MAX);
struct mod_sub_list_ctx visit_ctx;
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
uint16_t company, addr, id;
addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
company = net_buf_simple_pull_le16(buf);
id = net_buf_simple_pull_le16(buf);
LOG_DBG("addr 0x%04x company 0x%04x id 0x%04x", addr, company, id);
bt_mesh_model_msg_init(&msg, OP_MOD_SUB_LIST_VND);
elem = bt_mesh_elem_find(addr);
if (!elem) {
net_buf_simple_add_u8(&msg, STATUS_INVALID_ADDRESS);
net_buf_simple_add_le16(&msg, addr);
net_buf_simple_add_le16(&msg, company);
net_buf_simple_add_le16(&msg, id);
goto send_list;
}
mod = bt_mesh_model_find_vnd(elem, company, id);
if (!mod) {
net_buf_simple_add_u8(&msg, STATUS_INVALID_MODEL);
net_buf_simple_add_le16(&msg, addr);
net_buf_simple_add_le16(&msg, company);
net_buf_simple_add_le16(&msg, id);
goto send_list;
}
net_buf_simple_add_u8(&msg, STATUS_SUCCESS);
net_buf_simple_add_le16(&msg, addr);
net_buf_simple_add_le16(&msg, company);
net_buf_simple_add_le16(&msg, id);
visit_ctx.msg = &msg;
visit_ctx.elem_idx = mod->rt->elem_idx;
bt_mesh_model_extensions_walk(mod, mod_sub_list_visitor, &visit_ctx);
send_list:
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Vendor Model Subscription List");
}
return 0;
}
static int mod_sub_va_add(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
const struct bt_mesh_va *va;
uint16_t elem_addr, sub_addr = BT_MESH_ADDR_UNASSIGNED;
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
const uint8_t *uuid;
uint8_t *mod_id;
uint16_t *group_entry;
const uint8_t **label_entry;
uint8_t status;
bool vnd;
if ((buf->len != 20U) && (buf->len != 22U)) {
LOG_ERR("The message size for the application opcode is incorrect.");
return -EMSGSIZE;
}
elem_addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
uuid = net_buf_simple_pull_mem(buf, 16);
LOG_DBG("elem_addr 0x%04x", elem_addr);
mod_id = buf->data;
elem = bt_mesh_elem_find(elem_addr);
if (!elem) {
mod = NULL;
vnd = (buf->len == 4U);
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
mod = get_model(elem, buf, &vnd);
if (!mod) {
status = STATUS_INVALID_MODEL;
goto send_status;
}
status = bt_mesh_va_add(uuid, &va);
if (status != STATUS_SUCCESS) {
goto send_status;
}
if (bt_mesh_model_find_uuid(&mod, va->uuid)) {
/* Tried to add existing subscription */
status = STATUS_SUCCESS;
(void)bt_mesh_va_del(va->uuid);
goto send_status;
}
label_entry = bt_mesh_model_find_uuid(&mod, NULL);
if (!label_entry) {
status = STATUS_INSUFF_RESOURCES;
(void)bt_mesh_va_del(va->uuid);
goto send_status;
}
group_entry = NULL;
for (int i = 0; i < mod->groups_cnt; i++) {
if (mod->groups[i] == BT_MESH_ADDR_UNASSIGNED) {
group_entry = &mod->groups[i];
break;
}
}
/* bt_mesh_model_find_uuid() should find a model where both, uuids and groups lists have
* empty entry.
*/
if (!group_entry) {
status = STATUS_INSUFF_RESOURCES;
(void)bt_mesh_va_del(va->uuid);
goto send_status;
}
*group_entry = va->addr;
*label_entry = va->uuid;
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) && va->ref == 1 &&
!bt_mesh_va_collision_check(va->addr)) {
bt_mesh_lpn_group_add(va->addr);
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_model_sub_store(mod);
}
status = STATUS_SUCCESS;
sub_addr = va->addr;
send_status:
return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
mod_id, vnd);
}
static int mod_sub_va_del(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
const struct bt_mesh_va *va;
uint16_t elem_addr, sub_addr = BT_MESH_ADDR_UNASSIGNED;
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
const uint8_t *uuid;
uint8_t *mod_id;
const uint8_t **label_match;
uint8_t status;
bool vnd;
if ((buf->len != 20U) && (buf->len != 22U)) {
LOG_ERR("The message size for the application opcode is incorrect.");
return -EMSGSIZE;
}
elem_addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
uuid = net_buf_simple_pull_mem(buf, 16);
LOG_DBG("elem_addr 0x%04x", elem_addr);
mod_id = buf->data;
elem = bt_mesh_elem_find(elem_addr);
if (!elem) {
mod = NULL;
vnd = (buf->len == 4U);
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
mod = get_model(elem, buf, &vnd);
if (!mod) {
status = STATUS_INVALID_MODEL;
goto send_status;
}
va = bt_mesh_va_find(uuid);
if (!va) {
status = STATUS_CANNOT_REMOVE;
goto send_status;
}
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) && va->ref == 1 &&
!bt_mesh_va_collision_check(va->addr)) {
bt_mesh_lpn_group_del(&va->addr, 1);
}
label_match = bt_mesh_model_find_uuid(&mod, va->uuid);
if (!label_match) {
status = STATUS_CANNOT_REMOVE;
goto send_status;
}
for (int i = 0; i < mod->groups_cnt; i++) {
if (mod->groups[i] == va->addr) {
mod->groups[i] = BT_MESH_ADDR_UNASSIGNED;
break;
}
}
*label_match = NULL;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_model_sub_store(mod);
}
sub_addr = va->addr;
(void)bt_mesh_va_del(va->uuid);
status = STATUS_SUCCESS;
send_status:
return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
mod_id, vnd);
}
static int mod_sub_va_overwrite(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
const struct bt_mesh_va *va;
uint16_t elem_addr, sub_addr = BT_MESH_ADDR_UNASSIGNED;
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
const uint8_t *uuid;
uint8_t *mod_id;
uint8_t status;
bool vnd;
if ((buf->len != 20U) && (buf->len != 22U)) {
LOG_ERR("The message size for the application opcode is incorrect.");
return -EMSGSIZE;
}
elem_addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
uuid = net_buf_simple_pull_mem(buf, 16);
LOG_DBG("elem_addr 0x%04x", elem_addr);
mod_id = buf->data;
elem = bt_mesh_elem_find(elem_addr);
if (!elem) {
mod = NULL;
vnd = (buf->len == 4U);
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
mod = get_model(elem, buf, &vnd);
if (!mod) {
status = STATUS_INVALID_MODEL;
goto send_status;
}
if (CONFIG_BT_MESH_LABEL_COUNT == 0 || mod->groups_cnt == 0) {
(void)va;
status = STATUS_INSUFF_RESOURCES;
goto send_status;
}
#if CONFIG_BT_MESH_LABEL_COUNT > 0
status = bt_mesh_va_add(uuid, &va);
if (status != STATUS_SUCCESS) {
goto send_status;
}
bt_mesh_model_extensions_walk(mod, mod_sub_clear_visitor, NULL);
mod->groups[0] = va->addr;
mod->uuids[0] = va->uuid;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_model_sub_store(mod);
}
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) && va->ref == 1 &&
!bt_mesh_va_collision_check(va->addr)) {
bt_mesh_lpn_group_add(va->addr);
}
sub_addr = va->addr;
#endif
send_status:
return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
mod_id, vnd);
}
static int send_net_key_status(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx, uint16_t idx,
uint8_t status)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_NET_KEY_STATUS, 3);
bt_mesh_model_msg_init(&msg, OP_NET_KEY_STATUS);
net_buf_simple_add_u8(&msg, status);
net_buf_simple_add_le16(&msg, idx);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send NetKey Status");
}
return 0;
}
static int net_key_add(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint8_t status;
uint16_t idx;
idx = net_buf_simple_pull_le16(buf);
if (idx > 0xfff) {
LOG_ERR("Invalid NetKeyIndex 0x%04x", idx);
return -EINVAL;
}
LOG_DBG("idx 0x%04x", idx);
status = bt_mesh_subnet_add(idx, buf->data);
return send_net_key_status(model, ctx, idx, status);
}
static int net_key_update(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint8_t status;
uint16_t idx;
idx = net_buf_simple_pull_le16(buf);
if (idx > 0xfff) {
LOG_ERR("Invalid NetKeyIndex 0x%04x", idx);
return -EINVAL;
}
status = bt_mesh_subnet_update(idx, buf->data);
return send_net_key_status(model, ctx, idx, status);
}
static int net_key_del(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint16_t del_idx;
del_idx = net_buf_simple_pull_le16(buf);
if (del_idx > 0xfff) {
LOG_ERR("Invalid NetKeyIndex 0x%04x", del_idx);
return -EINVAL;
}
LOG_DBG("idx 0x%04x", del_idx);
/* The key that the message was encrypted with cannot be removed.
* The NetKey List must contain a minimum of one NetKey.
*/
if (ctx->net_idx == del_idx) {
return send_net_key_status(model, ctx, del_idx,
STATUS_CANNOT_REMOVE);
}
(void)bt_mesh_subnet_del(del_idx);
return send_net_key_status(model, ctx, del_idx, STATUS_SUCCESS);
}
static int net_key_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_NET_KEY_LIST,
IDX_LEN(CONFIG_BT_MESH_SUBNET_COUNT));
uint16_t net_idx[CONFIG_BT_MESH_SUBNET_COUNT];
ssize_t count;
bt_mesh_model_msg_init(&msg, OP_NET_KEY_LIST);
count = bt_mesh_subnets_get(net_idx, ARRAY_SIZE(net_idx), 0);
if (count < 0 || count > ARRAY_SIZE(net_idx)) {
count = ARRAY_SIZE(net_idx);
}
key_idx_pack_list(&msg, net_idx, count);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send NetKey List");
}
return 0;
}
static int send_node_id_status(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
uint8_t status,
uint16_t net_idx, uint8_t node_id)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_NODE_IDENTITY_STATUS, 4);
bt_mesh_model_msg_init(&msg, OP_NODE_IDENTITY_STATUS);
net_buf_simple_add_u8(&msg, status);
net_buf_simple_add_le16(&msg, net_idx);
net_buf_simple_add_u8(&msg, node_id);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Node Identity Status");
}
return 0;
}
static int node_identity_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
enum bt_mesh_feat_state node_id;
uint8_t status;
uint16_t idx;
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
idx = net_buf_simple_pull_le16(buf);
if (idx > 0xfff) {
LOG_ERR("Invalid NetKeyIndex 0x%04x", idx);
return -EINVAL;
}
status = bt_mesh_subnet_node_id_get(idx, &node_id);
return send_node_id_status(model, ctx, status, idx, node_id);
}
static int node_identity_set(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint8_t node_id, status;
uint16_t idx;
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
idx = net_buf_simple_pull_le16(buf);
if (idx > 0xfff) {
LOG_WRN("Invalid NetKeyIndex 0x%04x", idx);
return -EINVAL;
}
node_id = net_buf_simple_pull_u8(buf);
if (node_id != 0x00 && node_id != 0x01) {
LOG_WRN("Invalid Node ID value 0x%02x", node_id);
return -EINVAL;
}
status = bt_mesh_subnet_node_id_set(idx, node_id);
if (status == STATUS_INVALID_NETKEY) {
return send_node_id_status(model, ctx, status, idx,
BT_MESH_NODE_IDENTITY_STOPPED);
}
if (status == STATUS_FEAT_NOT_SUPP) {
/* Should return success, even if feature isn't supported: */
return send_node_id_status(model, ctx, STATUS_SUCCESS, idx,
BT_MESH_NODE_IDENTITY_NOT_SUPPORTED);
}
return send_node_id_status(model, ctx, status, idx, node_id);
}
static void create_mod_app_status(struct net_buf_simple *msg,
const struct bt_mesh_model *mod, bool vnd,
uint16_t elem_addr, uint16_t app_idx,
uint8_t status, uint8_t *mod_id)
{
bt_mesh_model_msg_init(msg, OP_MOD_APP_STATUS);
net_buf_simple_add_u8(msg, status);
net_buf_simple_add_le16(msg, elem_addr);
net_buf_simple_add_le16(msg, app_idx);
if (vnd) {
memcpy(net_buf_simple_add(msg, 4), mod_id, 4);
} else {
memcpy(net_buf_simple_add(msg, 2), mod_id, 2);
}
}
static int mod_app_bind(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_MOD_APP_STATUS, 9);
uint16_t elem_addr, key_app_idx;
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
uint8_t *mod_id, status;
bool vnd;
if ((buf->len != 6U) && (buf->len != 8U)) {
LOG_ERR("The message size for the application opcode is incorrect.");
return -EMSGSIZE;
}
elem_addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
key_app_idx = net_buf_simple_pull_le16(buf);
mod_id = buf->data;
elem = bt_mesh_elem_find(elem_addr);
if (!elem) {
mod = NULL;
vnd = (buf->len == 4U);
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
mod = get_model(elem, buf, &vnd);
if (!mod) {
status = STATUS_INVALID_MODEL;
goto send_status;
}
/* Some models only allow device key based access */
if (mod->rt->flags & BT_MESH_MOD_DEVKEY_ONLY) {
LOG_ERR("Client tried to bind AppKey to DevKey based model");
status = STATUS_CANNOT_BIND;
goto send_status;
}
status = mod_bind(mod, key_app_idx);
if (IS_ENABLED(CONFIG_BT_TESTING) && status == STATUS_SUCCESS) {
bt_mesh_test_model_bound(ctx->addr, mod, key_app_idx);
}
send_status:
LOG_DBG("status 0x%02x", status);
create_mod_app_status(&msg, mod, vnd, elem_addr, key_app_idx, status,
mod_id);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Model App Bind Status response");
}
return 0;
}
static int mod_app_unbind(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_MOD_APP_STATUS, 9);
uint16_t elem_addr, key_app_idx;
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
uint8_t *mod_id, status;
bool vnd;
if ((buf->len != 6U) && (buf->len != 8U)) {
LOG_ERR("The message size for the application opcode is incorrect.");
return -EMSGSIZE;
}
elem_addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
key_app_idx = net_buf_simple_pull_le16(buf);
mod_id = buf->data;
elem = bt_mesh_elem_find(elem_addr);
if (!elem) {
mod = NULL;
vnd = (buf->len == 4U);
status = STATUS_INVALID_ADDRESS;
goto send_status;
}
mod = get_model(elem, buf, &vnd);
if (!mod) {
status = STATUS_INVALID_MODEL;
goto send_status;
}
status = mod_unbind(mod, key_app_idx, true);
if (IS_ENABLED(CONFIG_BT_TESTING) && status == STATUS_SUCCESS) {
bt_mesh_test_model_unbound(ctx->addr, mod, key_app_idx);
}
send_status:
LOG_DBG("status 0x%02x", status);
create_mod_app_status(&msg, mod, vnd, elem_addr, key_app_idx, status,
mod_id);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Model App Unbind Status response");
}
return 0;
}
#define KEY_LIST_LEN (CONFIG_BT_MESH_MODEL_KEY_COUNT * 2)
static int mod_app_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
NET_BUF_SIMPLE_DEFINE(msg,
MAX(BT_MESH_MODEL_BUF_LEN(OP_VND_MOD_APP_LIST,
9 + KEY_LIST_LEN),
BT_MESH_MODEL_BUF_LEN(OP_SIG_MOD_APP_LIST,
9 + KEY_LIST_LEN)));
const struct bt_mesh_model *mod;
const struct bt_mesh_elem *elem;
uint8_t *mod_id, status;
uint16_t elem_addr;
bool vnd;
if ((buf->len != 4U) && (buf->len != 6U)) {
LOG_ERR("The message size for the application opcode is incorrect.");
return -EMSGSIZE;
}
elem_addr = net_buf_simple_pull_le16(buf);
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
LOG_WRN("Prohibited element address");
return -EINVAL;
}
mod_id = buf->data;
LOG_DBG("elem_addr 0x%04x", elem_addr);
elem = bt_mesh_elem_find(elem_addr);
if (!elem) {
mod = NULL;
vnd = (buf->len == 4U);
status = STATUS_INVALID_ADDRESS;
goto send_list;
}
mod = get_model(elem, buf, &vnd);
if (!mod) {
status = STATUS_INVALID_MODEL;
goto send_list;
}
status = STATUS_SUCCESS;
send_list:
if (vnd) {
bt_mesh_model_msg_init(&msg, OP_VND_MOD_APP_LIST);
} else {
bt_mesh_model_msg_init(&msg, OP_SIG_MOD_APP_LIST);
}
net_buf_simple_add_u8(&msg, status);
net_buf_simple_add_le16(&msg, elem_addr);
if (vnd) {
net_buf_simple_add_mem(&msg, mod_id, 4);
} else {
net_buf_simple_add_mem(&msg, mod_id, 2);
}
if (mod) {
key_idx_pack_list(&msg, mod->keys, mod->keys_cnt);
}
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Model Application List message");
}
return 0;
}
static void reset_send_start(uint16_t duration, int err, void *cb_data)
{
if (err) {
LOG_ERR("Sending Node Reset Status failed (err %d)", err);
k_work_submit(&node_reset_pending);
}
}
static void reset_send_end(int err, void *cb_data)
{
k_work_submit(&node_reset_pending);
}
static int node_reset(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
static const struct bt_mesh_send_cb reset_cb = {
.start = reset_send_start,
.end = reset_send_end,
};
BT_MESH_MODEL_BUF_DEFINE(msg, OP_NODE_RESET_STATUS, 0);
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
bt_mesh_model_msg_init(&msg, OP_NODE_RESET_STATUS);
if (bt_mesh_model_send(model, ctx, &msg, &reset_cb, NULL)) {
LOG_ERR("Unable to send Node Reset Status");
}
return 0;
}
static int send_friend_status(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_FRIEND_STATUS, 1);
bt_mesh_model_msg_init(&msg, OP_FRIEND_STATUS);
net_buf_simple_add_u8(&msg, bt_mesh_friend_get());
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Friend Status");
}
return 0;
}
static int friend_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
return send_friend_status(model, ctx);
}
static int friend_set(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
ctx->addr, buf->len, bt_hex(buf->data, buf->len));
if (buf->data[0] != 0x00 && buf->data[0] != 0x01) {
LOG_WRN("Invalid Friend value 0x%02x", buf->data[0]);
return -EINVAL;
}
(void)bt_mesh_friend_set(buf->data[0]);
return send_friend_status(model, ctx);
}
static int lpn_timeout_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_LPN_TIMEOUT_STATUS, 5);
struct bt_mesh_friend *frnd;
int32_t timeout_steps;
uint16_t lpn_addr;
lpn_addr = net_buf_simple_pull_le16(buf);
LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x lpn_addr 0x%02x", ctx->net_idx,
ctx->app_idx, ctx->addr, lpn_addr);
if (!BT_MESH_ADDR_IS_UNICAST(lpn_addr)) {
LOG_WRN("Invalid LPNAddress; ignoring msg");
return -EINVAL;
}
bt_mesh_model_msg_init(&msg, OP_LPN_TIMEOUT_STATUS);
net_buf_simple_add_le16(&msg, lpn_addr);
if (!IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
timeout_steps = 0;
goto send_rsp;
}
frnd = bt_mesh_friend_find(BT_MESH_KEY_ANY, lpn_addr, true, true);
if (!frnd) {
timeout_steps = 0;
goto send_rsp;
}
/* PollTimeout should be reported in steps of 100ms. */
timeout_steps = frnd->poll_to / 100;
send_rsp:
net_buf_simple_add_le24(&msg, timeout_steps);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send LPN PollTimeout Status");
}
return 0;
}
static int send_krp_status(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx, uint16_t idx,
uint8_t phase, uint8_t status)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_KRP_STATUS, 4);
bt_mesh_model_msg_init(&msg, OP_KRP_STATUS);
net_buf_simple_add_u8(&msg, status);
net_buf_simple_add_le16(&msg, idx);
net_buf_simple_add_u8(&msg, phase);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Key Refresh State Status");
}
return 0;
}
static int krp_get(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint8_t kr_phase, status;
uint16_t idx;
idx = net_buf_simple_pull_le16(buf);
if (idx > 0xfff) {
LOG_ERR("Invalid NetKeyIndex 0x%04x", idx);
return -EINVAL;
}
LOG_DBG("idx 0x%04x", idx);
status = bt_mesh_subnet_kr_phase_get(idx, &kr_phase);
return send_krp_status(model, ctx, idx, kr_phase, status);
}
static int krp_set(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint8_t phase, status;
uint16_t idx;
idx = net_buf_simple_pull_le16(buf);
phase = net_buf_simple_pull_u8(buf);
if (idx > 0xfff) {
LOG_ERR("Invalid NetKeyIndex 0x%04x", idx);
return -EINVAL;
}
status = bt_mesh_subnet_kr_phase_set(idx, &phase);
if (status == STATUS_CANNOT_UPDATE) {
LOG_ERR("Invalid kr phase transition 0x%02x", phase);
return -EINVAL;
}
return send_krp_status(model, ctx, idx, phase, status);
}
static uint8_t hb_sub_count_log(uint32_t val)
{
if (val == 0xffff) {
return 0xff;
} else {
return bt_mesh_hb_log(val);
}
}
static uint8_t hb_pub_count_log(uint16_t val)
Bluetooth: Mesh: Fix HB Pub Count Log calculation routine "4.1.2 Log field transformation In order to compress two-octet values into one-octet fields, the following logarithmic transformation is used: any two-octet value is mapped onto a one-octet field value representing the largest integer n, where 2^(n-1) is less than or equal to the two-octet value." Log field transformation table: Log Field Value 2-octet Value 0x01 0x0001 0x02 0x0002 through 0x0003 0x03 0x0004 through 0x0007 0x04 0x0008 through 0x000F 0x05 0x0010 through 0x001F 0x06 0x0020 through 0x003F 0x07 0x0040 through 0x007F 0x08 0x0080 through 0x00FF 0x09 0x0100 through 0x01FF 0x0A 0x0200 through 0x03FF 0x0B 0x0400 through 0x07FF 0x0C 0x0800 through 0x0FFF 0x0D 0x1000 through 0x1FFF 0x0E 0x2000 through 0x3FFF 0x0F 0x4000 through 0x7FFF 0x10 0x8000 through 0xFFFF "4.2.17.2 Heartbeat Publication Count Log The Heartbeat Publication Count Log value between 0x01 and 0x11 shall represent that smallest integer n where 2^(n-1) is greater than or equal to the Heartbeat Publication Count value. For example, if the Heartbeat Publication Count value is 0x0579, then the Heartbeat Publication Count Log value would be 0x0C." According to this definition 2^(n-1) is an upper bound for n log value. Proposed Publication Count Log transformation table: Pub Count Log Value 2-octet Value 0x01 0x0001 0x02 0x0002 0x03 0x0003 through 0x0004 0x04 0x0005 through 0x0008 0x05 0x0009 through 0x0010 0x06 0x0011 through 0x0020 0x07 0x0021 through 0x0040 0x08 0x0041 through 0x0080 0x09 0x0081 through 0x0100 0x0A 0x0101 through 0x0200 0x0B 0x0201 through 0x0400 0x0C 0x0401 through 0x0800 0x0D 0x0801 through 0x1000 0x0E 0x1001 through 0x2000 0x0F 0x2001 through 0x4000 0x10 0x4001 through 0x8000 0x11 0x8001 through 0x10000 According to Log field transformation table 0x0579 would be transformed to 0x0B and should be to transformed to 0x0C. This is required to pass MESH/NODE/CFG/HBP/BV-01-C. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-02 15:24:24 +02:00
{
if (!val) {
return 0x00;
} else if (val == 0x01) {
return 0x01;
} else if (val == 0xfffe) {
return 0x11;
Bluetooth: Mesh: Fix HB Pub Count Log calculation routine "4.1.2 Log field transformation In order to compress two-octet values into one-octet fields, the following logarithmic transformation is used: any two-octet value is mapped onto a one-octet field value representing the largest integer n, where 2^(n-1) is less than or equal to the two-octet value." Log field transformation table: Log Field Value 2-octet Value 0x01 0x0001 0x02 0x0002 through 0x0003 0x03 0x0004 through 0x0007 0x04 0x0008 through 0x000F 0x05 0x0010 through 0x001F 0x06 0x0020 through 0x003F 0x07 0x0040 through 0x007F 0x08 0x0080 through 0x00FF 0x09 0x0100 through 0x01FF 0x0A 0x0200 through 0x03FF 0x0B 0x0400 through 0x07FF 0x0C 0x0800 through 0x0FFF 0x0D 0x1000 through 0x1FFF 0x0E 0x2000 through 0x3FFF 0x0F 0x4000 through 0x7FFF 0x10 0x8000 through 0xFFFF "4.2.17.2 Heartbeat Publication Count Log The Heartbeat Publication Count Log value between 0x01 and 0x11 shall represent that smallest integer n where 2^(n-1) is greater than or equal to the Heartbeat Publication Count value. For example, if the Heartbeat Publication Count value is 0x0579, then the Heartbeat Publication Count Log value would be 0x0C." According to this definition 2^(n-1) is an upper bound for n log value. Proposed Publication Count Log transformation table: Pub Count Log Value 2-octet Value 0x01 0x0001 0x02 0x0002 0x03 0x0003 through 0x0004 0x04 0x0005 through 0x0008 0x05 0x0009 through 0x0010 0x06 0x0011 through 0x0020 0x07 0x0021 through 0x0040 0x08 0x0041 through 0x0080 0x09 0x0081 through 0x0100 0x0A 0x0101 through 0x0200 0x0B 0x0201 through 0x0400 0x0C 0x0401 through 0x0800 0x0D 0x0801 through 0x1000 0x0E 0x1001 through 0x2000 0x0F 0x2001 through 0x4000 0x10 0x4001 through 0x8000 0x11 0x8001 through 0x10000 According to Log field transformation table 0x0579 would be transformed to 0x0B and should be to transformed to 0x0C. This is required to pass MESH/NODE/CFG/HBP/BV-01-C. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-02 15:24:24 +02:00
} else if (val == 0xffff) {
return 0xff;
} else {
return 32 - __builtin_clz(val - 1) + 1;
}
}
struct hb_pub_param {
uint16_t dst;
uint8_t count_log;
uint8_t period_log;
uint8_t ttl;
uint16_t feat;
uint16_t net_idx;
} __packed;
static int hb_pub_send_status(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx, uint8_t status,
const struct bt_mesh_hb_pub *pub)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEARTBEAT_PUB_STATUS, 10);
LOG_DBG("src 0x%04x status 0x%02x", ctx->addr, status);
bt_mesh_model_msg_init(&msg, OP_HEARTBEAT_PUB_STATUS);
net_buf_simple_add_u8(&msg, status);
net_buf_simple_add_le16(&msg, pub->dst);
if (pub->dst == BT_MESH_ADDR_UNASSIGNED) {
(void)memset(net_buf_simple_add(&msg, 7), 0, 7);
} else {
net_buf_simple_add_u8(&msg, hb_pub_count_log(pub->count));
net_buf_simple_add_u8(&msg, bt_mesh_hb_log(pub->period));
net_buf_simple_add_u8(&msg, pub->ttl);
net_buf_simple_add_le16(&msg, pub->feat);
net_buf_simple_add_le16(&msg, pub->net_idx & 0xfff);
}
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Heartbeat Publication Status");
}
return 0;
}
static int heartbeat_pub_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_hb_pub pub;
LOG_DBG("src 0x%04x", ctx->addr);
bt_mesh_hb_pub_get(&pub);
return hb_pub_send_status(model, ctx, STATUS_SUCCESS, &pub);
}
static int heartbeat_pub_set(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct hb_pub_param *param = (void *)buf->data;
struct bt_mesh_hb_pub pub;
uint8_t status;
LOG_DBG("src 0x%04x", ctx->addr);
pub.dst = sys_le16_to_cpu(param->dst);
if (param->count_log == 0x11) {
/* Special case defined in MshPRFv1.1 Errata 11737 */
pub.count = 0xfffe;
} else {
pub.count = bt_mesh_hb_pwr2(param->count_log);
}
if (param->period_log == 0x11) {
pub.period = 0x10000;
} else {
pub.period = bt_mesh_hb_pwr2(param->period_log);
}
pub.ttl = param->ttl;
pub.feat = sys_le16_to_cpu(param->feat);
pub.net_idx = sys_le16_to_cpu(param->net_idx);
/* All other address types but virtual are valid */
if (BT_MESH_ADDR_IS_VIRTUAL(pub.dst)) {
status = STATUS_INVALID_ADDRESS;
goto rsp;
}
if (param->count_log > 0x11 && param->count_log != 0xff) {
status = STATUS_CANNOT_SET;
goto rsp;
}
if (param->period_log > 0x11) {
status = STATUS_CANNOT_SET;
goto rsp;
}
if (param->ttl > BT_MESH_TTL_MAX && param->ttl != BT_MESH_TTL_DEFAULT) {
LOG_ERR("Invalid TTL value 0x%02x", param->ttl);
return -EINVAL;
}
if (pub.net_idx > 0xfff) {
LOG_ERR("Invalid NetKeyIndex 0x%04x", pub.net_idx);
return -EINVAL;
}
status = bt_mesh_hb_pub_set(&pub);
rsp:
return hb_pub_send_status(model, ctx, status, &pub);
}
static int hb_sub_send_status(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
const struct bt_mesh_hb_sub *sub)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEARTBEAT_SUB_STATUS, 9);
LOG_DBG("src 0x%04x ", ctx->addr);
bt_mesh_model_msg_init(&msg, OP_HEARTBEAT_SUB_STATUS);
net_buf_simple_add_u8(&msg, STATUS_SUCCESS);
net_buf_simple_add_le16(&msg, sub->src);
net_buf_simple_add_le16(&msg, sub->dst);
net_buf_simple_add_u8(&msg, bt_mesh_hb_log(sub->remaining));
net_buf_simple_add_u8(&msg, hb_sub_count_log(sub->count));
net_buf_simple_add_u8(&msg, sub->min_hops);
net_buf_simple_add_u8(&msg, sub->max_hops);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Unable to send Heartbeat Subscription Status");
}
return 0;
}
static int heartbeat_sub_get(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_hb_sub sub;
LOG_DBG("src 0x%04x", ctx->addr);
bt_mesh_hb_sub_get(&sub);
return hb_sub_send_status(model, ctx, &sub);
}
static int heartbeat_sub_set(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint8_t period_log, status;
struct bt_mesh_hb_sub sub;
uint16_t sub_src, sub_dst;
uint32_t period;
int err;
LOG_DBG("src 0x%04x", ctx->addr);
sub_src = net_buf_simple_pull_le16(buf);
sub_dst = net_buf_simple_pull_le16(buf);
period_log = net_buf_simple_pull_u8(buf);
LOG_DBG("sub_src 0x%04x sub_dst 0x%04x period 0x%02x", sub_src, sub_dst, period_log);
if (period_log > 0x11) {
LOG_WRN("Prohibited subscription period 0x%02x", period_log);
return -EINVAL;
}
if (period_log == 0x11) {
period = 0x10000;
} else {
period = bt_mesh_hb_pwr2(period_log);
}
status = bt_mesh_hb_sub_set(sub_src, sub_dst, period);
if (status != STATUS_SUCCESS) {
/* All errors are caused by invalid packets, which should be
* ignored.
*/
return -EINVAL;
}
bt_mesh_hb_sub_get(&sub);
/* MESH/NODE/CFG/HBS/BV-01-C expects the MinHops to be 0x7f after
* disabling subscription, but 0x00 for subsequent Get requests.
*/
if (sub.src == BT_MESH_ADDR_UNASSIGNED || !period_log) {
sub.min_hops = BT_MESH_TTL_MAX;
}
err = hb_sub_send_status(model, ctx, &sub);
if (err) {
return err;
}
/* MESH/NODE/CFG/HBS/BV-02-C expects us to return previous
* count value and then reset it to 0.
*/
if (sub.src != BT_MESH_ADDR_UNASSIGNED &&
sub.dst != BT_MESH_ADDR_UNASSIGNED && !period) {
bt_mesh_hb_sub_reset_count();
}
return 0;
}
const struct bt_mesh_model_op bt_mesh_cfg_srv_op[] = {
{ OP_DEV_COMP_DATA_GET, BT_MESH_LEN_EXACT(1), dev_comp_data_get },
{ OP_APP_KEY_ADD, BT_MESH_LEN_EXACT(19), app_key_add },
{ OP_APP_KEY_UPDATE, BT_MESH_LEN_EXACT(19), app_key_update },
{ OP_APP_KEY_DEL, BT_MESH_LEN_EXACT(3), app_key_del },
{ OP_APP_KEY_GET, BT_MESH_LEN_EXACT(2), app_key_get },
{ OP_BEACON_GET, BT_MESH_LEN_EXACT(0), beacon_get },
{ OP_BEACON_SET, BT_MESH_LEN_EXACT(1), beacon_set },
{ OP_DEFAULT_TTL_GET, BT_MESH_LEN_EXACT(0), default_ttl_get },
{ OP_DEFAULT_TTL_SET, BT_MESH_LEN_EXACT(1), default_ttl_set },
{ OP_GATT_PROXY_GET, BT_MESH_LEN_EXACT(0), gatt_proxy_get },
{ OP_GATT_PROXY_SET, BT_MESH_LEN_EXACT(1), gatt_proxy_set },
{ OP_NET_TRANSMIT_GET, BT_MESH_LEN_EXACT(0), net_transmit_get },
{ OP_NET_TRANSMIT_SET, BT_MESH_LEN_EXACT(1), net_transmit_set },
{ OP_RELAY_GET, BT_MESH_LEN_EXACT(0), relay_get },
{ OP_RELAY_SET, BT_MESH_LEN_EXACT(2), relay_set },
{ OP_MOD_PUB_GET, BT_MESH_LEN_MIN(4), mod_pub_get },
{ OP_MOD_PUB_SET, BT_MESH_LEN_MIN(11), mod_pub_set },
{ OP_MOD_PUB_VA_SET, BT_MESH_LEN_MIN(25), mod_pub_va_set },
{ OP_MOD_SUB_ADD, BT_MESH_LEN_MIN(6), mod_sub_add },
{ OP_MOD_SUB_VA_ADD, BT_MESH_LEN_MIN(20), mod_sub_va_add },
{ OP_MOD_SUB_DEL, BT_MESH_LEN_MIN(6), mod_sub_del },
{ OP_MOD_SUB_VA_DEL, BT_MESH_LEN_MIN(20), mod_sub_va_del },
{ OP_MOD_SUB_OVERWRITE, BT_MESH_LEN_MIN(6), mod_sub_overwrite },
{ OP_MOD_SUB_VA_OVERWRITE, BT_MESH_LEN_MIN(20), mod_sub_va_overwrite },
{ OP_MOD_SUB_DEL_ALL, BT_MESH_LEN_MIN(4), mod_sub_del_all },
{ OP_MOD_SUB_GET, BT_MESH_LEN_EXACT(4), mod_sub_get },
{ OP_MOD_SUB_GET_VND, BT_MESH_LEN_EXACT(6), mod_sub_get_vnd },
{ OP_NET_KEY_ADD, BT_MESH_LEN_EXACT(18), net_key_add },
{ OP_NET_KEY_UPDATE, BT_MESH_LEN_EXACT(18), net_key_update },
{ OP_NET_KEY_DEL, BT_MESH_LEN_EXACT(2), net_key_del },
{ OP_NET_KEY_GET, BT_MESH_LEN_EXACT(0), net_key_get },
{ OP_NODE_IDENTITY_GET, BT_MESH_LEN_EXACT(2), node_identity_get },
{ OP_NODE_IDENTITY_SET, BT_MESH_LEN_EXACT(3), node_identity_set },
{ OP_MOD_APP_BIND, BT_MESH_LEN_MIN(6), mod_app_bind },
{ OP_MOD_APP_UNBIND, BT_MESH_LEN_MIN(6), mod_app_unbind },
{ OP_SIG_MOD_APP_GET, BT_MESH_LEN_MIN(4), mod_app_get },
{ OP_VND_MOD_APP_GET, BT_MESH_LEN_MIN(6), mod_app_get },
{ OP_NODE_RESET, BT_MESH_LEN_EXACT(0), node_reset },
{ OP_FRIEND_GET, BT_MESH_LEN_EXACT(0), friend_get },
{ OP_FRIEND_SET, BT_MESH_LEN_EXACT(1), friend_set },
{ OP_LPN_TIMEOUT_GET, BT_MESH_LEN_EXACT(2), lpn_timeout_get },
{ OP_KRP_GET, BT_MESH_LEN_EXACT(2), krp_get },
{ OP_KRP_SET, BT_MESH_LEN_EXACT(3), krp_set },
{ OP_HEARTBEAT_PUB_GET, BT_MESH_LEN_EXACT(0), heartbeat_pub_get },
{ OP_HEARTBEAT_PUB_SET, BT_MESH_LEN_EXACT(9), heartbeat_pub_set },
{ OP_HEARTBEAT_SUB_GET, BT_MESH_LEN_EXACT(0), heartbeat_sub_get },
{ OP_HEARTBEAT_SUB_SET, BT_MESH_LEN_EXACT(5), heartbeat_sub_set },
BT_MESH_MODEL_OP_END,
};
static int cfg_srv_init(const struct bt_mesh_model *model)
{
if (!bt_mesh_model_in_primary(model)) {
LOG_ERR("Configuration Server only allowed in primary element");
return -EINVAL;
}
/*
* Configuration Model security is device-key based and only the local
* device-key is allowed to access this model.
*/
model->keys[0] = BT_MESH_KEY_DEV_LOCAL;
model->rt->flags |= BT_MESH_MOD_DEVKEY_ONLY;
return 0;
}
const struct bt_mesh_model_cb bt_mesh_cfg_srv_cb = {
.init = cfg_srv_init,
};
static void mod_reset(const struct bt_mesh_model *mod, const struct bt_mesh_elem *elem,
bool vnd, bool primary, void *user_data)
{
size_t clear_count;
/* Clear model state that isn't otherwise cleared. E.g. AppKey
* binding and model publication is cleared as a consequence
* of removing all app keys, however model subscription and user data
* clearing must be taken care of here.
*/
clear_count = mod_sub_list_clear(mod);
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
if (clear_count) {
bt_mesh_model_sub_store(mod);
}
}
if (mod->cb && mod->cb->reset) {
mod->cb->reset(mod);
}
}
void bt_mesh_model_reset(void)
{
bt_mesh_model_foreach(mod_reset, NULL);
}