diff --git a/include/zephyr/bluetooth/mesh/access.h b/include/zephyr/bluetooth/mesh/access.h index 99352e1da23..4a0aa9781b5 100644 --- a/include/zephyr/bluetooth/mesh/access.h +++ b/include/zephyr/bluetooth/mesh/access.h @@ -757,6 +757,15 @@ int bt_mesh_model_extend(struct bt_mesh_model *extending_mod, */ bool bt_mesh_model_is_extended(struct bt_mesh_model *model); +/** @brief Indicate that the composition data will change on next bootup. + * + * Tell the config server that the composition data is expected to change on + * the next bootup, and the current composition data should be backed up. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_comp_change_prepare(void); + /** 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 ff436d03676..5cec971080b 100644 --- a/subsys/bluetooth/mesh/access.c +++ b/subsys/bluetooth/mesh/access.c @@ -1078,6 +1078,20 @@ static int vnd_mod_set(const char *name, size_t len_rd, BT_MESH_SETTINGS_DEFINE(vnd_mod, "v", vnd_mod_set); +static int comp_set(const char *name, size_t len_rd, settings_read_cb read_cb, + void *cb_arg) +{ + /* Only need to know that the entry exists. Will load the contents on + * demand. + */ + if (len_rd > 0) { + atomic_set_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY); + } + + return 0; +} +BT_MESH_SETTINGS_DEFINE(comp, "cmp", comp_set); + static void encode_mod_path(struct bt_mesh_model *mod, bool vnd, const char *key, char *path, size_t path_len) { @@ -1222,6 +1236,89 @@ void bt_mesh_model_pub_store(struct bt_mesh_model *mod) bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_MOD_PENDING); } +int bt_mesh_comp_store(void) +{ + NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_TX_SDU_MAX); + int err; + + err = bt_mesh_comp_get_page_0(&buf); + if (err) { + LOG_ERR("Failed to read composition data: %d", err); + return err; + } + + err = settings_save_one("bt/mesh/cmp", buf.data, buf.len); + if (err) { + LOG_ERR("Failed to store composition data: %d", err); + } else { + LOG_DBG("Stored composition data"); + } + + return err; +} + +int bt_mesh_comp_change_prepare(void) +{ + if (!IS_ENABLED(CONFIG_BT_SETTINGS)) { + return -ENOTSUP; + } + + return bt_mesh_comp_store(); +} + +void bt_mesh_comp_clear(void) +{ + int err; + + err = settings_delete("bt/mesh/cmp"); + if (err) { + LOG_ERR("Failed to clear composition data: %d", err); + } else { + LOG_DBG("Cleared composition data page 128"); + } + + atomic_clear_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY); +} + +static int read_comp_cb(const char *key, size_t len, settings_read_cb read_cb, + void *cb_arg, void *param) +{ + struct net_buf_simple *buf = param; + + if (len > net_buf_simple_tailroom(buf)) { + return -ENOBUFS; + } + + len = read_cb(cb_arg, net_buf_simple_tail(buf), len); + if (len > 0) { + net_buf_simple_add(buf, len); + } + + return -EALREADY; +} + +int bt_mesh_comp_read(struct net_buf_simple *buf) +{ + size_t original_len = buf->len; + int err; + + if (!IS_ENABLED(CONFIG_BT_SETTINGS)) { + return -ENOTSUP; + } + + err = settings_load_subtree_direct("bt/mesh/cmp", read_comp_cb, buf); + if (err) { + LOG_ERR("Failed reading composition data: %d", err); + return err; + } + + if (buf->len == original_len) { + return -ENOENT; + } + + return 0; +} + int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd, const char *name, const void *data, size_t data_len) diff --git a/subsys/bluetooth/mesh/access.h b/subsys/bluetooth/mesh/access.h index 4d0bfafb1df..0fe1f514682 100644 --- a/subsys/bluetooth/mesh/access.h +++ b/subsys/bluetooth/mesh/access.h @@ -57,6 +57,9 @@ void bt_mesh_model_recv(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); int bt_mesh_comp_register(const struct bt_mesh_comp *comp); +int bt_mesh_comp_store(void); +void bt_mesh_comp_clear(void); +int bt_mesh_comp_read(struct net_buf_simple *buf); void bt_mesh_model_pending_store(void); void bt_mesh_model_bind_store(struct bt_mesh_model *mod); diff --git a/subsys/bluetooth/mesh/cfg_cli.c b/subsys/bluetooth/mesh/cfg_cli.c index 079549e751e..de3a212413b 100644 --- a/subsys/bluetooth/mesh/cfg_cli.c +++ b/subsys/bluetooth/mesh/cfg_cli.c @@ -47,8 +47,8 @@ static int comp_data_status(struct bt_mesh_model *model, struct net_buf_simple *buf) { struct comp_data *param; - uint8_t page; size_t to_copy; + uint8_t page; 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)); diff --git a/subsys/bluetooth/mesh/cfg_srv.c b/subsys/bluetooth/mesh/cfg_srv.c index ccd63018676..5f36195c0ae 100644 --- a/subsys/bluetooth/mesh/cfg_srv.c +++ b/subsys/bluetooth/mesh/cfg_srv.c @@ -132,7 +132,11 @@ static int dev_comp_data_get(struct bt_mesh_model *model, ctx->addr, buf->len, bt_hex(buf->data, buf->len)); page = net_buf_simple_pull_u8(buf); - if (page != 0U) { + + if (page >= 128U && atomic_test_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY)) { + LOG_DBG("Composition data page 128"); + page = 128U; + } else if (page != 0U) { LOG_DBG("Composition page %u not available", page); page = 0U; } @@ -140,10 +144,21 @@ static int dev_comp_data_get(struct bt_mesh_model *model, bt_mesh_model_msg_init(&sdu, OP_DEV_COMP_DATA_STATUS); net_buf_simple_add_u8(&sdu, page); - err = bt_mesh_comp_get_page_0(&sdu); - if (err) { - LOG_ERR("Unable to get composition page 0"); - return err; + if (atomic_test_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY) == (page == 0U)) { + sdu.size -= BT_MESH_MIC_SHORT; + err = bt_mesh_comp_read(&sdu); + if (err) { + LOG_ERR("Unable to get stored composition data"); + return err; + } + + sdu.size += BT_MESH_MIC_SHORT; + } else { + err = bt_mesh_comp_get_page_0(&sdu); + if (err < 0) { + LOG_ERR("Unable to get composition page 0"); + return err; + } } if (bt_mesh_model_send(model, ctx, &sdu, NULL, NULL)) { diff --git a/subsys/bluetooth/mesh/net.h b/subsys/bluetooth/mesh/net.h index a5002bbd68f..f877bfc6d13 100644 --- a/subsys/bluetooth/mesh/net.h +++ b/subsys/bluetooth/mesh/net.h @@ -177,6 +177,7 @@ enum { BT_MESH_IVU_INITIATOR, /* IV Update initiated by us */ BT_MESH_IVU_TEST, /* IV Update test mode */ BT_MESH_IVU_PENDING, /* Update blocked by SDU in progress */ + BT_MESH_COMP_DIRTY, /* Composition data is dirty */ /* Feature flags */ BT_MESH_RELAY,