1 /*
2  * Copyright (c) 2022 Nordic Semiconductor
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Opcode aggregator test
7  */
8 
9 #include "mesh_test.h"
10 
11 #include <string.h>
12 
13 #include <zephyr/logging/log.h>
14 
15 LOG_MODULE_REGISTER(test_op_agg, LOG_LEVEL_INF);
16 
17 #define CLI_ADDR    0x7728
18 #define SRV_ADDR    0x18f8
19 #define WAIT_TIME   15 /* seconds */
20 #define SEM_TIMEOUT K_SECONDS(10)
21 
22 #define BT_MESH_DUMMY_VND_MOD_GET_OP	BT_MESH_MODEL_OP_3(0xDC, TEST_VND_COMPANY_ID)
23 #define BT_MESH_DUMMY_VND_MOD_STATUS_OP BT_MESH_MODEL_OP_3(0xCD, TEST_VND_COMPANY_ID)
24 
25 #define BT_MESH_DUMMY_VND_MOD_MSG_MINLEN 7
26 #define BT_MESH_DUMMY_VND_MOD_MSG_MAXLEN 8
27 
28 /* The 34 messages make up the aggregated message sequence, expecting a 380 byte status response. */
29 #define TEST_SEND_ITR 34
30 
31 /* Spec: 4.3.9.4: Table 4.273 defines the structure of the OPCODES_AGGREGATOR_STATUS message. */
32 #define OPCODES_AGG_STATUS_MSG_BASE_STRUCTURE_LEN 5
33 /* SPEC: 4.3.9.1: Length_format + Length_Short.*/
34 #define OPCODES_AGG_ITEM_SHORT_FORMAT_LEN	  1
35 /* SPEC: 4.3.9.1: The structure of an Aggregator Item field is defined in Table 4.270 */
36 #define OPCODES_STATUS_ITEM_LEN(param_len)                                                         \
37 	(OPCODES_AGG_ITEM_SHORT_FORMAT_LEN +                                                       \
38 	 BT_MESH_MODEL_OP_LEN(BT_MESH_DUMMY_VND_MOD_STATUS_OP) + param_len)
39 /* Spec: 4.3.9.3 OPCODES_AGGREGATOR_STATUS. The test initiates 33+1 get/status message iterations.*/
40 #define OP_AGG_STATUS_ACCESS_PAYLOAD                                                               \
41 	(OPCODES_AGG_STATUS_MSG_BASE_STRUCTURE_LEN +                                               \
42 	 (OPCODES_STATUS_ITEM_LEN(BT_MESH_DUMMY_VND_MOD_MSG_MINLEN) * (TEST_SEND_ITR - 1)) +       \
43 	 OPCODES_STATUS_ITEM_LEN(BT_MESH_DUMMY_VND_MOD_MSG_MAXLEN))
44 
45 /* Ensure that a 380-byte opcode aggregator get/status access payload is being sent. */
46 BUILD_ASSERT(OP_AGG_STATUS_ACCESS_PAYLOAD == (BT_MESH_TX_SDU_MAX - BT_MESH_MIC_SHORT));
47 
48 static int status_rcvd_count;
49 static int get_rcvd_count;
50 static struct k_sem cli_suspend_sem;
51 static struct k_sem srv_suspend_sem;
52 static const uint8_t dev_key[16] = {0xaa};
53 static uint8_t cli_sent_array[TEST_SEND_ITR], cli_rcvd_array[TEST_SEND_ITR];
54 static struct bt_mesh_prov prov;
55 static struct bt_mesh_cfg_cli cfg_cli;
56 
get_handler(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)57 static int get_handler(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
58 		       struct net_buf_simple *buf)
59 {
60 	uint8_t seq = net_buf_simple_pull_u8(buf);
61 
62 	get_rcvd_count++;
63 
64 	BT_MESH_MODEL_BUF_DEFINE(msg, BT_MESH_DUMMY_VND_MOD_STATUS_OP,
65 				 BT_MESH_DUMMY_VND_MOD_MSG_MAXLEN);
66 	bt_mesh_model_msg_init(&msg, BT_MESH_DUMMY_VND_MOD_STATUS_OP);
67 
68 	net_buf_simple_add_u8(&msg, seq);
69 	memset(net_buf_simple_add(&msg, BT_MESH_DUMMY_VND_MOD_MSG_MINLEN - 1), 0,
70 		BT_MESH_DUMMY_VND_MOD_MSG_MINLEN);
71 
72 	/* Last message: One additional byte is added to fill the available access payload.*/
73 	if (get_rcvd_count >= TEST_SEND_ITR) {
74 		net_buf_simple_add(&msg, 1);
75 		k_sem_give(&srv_suspend_sem);
76 	}
77 
78 	return bt_mesh_model_send(model, ctx, &msg, NULL, NULL);
79 }
80 
status_handler(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)81 static int status_handler(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
82 			  struct net_buf_simple *buf)
83 {
84 	uint8_t seq = net_buf_simple_pull_u8(buf);
85 
86 	status_rcvd_count++;
87 	cli_rcvd_array[status_rcvd_count - 1] = seq;
88 
89 	if (status_rcvd_count >= TEST_SEND_ITR) {
90 		k_sem_give(&cli_suspend_sem);
91 	}
92 
93 	return 0;
94 }
95 
dummy_vnd_mod_get(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,uint8_t seq)96 static int dummy_vnd_mod_get(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
97 			     uint8_t seq)
98 {
99 	BT_MESH_MODEL_BUF_DEFINE(msg, BT_MESH_DUMMY_VND_MOD_GET_OP,
100 				 BT_MESH_DUMMY_VND_MOD_MSG_MAXLEN);
101 
102 	bt_mesh_model_msg_init(&msg, BT_MESH_DUMMY_VND_MOD_GET_OP);
103 
104 	net_buf_simple_add_u8(&msg, seq);
105 	memset(net_buf_simple_add(&msg, BT_MESH_DUMMY_VND_MOD_MSG_MINLEN - 1), 0,
106 		BT_MESH_DUMMY_VND_MOD_MSG_MINLEN);
107 
108 	/* Last message: One additional byte is added to fill the available access payload.*/
109 	if (seq >= TEST_SEND_ITR - 1) {
110 		net_buf_simple_add(&msg, 1);
111 	}
112 
113 	return bt_mesh_model_send(model, ctx, &msg, NULL, NULL);
114 }
115 
116 const struct bt_mesh_model_op _dummy_vnd_mod_op[] = {
117 	{BT_MESH_DUMMY_VND_MOD_GET_OP, BT_MESH_DUMMY_VND_MOD_MSG_MINLEN, get_handler},
118 	{BT_MESH_DUMMY_VND_MOD_STATUS_OP, BT_MESH_DUMMY_VND_MOD_MSG_MINLEN, status_handler},
119 	BT_MESH_MODEL_OP_END,
120 };
121 
122 static const struct bt_mesh_elem elements[] = {BT_MESH_ELEM(
123 	0,
124 	MODEL_LIST(BT_MESH_MODEL_CFG_SRV, BT_MESH_MODEL_CFG_CLI(&cfg_cli), BT_MESH_MODEL_OP_AGG_SRV,
125 		   BT_MESH_MODEL_OP_AGG_CLI),
126 	MODEL_LIST(BT_MESH_MODEL_VND_CB(TEST_VND_COMPANY_ID, TEST_VND_MOD_ID, _dummy_vnd_mod_op,
127 					NULL, NULL, NULL)))};
128 
129 static const struct bt_mesh_comp comp = {
130 	.cid = TEST_VND_COMPANY_ID,
131 	.elem = elements,
132 	.elem_count = ARRAY_SIZE(elements),
133 };
134 
op_agg_test_prov_and_conf(uint16_t addr)135 static void op_agg_test_prov_and_conf(uint16_t addr)
136 {
137 	uint8_t status;
138 	int err;
139 
140 	ASSERT_OK(bt_mesh_provision(test_net_key, 0, 0, 0, addr, dev_key));
141 
142 	err = bt_mesh_cfg_cli_app_key_add(0, addr, 0, 0, test_app_key, &status);
143 	if (err || status) {
144 		FAIL("AppKey add failed (err %d, status %u)", err, status);
145 	}
146 
147 	err = bt_mesh_cfg_cli_mod_app_bind(0, addr, addr, 0, BT_MESH_MODEL_ID_OP_AGG_CLI,
148 					    &status);
149 	if (err || status) {
150 		FAIL("Failed to bind OP_AGG_CLI to application (err %d, status %u)", err, status);
151 	}
152 	err = bt_mesh_cfg_cli_mod_app_bind(0, addr, addr, 0, BT_MESH_MODEL_ID_OP_AGG_SRV,
153 					    &status);
154 	if (err || status) {
155 		FAIL("Failed to bind OP_AGG_SRV to application (err %d, status %u)", err, status);
156 	}
157 	err = bt_mesh_cfg_cli_mod_app_bind_vnd(0, addr, addr, 0, TEST_VND_MOD_ID,
158 						TEST_VND_COMPANY_ID, &status);
159 	if (err || status) {
160 		FAIL("Failed to bind OP_AGG_TEST_MOD to application (err %d, status %u)", err,
161 		     status);
162 	}
163 }
164 
common_init(uint16_t own_addr,uint16_t dst_addr,bool agg_cli_fill)165 static void common_init(uint16_t own_addr, uint16_t dst_addr, bool agg_cli_fill)
166 {
167 	bt_mesh_test_cfg_set(NULL, WAIT_TIME);
168 	bt_mesh_device_setup(&prov, &comp);
169 	op_agg_test_prov_and_conf(own_addr);
170 
171 	ASSERT_OK(k_sem_init(&cli_suspend_sem, 0, 1));
172 	ASSERT_OK(k_sem_init(&srv_suspend_sem, 0, 1));
173 	ASSERT_OK(bt_mesh_op_agg_cli_seq_start(0, 0, dst_addr, dst_addr));
174 
175 	if (!agg_cli_fill) {
176 		return;
177 	}
178 
179 	struct bt_mesh_msg_ctx ctx = {
180 		.net_idx = 0,
181 		.app_idx = 0,
182 		.addr = dst_addr,
183 	};
184 
185 	/* Populate the op_agg sequence */
186 	for (int i = 0; i < TEST_SEND_ITR; i++) {
187 		cli_sent_array[i] = i;
188 		ASSERT_OK(dummy_vnd_mod_get(&elements[0].vnd_models[0], &ctx, i));
189 	}
190 }
191 
confirm_agg_seq(void)192 static void confirm_agg_seq(void)
193 {
194 	/* Wait for all expected GET messages to be received */
195 	if (k_sem_take(&srv_suspend_sem, SEM_TIMEOUT)) {
196 		FAIL("Server suspension timed out. Get-messages received: %d", get_rcvd_count);
197 	}
198 }
199 
confirm_agg_status(void)200 static void confirm_agg_status(void)
201 {
202 	/* Wait for all expected STATUS messages to be received */
203 	if (k_sem_take(&cli_suspend_sem, SEM_TIMEOUT)) {
204 		FAIL("Client suspension timed out. Status-messages received: %d",
205 		     status_rcvd_count);
206 	}
207 
208 	if (memcmp(cli_sent_array, cli_rcvd_array, ARRAY_SIZE(cli_rcvd_array))) {
209 		FAIL("Message arrays (sent / rcvd) are not equal.");
210 	}
211 }
212 
test_cli_max_len_sequence_msg_send(void)213 static void test_cli_max_len_sequence_msg_send(void)
214 {
215 	common_init(CLI_ADDR, SRV_ADDR, true);
216 	ASSERT_OK(bt_mesh_op_agg_cli_seq_send());
217 	confirm_agg_status();
218 	PASS();
219 }
220 
test_srv_max_len_status_msg_send(void)221 static void test_srv_max_len_status_msg_send(void)
222 {
223 	common_init(SRV_ADDR, CLI_ADDR, false);
224 	confirm_agg_seq();
225 	PASS();
226 }
227 
test_tester_model_coex(void)228 static void test_tester_model_coex(void)
229 {
230 	common_init(CLI_ADDR, SRV_ADDR, true);
231 
232 	/* Immediately send aggregated sequence to srv device */
233 	ASSERT_OK(bt_mesh_op_agg_cli_seq_send());
234 
235 	/* Confirm status messages for sequence */
236 	confirm_agg_status();
237 
238 	/* Confirm incoming sequence messages from server */
239 	confirm_agg_seq();
240 
241 	PASS();
242 }
243 
test_dut_model_coex(void)244 static void test_dut_model_coex(void)
245 {
246 	/* Start an aggregated sequence, but postpone sending it */
247 	common_init(SRV_ADDR, CLI_ADDR, true);
248 
249 	/* Wait and confirm incoming sequence messages from cli device */
250 	confirm_agg_seq();
251 
252 	/* After incoming sequence completes, send aggregated sequence to srv device */
253 	ASSERT_OK(bt_mesh_op_agg_cli_seq_send());
254 
255 	/* Confirm status messages for sequence */
256 	confirm_agg_status();
257 
258 	PASS();
259 }
260 
test_dut_model_coex_loopback(void)261 static void test_dut_model_coex_loopback(void)
262 {
263 	/* Start an aggregated sequence */
264 	common_init(SRV_ADDR, SRV_ADDR, true);
265 
266 	/* Send aggregated sequence to server model over loopback */
267 	ASSERT_OK(bt_mesh_op_agg_cli_seq_send());
268 
269 	/* Confirm incoming sequence messages */
270 	confirm_agg_seq();
271 
272 	/* Confirm status messages for sequence */
273 	confirm_agg_status();
274 
275 	PASS();
276 }
277 
278 #define TEST_CASE(role, name, description)         \
279 	{                                              \
280 		.test_id = "op_agg_" #role "_" #name,      \
281 		.test_descr = description,                 \
282 		.test_tick_f = bt_mesh_test_timeout,       \
283 		.test_main_f = test_##role##_##name,       \
284 	}
285 
286 static const struct bst_test_instance test_op_agg[] = {
287 	TEST_CASE(cli, max_len_sequence_msg_send,
288 		  "OpAggCli composes a sequence request list, expecting a 380 Byte status message "
289 		  "in return."),
290 	TEST_CASE(tester, model_coex, "Tester: Coexistence of OpAggSrv and OpAggCli."),
291 	TEST_CASE(srv, max_len_status_msg_send,
292 		  "OpAggSrv will respond with a 380 Byte status message. "),
293 	TEST_CASE(dut, model_coex, "DUT: Coexistence of OpAggSrv and OpAggCli."),
294 	TEST_CASE(dut, model_coex_loopback, "DUT: Coexistence for OpAggSrv and OpAggCli loopback."),
295 
296 	BSTEST_END_MARKER};
297 
test_op_agg_install(struct bst_test_list * tests)298 struct bst_test_list *test_op_agg_install(struct bst_test_list *tests)
299 {
300 	tests = bst_add_tests(tests, test_op_agg);
301 	return tests;
302 }
303