diff --git a/include/zephyr/bluetooth/mesh.h b/include/zephyr/bluetooth/mesh.h index e24f1007063..cb4041c394b 100644 --- a/include/zephyr/bluetooth/mesh.h +++ b/include/zephyr/bluetooth/mesh.h @@ -34,5 +34,7 @@ #include #include #include +#include +#include #endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_H_ */ diff --git a/include/zephyr/bluetooth/mesh/access.h b/include/zephyr/bluetooth/mesh/access.h index 4923dbee340..a22f4f76077 100644 --- a/include/zephyr/bluetooth/mesh/access.h +++ b/include/zephyr/bluetooth/mesh/access.h @@ -128,6 +128,8 @@ struct bt_mesh_elem { #define BT_MESH_MODEL_ID_HEALTH_CLI 0x0003 #define BT_MESH_MODEL_ID_REMOTE_PROV_SRV 0x0004 #define BT_MESH_MODEL_ID_REMOTE_PROV_CLI 0x0005 +#define BT_MESH_MODEL_ID_LARGE_COMP_DATA_SRV 0x0012 +#define BT_MESH_MODEL_ID_LARGE_COMP_DATA_CLI 0x0013 /* Models from the Mesh Model Specification */ #define BT_MESH_MODEL_ID_GEN_ONOFF_SRV 0x1000 @@ -306,6 +308,37 @@ struct bt_mesh_model_op { /** + * + * @brief Composition data SIG model entry with callback functions and metadata. + * + * @param _id Model ID. + * @param _op Array of model opcode handlers. + * @param _pub Model publish parameters. + * @param _user_data User data for the model. + * @param _cb Callback structure, or NULL to keep no callbacks. + * @param _metadata Metadata structure. + */ +#if defined(CONFIG_BT_MESH_LARGE_COMP_DATA_SRV) +#define BT_MESH_MODEL_METADATA_CB(_id, _op, _pub, _user_data, _cb, _metadata) \ +{ \ + .id = (_id), \ + .pub = _pub, \ + .keys = (uint16_t []) BT_MESH_MODEL_KEYS_UNUSED(CONFIG_BT_MESH_MODEL_KEY_COUNT), \ + .keys_cnt = CONFIG_BT_MESH_MODEL_KEY_COUNT, \ + .groups = (uint16_t []) BT_MESH_MODEL_GROUPS_UNASSIGNED(CONFIG_BT_MESH_MODEL_GROUP_COUNT), \ + .groups_cnt = CONFIG_BT_MESH_MODEL_GROUP_COUNT, \ + .op = _op, \ + .cb = _cb, \ + .user_data = _user_data, \ + .metadata = _metadata, \ +} +#else +#define BT_MESH_MODEL_METADATA_CB(_id, _op, _pub, _user_data, _cb, _metadata) \ + BT_MESH_MODEL_CB(_id, _op, _pub, _user_data, _cb) +#endif + +/** + * * @brief Composition data vendor model entry with callback functions. * * @param _company Company ID. @@ -320,6 +353,33 @@ struct bt_mesh_model_op { CONFIG_BT_MESH_MODEL_KEY_COUNT, \ CONFIG_BT_MESH_MODEL_GROUP_COUNT, _cb) +/** + * + * @brief Composition data vendor model entry with callback functions and metadata. + * + * @param _company Company ID. + * @param _id Model ID. + * @param _op Array of model opcode handlers. + * @param _pub Model publish parameters. + * @param _user_data User data for the model. + * @param _cb Callback structure, or NULL to keep no callbacks. + * @param _metadata Metadata structure. + */ +#define BT_MESH_MODEL_VND_METADATA_CB(_company, _id, _op, _pub, _user_data, _cb, _metadata) \ +{ \ + .vnd.company = (_company), \ + .vnd.id = (_id), \ + .op = _op, \ + .pub = _pub, \ + .keys = (uint16_t []) BT_MESH_MODEL_KEYS_UNUSED(CONFIG_BT_MESH_MODEL_KEY_COUNT), \ + .keys_cnt = CONFIG_BT_MESH_MODEL_KEY_COUNT, \ + .groups = (uint16_t []) BT_MESH_MODEL_GROUPS_UNASSIGNED(CONFIG_BT_MESH_MODEL_GROUP_COUNT), \ + .groups_cnt = CONFIG_BT_MESH_MODEL_GROUP_COUNT, \ + .user_data = _user_data, \ + .cb = _cb, \ + .metadata = _metadata, \ +} + /** * @brief Composition data SIG model entry. * @@ -496,6 +556,41 @@ struct bt_mesh_model_pub { .update = _update, \ } +/** Models Metadata Entry struct + * + * The struct should primarily be created using the + * BT_MESH_MODELS_METADATA_ENTRY macro. + */ +struct bt_mesh_models_metadata_entry { + /* Length of the metadata */ + const uint16_t len; + + /* ID of the metadata */ + const uint16_t id; + + /* Pointer to raw data */ + void *data; +}; + +/** + * + * Initialize a Models Metadata entry structure in a list. + * + * @param _len Length of the metadata entry. + * @param _id ID of the Models Metadata entry. + * @param _data Pointer to a contiguous memory that contains the metadata. + */ +#define BT_MESH_MODELS_METADATA_ENTRY(_len, _id, _data) \ + { \ + .len = (_len), .id = _id, .data = _data, \ + } + +/** Helper to define an empty Models metadata array */ +#define BT_MESH_MODELS_METADATA_NONE NULL + +/** End of the Models Metadata list. Must always be present. */ +#define BT_MESH_MODELS_METADATA_END { 0, 0, NULL } + /** Model callback functions. */ struct bt_mesh_model_cb { /** @brief Set value handler of user data tied to the model. @@ -600,6 +695,11 @@ struct bt_mesh_model { struct bt_mesh_model *next; #endif +#ifdef CONFIG_BT_MESH_LARGE_COMP_DATA_SRV + /* Pointer to the array of model metadata entries. */ + struct bt_mesh_models_metadata_entry **metadata; +#endif + /** Model-specific user data */ void *user_data; }; diff --git a/include/zephyr/bluetooth/mesh/health_srv.h b/include/zephyr/bluetooth/mesh/health_srv.h index b9e6f229f70..1615d9c8d92 100644 --- a/include/zephyr/bluetooth/mesh/health_srv.h +++ b/include/zephyr/bluetooth/mesh/health_srv.h @@ -153,6 +153,11 @@ struct bt_mesh_health_srv { /** Attention Timer state */ struct k_work_delayable attn_timer; + +#ifdef CONFIG_BT_MESH_LARGE_COMP_DATA_SRV + /** Pointer to the array with Health Test Info Metadata */ + struct bt_mesh_models_metadata_entry *metadata; +#endif }; /** @@ -166,9 +171,41 @@ struct bt_mesh_health_srv { * * @return New mesh model instance. */ +#ifdef CONFIG_BT_MESH_LARGE_COMP_DATA_SRV +#define BT_MESH_MODEL_HEALTH_SRV(srv, pub) \ + BT_MESH_MODEL_METADATA_CB(BT_MESH_MODEL_ID_HEALTH_SRV, bt_mesh_health_srv_op, \ + pub, srv, &bt_mesh_health_srv_cb, &(srv)->metadata) +#else #define BT_MESH_MODEL_HEALTH_SRV(srv, pub) \ BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_HEALTH_SRV, bt_mesh_health_srv_op, \ pub, srv, &bt_mesh_health_srv_cb) +#endif + +/** + * + * Health Test Information Metadata ID. + */ +#define BT_MESH_HEALTH_TEST_INFO_METADATA_ID 0x0001 + +#define BT_MESH_HEALTH_TEST_INFO_METADATA(tests) \ + { \ + .len = ARRAY_SIZE(tests), \ + .id = BT_MESH_HEALTH_TEST_INFO_METADATA_ID, \ + .data = tests, \ + } + +/** + * + * Define a Health Test Info Metadata array. + * + * @param cid Company ID of the Health Test suite. + * @param tests A comma separated list of tests. + * + * @return A comma separated list of values that make Health Test Info Metadata + */ +#define BT_MESH_HEALTH_TEST_INFO(cid, tests...) \ + (cid & 0xff), (cid >> 8), sizeof((uint8_t[]){ tests }), tests + /** @brief Notify the stack that the fault array state of the given element has * changed. diff --git a/include/zephyr/bluetooth/mesh/large_comp_data_cli.h b/include/zephyr/bluetooth/mesh/large_comp_data_cli.h new file mode 100644 index 00000000000..7006410757b --- /dev/null +++ b/include/zephyr/bluetooth/mesh/large_comp_data_cli.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @defgroup bt_mesh_large_comp_data_cli Large Composition Data Client model + * @{ + * @brief API for the Large Composition Data Client model. + */ +#ifndef BT_MESH_LARGE_COMP_DATA_CLI_H__ +#define BT_MESH_LARGE_COMP_DATA_CLI_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * @brief Large Composition Data Client model composition data entry. + */ +#define BT_MESH_MODEL_LARGE_COMP_DATA_CLI \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_LARGE_COMP_DATA_CLI, \ + _bt_mesh_large_comp_data_cli_op, NULL, NULL, \ + &_bt_mesh_large_comp_data_cli_cb) + +/** @brief Send Large Composition Data Get message. + * + * This API is used to read a portion of a page of the Composition Data. + * + * @param net_idx Network index to encrypt with. + * @param addr Target node element address. + * @param page Composition Data page to read. + * @param offset Offset within the Composition Data Page. + * @param comp Output buffer for storing received Composition data. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_large_comp_data_get(uint16_t net_idx, uint16_t addr, uint8_t page, + size_t offset, struct net_buf_simple *comp); + +/** @brief Send Models Metadata Get message. + * + * This API is used to read a portion of a page of the Models Metadata state. + * + * @param net_idx Network index to encrypt with. + * @param addr Target node element address. + * @param page Models Metadata page to read. + * @param offset Offset within the Models Metadata Page. + * @param metadata Output buffer for storing received Models Metadata. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_models_metadata_get(uint16_t net_idx, uint16_t addr, uint8_t page, + size_t offset, struct net_buf_simple *metadata); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_large_comp_data_cli_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_large_comp_data_cli_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif +/** + * @} + */ + +#endif /* BT_MESH_LARGE_COMP_DATA_CLI_H__ */ diff --git a/include/zephyr/bluetooth/mesh/large_comp_data_srv.h b/include/zephyr/bluetooth/mesh/large_comp_data_srv.h new file mode 100644 index 00000000000..e2b82e769ff --- /dev/null +++ b/include/zephyr/bluetooth/mesh/large_comp_data_srv.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @defgroup bt_mesh_large_comp_data_srv Large Composition Data Server model + * @{ + * @brief API for the Large Composition Data Server model. + */ +#ifndef BT_MESH_LARGE_COMP_DATA_SRV_H__ +#define BT_MESH_LARGE_COMP_DATA_SRV_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * @brief Large Composition Data Server model composition data entry. + */ +#define BT_MESH_MODEL_LARGE_COMP_DATA_SRV \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_LARGE_COMP_DATA_SRV, \ + _bt_mesh_large_comp_data_srv_op, NULL, NULL, \ + &_bt_mesh_large_comp_data_srv_cb) + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_large_comp_data_srv_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_large_comp_data_srv_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_LARGE_COMP_DATA_SRV_H__ */ + +/** + * @} + */ diff --git a/subsys/bluetooth/mesh/CMakeLists.txt b/subsys/bluetooth/mesh/CMakeLists.txt index 9fe193527ca..8e3b7c2e07e 100644 --- a/subsys/bluetooth/mesh/CMakeLists.txt +++ b/subsys/bluetooth/mesh/CMakeLists.txt @@ -58,6 +58,10 @@ zephyr_library_sources_ifdef(CONFIG_BT_MESH_CFG_CLI cfg_cli.c) zephyr_library_sources_ifdef(CONFIG_BT_MESH_HEALTH_CLI health_cli.c) +zephyr_library_sources_ifdef(CONFIG_BT_MESH_LARGE_COMP_DATA_SRV large_comp_data_srv.c) + +zephyr_library_sources_ifdef(CONFIG_BT_MESH_LARGE_COMP_DATA_CLI large_comp_data_cli.c) + zephyr_library_sources_ifdef(CONFIG_BT_MESH_SELF_TEST test.c) zephyr_library_sources_ifdef(CONFIG_BT_MESH_CDB cdb.c) diff --git a/subsys/bluetooth/mesh/Kconfig b/subsys/bluetooth/mesh/Kconfig index 31c8f48d3fa..05b5ff702a9 100644 --- a/subsys/bluetooth/mesh/Kconfig +++ b/subsys/bluetooth/mesh/Kconfig @@ -1209,6 +1209,16 @@ config BT_MESH_RPR_SRV_AD_DATA_MAX extended scanning. endmenu +config BT_MESH_LARGE_COMP_DATA_SRV + bool "Support for Large Composition Data Server Model" + help + Enable support for the Large Composition Data Server model. + +config BT_MESH_LARGE_COMP_DATA_CLI + bool "Support for Large Composition Data Client Model" + help + Enable support for the Large Composition Data Client model. + endif # BT_MESH_V1d1 rsource "shell/Kconfig" diff --git a/subsys/bluetooth/mesh/access.c b/subsys/bluetooth/mesh/access.c index 5cec971080b..33f94386058 100644 --- a/subsys/bluetooth/mesh/access.c +++ b/subsys/bluetooth/mesh/access.c @@ -42,6 +42,11 @@ struct mod_pub_val { cred:1; }; +struct comp_foreach_model_arg { + struct net_buf_simple *buf; + size_t *offset; +}; + static const struct bt_mesh_comp *dev_comp; static uint16_t dev_primary_addr; static void (*msg_cb)(uint32_t opcode, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); @@ -71,6 +76,343 @@ void bt_mesh_model_foreach(void (*func)(struct bt_mesh_model *mod, } } +static size_t bt_mesh_comp_elem_size(const struct bt_mesh_elem *elem) +{ + return (4 + (elem->model_count * 2U) + (elem->vnd_model_count * 4U)); +} + +static uint8_t *data_buf_add_u8_offset(struct net_buf_simple *buf, + uint8_t val, size_t *offset) +{ + if (*offset >= 1) { + *offset -= 1; + return NULL; + } + + return net_buf_simple_add_u8(buf, val); +} + +static void data_buf_add_le16_offset(struct net_buf_simple *buf, + uint16_t val, size_t *offset) +{ + if (*offset >= 2) { + *offset -= 2; + return; + } else if (*offset == 1) { + *offset -= 1; + net_buf_simple_add_u8(buf, (val >> 8)); + } else { + net_buf_simple_add_le16(buf, val); + } +} + +static void comp_add_model(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, void *user_data) +{ + struct comp_foreach_model_arg *arg = user_data; + + if (vnd) { + data_buf_add_le16_offset(arg->buf, mod->vnd.company, arg->offset); + data_buf_add_le16_offset(arg->buf, mod->vnd.id, arg->offset); + } else { + data_buf_add_le16_offset(arg->buf, mod->id, arg->offset); + } +} + +#ifdef CONFIG_BT_MESH_LARGE_COMP_DATA_SRV +static void data_buf_add_mem_offset(struct net_buf_simple *buf, + const void *mem, size_t len, + size_t *offset) +{ + if (*offset >= len) { + *offset -= len; + return; + } else if (*offset > 0) { + net_buf_simple_add_mem(buf, ((uint8_t *)mem), (len - *offset)); + + } else { + net_buf_simple_add_mem(buf, mem, len); + } +} + +static size_t metadata_model_size(struct bt_mesh_model *mod, + struct bt_mesh_elem *elem, bool vnd) +{ + const struct bt_mesh_models_metadata_entry *entry; + size_t size = 0; + + if (vnd) { + size += sizeof(mod->vnd.company); + size += sizeof(mod->vnd.id); + } else { + size += sizeof(mod->id); + } + + size += sizeof(uint8_t); + + if (!mod->metadata) { + return size; + } + + for (entry = *mod->metadata; entry && entry->len; ++entry) { + size += sizeof(entry->len) + sizeof(entry->id) + entry->len; + } + + return size; +} + +size_t bt_mesh_metadata_page_0_size(void) +{ + const struct bt_mesh_comp *comp; + size_t size = 0; + int i, j; + + comp = bt_mesh_comp_get(); + + for (i = 0; i < dev_comp->elem_count; i++) { + struct bt_mesh_elem *elem = &dev_comp->elem[i]; + + size += sizeof(elem->model_count) + + sizeof(elem->vnd_model_count); + + for (j = 0; j < elem->model_count; j++) { + struct bt_mesh_model *model = &elem->models[j]; + + size += metadata_model_size(model, elem, false); + } + + for (j = 0; j < elem->vnd_model_count; j++) { + struct bt_mesh_model *model = &elem->vnd_models[j]; + + size += metadata_model_size(model, elem, true); + } + } + + return size; +} + +static int metadata_add_model(struct bt_mesh_model *mod, + struct bt_mesh_elem *elem, bool vnd, + void *user_data) +{ + const struct bt_mesh_models_metadata_entry *entry; + struct comp_foreach_model_arg *arg = user_data; + struct net_buf_simple *buf = arg->buf; + size_t *offset = arg->offset; + size_t model_size; + uint8_t count = 0; + uint8_t *count_ptr; + + model_size = metadata_model_size(mod, elem, vnd); + + if (*offset <= model_size) { + *offset -= model_size; + return 0; + } + + if (net_buf_simple_tailroom(buf) < (model_size + BT_MESH_MIC_SHORT)) { + LOG_ERR("Too large metadata"); + return -E2BIG; + } + + comp_add_model(mod, elem, vnd, user_data); + + count_ptr = data_buf_add_u8_offset(buf, 0, offset); + + if (mod->metadata) { + for (entry = *mod->metadata; entry && entry->data != NULL; ++entry) { + data_buf_add_le16_offset(buf, entry->len, offset); + data_buf_add_le16_offset(buf, entry->id, offset); + data_buf_add_mem_offset(buf, entry->data, entry->len, offset); + count++; + } + } + + if (count_ptr) { + *count_ptr = count; + } + + return 0; +} + +int bt_mesh_metadata_get_page_0(struct net_buf_simple *buf, size_t offset) +{ + const struct bt_mesh_comp *comp; + struct comp_foreach_model_arg arg = { + .buf = buf, + .offset = &offset, + }; + uint8_t *mod_count_ptr; + uint8_t *vnd_count_ptr; + uint8_t mod_count = 0; + int i, j, err; + + comp = bt_mesh_comp_get(); + + for (i = 0; i < comp->elem_count; i++) { + struct bt_mesh_elem *elem = &dev_comp->elem[i]; + + /* Check that the buffer has available tailroom for metadata item counts */ + if (net_buf_simple_tailroom(buf) < (((offset == 0) ? 2 : (offset == 1) ? 1 : 0) + + BT_MESH_MIC_SHORT)) { + LOG_DBG("Model metadata didn't fit in the buffer"); + return -E2BIG; + } + mod_count_ptr = data_buf_add_u8_offset(buf, 0, &offset); + vnd_count_ptr = data_buf_add_u8_offset(buf, 0, &offset); + + for (j = 0; j < elem->model_count; j++) { + struct bt_mesh_model *model = &elem->models[j]; + + if (!model->metadata) { + continue; + } + + ++mod_count; + err = metadata_add_model(model, elem, false, &arg); + if (err) { + return err; + } + } + + if (mod_count_ptr) { + *mod_count_ptr = mod_count; + } + + mod_count = 0; + + for (j = 0; j < elem->vnd_model_count; j++) { + struct bt_mesh_model *model = &elem->vnd_models[j]; + + if (!model->metadata) { + continue; + } + + ++mod_count; + err = metadata_add_model(model, elem, true, &arg); + if (err) { + return err; + } + } + + if (vnd_count_ptr) { + *vnd_count_ptr = mod_count; + } + } + + return 0; +} +#endif + +size_t bt_mesh_comp_page_0_size(void) +{ + const struct bt_mesh_comp *comp; + const struct bt_mesh_elem *elem; + size_t size = 10; + int i; + + comp = bt_mesh_comp_get(); + + for (i = 0; i < comp->elem_count; i++) { + elem = &comp->elem[i]; + size += bt_mesh_comp_elem_size(elem); + } + + return size; +} + +static int comp_add_elem(struct net_buf_simple *buf, struct bt_mesh_elem *elem, + size_t *offset) +{ + struct comp_foreach_model_arg arg = { + .buf = buf, + .offset = offset, + }; + const size_t elem_size = bt_mesh_comp_elem_size(elem); + int i; + + if (*offset >= elem_size) { + *offset -= elem_size; + return 0; + } + + if (net_buf_simple_tailroom(buf) < (elem_size + BT_MESH_MIC_SHORT)) { + if (IS_ENABLED(CONFIG_BT_MESH_LARGE_COMP_DATA_SRV)) { + /* Mesh Profile 1.1 Section 4.4.1.2.2: + * If the complete list of models does not fit in the Data field, + * the element shall not be reported. + */ + LOG_DBG("Element 0x%04x didn't fit in the Data field", + elem->addr); + return 0; + } + + LOG_ERR("Too large device composition"); + return -E2BIG; + } + + data_buf_add_le16_offset(buf, elem->loc, offset); + + data_buf_add_u8_offset(buf, elem->model_count, offset); + data_buf_add_u8_offset(buf, elem->vnd_model_count, offset); + + for (i = 0; i < elem->model_count; i++) { + struct bt_mesh_model *model = &elem->models[i]; + + comp_add_model(model, elem, false, &arg); + } + + for (i = 0; i < elem->vnd_model_count; i++) { + struct bt_mesh_model *model = &elem->vnd_models[i]; + + comp_add_model(model, elem, true, &arg); + } + + return 0; +} + +int bt_mesh_comp_data_get_page_0(struct net_buf_simple *buf, size_t offset) +{ + uint16_t feat = 0U; + const struct bt_mesh_comp *comp; + int i; + + comp = bt_mesh_comp_get(); + + if (IS_ENABLED(CONFIG_BT_MESH_RELAY)) { + feat |= BT_MESH_FEAT_RELAY; + } + + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) { + feat |= BT_MESH_FEAT_PROXY; + } + + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + feat |= BT_MESH_FEAT_FRIEND; + } + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + feat |= BT_MESH_FEAT_LOW_POWER; + } + + data_buf_add_le16_offset(buf, comp->cid, &offset); + data_buf_add_le16_offset(buf, comp->pid, &offset); + data_buf_add_le16_offset(buf, comp->vid, &offset); + data_buf_add_le16_offset(buf, CONFIG_BT_MESH_CRPL, &offset); + data_buf_add_le16_offset(buf, feat, &offset); + + for (i = 0; i < comp->elem_count; i++) { + int err; + + err = comp_add_elem(buf, &comp->elem[i], &offset); + if (err) { + return err; + } + } + + return 0; +} + int32_t bt_mesh_model_pub_period_get(struct bt_mesh_model *mod) { int32_t period; @@ -1241,7 +1583,7 @@ 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); + err = bt_mesh_comp_data_get_page_0(&buf, 0); if (err) { LOG_ERR("Failed to read composition data: %d", err); return err; diff --git a/subsys/bluetooth/mesh/access.h b/subsys/bluetooth/mesh/access.h index 0fe1f514682..c97d4edf145 100644 --- a/subsys/bluetooth/mesh/access.h +++ b/subsys/bluetooth/mesh/access.h @@ -22,6 +22,10 @@ enum { void bt_mesh_elem_register(struct bt_mesh_elem *elem, uint8_t count); uint8_t bt_mesh_elem_count(void); +size_t bt_mesh_comp_page_0_size(void); +int bt_mesh_comp_data_get_page_0(struct net_buf_simple *buf, size_t offset); +size_t bt_mesh_metadata_page_0_size(void); +int bt_mesh_metadata_get_page_0(struct net_buf_simple *buf, size_t offset); /* Find local element based on unicast address */ struct bt_mesh_elem *bt_mesh_elem_find(uint16_t addr); diff --git a/subsys/bluetooth/mesh/cfg_srv.c b/subsys/bluetooth/mesh/cfg_srv.c index 5f36195c0ae..275839b2c0e 100644 --- a/subsys/bluetooth/mesh/cfg_srv.c +++ b/subsys/bluetooth/mesh/cfg_srv.c @@ -47,79 +47,6 @@ static void node_reset_pending_handler(struct k_work *work) static K_WORK_DEFINE(node_reset_pending, node_reset_pending_handler); -static int comp_add_elem(struct net_buf_simple *buf, struct bt_mesh_elem *elem, - bool primary) -{ - struct bt_mesh_model *mod; - int i; - - if (net_buf_simple_tailroom(buf) < - 4 + (elem->model_count * 2U) + (elem->vnd_model_count * 4U)) { - LOG_ERR("Too large device composition"); - return -E2BIG; - } - - net_buf_simple_add_le16(buf, elem->loc); - - net_buf_simple_add_u8(buf, elem->model_count); - net_buf_simple_add_u8(buf, elem->vnd_model_count); - - for (i = 0; i < elem->model_count; i++) { - mod = &elem->models[i]; - net_buf_simple_add_le16(buf, mod->id); - } - - for (i = 0; i < elem->vnd_model_count; i++) { - mod = &elem->vnd_models[i]; - net_buf_simple_add_le16(buf, mod->vnd.company); - net_buf_simple_add_le16(buf, mod->vnd.id); - } - - return 0; -} - -int bt_mesh_comp_get_page_0(struct net_buf_simple *buf) -{ - uint16_t feat = 0U; - const struct bt_mesh_comp *comp; - int i; - - comp = bt_mesh_comp_get(); - - if (IS_ENABLED(CONFIG_BT_MESH_RELAY)) { - feat |= BT_MESH_FEAT_RELAY; - } - - if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) { - feat |= BT_MESH_FEAT_PROXY; - } - - if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { - feat |= BT_MESH_FEAT_FRIEND; - } - - if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { - feat |= BT_MESH_FEAT_LOW_POWER; - } - - net_buf_simple_add_le16(buf, comp->cid); - net_buf_simple_add_le16(buf, comp->pid); - net_buf_simple_add_le16(buf, comp->vid); - net_buf_simple_add_le16(buf, CONFIG_BT_MESH_CRPL); - net_buf_simple_add_le16(buf, feat); - - for (i = 0; i < comp->elem_count; i++) { - int err; - - err = comp_add_elem(buf, &comp->elem[i], i == 0); - if (err) { - return err; - } - } - - return 0; -} - static int dev_comp_data_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) @@ -154,7 +81,7 @@ static int dev_comp_data_get(struct bt_mesh_model *model, sdu.size += BT_MESH_MIC_SHORT; } else { - err = bt_mesh_comp_get_page_0(&sdu); + err = bt_mesh_comp_data_get_page_0(&sdu, 0); if (err < 0) { LOG_ERR("Unable to get composition page 0"); return err; diff --git a/subsys/bluetooth/mesh/dfu_metadata.c b/subsys/bluetooth/mesh/dfu_metadata.c index 83e0ec26623..2173d201ad5 100644 --- a/subsys/bluetooth/mesh/dfu_metadata.c +++ b/subsys/bluetooth/mesh/dfu_metadata.c @@ -10,7 +10,7 @@ #include #include "crypto.h" -#include "foundation.h" +#include "access.h" int bt_mesh_dfu_metadata_decode(struct net_buf_simple *buf, struct bt_mesh_dfu_metadata *metadata) @@ -91,7 +91,7 @@ int bt_mesh_dfu_metadata_comp_hash_local_get(uint8_t *key, uint32_t *hash) NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_TX_SDU_MAX); int err; - err = bt_mesh_comp_get_page_0(&buf); + err = bt_mesh_comp_data_get_page_0(&buf, 0); if (err) { return err; } diff --git a/subsys/bluetooth/mesh/foundation.h b/subsys/bluetooth/mesh/foundation.h index 1399c03f8b6..7fcb6e56fe8 100644 --- a/subsys/bluetooth/mesh/foundation.h +++ b/subsys/bluetooth/mesh/foundation.h @@ -91,6 +91,11 @@ #define OP_VND_MOD_APP_GET BT_MESH_MODEL_OP_2(0x80, 0x4d) #define OP_VND_MOD_APP_LIST BT_MESH_MODEL_OP_2(0x80, 0x4e) +#define OP_LARGE_COMP_DATA_GET BT_MESH_MODEL_OP_2(0x80, 0x74) +#define OP_LARGE_COMP_DATA_STATUS BT_MESH_MODEL_OP_2(0x80, 0x75) +#define OP_MODELS_METADATA_GET BT_MESH_MODEL_OP_2(0x80, 0x76) +#define OP_MODELS_METADATA_STATUS BT_MESH_MODEL_OP_2(0x80, 0x77) + #define STATUS_SUCCESS 0x00 #define STATUS_INVALID_ADDRESS 0x01 #define STATUS_INVALID_MODEL 0x02 @@ -116,8 +121,6 @@ void bt_mesh_attention(struct bt_mesh_model *model, uint8_t time); #include -int bt_mesh_comp_get_page_0(struct net_buf_simple *buf); - static inline void key_idx_pack(struct net_buf_simple *buf, uint16_t idx1, uint16_t idx2) { diff --git a/subsys/bluetooth/mesh/large_comp_data_cli.c b/subsys/bluetooth/mesh/large_comp_data_cli.c new file mode 100644 index 00000000000..5ec45239bc8 --- /dev/null +++ b/subsys/bluetooth/mesh/large_comp_data_cli.c @@ -0,0 +1,183 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_mesh_large_comp_data_cli); + +#include "net.h" +#include "access.h" +#include "foundation.h" + +/** Mesh Large Composition Data Client Model Context */ +static struct bt_mesh_large_comp_data_cli { + /** Composition data model entry pointer. */ + struct bt_mesh_model *model; + + /* Internal parameters for tracking message responses. */ + struct bt_mesh_msg_ack_ctx ack_ctx; +} cli; + +static int32_t msg_timeout; + +static int large_comp_data_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct net_buf_simple *comp; + size_t to_copy; + + 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)); + + if (!bt_mesh_msg_ack_ctx_match(&cli.ack_ctx, OP_LARGE_COMP_DATA_STATUS, + ctx->addr, (void **)&comp)) { + return 0; + } + + to_copy = MIN(net_buf_simple_tailroom(comp), buf->len); + net_buf_simple_add_mem(comp, buf->data, to_copy); + + bt_mesh_msg_ack_ctx_rx(&cli.ack_ctx); + + return 0; +} + +static int models_metadata_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct net_buf_simple *metadata; + size_t to_copy; + + 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)); + + if (!bt_mesh_msg_ack_ctx_match(&cli.ack_ctx, OP_MODELS_METADATA_STATUS, + ctx->addr, (void **)&metadata)) { + return 0; + } + + metadata = (struct net_buf_simple *)cli.ack_ctx.user_data; + + to_copy = MIN(net_buf_simple_tailroom(metadata), buf->len); + net_buf_simple_add_mem(metadata, buf->data, to_copy); + + bt_mesh_msg_ack_ctx_rx(&cli.ack_ctx); + + return 0; +} + +const struct bt_mesh_model_op _bt_mesh_large_comp_data_cli_op[] = { + { OP_LARGE_COMP_DATA_STATUS, BT_MESH_LEN_MIN(5), large_comp_data_status }, + { OP_MODELS_METADATA_STATUS, BT_MESH_LEN_MIN(5), models_metadata_status }, + BT_MESH_MODEL_OP_END, +}; + +static int large_comp_data_cli_init(struct bt_mesh_model *model) +{ + if (!bt_mesh_model_in_primary(model)) { + LOG_ERR("Configuration Client only allowed in primary element"); + return -EINVAL; + } + + model->keys[0] = BT_MESH_KEY_DEV_ANY; + model->flags |= BT_MESH_MOD_DEVKEY_ONLY; + + cli.model = model; + + msg_timeout = 5000; + bt_mesh_msg_ack_ctx_init(&cli.ack_ctx); + + return 0; +} + +const struct bt_mesh_model_cb _bt_mesh_large_comp_data_cli_cb = { + .init = large_comp_data_cli_init, +}; + +static int cli_prepare(void *param, uint32_t op, uint16_t addr) +{ + return bt_mesh_msg_ack_ctx_prepare(&cli.ack_ctx, op, addr, param); +} + +int bt_mesh_large_comp_data_get(uint16_t net_idx, uint16_t addr, uint8_t page, + size_t offset, struct net_buf_simple *comp) +{ + BT_MESH_MODEL_BUF_DEFINE(msg, OP_LARGE_COMP_DATA_GET, 3); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = cli_prepare(comp, OP_LARGE_COMP_DATA_STATUS, addr); + if (err) { + return err; + } + + bt_mesh_model_msg_init(&msg, OP_LARGE_COMP_DATA_GET); + net_buf_simple_add_u8(&msg, page); + net_buf_simple_add_le16(&msg, offset); + + err = bt_mesh_model_send(cli.model, &ctx, &msg, NULL, NULL); + if (err) { + LOG_ERR("model_send() failed (err %d)", err); + bt_mesh_msg_ack_ctx_clear(&cli.ack_ctx); + return err; + } + + return bt_mesh_msg_ack_ctx_wait(&cli.ack_ctx, K_MSEC(msg_timeout)); +} + +int bt_mesh_models_metadata_get(uint16_t net_idx, uint16_t addr, uint8_t page, + size_t offset, struct net_buf_simple *metadata) +{ + BT_MESH_MODEL_BUF_DEFINE(msg, OP_MODELS_METADATA_STATUS, 3); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = cli_prepare(metadata, OP_MODELS_METADATA_STATUS, addr); + if (err) { + return err; + } + + bt_mesh_model_msg_init(&msg, OP_MODELS_METADATA_GET); + net_buf_simple_add_u8(&msg, page); + net_buf_simple_add_le16(&msg, offset); + + err = bt_mesh_model_send(cli.model, &ctx, &msg, NULL, NULL); + if (err) { + LOG_ERR("model_send() failed (err %d)", err); + bt_mesh_msg_ack_ctx_clear(&cli.ack_ctx); + return err; + } + + return bt_mesh_msg_ack_ctx_wait(&cli.ack_ctx, K_MSEC(msg_timeout)); +} diff --git a/subsys/bluetooth/mesh/large_comp_data_srv.c b/subsys/bluetooth/mesh/large_comp_data_srv.c new file mode 100644 index 00000000000..582f9221de3 --- /dev/null +++ b/subsys/bluetooth/mesh/large_comp_data_srv.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_mesh_large_comp_data_srv); + +#include "access.h" +#include "cfg.h" +#include "foundation.h" +#include "net.h" +#include "mesh.h" +#include "transport.h" + +#define DUMMY_2_BYTE_OP BT_MESH_MODEL_OP_2(0xff, 0xff) +#define BT_MESH_MODEL_PAYLOAD_MAX \ + (BT_MESH_TX_SDU_MAX - BT_MESH_MODEL_OP_LEN(DUMMY_2_BYTE_OP) - \ + BT_MESH_MIC_SHORT) + +/** Mesh Large Composition Data Server Model Context */ +static struct bt_mesh_large_comp_data_srv { + /** Composition data model entry pointer. */ + struct bt_mesh_model *model; +} srv; + +static int handle_large_comp_data_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + BT_MESH_MODEL_BUF_DEFINE(rsp, OP_LARGE_COMP_DATA_STATUS, + BT_MESH_MODEL_PAYLOAD_MAX); + uint8_t page; + size_t offset, total_size; + int err; + + 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)); + + page = net_buf_simple_pull_u8(buf); + offset = net_buf_simple_pull_le16(buf); + + LOG_DBG("page %u offset %u", page, offset); + + bt_mesh_model_msg_init(&rsp, OP_LARGE_COMP_DATA_STATUS); + + if (page != 0U) { + LOG_DBG("Composition page %u not available", page); + page = 0U; + } + + net_buf_simple_add_u8(&rsp, page); + + total_size = bt_mesh_comp_page_0_size(); + net_buf_simple_add_le16(&rsp, offset); + net_buf_simple_add_le16(&rsp, total_size); + + if (offset < total_size) { + err = bt_mesh_comp_data_get_page_0(&rsp, offset); + if (err && err != -E2BIG) { + LOG_ERR("comp_get_page_0 returned error"); + return err; + } + } + + if (bt_mesh_model_send(model, ctx, &rsp, NULL, NULL)) { + LOG_ERR("Unable to send Large Composition Data Status"); + } + + return 0; +} + +static int handle_models_metadata_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + BT_MESH_MODEL_BUF_DEFINE(rsp, OP_MODELS_METADATA_STATUS, + BT_MESH_MODEL_PAYLOAD_MAX); + uint8_t page; + size_t offset, total_size; + int err; + + 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)); + + page = net_buf_simple_pull_u8(buf); + offset = net_buf_simple_pull_le16(buf); + + LOG_DBG("page %u offset %u", page, offset); + + bt_mesh_model_msg_init(&rsp, OP_MODELS_METADATA_STATUS); + + if (page != 0U) { + LOG_DBG("Composition page %u not available", page); + page = 0U; + } + + net_buf_simple_add_u8(&rsp, page); + + total_size = bt_mesh_metadata_page_0_size(); + net_buf_simple_add_le16(&rsp, offset); + net_buf_simple_add_le16(&rsp, total_size); + + if (offset < total_size) { + err = bt_mesh_metadata_get_page_0(&rsp, offset); + if (err && err != -E2BIG) { + LOG_ERR("bt_mesh_metadata_get_page_0 returned error"); + return err; + } + } + + if (bt_mesh_model_send(model, ctx, &rsp, NULL, NULL)) { + LOG_ERR("Unable to send Models Metadata Status"); + } + + return 0; +} + +const struct bt_mesh_model_op _bt_mesh_large_comp_data_srv_op[] = { + { OP_LARGE_COMP_DATA_GET, BT_MESH_LEN_EXACT(3), handle_large_comp_data_get }, + { OP_MODELS_METADATA_GET, BT_MESH_LEN_EXACT(3), handle_models_metadata_get }, + BT_MESH_MODEL_OP_END, +}; + +static int large_comp_data_srv_init(struct bt_mesh_model *model) +{ + if (!bt_mesh_model_in_primary(model)) { + LOG_ERR("Large Composition Data Server only allowed in primary element"); + return -EINVAL; + } + + /* Large Composition Data Server model shall use the device key */ + model->keys[0] = BT_MESH_KEY_DEV; + model->flags |= BT_MESH_MOD_DEVKEY_ONLY; + + srv.model = model; + + return 0; +} + +const struct bt_mesh_model_cb _bt_mesh_large_comp_data_srv_cb = { + .init = large_comp_data_srv_init, +};