Bluetooth: Mesh: Refactor Mesh Model Extensions
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 <michal.narajowski@codecoup.pl>
This commit is contained in:
parent
6c90860a69
commit
e167ca6539
4 changed files with 80 additions and 92 deletions
|
@ -509,11 +509,10 @@ struct bt_mesh_model {
|
||||||
const struct bt_mesh_model_cb * const cb;
|
const struct bt_mesh_model_cb * const cb;
|
||||||
|
|
||||||
#ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS
|
#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;
|
struct bt_mesh_model *next;
|
||||||
/* Pointer to the first model this model extends. */
|
|
||||||
struct bt_mesh_model *extends;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Model-specific user data */
|
/** Model-specific user data */
|
||||||
void *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
|
* 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.
|
* 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
|
* 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
|
* itself be extended.
|
||||||
* extended by one other model.
|
|
||||||
*
|
*
|
||||||
* 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
|
* 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.
|
* capacity.
|
||||||
*
|
*
|
||||||
* @param mod Mesh model.
|
* @param extending_mod Mesh model that is extending the base model.
|
||||||
* @param base_mod The model being extended.
|
* @param base_mod The model being extended.
|
||||||
*
|
*
|
||||||
* @retval 0 Successfully extended the base_mod model.
|
* @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);
|
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 */
|
/** Node Composition */
|
||||||
struct bt_mesh_comp {
|
struct bt_mesh_comp {
|
||||||
uint16_t cid; /**< Company ID */
|
uint16_t cid; /**< Company ID */
|
||||||
|
|
|
@ -32,7 +32,7 @@ enum {
|
||||||
BT_MESH_MOD_BIND_PENDING = BIT(0),
|
BT_MESH_MOD_BIND_PENDING = BIT(0),
|
||||||
BT_MESH_MOD_SUB_PENDING = BIT(1),
|
BT_MESH_MOD_SUB_PENDING = BIT(1),
|
||||||
BT_MESH_MOD_PUB_PENDING = BIT(2),
|
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. */
|
/* Model publication information for persistent storage. */
|
||||||
|
@ -422,8 +422,7 @@ struct find_group_visitor_ctx {
|
||||||
uint16_t addr;
|
uint16_t addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum bt_mesh_walk find_group_mod_visitor(struct bt_mesh_model *mod,
|
static enum bt_mesh_walk find_group_mod_visitor(struct bt_mesh_model *mod, void *user_data)
|
||||||
uint32_t depth, void *user_data)
|
|
||||||
{
|
{
|
||||||
struct find_group_visitor_ctx *ctx = 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,
|
.addr = addr,
|
||||||
};
|
};
|
||||||
|
|
||||||
bt_mesh_model_tree_walk(bt_mesh_model_root(*mod),
|
bt_mesh_model_extensions_walk(*mod, find_group_mod_visitor, &ctx);
|
||||||
find_group_mod_visitor, &ctx);
|
|
||||||
|
|
||||||
*mod = ctx.mod;
|
*mod = ctx.mod;
|
||||||
return ctx.entry;
|
return ctx.entry;
|
||||||
|
@ -812,80 +810,74 @@ const struct bt_mesh_comp *bt_mesh_comp_get(void)
|
||||||
return dev_comp;
|
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
|
#ifndef CONFIG_BT_MESH_MODEL_EXTENSIONS
|
||||||
while (mod->next) {
|
(void)cb(model, user_data);
|
||||||
mod = mod->next;
|
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,
|
for (it = model; (it != NULL) && (it->next != model); it = it->next) {
|
||||||
enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod,
|
if (cb(it, user_data) == BT_MESH_WALK_STOP) {
|
||||||
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) {
|
|
||||||
return;
|
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
|
#endif
|
||||||
} while (m && depth > 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS
|
#ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS
|
||||||
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)
|
||||||
struct bt_mesh_model *base_mod)
|
|
||||||
{
|
{
|
||||||
/* Form a cyclical LCRS tree:
|
struct bt_mesh_model *a = extending_mod;
|
||||||
* The extends-pointer points to the first child, and the next-pointer
|
struct bt_mesh_model *b = base_mod;
|
||||||
* points to the next sibling. The last sibling is marked by the
|
struct bt_mesh_model *a_next = a->next;
|
||||||
* BT_MESH_MOD_NEXT_IS_PARENT flag, and its next-pointer points back to
|
struct bt_mesh_model *b_next = b->next;
|
||||||
* the parent. This way, the whole tree is accessible from any node.
|
struct bt_mesh_model *it;
|
||||||
*
|
|
||||||
* We add children (extend them) by inserting them as the first child.
|
base_mod->flags |= BT_MESH_MOD_EXTENDED;
|
||||||
*/
|
|
||||||
if (base_mod->next) {
|
if (a == b) {
|
||||||
return -EALREADY;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mod->extends) {
|
/* Check if a's list contains b */
|
||||||
base_mod->next = mod->extends;
|
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 {
|
} else {
|
||||||
base_mod->next = mod;
|
b->next = a;
|
||||||
base_mod->flags |= BT_MESH_MOD_NEXT_IS_PARENT;
|
}
|
||||||
|
|
||||||
|
if (b_next) {
|
||||||
|
a->next = b_next;
|
||||||
|
} else {
|
||||||
|
a->next = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
mod->extends = base_mod;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#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,
|
static int mod_set_bind(struct bt_mesh_model *mod, size_t len_rd,
|
||||||
settings_read_cb read_cb, void *cb_arg)
|
settings_read_cb read_cb, void *cb_arg)
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,12 +19,10 @@ struct bt_mesh_elem *bt_mesh_elem_find(uint16_t addr);
|
||||||
|
|
||||||
bool bt_mesh_has_addr(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_extensions_walk(struct bt_mesh_model *root,
|
||||||
void bt_mesh_model_tree_walk(struct bt_mesh_model *root,
|
enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod,
|
||||||
enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod,
|
void *user_data),
|
||||||
uint32_t depth,
|
void *user_data);
|
||||||
void *user_data),
|
|
||||||
void *user_data);
|
|
||||||
|
|
||||||
uint16_t *bt_mesh_model_find_group(struct bt_mesh_model **mod, uint16_t addr);
|
uint16_t *bt_mesh_model_find_group(struct bt_mesh_model **mod, uint16_t addr);
|
||||||
|
|
||||||
|
|
|
@ -1145,8 +1145,7 @@ send_status:
|
||||||
mod_id, vnd);
|
mod_id, vnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum bt_mesh_walk mod_sub_clear_visitor(struct bt_mesh_model *mod,
|
static enum bt_mesh_walk mod_sub_clear_visitor(struct bt_mesh_model *mod, void *user_data)
|
||||||
uint32_t depth, void *user_data)
|
|
||||||
{
|
{
|
||||||
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
|
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
|
||||||
bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups));
|
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) {
|
if (ARRAY_SIZE(mod->groups) > 0) {
|
||||||
bt_mesh_model_tree_walk(bt_mesh_model_root(mod),
|
bt_mesh_model_extensions_walk(mod, mod_sub_clear_visitor, NULL);
|
||||||
mod_sub_clear_visitor, NULL);
|
|
||||||
|
|
||||||
mod->groups[0] = sub_addr;
|
mod->groups[0] = sub_addr;
|
||||||
status = STATUS_SUCCESS;
|
status = STATUS_SUCCESS;
|
||||||
|
@ -1269,8 +1267,7 @@ static int mod_sub_del_all(struct bt_mesh_model *model,
|
||||||
goto send_status;
|
goto send_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_clear_visitor,
|
bt_mesh_model_extensions_walk(mod, mod_sub_clear_visitor, NULL);
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||||
bt_mesh_model_sub_store(mod);
|
bt_mesh_model_sub_store(mod);
|
||||||
|
@ -1288,8 +1285,7 @@ struct mod_sub_list_ctx {
|
||||||
struct net_buf_simple *msg;
|
struct net_buf_simple *msg;
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum bt_mesh_walk mod_sub_list_visitor(struct bt_mesh_model *mod,
|
static enum bt_mesh_walk mod_sub_list_visitor(struct bt_mesh_model *mod, void *ctx)
|
||||||
uint32_t depth, void *ctx)
|
|
||||||
{
|
{
|
||||||
struct mod_sub_list_ctx *visit = ctx;
|
struct mod_sub_list_ctx *visit = ctx;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
@ -1365,8 +1361,7 @@ static int mod_sub_get(struct bt_mesh_model *model,
|
||||||
|
|
||||||
visit_ctx.msg = &msg;
|
visit_ctx.msg = &msg;
|
||||||
visit_ctx.elem_idx = mod->elem_idx;
|
visit_ctx.elem_idx = mod->elem_idx;
|
||||||
bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_list_visitor,
|
bt_mesh_model_extensions_walk(mod, mod_sub_list_visitor, &visit_ctx);
|
||||||
&visit_ctx);
|
|
||||||
|
|
||||||
send_list:
|
send_list:
|
||||||
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
|
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.msg = &msg;
|
||||||
visit_ctx.elem_idx = mod->elem_idx;
|
visit_ctx.elem_idx = mod->elem_idx;
|
||||||
bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_list_visitor,
|
bt_mesh_model_extensions_walk(mod, mod_sub_list_visitor, &visit_ctx);
|
||||||
&visit_ctx);
|
|
||||||
|
|
||||||
send_list:
|
send_list:
|
||||||
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
|
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);
|
status = bt_mesh_va_add(label_uuid, &sub_addr);
|
||||||
if (status == STATUS_SUCCESS) {
|
if (status == STATUS_SUCCESS) {
|
||||||
bt_mesh_model_tree_walk(bt_mesh_model_root(mod),
|
bt_mesh_model_extensions_walk(mod, mod_sub_clear_visitor, NULL);
|
||||||
mod_sub_clear_visitor, NULL);
|
|
||||||
mod->groups[0] = sub_addr;
|
mod->groups[0] = sub_addr;
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue