| /* |
| * 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 "host/testing.h" |
| |
| #include "mesh.h" |
| #include "adv.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" |
| |
| #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 comp_add_elem(struct net_buf_simple *buf, struct bt_mesh_elem *elem, |
| bool primary) |
| { |
| struct bt_mesh_model *mod; |
| int i; |
| |
| if (net_buf_simple_tailroom(buf) < |
| 4 + (elem->model_count * 2U) + (elem->vnd_model_count * 4U)) { |
| LOG_ERR("Too large device composition"); |
| return -E2BIG; |
| } |
| |
| net_buf_simple_add_le16(buf, elem->loc); |
| |
| net_buf_simple_add_u8(buf, elem->model_count); |
| net_buf_simple_add_u8(buf, elem->vnd_model_count); |
| |
| for (i = 0; i < elem->model_count; i++) { |
| mod = &elem->models[i]; |
| net_buf_simple_add_le16(buf, mod->id); |
| } |
| |
| for (i = 0; i < elem->vnd_model_count; i++) { |
| mod = &elem->vnd_models[i]; |
| net_buf_simple_add_le16(buf, mod->vnd.company); |
| net_buf_simple_add_le16(buf, mod->vnd.id); |
| } |
| |
| return 0; |
| } |
| |
| static int comp_get_page_0(struct net_buf_simple *buf) |
| { |
| uint16_t feat = 0U; |
| const struct bt_mesh_comp *comp; |
| int i; |
| |
| comp = bt_mesh_comp_get(); |
| |
| if (IS_ENABLED(CONFIG_BT_MESH_RELAY)) { |
| feat |= BT_MESH_FEAT_RELAY; |
| } |
| |
| if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) { |
| feat |= BT_MESH_FEAT_PROXY; |
| } |
| |
| if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { |
| feat |= BT_MESH_FEAT_FRIEND; |
| } |
| |
| if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { |
| feat |= BT_MESH_FEAT_LOW_POWER; |
| } |
| |
| net_buf_simple_add_le16(buf, comp->cid); |
| net_buf_simple_add_le16(buf, comp->pid); |
| net_buf_simple_add_le16(buf, comp->vid); |
| net_buf_simple_add_le16(buf, CONFIG_BT_MESH_CRPL); |
| net_buf_simple_add_le16(buf, feat); |
| |
| for (i = 0; i < comp->elem_count; i++) { |
| int err; |
| |
| err = comp_add_elem(buf, &comp->elem[i], i == 0); |
| if (err) { |
| return err; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int dev_comp_data_get(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 = net_buf_simple_pull_u8(buf); |
| if (page != 0U) { |
| LOG_DBG("Composition page %u not available", page); |
| page = 0U; |
| } |
| |
| bt_mesh_model_msg_init(&sdu, OP_DEV_COMP_DATA_STATUS); |
| |
| net_buf_simple_add_u8(&sdu, page); |
| err = comp_get_page_0(&sdu); |
| if (err) { |
| LOG_ERR("Unable to get composition page 0"); |
| return err; |
| } |
| |
| if (bt_mesh_model_send(model, ctx, &sdu, NULL, NULL)) { |
| LOG_ERR("Unable to send Device Composition Status response"); |
| } |
| |
| return err; |
| } |
| |
| static struct bt_mesh_model *get_model(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->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->addr); |
| |
| *vnd = true; |
| |
| return bt_mesh_model_find_vnd(elem, company, id); |
| } |
| } |
| |
| static uint8_t _mod_pub_set(struct bt_mesh_model *model, uint16_t pub_addr, |
| 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; |
| |
| 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)) { |
| uint8_t *uuid = bt_mesh_va_label_get(model->pub->addr); |
| |
| if (uuid) { |
| bt_mesh_va_del(uuid, NULL); |
| } |
| } |
| #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; |
| |
| 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(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++) { |
| /* 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(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, |
| 0, 0, 0, 0, 0, store); |
| } |
| } |
| |
| return STATUS_SUCCESS; |
| } |
| |
| static int send_app_key_status(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(&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(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(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(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(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(struct bt_mesh_model *mod, |
| 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(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(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(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; |
| int i; |
| |
| 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); |
| } |
| |
| for (i = 0; i < count - 1; i += 2) { |
| key_idx_pack(&msg, app_idx[i], app_idx[i + 1]); |
| } |
| |
| if (i < count) { |
| net_buf_simple_add_le16(&msg, app_idx[i]); |
| } |
| |
| 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(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(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(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(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(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(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(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]); |
| |
| return send_gatt_proxy_status(model, ctx); |
| } |
| |
| static int net_transmit_get(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(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(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(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(struct bt_mesh_model *cfg_mod, |
| struct bt_mesh_msg_ctx *ctx, uint16_t elem_addr, |
| uint16_t pub_addr, bool vnd, |
| 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(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| uint16_t elem_addr, pub_addr = 0U; |
| struct bt_mesh_model *mod; |
| 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(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; |
| struct bt_mesh_model *mod; |
| 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, 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(struct bt_mesh_model *mod) |
| { |
| uint8_t *label_uuid; |
| size_t clear_count; |
| int i; |
| |
| /* Unref stored labels related to this model */ |
| for (i = 0, clear_count = 0; i < mod->groups_cnt; i++) { |
| if (!BT_MESH_ADDR_IS_VIRTUAL(mod->groups[i])) { |
| if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) { |
| mod->groups[i] = BT_MESH_ADDR_UNASSIGNED; |
| clear_count++; |
| } |
| |
| continue; |
| } |
| |
| label_uuid = bt_mesh_va_label_get(mod->groups[i]); |
| |
| mod->groups[i] = BT_MESH_ADDR_UNASSIGNED; |
| clear_count++; |
| |
| if (label_uuid) { |
| bt_mesh_va_del(label_uuid, NULL); |
| } else { |
| LOG_ERR("Label UUID not found"); |
| } |
| } |
| |
| return clear_count; |
| } |
| |
| static int mod_pub_va_set(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; |
| struct bt_mesh_model *mod; |
| struct bt_mesh_elem *elem; |
| uint8_t *label_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; |
| } |
| |
| label_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); |
| pub_addr = 0U; |
| status = STATUS_INVALID_ADDRESS; |
| goto send_status; |
| } |
| |
| mod = get_model(elem, buf, &vnd); |
| if (!mod) { |
| pub_addr = 0U; |
| status = STATUS_INVALID_MODEL; |
| goto send_status; |
| } |
| |
| status = bt_mesh_va_add(label_uuid, &pub_addr); |
| if (status != STATUS_SUCCESS) { |
| goto send_status; |
| } |
| |
| status = _mod_pub_set(mod, pub_addr, pub_app_idx, cred_flag, pub_ttl, |
| pub_period, retransmit, true); |
| if (status != STATUS_SUCCESS) { |
| bt_mesh_va_del(label_uuid, NULL); |
| } |
| |
| send_status: |
| return send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod, |
| status, mod_id); |
| } |
| |
| static int send_mod_sub_status(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(struct bt_mesh_model *model, |
| struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| uint16_t elem_addr, sub_addr; |
| struct bt_mesh_model *mod; |
| 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(struct bt_mesh_model *model, |
| struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| uint16_t elem_addr, sub_addr; |
| struct bt_mesh_model *mod; |
| 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(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(struct bt_mesh_model *model, |
| struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| uint16_t elem_addr, sub_addr; |
| struct bt_mesh_model *mod; |
| 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(struct bt_mesh_model *model, |
| struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| struct bt_mesh_model *mod; |
| 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(struct bt_mesh_model *mod, void *ctx) |
| { |
| struct mod_sub_list_ctx *visit = ctx; |
| int count = 0; |
| int i; |
| |
| if (mod->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->elem_idx, mod->id, count); |
| |
| return BT_MESH_WALK_CONTINUE; |
| } |
| |
| static int mod_sub_get(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; |
| struct bt_mesh_model *mod; |
| 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->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(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; |
| struct bt_mesh_model *mod; |
| 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->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(struct bt_mesh_model *model, |
| struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| uint16_t elem_addr, sub_addr; |
| struct bt_mesh_model *mod; |
| struct bt_mesh_elem *elem; |
| uint8_t *label_uuid; |
| uint8_t *mod_id; |
| uint16_t *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; |
| } |
| |
| label_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); |
| sub_addr = BT_MESH_ADDR_UNASSIGNED; |
| status = STATUS_INVALID_ADDRESS; |
| goto send_status; |
| } |
| |
| mod = get_model(elem, buf, &vnd); |
| if (!mod) { |
| sub_addr = BT_MESH_ADDR_UNASSIGNED; |
| status = STATUS_INVALID_MODEL; |
| goto send_status; |
| } |
| |
| status = bt_mesh_va_add(label_uuid, &sub_addr); |
| if (status != STATUS_SUCCESS) { |
| goto send_status; |
| } |
| |
| if (bt_mesh_model_find_group(&mod, sub_addr)) { |
| /* Tried to add existing subscription */ |
| status = STATUS_SUCCESS; |
| bt_mesh_va_del(label_uuid, NULL); |
| goto send_status; |
| } |
| |
| |
| entry = bt_mesh_model_find_group(&mod, BT_MESH_ADDR_UNASSIGNED); |
| if (!entry) { |
| status = STATUS_INSUFF_RESOURCES; |
| bt_mesh_va_del(label_uuid, NULL); |
| goto send_status; |
| } |
| |
| *entry = sub_addr; |
| |
| if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { |
| bt_mesh_lpn_group_add(sub_addr); |
| } |
| |
| 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, sub_addr, |
| mod_id, vnd); |
| } |
| |
| static int mod_sub_va_del(struct bt_mesh_model *model, |
| struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| uint16_t elem_addr, sub_addr; |
| struct bt_mesh_model *mod; |
| struct bt_mesh_elem *elem; |
| uint8_t *label_uuid; |
| uint8_t *mod_id; |
| uint16_t *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; |
| } |
| |
| label_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); |
| sub_addr = BT_MESH_ADDR_UNASSIGNED; |
| status = STATUS_INVALID_ADDRESS; |
| goto send_status; |
| } |
| |
| mod = get_model(elem, buf, &vnd); |
| if (!mod) { |
| sub_addr = BT_MESH_ADDR_UNASSIGNED; |
| status = STATUS_INVALID_MODEL; |
| goto send_status; |
| } |
| |
| status = bt_mesh_va_del(label_uuid, &sub_addr); |
| if (sub_addr == BT_MESH_ADDR_UNASSIGNED) { |
| goto send_status; |
| } |
| |
| 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); |
| } |
| |
| status = STATUS_SUCCESS; |
| } else { |
| status = STATUS_CANNOT_REMOVE; |
| } |
| |
| send_status: |
| return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, |
| mod_id, vnd); |
| } |
| |
| static int mod_sub_va_overwrite(struct bt_mesh_model *model, |
| struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| uint16_t elem_addr, sub_addr = BT_MESH_ADDR_UNASSIGNED; |
| struct bt_mesh_model *mod; |
| struct bt_mesh_elem *elem; |
| uint8_t *label_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; |
| } |
| |
| label_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 (mod->groups_cnt > 0) { |
| |
| status = bt_mesh_va_add(label_uuid, &sub_addr); |
| if (status == STATUS_SUCCESS) { |
| bt_mesh_model_extensions_walk(mod, mod_sub_clear_visitor, NULL); |
| mod->groups[0] = sub_addr; |
| |
| 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 send_net_key_status(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(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(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(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(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; |
| int i; |
| |
| 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); |
| } |
| |
| for (i = 0; i < count - 1; i += 2) { |
| key_idx_pack(&msg, net_idx[i], net_idx[i + 1]); |
| } |
| |
| if (i < count) { |
| net_buf_simple_add_le16(&msg, net_idx[i]); |
| } |
| |
| 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(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(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(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, |
| 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(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; |
| struct bt_mesh_model *mod; |
| 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; |
| } |
| |
| /* Configuration Server only allows device key based access */ |
| if (model == mod) { |
| LOG_ERR("Client tried to bind AppKey to Configuration 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_test_mesh_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(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; |
| struct bt_mesh_model *mod; |
| 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_test_mesh_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(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))); |
| struct bt_mesh_model *mod; |
| 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) { |
| int i; |
| |
| for (i = 0; i < mod->keys_cnt; i++) { |
| if (mod->keys[i] != BT_MESH_KEY_UNUSED) { |
| net_buf_simple_add_le16(&msg, mod->keys[i]); |
| } |
| } |
| } |
| |
| 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(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(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(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(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(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(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(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(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_pub_count_log(uint16_t val) |
| { |
| if (!val) { |
| return 0x00; |
| } else if (val == 0x01) { |
| return 0x01; |
| } 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(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); |
| 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); |
| |
| 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(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(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); |
| pub.count = bt_mesh_hb_pwr2(param->count_log); |
| 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 > 0x10) { |
| 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(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, bt_mesh_hb_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(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(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; |
| } |
| |
| 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(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; |
| |
| return 0; |
| } |
| |
| const struct bt_mesh_model_cb bt_mesh_cfg_srv_cb = { |
| .init = cfg_srv_init, |
| }; |
| |
| static void mod_reset(struct bt_mesh_model *mod, 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); |
| } |