From e167ca653907ff0979e38636009913bce8cd7374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Narajowski?= Date: Mon, 26 Jul 2021 16:16:01 +0200 Subject: [PATCH] Bluetooth: Mesh: Refactor Mesh Model Extensions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing extension tree does not support all the features that are defined by the specification (e.g. multiple parents). This patch approaches this problem by defining a circular single-linked list of extension models. So for a given model, all models that are on the same list as that model are in some extension relationship with that model. All models on a list represent a single connected component of an extension graph but without defining specific relationships between each pair of models. This list is used to manage a shared subscription list as per the Mesh Profile Specification: ```4.2.4 Subscription List Within an element, each model has a separate instance of a Subscription List, unless the model extends another model on that element. Instances of models that extend other models (i.e., all models within an extension relation tree) shall share a single instance of a Subscription List per element. ``` Signed-off-by: MichaƂ Narajowski --- include/bluetooth/mesh/access.h | 29 +++++---- subsys/bluetooth/mesh/access.c | 112 +++++++++++++++----------------- subsys/bluetooth/mesh/access.h | 10 ++- subsys/bluetooth/mesh/cfg_srv.c | 21 ++---- 4 files changed, 80 insertions(+), 92 deletions(-) diff --git a/include/bluetooth/mesh/access.h b/include/bluetooth/mesh/access.h index e9f387bf51a..7385fb9692b 100644 --- a/include/bluetooth/mesh/access.h +++ b/include/bluetooth/mesh/access.h @@ -509,11 +509,10 @@ struct bt_mesh_model { const struct bt_mesh_model_cb * const cb; #ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS - /* Pointer to the next model in a model extension tree. */ + /* Pointer to the next model in a model extension list. */ 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; }; @@ -634,25 +633,31 @@ int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd, * 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. + * itself be extended. * - * A set of models that extend each other form a model extension tree. + * A set of models that extend each other form a model extension list. * - * All models in an extension tree share one subscription list per element. The + * All models in an extension list 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 + * extension list and element, giving the models extended subscription list * capacity. * - * @param mod Mesh model. - * @param base_mod The model being extended. + * @param extending_mod Mesh model that is extending the base model. + * @param 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, +int bt_mesh_model_extend(struct bt_mesh_model *extending_mod, struct bt_mesh_model *base_mod); +/** @brief Check if model is extended by another model. + * + * @param model The model to check. + * + * @retval true If model is extended by another model, otherwise false + */ +bool bt_mesh_model_is_extended(struct bt_mesh_model *model); + /** Node Composition */ struct bt_mesh_comp { uint16_t cid; /**< Company ID */ diff --git a/subsys/bluetooth/mesh/access.c b/subsys/bluetooth/mesh/access.c index cec98380c5f..0780841e1dc 100644 --- a/subsys/bluetooth/mesh/access.c +++ b/subsys/bluetooth/mesh/access.c @@ -32,7 +32,7 @@ enum { BT_MESH_MOD_BIND_PENDING = BIT(0), BT_MESH_MOD_SUB_PENDING = BIT(1), BT_MESH_MOD_PUB_PENDING = BIT(2), - BT_MESH_MOD_NEXT_IS_PARENT = BIT(3), + BT_MESH_MOD_EXTENDED = BIT(3), }; /* Model publication information for persistent storage. */ @@ -422,8 +422,7 @@ struct find_group_visitor_ctx { uint16_t addr; }; -static enum bt_mesh_walk find_group_mod_visitor(struct bt_mesh_model *mod, - uint32_t depth, void *user_data) +static enum bt_mesh_walk find_group_mod_visitor(struct bt_mesh_model *mod, void *user_data) { struct find_group_visitor_ctx *ctx = user_data; @@ -448,8 +447,7 @@ uint16_t *bt_mesh_model_find_group(struct bt_mesh_model **mod, uint16_t addr) .addr = addr, }; - bt_mesh_model_tree_walk(bt_mesh_model_root(*mod), - find_group_mod_visitor, &ctx); + bt_mesh_model_extensions_walk(*mod, find_group_mod_visitor, &ctx); *mod = ctx.mod; return ctx.entry; @@ -812,80 +810,74 @@ 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) +void bt_mesh_model_extensions_walk(struct bt_mesh_model *model, + enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod, + void *user_data), + void *user_data) { -#ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS - while (mod->next) { - mod = mod->next; +#ifndef CONFIG_BT_MESH_MODEL_EXTENSIONS + (void)cb(model, user_data); + return; +#else + struct bt_mesh_model *it; + + if (model->next == NULL) { + (void)cb(model, user_data); + return; } -#endif - return mod; -} -void bt_mesh_model_tree_walk(struct bt_mesh_model *root, - enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod, - uint32_t depth, - void *user_data), - void *user_data) -{ - struct bt_mesh_model *m = root; - int depth = 0; - /* 'skip' is set to true when we ascend from child to parent node. - * In that case, we want to skip calling the callback on the parent - * node and we don't want to descend onto a child node as those - * nodes have already been visited. - */ - bool skip = false; - - do { - if (!skip && - cb(m, (uint32_t)depth, user_data) == BT_MESH_WALK_STOP) { + for (it = model; (it != NULL) && (it->next != model); it = it->next) { + if (cb(it, user_data) == BT_MESH_WALK_STOP) { return; } -#ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS - if (!skip && m->extends) { - m = m->extends; - depth++; - } else if (m->flags & BT_MESH_MOD_NEXT_IS_PARENT) { - m = m->next; - depth--; - skip = true; - } else { - m = m->next; - skip = false; - } + } #endif - } while (m && depth > 0); } #ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS -int bt_mesh_model_extend(struct bt_mesh_model *mod, - struct bt_mesh_model *base_mod) +int bt_mesh_model_extend(struct bt_mesh_model *extending_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; + struct bt_mesh_model *a = extending_mod; + struct bt_mesh_model *b = base_mod; + struct bt_mesh_model *a_next = a->next; + struct bt_mesh_model *b_next = b->next; + struct bt_mesh_model *it; + + base_mod->flags |= BT_MESH_MOD_EXTENDED; + + if (a == b) { + return 0; } - if (mod->extends) { - base_mod->next = mod->extends; + /* Check if a's list contains b */ + for (it = a; (it != NULL) && (it->next != a); it = it->next) { + if (it == b) { + return 0; + } + } + + /* Merge lists */ + if (a_next) { + b->next = a_next; } else { - base_mod->next = mod; - base_mod->flags |= BT_MESH_MOD_NEXT_IS_PARENT; + b->next = a; + } + + if (b_next) { + a->next = b_next; + } else { + a->next = b; } - mod->extends = base_mod; return 0; } #endif +bool bt_mesh_model_is_extended(struct bt_mesh_model *model) +{ + return model->flags & BT_MESH_MOD_EXTENDED; +} + static int mod_set_bind(struct bt_mesh_model *mod, size_t len_rd, settings_read_cb read_cb, void *cb_arg) { diff --git a/subsys/bluetooth/mesh/access.h b/subsys/bluetooth/mesh/access.h index 855a9ee168e..17b1944b883 100644 --- a/subsys/bluetooth/mesh/access.h +++ b/subsys/bluetooth/mesh/access.h @@ -19,12 +19,10 @@ struct bt_mesh_elem *bt_mesh_elem_find(uint16_t addr); bool bt_mesh_has_addr(uint16_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, - uint32_t depth, - void *user_data), - void *user_data); +void bt_mesh_model_extensions_walk(struct bt_mesh_model *root, + enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod, + void *user_data), + void *user_data); uint16_t *bt_mesh_model_find_group(struct bt_mesh_model **mod, uint16_t addr); diff --git a/subsys/bluetooth/mesh/cfg_srv.c b/subsys/bluetooth/mesh/cfg_srv.c index 302879019ec..fe96129dbe4 100644 --- a/subsys/bluetooth/mesh/cfg_srv.c +++ b/subsys/bluetooth/mesh/cfg_srv.c @@ -1145,8 +1145,7 @@ send_status: mod_id, vnd); } -static enum bt_mesh_walk mod_sub_clear_visitor(struct bt_mesh_model *mod, - uint32_t depth, void *user_data) +static enum bt_mesh_walk mod_sub_clear_visitor(struct bt_mesh_model *mod, void *user_data) { if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); @@ -1206,8 +1205,7 @@ static int mod_sub_overwrite(struct bt_mesh_model *model, if (ARRAY_SIZE(mod->groups) > 0) { - bt_mesh_model_tree_walk(bt_mesh_model_root(mod), - mod_sub_clear_visitor, NULL); + bt_mesh_model_extensions_walk(mod, mod_sub_clear_visitor, NULL); mod->groups[0] = sub_addr; status = STATUS_SUCCESS; @@ -1269,8 +1267,7 @@ static int mod_sub_del_all(struct bt_mesh_model *model, goto send_status; } - bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_clear_visitor, - NULL); + bt_mesh_model_extensions_walk(mod, mod_sub_clear_visitor, NULL); if (IS_ENABLED(CONFIG_BT_SETTINGS)) { bt_mesh_model_sub_store(mod); @@ -1288,8 +1285,7 @@ struct mod_sub_list_ctx { struct net_buf_simple *msg; }; -static enum bt_mesh_walk mod_sub_list_visitor(struct bt_mesh_model *mod, - uint32_t depth, void *ctx) +static enum bt_mesh_walk mod_sub_list_visitor(struct bt_mesh_model *mod, void *ctx) { struct mod_sub_list_ctx *visit = ctx; int count = 0; @@ -1365,8 +1361,7 @@ static int mod_sub_get(struct bt_mesh_model *model, 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); + bt_mesh_model_extensions_walk(mod, mod_sub_list_visitor, &visit_ctx); send_list: if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { @@ -1425,8 +1420,7 @@ static int mod_sub_get_vnd(struct bt_mesh_model *model, 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); + bt_mesh_model_extensions_walk(mod, mod_sub_list_visitor, &visit_ctx); send_list: if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { @@ -1639,8 +1633,7 @@ static int mod_sub_va_overwrite(struct bt_mesh_model *model, status = bt_mesh_va_add(label_uuid, &sub_addr); if (status == STATUS_SUCCESS) { - bt_mesh_model_tree_walk(bt_mesh_model_root(mod), - mod_sub_clear_visitor, NULL); + bt_mesh_model_extensions_walk(mod, mod_sub_clear_visitor, NULL); mod->groups[0] = sub_addr; if (IS_ENABLED(CONFIG_BT_SETTINGS)) {