/* * Copyright (c) 2023 Nordic Semiconductor * * SPDX-License-Identifier: Apache-2.0 * * Composition Data Page 1 (CDP1) test */ #include "mesh_test.h" #include #include #include #include #include #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; }