Bluetooth: Mesh: Model extension concept
Adds the model extension concept to the access layer, as described in the Mesh Profile Specification, Section 2.3.6. Extensions are implemented as a tree, using two pointers in each model: The extends pointer points to the first extended model, and the next pointer points to the next sibling or (if the NEXT_IS_PARENT flag is set) the parent model in the tree, forming a cyclical "Left-child right-sibling" (LCRS) tree. The tree root can be obtained by calling bt_mesh_model_root_get(), and the extended models can be walked by calling bt_mesh_model_tree_walk(). According to the Mesh Profile Specification Section 4.2.3, all models in the same extension tree share one subscription list per element. This is implemented by walking the model's extension tree, and pooling the subscription lists of all models in the same element into one. If the config server adds a subscription to a model, it may be stored in any of the model tree's models' subscription lists. No two models in the same extension tree and element will have duplicate groups listed. This allows us to increase extended models' capacity for subscriptions significantly. Signed-off-by: Trond Einar Snekvik <Trond.Einar.Snekvik@nordicsemi.no>
This commit is contained in:
parent
0b27cacabd
commit
0151d6dc33
8 changed files with 302 additions and 124 deletions
|
@ -1979,6 +1979,7 @@ PREDEFINED = "CONFIG_SYS_CLOCK_EXISTS=y" \
|
||||||
"CONFIG_USERSPACE=y" \
|
"CONFIG_USERSPACE=y" \
|
||||||
"CONFIG_BT_BREDR=y" \
|
"CONFIG_BT_BREDR=y" \
|
||||||
"CONFIG_FLASH_PAGE_LAYOUT=y" \
|
"CONFIG_FLASH_PAGE_LAYOUT=y" \
|
||||||
|
"CONFIG_BT_MESH_MODEL_EXTENSIONS=y" \
|
||||||
"NET_MGMT_DEFINE_REQUEST_HANDLER(x)=" \
|
"NET_MGMT_DEFINE_REQUEST_HANDLER(x)=" \
|
||||||
"DEVICE_DEFINE()=" \
|
"DEVICE_DEFINE()=" \
|
||||||
"DEVICE_AND_API_INIT()=" \
|
"DEVICE_AND_API_INIT()=" \
|
||||||
|
|
|
@ -501,7 +501,7 @@ struct bt_mesh_model {
|
||||||
/* Internal information, mainly for persistent storage */
|
/* Internal information, mainly for persistent storage */
|
||||||
u8_t elem_idx; /* Belongs to Nth element */
|
u8_t elem_idx; /* Belongs to Nth element */
|
||||||
u8_t mod_idx; /* Is the Nth model in the 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 */
|
/* Model Publication */
|
||||||
struct bt_mesh_model_pub * const pub;
|
struct bt_mesh_model_pub * const pub;
|
||||||
|
@ -517,6 +517,12 @@ struct bt_mesh_model {
|
||||||
/* Model callback structure. */
|
/* Model callback structure. */
|
||||||
const struct bt_mesh_model_cb * const cb;
|
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 */
|
/* Model-specific user data */
|
||||||
void *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,
|
int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd,
|
||||||
const void *data, size_t data_len);
|
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 */
|
/** Node Composition */
|
||||||
struct bt_mesh_comp {
|
struct bt_mesh_comp {
|
||||||
u16_t cid;
|
u16_t cid;
|
||||||
|
|
|
@ -29,6 +29,7 @@ CONFIG_BT_L2CAP_TX_MTU=69
|
||||||
CONFIG_BT_L2CAP_TX_BUF_COUNT=5
|
CONFIG_BT_L2CAP_TX_BUF_COUNT=5
|
||||||
|
|
||||||
CONFIG_BT_MESH=y
|
CONFIG_BT_MESH=y
|
||||||
|
CONFIG_BT_MESH_MODEL_EXTENSIONS=y
|
||||||
CONFIG_BT_MESH_RELAY=y
|
CONFIG_BT_MESH_RELAY=y
|
||||||
CONFIG_BT_MESH_LOW_POWER=n
|
CONFIG_BT_MESH_LOW_POWER=n
|
||||||
CONFIG_BT_MESH_FRIEND=y
|
CONFIG_BT_MESH_FRIEND=y
|
||||||
|
|
|
@ -451,6 +451,12 @@ config BT_MESH_SHELL
|
||||||
Activate shell module that provides Bluetooth Mesh commands to
|
Activate shell module that provides Bluetooth Mesh commands to
|
||||||
the console.
|
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
|
if BT_SETTINGS
|
||||||
|
|
||||||
config BT_MESH_STORE_TIMEOUT
|
config BT_MESH_STORE_TIMEOUT
|
||||||
|
|
|
@ -339,7 +339,7 @@ u16_t bt_mesh_primary_addr(void)
|
||||||
return dev_primary_addr;
|
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;
|
int i;
|
||||||
|
|
||||||
|
@ -352,6 +352,45 @@ u16_t *bt_mesh_model_find_group(struct bt_mesh_model *mod, u16_t addr)
|
||||||
return NULL;
|
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,
|
static struct bt_mesh_model *bt_mesh_elem_find_group(struct bt_mesh_elem *elem,
|
||||||
u16_t group_addr)
|
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++) {
|
for (i = 0; i < elem->model_count; i++) {
|
||||||
model = &elem->models[i];
|
model = &elem->models[i];
|
||||||
|
|
||||||
match = bt_mesh_model_find_group(model, group_addr);
|
match = model_group_get(model, group_addr);
|
||||||
if (match) {
|
if (match) {
|
||||||
return model;
|
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++) {
|
for (i = 0; i < elem->vnd_model_count; i++) {
|
||||||
model = &elem->vnd_models[i];
|
model = &elem->vnd_models[i];
|
||||||
|
|
||||||
match = bt_mesh_model_find_group(model, group_addr);
|
match = model_group_get(model, group_addr);
|
||||||
if (match) {
|
if (match) {
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
@ -424,9 +463,19 @@ static bool model_has_key(struct bt_mesh_model *mod, u16_t key)
|
||||||
return false;
|
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,
|
static const struct bt_mesh_model_op *find_op(struct bt_mesh_model *models,
|
||||||
u8_t model_count, u16_t dst,
|
u8_t model_count, u32_t opcode,
|
||||||
u16_t app_idx, u32_t opcode,
|
|
||||||
struct bt_mesh_model **model)
|
struct bt_mesh_model **model)
|
||||||
{
|
{
|
||||||
u8_t i;
|
u8_t i;
|
||||||
|
@ -436,17 +485,6 @@ static const struct bt_mesh_model_op *find_op(struct bt_mesh_model *models,
|
||||||
|
|
||||||
*model = &models[i];
|
*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++) {
|
for (op = (*model)->op; op->func; op++) {
|
||||||
if (op->opcode == opcode) {
|
if (op->opcode == opcode) {
|
||||||
return op;
|
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++) {
|
for (i = 0; i < dev_comp->elem_count; i++) {
|
||||||
struct bt_mesh_elem *elem = &dev_comp->elem[i];
|
struct bt_mesh_elem *elem = &dev_comp->elem[i];
|
||||||
|
struct net_buf_simple_state state;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SIG models cannot contain 3-byte (vendor) OpCodes, and
|
/* SIG models cannot contain 3-byte (vendor) OpCodes, and
|
||||||
* vendor models cannot contain SIG (1- or 2-byte) OpCodes, so
|
* vendor models cannot contain SIG (1- or 2-byte) OpCodes, so
|
||||||
* we only need to do the lookup in one of the model lists.
|
* 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;
|
models = elem->models;
|
||||||
count = elem->model_count;
|
count = elem->model_count;
|
||||||
} else {
|
} else {
|
||||||
|
@ -555,14 +582,22 @@ void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf)
|
||||||
count = elem->vnd_model_count;
|
count = elem->vnd_model_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
op = find_op(models, count, rx->ctx.recv_dst, rx->ctx.app_idx,
|
op = find_op(models, count, opcode, &model);
|
||||||
opcode, &model);
|
if (!op) {
|
||||||
if (op) {
|
BT_DBG("No OpCode 0x%08x for elem %d", opcode, i);
|
||||||
struct net_buf_simple_state state;
|
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) {
|
if (buf->len < op->min_len) {
|
||||||
BT_ERR("Too short message for OpCode 0x%08x",
|
BT_ERR("Too short message for OpCode 0x%08x", opcode);
|
||||||
opcode);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -573,10 +608,6 @@ void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf)
|
||||||
net_buf_simple_save(buf, &state);
|
net_buf_simple_save(buf, &state);
|
||||||
op->func(model, &rx->ctx, buf);
|
op->func(model, &rx->ctx, buf);
|
||||||
net_buf_simple_restore(buf, &state);
|
net_buf_simple_restore(buf, &state);
|
||||||
|
|
||||||
} else {
|
|
||||||
BT_DBG("No OpCode 0x%08x for elem %d", opcode, i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -748,3 +779,68 @@ 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)
|
||||||
|
{
|
||||||
|
#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
|
||||||
|
|
|
@ -12,6 +12,13 @@ enum {
|
||||||
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_DATA_PRESENT = BIT(3),
|
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);
|
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 */
|
/* Find local element based on unicast or group address */
|
||||||
struct bt_mesh_elem *bt_mesh_elem_find(u16_t addr);
|
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);
|
bool bt_mesh_fixed_group_match(u16_t addr);
|
||||||
|
|
||||||
|
|
|
@ -1378,8 +1378,8 @@ static void mod_sub_add(struct bt_mesh_model *model,
|
||||||
struct bt_mesh_elem *elem;
|
struct bt_mesh_elem *elem;
|
||||||
u8_t *mod_id;
|
u8_t *mod_id;
|
||||||
u8_t status;
|
u8_t status;
|
||||||
|
u16_t *entry;
|
||||||
bool vnd;
|
bool vnd;
|
||||||
int i;
|
|
||||||
|
|
||||||
elem_addr = net_buf_simple_pull_le16(buf);
|
elem_addr = net_buf_simple_pull_le16(buf);
|
||||||
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
|
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
|
||||||
|
@ -1412,23 +1412,20 @@ static void mod_sub_add(struct bt_mesh_model *model,
|
||||||
goto send_status;
|
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 */
|
/* Tried to add existing subscription */
|
||||||
BT_DBG("found existing subscription");
|
BT_DBG("found existing subscription");
|
||||||
status = STATUS_SUCCESS;
|
status = STATUS_SUCCESS;
|
||||||
goto send_status;
|
goto send_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(mod->groups); i++) {
|
entry = bt_mesh_model_find_group(&mod, BT_MESH_ADDR_UNASSIGNED);
|
||||||
if (mod->groups[i] == BT_MESH_ADDR_UNASSIGNED) {
|
if (!entry) {
|
||||||
mod->groups[i] = sub_addr;
|
status = STATUS_INSUFF_RESOURCES;
|
||||||
break;
|
goto send_status;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == ARRAY_SIZE(mod->groups)) {
|
*entry = sub_addr;
|
||||||
status = STATUS_INSUFF_RESOURCES;
|
|
||||||
} else {
|
|
||||||
status = STATUS_SUCCESS;
|
status = STATUS_SUCCESS;
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||||
|
@ -1438,7 +1435,7 @@ static void mod_sub_add(struct bt_mesh_model *model,
|
||||||
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
|
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
|
||||||
bt_mesh_lpn_group_add(sub_addr);
|
bt_mesh_lpn_group_add(sub_addr);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
send_status:
|
send_status:
|
||||||
send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
|
send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
|
||||||
|
@ -1497,7 +1494,7 @@ static void mod_sub_del(struct bt_mesh_model *model,
|
||||||
bt_mesh_lpn_group_del(&sub_addr, 1);
|
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) {
|
if (match) {
|
||||||
*match = BT_MESH_ADDR_UNASSIGNED;
|
*match = BT_MESH_ADDR_UNASSIGNED;
|
||||||
|
|
||||||
|
@ -1511,6 +1508,18 @@ send_status:
|
||||||
mod_id, vnd);
|
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,
|
static void mod_sub_overwrite(struct bt_mesh_model *model,
|
||||||
struct bt_mesh_msg_ctx *ctx,
|
struct bt_mesh_msg_ctx *ctx,
|
||||||
struct net_buf_simple *buf)
|
struct net_buf_simple *buf)
|
||||||
|
@ -1553,13 +1562,11 @@ static void mod_sub_overwrite(struct bt_mesh_model *model,
|
||||||
goto send_status;
|
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) {
|
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;
|
mod->groups[0] = sub_addr;
|
||||||
status = STATUS_SUCCESS;
|
status = STATUS_SUCCESS;
|
||||||
|
|
||||||
|
@ -1615,11 +1622,8 @@ static void mod_sub_del_all(struct bt_mesh_model *model,
|
||||||
goto send_status;
|
goto send_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
|
bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_clear_visitor,
|
||||||
bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups));
|
NULL);
|
||||||
}
|
|
||||||
|
|
||||||
mod_sub_list_clear(mod);
|
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||||
bt_mesh_store_mod_sub(mod);
|
bt_mesh_store_mod_sub(mod);
|
||||||
|
@ -1632,16 +1636,52 @@ send_status:
|
||||||
BT_MESH_ADDR_UNASSIGNED, mod_id, vnd);
|
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,
|
static void mod_sub_get(struct bt_mesh_model *model,
|
||||||
struct bt_mesh_msg_ctx *ctx,
|
struct bt_mesh_msg_ctx *ctx,
|
||||||
struct net_buf_simple *buf)
|
struct net_buf_simple *buf)
|
||||||
{
|
{
|
||||||
BT_MESH_MODEL_BUF_DEFINE(msg, OP_MOD_SUB_LIST,
|
NET_BUF_SIMPLE_DEFINE(msg, BT_MESH_TX_SDU_MAX);
|
||||||
5 + CONFIG_BT_MESH_MODEL_GROUP_COUNT * 2);
|
struct mod_sub_list_ctx visit_ctx;
|
||||||
struct bt_mesh_model *mod;
|
struct bt_mesh_model *mod;
|
||||||
struct bt_mesh_elem *elem;
|
struct bt_mesh_elem *elem;
|
||||||
u16_t addr, id;
|
u16_t addr, id;
|
||||||
int i;
|
|
||||||
|
|
||||||
addr = net_buf_simple_pull_le16(buf);
|
addr = net_buf_simple_pull_le16(buf);
|
||||||
if (!BT_MESH_ADDR_IS_UNICAST(addr)) {
|
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, addr);
|
||||||
net_buf_simple_add_le16(&msg, id);
|
net_buf_simple_add_le16(&msg, id);
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(mod->groups); i++) {
|
visit_ctx.msg = &msg;
|
||||||
if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) {
|
visit_ctx.elem_idx = mod->elem_idx;
|
||||||
net_buf_simple_add_le16(&msg, mod->groups[i]);
|
bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_list_visitor,
|
||||||
}
|
&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)) {
|
||||||
|
@ -1692,12 +1731,10 @@ static void mod_sub_get_vnd(struct bt_mesh_model *model,
|
||||||
struct bt_mesh_msg_ctx *ctx,
|
struct bt_mesh_msg_ctx *ctx,
|
||||||
struct net_buf_simple *buf)
|
struct net_buf_simple *buf)
|
||||||
{
|
{
|
||||||
BT_MESH_MODEL_BUF_DEFINE(msg, OP_MOD_SUB_LIST_VND,
|
NET_BUF_SIMPLE_DEFINE(msg, BT_MESH_TX_SDU_MAX);
|
||||||
7 + CONFIG_BT_MESH_MODEL_GROUP_COUNT * 2);
|
|
||||||
struct bt_mesh_model *mod;
|
struct bt_mesh_model *mod;
|
||||||
struct bt_mesh_elem *elem;
|
struct bt_mesh_elem *elem;
|
||||||
u16_t company, addr, id;
|
u16_t company, addr, id;
|
||||||
int i;
|
|
||||||
|
|
||||||
addr = net_buf_simple_pull_le16(buf);
|
addr = net_buf_simple_pull_le16(buf);
|
||||||
if (!BT_MESH_ADDR_IS_UNICAST(addr)) {
|
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, company);
|
||||||
net_buf_simple_add_le16(&msg, id);
|
net_buf_simple_add_le16(&msg, id);
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(mod->groups); i++) {
|
bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_list_visitor,
|
||||||
if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) {
|
&msg);
|
||||||
net_buf_simple_add_le16(&msg, mod->groups[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
send_list:
|
send_list:
|
||||||
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
|
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;
|
struct bt_mesh_elem *elem;
|
||||||
u8_t *label_uuid;
|
u8_t *label_uuid;
|
||||||
u8_t *mod_id;
|
u8_t *mod_id;
|
||||||
|
u16_t *entry;
|
||||||
u8_t status;
|
u8_t status;
|
||||||
bool vnd;
|
bool vnd;
|
||||||
int i;
|
|
||||||
|
|
||||||
elem_addr = net_buf_simple_pull_le16(buf);
|
elem_addr = net_buf_simple_pull_le16(buf);
|
||||||
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
|
if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
|
||||||
|
@ -1794,22 +1828,21 @@ static void mod_sub_va_add(struct bt_mesh_model *model,
|
||||||
goto send_status;
|
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 */
|
/* Tried to add existing subscription */
|
||||||
status = STATUS_SUCCESS;
|
status = STATUS_SUCCESS;
|
||||||
goto send_status;
|
goto send_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(mod->groups); i++) {
|
|
||||||
if (mod->groups[i] == BT_MESH_ADDR_UNASSIGNED) {
|
entry = bt_mesh_model_find_group(&mod, BT_MESH_ADDR_UNASSIGNED);
|
||||||
mod->groups[i] = sub_addr;
|
if (!entry) {
|
||||||
break;
|
status = STATUS_INSUFF_RESOURCES;
|
||||||
}
|
goto send_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == ARRAY_SIZE(mod->groups)) {
|
*entry = sub_addr;
|
||||||
status = STATUS_INSUFF_RESOURCES;
|
|
||||||
} else {
|
|
||||||
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
|
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
|
||||||
bt_mesh_lpn_group_add(sub_addr);
|
bt_mesh_lpn_group_add(sub_addr);
|
||||||
}
|
}
|
||||||
|
@ -1819,7 +1852,6 @@ static void mod_sub_va_add(struct bt_mesh_model *model,
|
||||||
}
|
}
|
||||||
|
|
||||||
status = STATUS_SUCCESS;
|
status = STATUS_SUCCESS;
|
||||||
}
|
|
||||||
|
|
||||||
send_status:
|
send_status:
|
||||||
send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
|
send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
|
||||||
|
@ -1876,7 +1908,7 @@ static void mod_sub_va_del(struct bt_mesh_model *model,
|
||||||
bt_mesh_lpn_group_del(&sub_addr, 1);
|
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) {
|
if (match) {
|
||||||
*match = BT_MESH_ADDR_UNASSIGNED;
|
*match = BT_MESH_ADDR_UNASSIGNED;
|
||||||
|
|
||||||
|
@ -1932,13 +1964,11 @@ static void mod_sub_va_overwrite(struct bt_mesh_model *model,
|
||||||
goto send_status;
|
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) {
|
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);
|
status = va_add(label_uuid, &sub_addr);
|
||||||
if (status == STATUS_SUCCESS) {
|
if (status == STATUS_SUCCESS) {
|
||||||
mod->groups[0] = sub_addr;
|
mod->groups[0] = sub_addr;
|
||||||
|
|
|
@ -1502,7 +1502,7 @@ static void store_pending_mod_sub(struct bt_mesh_model *mod, bool vnd)
|
||||||
char path[20];
|
char path[20];
|
||||||
int i, count, err;
|
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) {
|
if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) {
|
||||||
groups[count++] = mod->groups[i];
|
groups[count++] = mod->groups[i];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue