diff --git a/doc/zephyr.doxyfile.in b/doc/zephyr.doxyfile.in index b19ea8719a5..59544df5624 100644 --- a/doc/zephyr.doxyfile.in +++ b/doc/zephyr.doxyfile.in @@ -1979,6 +1979,7 @@ PREDEFINED = "CONFIG_SYS_CLOCK_EXISTS=y" \ "CONFIG_USERSPACE=y" \ "CONFIG_BT_BREDR=y" \ "CONFIG_FLASH_PAGE_LAYOUT=y" \ + "CONFIG_BT_MESH_MODEL_EXTENSIONS=y" \ "NET_MGMT_DEFINE_REQUEST_HANDLER(x)=" \ "DEVICE_DEFINE()=" \ "DEVICE_AND_API_INIT()=" \ diff --git a/include/bluetooth/mesh/access.h b/include/bluetooth/mesh/access.h index 55a6e87751d..981b4aed17b 100644 --- a/include/bluetooth/mesh/access.h +++ b/include/bluetooth/mesh/access.h @@ -501,7 +501,7 @@ struct bt_mesh_model { /* Internal information, mainly for persistent storage */ u8_t elem_idx; /* Belongs to Nth element */ u8_t mod_idx; /* Is the Nth model in the element */ - u16_t flags; /* Information about what has changed */ + u16_t flags; /* Model flags for internal bookkeeping */ /* Model Publication */ struct bt_mesh_model_pub * const pub; @@ -517,6 +517,12 @@ struct bt_mesh_model { /* Model callback structure. */ const struct bt_mesh_model_cb * const cb; +#ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS + /* Pointer to the next model in a model extension tree. */ + struct bt_mesh_model *next; + /* Pointer to the first model this model extends. */ + struct bt_mesh_model *extends; +#endif /* Model-specific user data */ void *user_data; }; @@ -622,6 +628,30 @@ static inline bool bt_mesh_model_in_primary(const struct bt_mesh_model *mod) int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd, const void *data, size_t data_len); +/** @brief Let a model extend another. + * + * Mesh models may be extended to reuse their functionality, forming a more + * complex model. A Mesh model may extend any number of models, in any element. + * The extensions may also be nested, ie a model that extends another may itself + * be extended. Extensions may not be cyclical, and a model can only be extended + * by one other model. + * + * A set of models that extend each other form a model extension tree. + * + * All models in an extension tree share one subscription list per element. The + * access layer will utilize the combined subscription list of all models in an + * extension tree and element, giving the models extended subscription list + * capacity. + * + * @param[in] mod Mesh model. + * @param[in] base_mod The model being extended. + * + * @retval 0 Successfully extended the base_mod model. + * @retval -EALREADY The base_mod model is already extended. + */ +int bt_mesh_model_extend(struct bt_mesh_model *mod, + struct bt_mesh_model *base_mod); + /** Node Composition */ struct bt_mesh_comp { u16_t cid; diff --git a/samples/bluetooth/mesh/prj.conf b/samples/bluetooth/mesh/prj.conf index afa644f8d2b..5ef0a9db9b2 100644 --- a/samples/bluetooth/mesh/prj.conf +++ b/samples/bluetooth/mesh/prj.conf @@ -29,6 +29,7 @@ CONFIG_BT_L2CAP_TX_MTU=69 CONFIG_BT_L2CAP_TX_BUF_COUNT=5 CONFIG_BT_MESH=y +CONFIG_BT_MESH_MODEL_EXTENSIONS=y CONFIG_BT_MESH_RELAY=y CONFIG_BT_MESH_LOW_POWER=n CONFIG_BT_MESH_FRIEND=y diff --git a/subsys/bluetooth/mesh/Kconfig b/subsys/bluetooth/mesh/Kconfig index 144ce628dc8..3a4f87895b1 100644 --- a/subsys/bluetooth/mesh/Kconfig +++ b/subsys/bluetooth/mesh/Kconfig @@ -451,6 +451,12 @@ config BT_MESH_SHELL Activate shell module that provides Bluetooth Mesh commands to the console. +config BT_MESH_MODEL_EXTENSIONS + bool "Support for Model extensions" + help + Enable support for the model extension concept, allowing the Access + layer to know about Mesh model relationships. + if BT_SETTINGS config BT_MESH_STORE_TIMEOUT diff --git a/subsys/bluetooth/mesh/access.c b/subsys/bluetooth/mesh/access.c index 30a923ec55f..d3ef4137f0a 100644 --- a/subsys/bluetooth/mesh/access.c +++ b/subsys/bluetooth/mesh/access.c @@ -339,7 +339,7 @@ u16_t bt_mesh_primary_addr(void) return dev_primary_addr; } -u16_t *bt_mesh_model_find_group(struct bt_mesh_model *mod, u16_t addr) +static u16_t *model_group_get(struct bt_mesh_model *mod, u16_t addr) { int i; @@ -352,6 +352,45 @@ u16_t *bt_mesh_model_find_group(struct bt_mesh_model *mod, u16_t addr) return NULL; } +struct find_group_visitor_ctx { + u16_t *entry; + struct bt_mesh_model *mod; + u16_t addr; +}; + +static enum bt_mesh_walk find_group_mod_visitor(struct bt_mesh_model *mod, + u32_t depth, void *user_data) +{ + struct find_group_visitor_ctx *ctx = user_data; + + if (mod->elem_idx != ctx->mod->elem_idx) { + return BT_MESH_WALK_CONTINUE; + } + + ctx->entry = model_group_get(mod, ctx->addr); + if (ctx->entry) { + ctx->mod = mod; + return BT_MESH_WALK_STOP; + } + + return BT_MESH_WALK_CONTINUE; +} + +u16_t *bt_mesh_model_find_group(struct bt_mesh_model **mod, u16_t addr) +{ + struct find_group_visitor_ctx ctx = { + .mod = *mod, + .entry = NULL, + .addr = addr, + }; + + bt_mesh_model_tree_walk(bt_mesh_model_root(*mod), + find_group_mod_visitor, &ctx); + + *mod = ctx.mod; + return ctx.entry; +} + static struct bt_mesh_model *bt_mesh_elem_find_group(struct bt_mesh_elem *elem, u16_t group_addr) { @@ -362,7 +401,7 @@ static struct bt_mesh_model *bt_mesh_elem_find_group(struct bt_mesh_elem *elem, for (i = 0; i < elem->model_count; i++) { model = &elem->models[i]; - match = bt_mesh_model_find_group(model, group_addr); + match = model_group_get(model, group_addr); if (match) { return model; } @@ -371,7 +410,7 @@ static struct bt_mesh_model *bt_mesh_elem_find_group(struct bt_mesh_elem *elem, for (i = 0; i < elem->vnd_model_count; i++) { model = &elem->vnd_models[i]; - match = bt_mesh_model_find_group(model, group_addr); + match = model_group_get(model, group_addr); if (match) { return model; } @@ -424,9 +463,19 @@ static bool model_has_key(struct bt_mesh_model *mod, u16_t key) return false; } +static bool model_has_dst(struct bt_mesh_model *mod, u16_t dst) +{ + if (BT_MESH_ADDR_IS_UNICAST(dst)) { + return (dev_comp->elem[mod->elem_idx].addr == dst); + } else if (BT_MESH_ADDR_IS_GROUP(dst) || BT_MESH_ADDR_IS_VIRTUAL(dst)) { + return bt_mesh_model_find_group(&mod, dst); + } + + return (mod->elem_idx == 0 && bt_mesh_fixed_group_match(dst)); +} + static const struct bt_mesh_model_op *find_op(struct bt_mesh_model *models, - u8_t model_count, u16_t dst, - u16_t app_idx, u32_t opcode, + u8_t model_count, u32_t opcode, struct bt_mesh_model **model) { u8_t i; @@ -436,17 +485,6 @@ static const struct bt_mesh_model_op *find_op(struct bt_mesh_model *models, *model = &models[i]; - if (BT_MESH_ADDR_IS_GROUP(dst) || - BT_MESH_ADDR_IS_VIRTUAL(dst)) { - if (!bt_mesh_model_find_group(*model, dst)) { - continue; - } - } - - if (!model_has_key(*model, app_idx)) { - continue; - } - for (op = (*model)->op; op->func; op++) { if (op->opcode == opcode) { return op; @@ -530,24 +568,13 @@ void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf) for (i = 0; i < dev_comp->elem_count; i++) { struct bt_mesh_elem *elem = &dev_comp->elem[i]; - - if (BT_MESH_ADDR_IS_UNICAST(rx->ctx.recv_dst)) { - if (elem->addr != rx->ctx.recv_dst) { - continue; - } - } else if (BT_MESH_ADDR_IS_GROUP(rx->ctx.recv_dst) || - BT_MESH_ADDR_IS_VIRTUAL(rx->ctx.recv_dst)) { - /* find_op() will do proper model/group matching */ - } else if (i != 0 || - !bt_mesh_fixed_group_match(rx->ctx.recv_dst)) { - continue; - } + struct net_buf_simple_state state; /* SIG models cannot contain 3-byte (vendor) OpCodes, and * vendor models cannot contain SIG (1- or 2-byte) OpCodes, so * we only need to do the lookup in one of the model lists. */ - if (opcode < 0x10000) { + if (BT_MESH_MODEL_OP_LEN(opcode) < 3) { models = elem->models; count = elem->model_count; } else { @@ -555,28 +582,32 @@ void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf) count = elem->vnd_model_count; } - op = find_op(models, count, rx->ctx.recv_dst, rx->ctx.app_idx, - opcode, &model); - if (op) { - struct net_buf_simple_state state; - - if (buf->len < op->min_len) { - BT_ERR("Too short message for OpCode 0x%08x", - opcode); - continue; - } - - /* The callback will likely parse the buffer, so - * store the parsing state in case multiple models - * receive the message. - */ - net_buf_simple_save(buf, &state); - op->func(model, &rx->ctx, buf); - net_buf_simple_restore(buf, &state); - - } else { + op = find_op(models, count, opcode, &model); + if (!op) { BT_DBG("No OpCode 0x%08x for elem %d", opcode, i); + continue; } + + if (!model_has_key(model, rx->ctx.app_idx)) { + continue; + } + + if (!model_has_dst(model, rx->ctx.recv_dst)) { + continue; + } + + if (buf->len < op->min_len) { + BT_ERR("Too short message for OpCode 0x%08x", opcode); + continue; + } + + /* The callback will likely parse the buffer, so + * store the parsing state in case multiple models + * receive the message. + */ + net_buf_simple_save(buf, &state); + op->func(model, &rx->ctx, buf); + net_buf_simple_restore(buf, &state); } } @@ -748,3 +779,68 @@ const struct bt_mesh_comp *bt_mesh_comp_get(void) { return dev_comp; } + +struct bt_mesh_model *bt_mesh_model_root(struct bt_mesh_model *mod) +{ +#ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS + while (mod->next) { + mod = mod->next; + } +#endif + return mod; +} + +void bt_mesh_model_tree_walk(struct bt_mesh_model *root, + enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod, + u32_t depth, + void *user_data), + void *user_data) +{ + struct bt_mesh_model *m = root; + u32_t depth = 0; + + do { + if (cb(m, depth, user_data) == BT_MESH_WALK_STOP) { + return; + } +#ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS + if (m->extends) { + m = m->extends; + depth++; + } else if (m->flags & BT_MESH_MOD_NEXT_IS_PARENT) { + m = m->next->next; + depth--; + } else { + m = m->next; + } +#endif + } while (m && m != root); +} + +#ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS +int bt_mesh_model_extend(struct bt_mesh_model *mod, + struct bt_mesh_model *base_mod) +{ + /* Form a cyclical LCRS tree: + * The extends-pointer points to the first child, and the next-pointer + * points to the next sibling. The last sibling is marked by the + * BT_MESH_MOD_NEXT_IS_PARENT flag, and its next-pointer points back to + * the parent. This way, the whole tree is accessible from any node. + * + * We add children (extend them) by inserting them as the first child. + */ + if (base_mod->next) { + return -EALREADY; + } + + if (mod->extends) { + base_mod->next = mod->extends; + } else { + base_mod->next = mod; + base_mod->flags |= BT_MESH_MOD_NEXT_IS_PARENT; + } + + mod->extends = base_mod; + return 0; +} +#endif diff --git a/subsys/bluetooth/mesh/access.h b/subsys/bluetooth/mesh/access.h index e80beec11a2..e13b778986a 100644 --- a/subsys/bluetooth/mesh/access.h +++ b/subsys/bluetooth/mesh/access.h @@ -12,6 +12,13 @@ enum { BT_MESH_MOD_SUB_PENDING = BIT(1), BT_MESH_MOD_PUB_PENDING = BIT(2), BT_MESH_MOD_DATA_PRESENT = BIT(3), + BT_MESH_MOD_NEXT_IS_PARENT = BIT(4), +}; + +/* Tree walk return codes */ +enum bt_mesh_walk { + BT_MESH_WALK_STOP, + BT_MESH_WALK_CONTINUE, }; void bt_mesh_elem_register(struct bt_mesh_elem *elem, u8_t count); @@ -21,7 +28,14 @@ u8_t bt_mesh_elem_count(void); /* Find local element based on unicast or group address */ struct bt_mesh_elem *bt_mesh_elem_find(u16_t addr); -u16_t *bt_mesh_model_find_group(struct bt_mesh_model *mod, u16_t addr); +struct bt_mesh_model *bt_mesh_model_root(struct bt_mesh_model *mod); +void bt_mesh_model_tree_walk(struct bt_mesh_model *root, + enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod, + u32_t depth, + void *user_data), + void *user_data); + +u16_t *bt_mesh_model_find_group(struct bt_mesh_model **mod, u16_t addr); bool bt_mesh_fixed_group_match(u16_t addr); diff --git a/subsys/bluetooth/mesh/cfg_srv.c b/subsys/bluetooth/mesh/cfg_srv.c index bb35e4079f2..d4453348e84 100644 --- a/subsys/bluetooth/mesh/cfg_srv.c +++ b/subsys/bluetooth/mesh/cfg_srv.c @@ -1378,8 +1378,8 @@ static void mod_sub_add(struct bt_mesh_model *model, struct bt_mesh_elem *elem; u8_t *mod_id; u8_t status; + u16_t *entry; bool vnd; - int i; elem_addr = net_buf_simple_pull_le16(buf); if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { @@ -1412,34 +1412,31 @@ static void mod_sub_add(struct bt_mesh_model *model, goto send_status; } - if (bt_mesh_model_find_group(mod, sub_addr)) { + if (bt_mesh_model_find_group(&mod, sub_addr)) { /* Tried to add existing subscription */ BT_DBG("found existing subscription"); status = STATUS_SUCCESS; goto send_status; } - for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { - if (mod->groups[i] == BT_MESH_ADDR_UNASSIGNED) { - mod->groups[i] = sub_addr; - break; - } - } - - if (i == ARRAY_SIZE(mod->groups)) { + entry = bt_mesh_model_find_group(&mod, BT_MESH_ADDR_UNASSIGNED); + if (!entry) { status = STATUS_INSUFF_RESOURCES; - } else { - status = STATUS_SUCCESS; - - if (IS_ENABLED(CONFIG_BT_SETTINGS)) { - bt_mesh_store_mod_sub(mod); - } - - if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { - bt_mesh_lpn_group_add(sub_addr); - } + goto send_status; } + *entry = sub_addr; + status = STATUS_SUCCESS; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_group_add(sub_addr); + } + + send_status: send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, mod_id, vnd); @@ -1497,7 +1494,7 @@ static void mod_sub_del(struct bt_mesh_model *model, bt_mesh_lpn_group_del(&sub_addr, 1); } - match = bt_mesh_model_find_group(mod, sub_addr); + match = bt_mesh_model_find_group(&mod, sub_addr); if (match) { *match = BT_MESH_ADDR_UNASSIGNED; @@ -1511,6 +1508,18 @@ send_status: mod_id, vnd); } +static enum bt_mesh_walk mod_sub_clear_visitor(struct bt_mesh_model *mod, + u32_t depth, void *user_data) +{ + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); + } + + mod_sub_list_clear(mod); + + return BT_MESH_WALK_CONTINUE; +} + static void mod_sub_overwrite(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) @@ -1553,13 +1562,11 @@ static void mod_sub_overwrite(struct bt_mesh_model *model, goto send_status; } - if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { - bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); - } - - mod_sub_list_clear(mod); if (ARRAY_SIZE(mod->groups) > 0) { + bt_mesh_model_tree_walk(bt_mesh_model_root(mod), + mod_sub_clear_visitor, NULL); + mod->groups[0] = sub_addr; status = STATUS_SUCCESS; @@ -1615,11 +1622,8 @@ static void mod_sub_del_all(struct bt_mesh_model *model, goto send_status; } - if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { - bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); - } - - mod_sub_list_clear(mod); + bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_clear_visitor, + NULL); if (IS_ENABLED(CONFIG_BT_SETTINGS)) { bt_mesh_store_mod_sub(mod); @@ -1632,16 +1636,52 @@ send_status: BT_MESH_ADDR_UNASSIGNED, mod_id, vnd); } +struct mod_sub_list_ctx { + u16_t elem_idx; + struct net_buf_simple *msg; +}; + +static enum bt_mesh_walk mod_sub_list_visitor(struct bt_mesh_model *mod, + u32_t depth, 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 < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] == BT_MESH_ADDR_UNASSIGNED) { + continue; + } + + if (net_buf_simple_tailroom(visit->msg) < + 2 + BT_MESH_MIC_SHORT) { + BT_WARN("No room for all groups"); + return BT_MESH_WALK_STOP; + } + + net_buf_simple_add_le16(visit->msg, mod->groups[i]); + count++; + } + + BT_DBG("sublist: model %u:%x: %u groups", mod->elem_idx, mod->id, + count); + + return BT_MESH_WALK_CONTINUE; +} + static void mod_sub_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { - BT_MESH_MODEL_BUF_DEFINE(msg, OP_MOD_SUB_LIST, - 5 + CONFIG_BT_MESH_MODEL_GROUP_COUNT * 2); + 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; u16_t addr, id; - int i; addr = net_buf_simple_pull_le16(buf); if (!BT_MESH_ADDR_IS_UNICAST(addr)) { @@ -1676,11 +1716,10 @@ static void mod_sub_get(struct bt_mesh_model *model, net_buf_simple_add_le16(&msg, addr); net_buf_simple_add_le16(&msg, id); - for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { - if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) { - net_buf_simple_add_le16(&msg, mod->groups[i]); - } - } + visit_ctx.msg = &msg; + visit_ctx.elem_idx = mod->elem_idx; + bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_list_visitor, + &visit_ctx); send_list: if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { @@ -1692,12 +1731,10 @@ static void mod_sub_get_vnd(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { - BT_MESH_MODEL_BUF_DEFINE(msg, OP_MOD_SUB_LIST_VND, - 7 + CONFIG_BT_MESH_MODEL_GROUP_COUNT * 2); + NET_BUF_SIMPLE_DEFINE(msg, BT_MESH_TX_SDU_MAX); struct bt_mesh_model *mod; struct bt_mesh_elem *elem; u16_t company, addr, id; - int i; addr = net_buf_simple_pull_le16(buf); if (!BT_MESH_ADDR_IS_UNICAST(addr)) { @@ -1736,11 +1773,8 @@ static void mod_sub_get_vnd(struct bt_mesh_model *model, net_buf_simple_add_le16(&msg, company); net_buf_simple_add_le16(&msg, id); - for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { - if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) { - net_buf_simple_add_le16(&msg, mod->groups[i]); - } - } + bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_list_visitor, + &msg); send_list: if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { @@ -1758,9 +1792,9 @@ static void mod_sub_va_add(struct bt_mesh_model *model, struct bt_mesh_elem *elem; u8_t *label_uuid; u8_t *mod_id; + u16_t *entry; u8_t status; bool vnd; - int i; elem_addr = net_buf_simple_pull_le16(buf); if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { @@ -1794,33 +1828,31 @@ static void mod_sub_va_add(struct bt_mesh_model *model, goto send_status; } - if (bt_mesh_model_find_group(mod, sub_addr)) { + if (bt_mesh_model_find_group(&mod, sub_addr)) { /* Tried to add existing subscription */ status = STATUS_SUCCESS; goto send_status; } - for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { - if (mod->groups[i] == BT_MESH_ADDR_UNASSIGNED) { - mod->groups[i] = sub_addr; - break; - } - } - if (i == ARRAY_SIZE(mod->groups)) { + entry = bt_mesh_model_find_group(&mod, BT_MESH_ADDR_UNASSIGNED); + if (!entry) { status = STATUS_INSUFF_RESOURCES; - } else { - if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { - bt_mesh_lpn_group_add(sub_addr); - } - - if (IS_ENABLED(CONFIG_BT_SETTINGS)) { - bt_mesh_store_mod_sub(mod); - } - - status = STATUS_SUCCESS; + 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_store_mod_sub(mod); + } + + status = STATUS_SUCCESS; + send_status: send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, mod_id, vnd); @@ -1876,7 +1908,7 @@ static void mod_sub_va_del(struct bt_mesh_model *model, bt_mesh_lpn_group_del(&sub_addr, 1); } - match = bt_mesh_model_find_group(mod, sub_addr); + match = bt_mesh_model_find_group(&mod, sub_addr); if (match) { *match = BT_MESH_ADDR_UNASSIGNED; @@ -1932,13 +1964,11 @@ static void mod_sub_va_overwrite(struct bt_mesh_model *model, goto send_status; } - if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { - bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); - } - - mod_sub_list_clear(mod); if (ARRAY_SIZE(mod->groups) > 0) { + bt_mesh_model_tree_walk(bt_mesh_model_root(mod), + mod_sub_clear_visitor, NULL); + status = va_add(label_uuid, &sub_addr); if (status == STATUS_SUCCESS) { mod->groups[0] = sub_addr; diff --git a/subsys/bluetooth/mesh/settings.c b/subsys/bluetooth/mesh/settings.c index e0225d07c9f..ee747757f0a 100644 --- a/subsys/bluetooth/mesh/settings.c +++ b/subsys/bluetooth/mesh/settings.c @@ -1502,7 +1502,7 @@ static void store_pending_mod_sub(struct bt_mesh_model *mod, bool vnd) char path[20]; int i, count, err; - for (i = 0, count = 0; i < ARRAY_SIZE(mod->groups); i++) { + for (i = 0, count = 0; i < CONFIG_BT_MESH_MODEL_GROUP_COUNT; i++) { if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) { groups[count++] = mod->groups[i]; }