Bluetooth: Mesh: Add missing comp pages to LCD mod

Adds support for all composition data pages to the
Large Composition Data model.

Signed-off-by: Anders Storrø <anders.storro@nordicsemi.no>
This commit is contained in:
Anders Storrø 2023-10-05 13:19:51 +02:00 committed by Johan Hedberg
commit 57cb1b1ccb
4 changed files with 199 additions and 94 deletions

View file

@ -1375,6 +1375,16 @@ config BT_MESH_PRIV_BEACON_CLI
endif # BT_MESH_PRIV_BEACONS
config BT_MESH_COMP_PST_BUF_SIZE
int "Composition Data Page persistence buffer size"
default 100
help
Stack allocated buffer used to temporarily hold Composition
Data Pages during flash operations. Should reflect the size
of the largest Composition Data Page present in the application.
Note that this buffer should still be large enough to restore previously stored
pages after a performed device firmware update.
config BT_MESH_COMP_PAGE_1
bool "Support for Composition Data Page 1"
depends on BT_MESH_MODEL_EXTENSIONS

View file

@ -173,6 +173,18 @@ static void data_buf_add_le16_offset(struct net_buf_simple *buf,
}
}
static void data_buf_add_mem_offset(struct net_buf_simple *buf, uint8_t *data, size_t len,
size_t *offset)
{
if (*offset >= len) {
*offset -= len;
return;
}
net_buf_simple_add_mem(buf, data + *offset, len - *offset);
*offset = 0;
}
static void comp_add_model(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
bool vnd, void *user_data)
{
@ -187,20 +199,6 @@ static void comp_add_model(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
}
#if defined(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)
@ -366,23 +364,6 @@ int bt_mesh_metadata_get_page_0(struct net_buf_simple *buf, size_t offset)
}
#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)
{
@ -398,7 +379,7 @@ static int comp_add_elem(struct net_buf_simple *buf, struct bt_mesh_elem *elem,
return 0;
}
if (net_buf_simple_tailroom(buf) < (elem_size + BT_MESH_MIC_SHORT)) {
if (net_buf_simple_tailroom(buf) < ((elem_size - *offset) + 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,
@ -516,8 +497,8 @@ static bool is_cor_present(struct bt_mesh_model *mod, uint8_t *cor_id)
return false;
}
static void prep_model_item_header(struct bt_mesh_model *mod, uint8_t *cor_id,
uint8_t *mod_cnt, struct net_buf_simple *buf)
static void prep_model_item_header(struct bt_mesh_model *mod, uint8_t *cor_id, uint8_t *mod_cnt,
struct net_buf_simple *buf, size_t *offset)
{
uint8_t ext_mod_cnt;
bool cor_present;
@ -536,41 +517,41 @@ static void prep_model_item_header(struct bt_mesh_model *mod, uint8_t *cor_id,
if (cor_present) {
mod_elem_info |= BIT(0);
}
net_buf_simple_add_u8(buf, mod_elem_info);
data_buf_add_u8_offset(buf, mod_elem_info, offset);
if (cor_present) {
net_buf_simple_add_u8(buf, *cor_id);
data_buf_add_u8_offset(buf, *cor_id, offset);
}
memset(mod_cnt, ext_mod_cnt, sizeof(uint8_t));
}
static void add_items_to_page(struct net_buf_simple *buf, struct bt_mesh_model *mod,
uint8_t ext_mod_cnt)
uint8_t ext_mod_cnt, size_t *offset)
{
int i, offset;
int i, elem_offset;
uint8_t mod_idx;
MOD_REL_LIST_FOR_EACH(i) {
if (IS_MOD_EXTENSION(mod, i)) {
offset = mod->elem_idx - mod_rel_list[i].elem_base;
elem_offset = mod->elem_idx - mod_rel_list[i].elem_base;
mod_idx = mod_rel_list[i].idx_base;
if (ext_mod_cnt < 32 &&
offset < 4 &&
offset > -5) {
elem_offset < 4 &&
elem_offset > -5) {
/* short format */
if (offset < 0) {
offset += 8;
if (elem_offset < 0) {
elem_offset += 8;
}
offset |= mod_idx << 3;
net_buf_simple_add_u8(buf, offset);
elem_offset |= mod_idx << 3;
data_buf_add_u8_offset(buf, elem_offset, offset);
} else {
/* long format */
if (offset < 0) {
offset += 256;
if (elem_offset < 0) {
elem_offset += 256;
}
net_buf_simple_add_u8(buf, offset);
net_buf_simple_add_u8(buf, mod_idx);
data_buf_add_u8_offset(buf, elem_offset, offset);
data_buf_add_u8_offset(buf, mod_idx, offset);
}
}
}
@ -613,7 +594,7 @@ static size_t page1_elem_size(struct bt_mesh_elem *elem)
return temp_size;
}
static int bt_mesh_comp_data_get_page_1(struct net_buf_simple *buf)
static int bt_mesh_comp_data_get_page_1(struct net_buf_simple *buf, size_t offset)
{
const struct bt_mesh_comp *comp;
uint8_t cor_id = 0;
@ -623,8 +604,14 @@ static int bt_mesh_comp_data_get_page_1(struct net_buf_simple *buf)
comp = bt_mesh_comp_get();
for (i = 0; i < comp->elem_count; i++) {
if (net_buf_simple_tailroom(buf) <
(page1_elem_size(&comp->elem[i]) + BT_MESH_MIC_SHORT)) {
size_t elem_size = page1_elem_size(&comp->elem[i]);
if (offset >= elem_size) {
offset -= elem_size;
continue;
}
if (net_buf_simple_tailroom(buf) < ((elem_size - offset) + 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,
@ -639,49 +626,53 @@ static int bt_mesh_comp_data_get_page_1(struct net_buf_simple *buf)
return -E2BIG;
}
net_buf_simple_add_u8(buf, comp->elem[i].model_count);
net_buf_simple_add_u8(buf, comp->elem[i].vnd_model_count);
data_buf_add_u8_offset(buf, comp->elem[i].model_count, &offset);
data_buf_add_u8_offset(buf, comp->elem[i].vnd_model_count, &offset);
for (j = 0; j < comp->elem[i].model_count; j++) {
prep_model_item_header(&comp->elem[i].models[j],
&cor_id, &ext_mod_cnt, buf);
prep_model_item_header(&comp->elem[i].models[j], &cor_id, &ext_mod_cnt, buf,
&offset);
if (ext_mod_cnt != 0) {
add_items_to_page(buf,
&comp->elem[i].models[j],
ext_mod_cnt);
add_items_to_page(buf, &comp->elem[i].models[j], ext_mod_cnt,
&offset);
}
}
for (j = 0; j < comp->elem[i].vnd_model_count; j++) {
prep_model_item_header(&comp->elem[i].vnd_models[j],
&cor_id, &ext_mod_cnt, buf);
prep_model_item_header(&comp->elem[i].vnd_models[j], &cor_id, &ext_mod_cnt,
buf, &offset);
if (ext_mod_cnt != 0) {
add_items_to_page(buf,
&comp->elem[i].vnd_models[j],
ext_mod_cnt);
add_items_to_page(buf, &comp->elem[i].vnd_models[j], ext_mod_cnt,
&offset);
}
}
}
return 0;
}
static int bt_mesh_comp_data_get_page_2(struct net_buf_simple *buf)
static int bt_mesh_comp_data_get_page_2(struct net_buf_simple *buf, size_t offset)
{
if (!dev_comp2) {
LOG_ERR("Composition data P2 not registered");
return -ENODEV;
}
size_t elem_size;
for (int i = 0; i < dev_comp2->record_cnt; i++) {
if (net_buf_simple_tailroom(buf) <
(8 + dev_comp2->record[i].elem_offset_cnt + dev_comp2->record[i].data_len +
BT_MESH_MIC_SHORT)) {
elem_size =
8 + dev_comp2->record[i].elem_offset_cnt + dev_comp2->record[i].data_len;
if (offset >= elem_size) {
offset -= elem_size;
continue;
}
if (net_buf_simple_tailroom(buf) < ((elem_size - offset) + 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("Record 0x%04x didn't fit in the Data field",
i);
LOG_DBG("Record 0x%04x didn't fit in the Data field", i);
return 0;
}
@ -689,20 +680,20 @@ static int bt_mesh_comp_data_get_page_2(struct net_buf_simple *buf)
return -E2BIG;
}
net_buf_simple_add_le16(buf, dev_comp2->record[i].id);
net_buf_simple_add_u8(buf, dev_comp2->record[i].version.x);
net_buf_simple_add_u8(buf, dev_comp2->record[i].version.y);
net_buf_simple_add_u8(buf, dev_comp2->record[i].version.z);
net_buf_simple_add_u8(buf, dev_comp2->record[i].elem_offset_cnt);
data_buf_add_le16_offset(buf, dev_comp2->record[i].id, &offset);
data_buf_add_u8_offset(buf, dev_comp2->record[i].version.x, &offset);
data_buf_add_u8_offset(buf, dev_comp2->record[i].version.y, &offset);
data_buf_add_u8_offset(buf, dev_comp2->record[i].version.z, &offset);
data_buf_add_u8_offset(buf, dev_comp2->record[i].elem_offset_cnt, &offset);
if (dev_comp2->record[i].elem_offset_cnt) {
net_buf_simple_add_mem(buf, dev_comp2->record[i].elem_offset,
dev_comp2->record[i].elem_offset_cnt);
data_buf_add_mem_offset(buf, (uint8_t *)dev_comp2->record[i].elem_offset,
dev_comp2->record[i].elem_offset_cnt, &offset);
}
net_buf_simple_add_le16(buf, dev_comp2->record[i].data_len);
data_buf_add_le16_offset(buf, dev_comp2->record[i].data_len, &offset);
if (dev_comp2->record[i].data_len) {
net_buf_simple_add_mem(buf, dev_comp2->record[i].data,
dev_comp2->record[i].data_len);
data_buf_add_mem_offset(buf, (uint8_t *)dev_comp2->record[i].data,
dev_comp2->record[i].data_len, &offset);
}
}
@ -2212,20 +2203,89 @@ int bt_mesh_comp_data_get_page(struct net_buf_simple *buf, size_t page, size_t o
if (page == 0 || page == 128) {
return bt_mesh_comp_data_get_page_0(buf, offset);
} else if (IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_1) && (page == 1 || page == 129)) {
return bt_mesh_comp_data_get_page_1(buf);
return bt_mesh_comp_data_get_page_1(buf, offset);
} else if (IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_2) && (page == 2 || page == 130)) {
return bt_mesh_comp_data_get_page_2(buf);
return bt_mesh_comp_data_get_page_2(buf, offset);
}
return -EINVAL;
}
size_t comp_page_0_size(void)
{
const struct bt_mesh_comp *comp;
const struct bt_mesh_elem *elem;
size_t size = 10; /* Non-variable length params of comp page 0. */
comp = bt_mesh_comp_get();
for (int i = 0; i < comp->elem_count; i++) {
elem = &comp->elem[i];
size += bt_mesh_comp_elem_size(elem);
}
return size;
}
size_t comp_page_1_size(void)
{
const struct bt_mesh_comp *comp;
size_t size = 0;
comp = bt_mesh_comp_get();
for (int i = 0; i < comp->elem_count; i++) {
size += page1_elem_size(&comp->elem[i]);
}
return size;
}
size_t comp_page_2_size(void)
{
size_t size = 0;
if (!dev_comp2) {
LOG_ERR("Composition data P2 not registered");
return size;
}
for (int i = 0; i < dev_comp2->record_cnt; i++) {
size += 8 + dev_comp2->record[i].elem_offset_cnt + dev_comp2->record[i].data_len;
}
return size;
}
size_t bt_mesh_comp_page_size(uint8_t page)
{
if (page == 0 || page == 128) {
return comp_page_0_size();
} else if (IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_1) && (page == 1 || page == 129)) {
return comp_page_1_size();
} else if (IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_2) && (page == 2 || page == 130)) {
return comp_page_2_size();
}
return 0;
}
int bt_mesh_comp_store(void)
{
NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_TX_SDU_MAX);
#if IS_ENABLED(CONFIG_BT_MESH_V1d1)
NET_BUF_SIMPLE_DEFINE(buf, CONFIG_BT_MESH_COMP_PST_BUF_SIZE);
int err;
for (int i = 0; i < ARRAY_SIZE(comp_data_pages); i++) {
size_t page_size = bt_mesh_comp_page_size(i);
if (page_size > CONFIG_BT_MESH_COMP_PST_BUF_SIZE) {
LOG_WRN("CDP%d is larger than the CDP persistence buffer. "
"Please increase the CDP persistence buffer size "
"to the required size (%d bytes)",
i, page_size);
}
net_buf_simple_reset(&buf);
err = bt_mesh_comp_data_get_page(&buf, comp_data_pages[i].page, 0);
@ -2242,7 +2302,7 @@ int bt_mesh_comp_store(void)
LOG_DBG("Stored CDP%d", comp_data_pages[i].page);
}
#endif
return 0;
}

View file

@ -23,7 +23,7 @@ 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);
size_t bt_mesh_comp_page_size(uint8_t page);
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);

View file

@ -59,24 +59,59 @@ static int handle_large_comp_data_get(struct bt_mesh_model *model, struct bt_mes
LOG_DBG("page %u offset %u", page, offset);
bt_mesh_model_msg_init(&rsp, OP_LARGE_COMP_DATA_STATUS);
if (page != 0U) {
if (page >= 130U && IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_2) &&
(atomic_test_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY) ||
IS_ENABLED(CONFIG_BT_MESH_RPR_SRV))) {
page = 130U;
} else if (page >= 129U && IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_1) &&
(atomic_test_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY) ||
IS_ENABLED(CONFIG_BT_MESH_RPR_SRV))) {
page = 129U;
} else if (page >= 128U && (atomic_test_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY) ||
IS_ENABLED(CONFIG_BT_MESH_RPR_SRV))) {
page = 128U;
} else if (page >= 2U && IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_2)) {
page = 2U;
} else if (page >= 1U && IS_ENABLED(CONFIG_BT_MESH_COMP_PAGE_1)) {
page = 1U;
} else 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");
if (atomic_test_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY) && page < 128) {
size_t msg_space;
NET_BUF_SIMPLE_DEFINE(temp_buf, CONFIG_BT_MESH_COMP_PST_BUF_SIZE);
err = bt_mesh_comp_read(&temp_buf, page);
if (err) {
LOG_ERR("Could not read comp data p%d, err: %d", page, err);
return err;
}
net_buf_simple_add_le16(&rsp, temp_buf.len);
if (offset > temp_buf.len) {
return 0;
}
msg_space = net_buf_simple_tailroom(&rsp) - BT_MESH_MIC_SHORT;
net_buf_simple_add_mem(
&rsp, temp_buf.data + offset,
(msg_space < (temp_buf.len - offset)) ? msg_space : temp_buf.len - offset);
} else {
total_size = bt_mesh_comp_page_size(page);
net_buf_simple_add_le16(&rsp, total_size);
if (offset < total_size) {
err = bt_mesh_comp_data_get_page(&rsp, page, offset);
if (err && err != -E2BIG) {
LOG_ERR("Could not read comp data p%d, err: %d", page, err);
return err;
}
}
}
if (bt_mesh_model_send(model, ctx, &rsp, NULL, NULL)) {