blob: 7884ef174376d5c4a8f02c624df092ed2d7f95ff [file] [log] [blame]
/*
* Copyright (c) 2023 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*
* Composition Data Page 1 (CDP1) test
*/
#include "mesh_test.h"
#include <stddef.h>
#include <string.h>
#include <mesh/access.h>
#include <mesh/net.h>
#include <zephyr/logging/log.h>
#define LOG_MODULE_NAME test_cdp1
LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_INF);
#define NODE_ADDR 0x00a1
#define WAIT_TIME 60 /* seconds */
#define TEST_MODEL_ID_1 0x2a2a
#define TEST_MODEL_ID_2 0x2b2b
#define TEST_MODEL_ID_3 0x2c2c
#define TEST_MODEL_ID_4 0x2d2d
#define TEST_MODEL_ID_5 0x2e2e
#define TEST_MODEL_ID_6 0x2f2f
#define TEST_VND_MODEL_ID_1 0x3a3a
#define TEST_MODEL_DECLARE(number) \
static int model_##number##_init(struct bt_mesh_model *model); \
static const struct bt_mesh_model_cb test_model_##number##_cb = { \
.init = model_##number##_init, \
}; \
static const struct bt_mesh_model_op model_op_##number[] = { \
BT_MESH_MODEL_OP_END, \
};
TEST_MODEL_DECLARE(1);
TEST_MODEL_DECLARE(2);
TEST_MODEL_DECLARE(3);
TEST_MODEL_DECLARE(4);
TEST_MODEL_DECLARE(5);
TEST_MODEL_DECLARE(6);
TEST_MODEL_DECLARE(vnd1);
static uint8_t app_key[16] = {0xaa};
static uint8_t net_key[16] = {0xcc};
static const struct bt_mesh_test_cfg node_1_cfg = {
.addr = NODE_ADDR,
.dev_key = {0xaa},
};
static struct bt_mesh_prov prov;
static struct bt_mesh_cfg_cli cfg_cli;
static struct bt_mesh_model models_1[] = {
BT_MESH_MODEL_CFG_SRV,
BT_MESH_MODEL_CFG_CLI(&cfg_cli),
BT_MESH_MODEL_CB(TEST_MODEL_ID_1, model_op_1, NULL, NULL, &test_model_1_cb),
BT_MESH_MODEL_CB(TEST_MODEL_ID_2, model_op_2, NULL, NULL, &test_model_2_cb),
BT_MESH_MODEL_CB(TEST_MODEL_ID_3, model_op_3, NULL, NULL, &test_model_3_cb),
};
static struct bt_mesh_model models_2[] = {
BT_MESH_MODEL_CB(TEST_MODEL_ID_4, model_op_4, NULL, NULL, &test_model_4_cb),
};
static struct bt_mesh_model models_3[] = {
BT_MESH_MODEL_CB(TEST_MODEL_ID_5, model_op_5, NULL, NULL, &test_model_5_cb),
};
static struct bt_mesh_model models_4[] = {
BT_MESH_MODEL_CB(TEST_MODEL_ID_6, model_op_6, NULL, NULL, &test_model_6_cb),
};
static struct bt_mesh_model models_vnd1[] = {
BT_MESH_MODEL_VND_CB(TEST_VND_COMPANY_ID, TEST_VND_MODEL_ID_1, model_op_vnd1, NULL, NULL,
&test_model_vnd1_cb),
};
static struct bt_mesh_elem elems[] = {
BT_MESH_ELEM(0, models_1, models_vnd1),
BT_MESH_ELEM(1, models_2, BT_MESH_MODEL_NONE),
BT_MESH_ELEM(2, models_3, BT_MESH_MODEL_NONE),
BT_MESH_ELEM(3, models_3, BT_MESH_MODEL_NONE),
BT_MESH_ELEM(4, models_4, BT_MESH_MODEL_NONE),
};
static const struct bt_mesh_comp comp = {
.cid = TEST_VND_COMPANY_ID,
.vid = 0xdead,
.pid = 0xface,
.elem = elems,
.elem_count = ARRAY_SIZE(elems),
};
/* The extensions and correspondence between models are as follows:
* Within elements:
* E0: M2 extends M1. VND1 extends M2. M3 and VND1 corresponds.
*
* Between elements:
* M3 on E0 extends M4 on E1.
* M2 on E0 and M4 on E1 corresponds.
* M6 on E4 extends M1 on E0
*/
static int model_1_init(struct bt_mesh_model *model)
{
return 0;
}
static int model_2_init(struct bt_mesh_model *model)
{
ASSERT_OK(bt_mesh_model_extend(model, bt_mesh_model_find(&elems[0], TEST_MODEL_ID_1)));
return 0;
}
static int model_3_init(struct bt_mesh_model *model)
{
return 0;
}
static int model_4_init(struct bt_mesh_model *model)
{
ASSERT_OK(bt_mesh_model_extend(bt_mesh_model_find(&elems[0], TEST_MODEL_ID_3), model));
ASSERT_OK(bt_mesh_model_correspond(model, bt_mesh_model_find(&elems[0], TEST_MODEL_ID_2)));
return 0;
}
static int model_5_init(struct bt_mesh_model *model)
{
return 0;
}
static int model_6_init(struct bt_mesh_model *model)
{
ASSERT_OK(bt_mesh_model_extend(model, bt_mesh_model_find(&elems[0], TEST_MODEL_ID_1)));
return 0;
}
static int model_vnd1_init(struct bt_mesh_model *model)
{
ASSERT_OK(bt_mesh_model_extend(model, bt_mesh_model_find(&elems[0], TEST_MODEL_ID_1)));
ASSERT_OK(bt_mesh_model_correspond(model, bt_mesh_model_find(&elems[0], TEST_MODEL_ID_3)));
return 0;
}
/* Hardcoded version of the CDP1 fields.
* Extensions are named extending-model_base-model.
*/
static struct bt_mesh_comp_p1_ext_item test_p1_ext_mod2_mod1 = {.type = SHORT,
.short_item = {
.elem_offset = 0,
.mod_item_idx = 2,
}};
static struct bt_mesh_comp_p1_ext_item test_p1_ext_vnd1_mod1 = {.type = SHORT,
.short_item = {
.elem_offset = 0,
.mod_item_idx = 2,
}};
static struct bt_mesh_comp_p1_ext_item test_p1_ext_mod3_mod4 = {.type = SHORT,
.short_item = {
.elem_offset = 7,
.mod_item_idx = 0,
}};
static struct bt_mesh_comp_p1_ext_item test_p1_ext_mod6_mod1 = {.type = LONG,
.long_item = {
.elem_offset = 4,
.mod_item_idx = 2,
}};
static const struct bt_mesh_comp_p1_model_item test_p1_cfg_srv_mod = {
.cor_present = 0,
.format = 0,
.ext_item_cnt = 0,
};
static const struct bt_mesh_comp_p1_model_item test_p1_cfg_cli_mod = {
.cor_present = 0,
.format = 0,
.ext_item_cnt = 0,
};
static const struct bt_mesh_comp_p1_model_item test_p1_mod1 = {
.cor_present = 0,
.format = 0,
.ext_item_cnt = 0,
};
static const struct bt_mesh_comp_p1_model_item test_p1_mod2 = {
.cor_present = 1,
.format = 0,
.ext_item_cnt = 1,
.cor_id = 0,
};
static const struct bt_mesh_comp_p1_model_item test_p1_mod3 = {
.cor_present = 1,
.format = 0,
.ext_item_cnt = 1,
.cor_id = 0,
};
static const struct bt_mesh_comp_p1_model_item test_p1_mod4 = {
.cor_present = 1,
.format = 0,
.ext_item_cnt = 0,
.cor_id = 0,
};
static const struct bt_mesh_comp_p1_model_item test_p1_mod5 = {
.cor_present = 0,
.format = 0,
.ext_item_cnt = 0,
};
static const struct bt_mesh_comp_p1_model_item test_p1_mod6 = {
.cor_present = 0,
.format = 1,
.ext_item_cnt = 1,
};
static const struct bt_mesh_comp_p1_model_item test_p1_vnd1 = {
.cor_present = 1,
.format = 0,
.ext_item_cnt = 1,
.cor_id = 0,
};
static const struct bt_mesh_comp_p1_model_item test_p1_elem0_models[] = {
test_p1_cfg_srv_mod, test_p1_cfg_cli_mod, test_p1_mod1,
test_p1_mod2, test_p1_mod3, test_p1_vnd1,
};
static const struct bt_mesh_comp_p1_model_item test_p1_elem1_models[] = {
test_p1_mod4,
};
static const struct bt_mesh_comp_p1_model_item test_p1_elem2_models[] = {
test_p1_mod5,
};
static const struct bt_mesh_comp_p1_model_item test_p1_elem3_models[] = {
test_p1_mod5,
};
static const struct bt_mesh_comp_p1_model_item test_p1_elem4_models[] = {
test_p1_mod6,
};
static const struct bt_mesh_comp_p1_model_item *test_p1_elem_models[] = {
test_p1_elem0_models, test_p1_elem1_models, test_p1_elem2_models,
test_p1_elem3_models, test_p1_elem4_models,
};
static const struct bt_mesh_comp_p1_elem test_p1_elem0 = {
.nsig = 5,
.nvnd = 1,
};
static const struct bt_mesh_comp_p1_elem test_p1_elem1 = {
.nsig = 1,
.nvnd = 0,
};
static const struct bt_mesh_comp_p1_elem test_p1_elem2 = {
.nsig = 1,
.nvnd = 0,
};
static const struct bt_mesh_comp_p1_elem test_p1_elem3 = {
.nsig = 1,
.nvnd = 0,
};
static const struct bt_mesh_comp_p1_elem test_p1_elem4 = {
.nsig = 1,
.nvnd = 0,
};
static struct bt_mesh_comp_p1_elem test_p1_elems[] = {
test_p1_elem0, test_p1_elem1, test_p1_elem2, test_p1_elem3, test_p1_elem4,
};
static void provision_and_configure(struct bt_mesh_test_cfg cfg)
{
int err;
uint8_t status;
err = bt_mesh_provision(net_key, 0, 0, 0, cfg.addr, cfg.dev_key);
if (err) {
FAIL("Provisioning failed (err %d)", err);
}
err = bt_mesh_cfg_cli_app_key_add(0, cfg.addr, 0, 0, app_key, &status);
if (err || status) {
FAIL("AppKey add failed (err %d, status %u)", err, status);
}
}
static void verify_model_item(struct bt_mesh_comp_p1_model_item *mod_item, int elem_idx,
int mod_idx, int offset)
{
ASSERT_EQUAL(test_p1_elem_models[elem_idx][mod_idx + offset].cor_present,
mod_item->cor_present);
ASSERT_EQUAL(test_p1_elem_models[elem_idx][mod_idx + offset].format, mod_item->format);
ASSERT_EQUAL(test_p1_elem_models[elem_idx][mod_idx + offset].ext_item_cnt,
mod_item->ext_item_cnt);
if (mod_item->cor_present) {
ASSERT_EQUAL(test_p1_elem_models[elem_idx][mod_idx + offset].cor_id,
mod_item->cor_id);
}
}
static void verify_ext_item(struct bt_mesh_comp_p1_ext_item *ext_item, int elem_idx, int mod_idx,
int offset)
{
struct bt_mesh_comp_p1_ext_item *test_p1_ext_item;
switch (elem_idx * 100 + (mod_idx + offset)) {
case 3: /* elem_idx=0, mod_idx=3, offset = 0 */
test_p1_ext_item = &test_p1_ext_mod2_mod1;
break;
case 4: /* elem_idx=0, mod_idx=4, offset = 0 */
test_p1_ext_item = &test_p1_ext_mod3_mod4;
break;
case 5: /* elem_idx=0, mod_idx=0, offset = 5 */
test_p1_ext_item = &test_p1_ext_vnd1_mod1;
break;
case 400: /* elem_idx=4, mod_idx=0, offset = 0 */
test_p1_ext_item = &test_p1_ext_mod6_mod1;
break;
default:
FAIL("Unexpected call to %s (elem %d, mod %d, offset %d)", __func__, elem_idx,
mod_idx, offset);
}
ASSERT_EQUAL(test_p1_ext_item->type, ext_item->type);
if (ext_item->type == SHORT) {
ASSERT_EQUAL(test_p1_ext_item->short_item.elem_offset,
ext_item->short_item.elem_offset);
ASSERT_EQUAL(test_p1_ext_item->short_item.mod_item_idx,
ext_item->short_item.mod_item_idx);
} else {
ASSERT_EQUAL(test_p1_ext_item->long_item.elem_offset,
ext_item->long_item.elem_offset);
ASSERT_EQUAL(test_p1_ext_item->long_item.mod_item_idx,
ext_item->long_item.mod_item_idx);
}
}
static void verify_cdp1(struct bt_mesh_comp_p1_elem *p1_elem,
struct bt_mesh_comp_p1_model_item *mod_item,
struct bt_mesh_comp_p1_ext_item *ext_item,
struct net_buf_simple *p1_dev_comp)
{
int elem_idx = 0;
while (bt_mesh_comp_p1_elem_pull(p1_dev_comp, p1_elem)) {
ASSERT_EQUAL(test_p1_elems[elem_idx].nsig, p1_elem->nsig);
ASSERT_EQUAL(test_p1_elems[elem_idx].nvnd, p1_elem->nvnd);
for (int mod_idx = 0; mod_idx < p1_elem->nsig; mod_idx++) {
if (bt_mesh_comp_p1_item_pull(p1_elem, mod_item)) {
verify_model_item(mod_item, elem_idx, mod_idx, 0);
}
for (int ext_mod_idx = 0; ext_mod_idx < mod_item->ext_item_cnt;
ext_mod_idx++) {
bt_mesh_comp_p1_pull_ext_item(mod_item, ext_item);
verify_ext_item(ext_item, elem_idx, mod_idx, 0);
}
}
for (int mod_idx = 0; mod_idx < p1_elem->nvnd; mod_idx++) {
if (bt_mesh_comp_p1_item_pull(p1_elem, mod_item)) {
verify_model_item(mod_item, elem_idx, mod_idx, p1_elem->nsig);
}
for (int ext_mod_idx = 0; ext_mod_idx < mod_item->ext_item_cnt;
ext_mod_idx++) {
bt_mesh_comp_p1_pull_ext_item(mod_item, ext_item);
verify_ext_item(ext_item, elem_idx, mod_idx, p1_elem->nsig);
}
}
elem_idx++;
}
}
static void test_node_data_comparison(void)
{
bt_mesh_test_cfg_set(NULL, WAIT_TIME);
bt_mesh_device_setup(&prov, &comp);
provision_and_configure(node_1_cfg);
NET_BUF_SIMPLE_DEFINE(p1_dev_comp, 500);
uint8_t page_rsp;
ASSERT_OK(bt_mesh_cfg_cli_comp_data_get(0, node_1_cfg.addr, 1, &page_rsp, &p1_dev_comp));
ASSERT_EQUAL(1, page_rsp);
NET_BUF_SIMPLE_DEFINE(p1_buf, 500);
NET_BUF_SIMPLE_DEFINE(p1_item_buf, 500);
struct bt_mesh_comp_p1_elem p1_elem = {._buf = &p1_buf};
struct bt_mesh_comp_p1_model_item mod_item = {._buf = &p1_item_buf};
struct bt_mesh_comp_p1_ext_item ext_item = {0};
verify_cdp1(&p1_elem, &mod_item, &ext_item, &p1_dev_comp);
PASS();
}
#define TEST_CASE(role, name, description) \
{ \
.test_id = "cdp1_" #role "_" #name, .test_descr = description, \
.test_tick_f = bt_mesh_test_timeout, .test_main_f = test_##role##_##name, \
}
static const struct bst_test_instance test_cdp1[] = {
TEST_CASE(node, data_comparison, "Compare encoded and decoded CDP1 data."),
BSTEST_END_MARKER};
struct bst_test_list *test_cdp1_install(struct bst_test_list *tests)
{
tests = bst_add_tests(tests, test_cdp1);
return tests;
}