|  | /* | 
|  | * 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 "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_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(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_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(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) | 
|  | { | 
|  | if (!val) { | 
|  | return 0x00; | 
|  | } else if (val == 0x01) { | 
|  | return 0x01; | 
|  | } else if (val == 0xfffe) { | 
|  | return 0x11; | 
|  | } 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); | 
|  | } |