1 /*
2  * Copyright (c) 2023 Nordic Semiconductor
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  */
7 
8 #include "mesh_test.h"
9 #include "gatt_common.h"
10 
11 #include <mesh/access.h>
12 #include <mesh/net.h>
13 
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(test_suspend, LOG_LEVEL_INF);
16 
17 #define WAIT_TIME        60 /* seconds */
18 #define SUSPEND_DURATION 15 /* seconds */
19 #define NUM_PUB           4 /* Times the DUT will publish per interval. */
20 
21 #define TEST_MODEL_ID_1 0x2a2a
22 #define TEST_MODEL_ID_2 0x2b2b
23 #define TEST_MESSAGE_OP 0x1f
24 
25 enum dut_mesh_status {
26 	DUT_SUSPENDED,
27 	DUT_RUNNING,
28 };
29 
30 static int model_1_init(const struct bt_mesh_model *model);
31 static int model_2_init(const struct bt_mesh_model *model);
32 
33 static uint8_t app_key[16] = {0xaa};
34 static uint8_t net_key[16] = {0xcc};
35 
36 static const struct bt_mesh_test_cfg dut_cfg = {
37 	.addr = 0x00a0,
38 	.dev_key = {0x01},
39 };
40 static const struct bt_mesh_test_cfg tester_cfg = {
41 	.addr = 0x00b0,
42 	.dev_key = {0x02},
43 };
44 
45 static uint8_t test_prov_uuid[16] = { 0x6c, 0x69, 0x6e, 0x67, 0x61, 0xaa };
46 static struct bt_mesh_prov prov = {
47 		.uuid = test_prov_uuid,
48 	};
49 static struct k_sem publish_sem;
50 static enum dut_mesh_status dut_status;
51 
model_1_update(const struct bt_mesh_model * model)52 static int model_1_update(const struct bt_mesh_model *model)
53 {
54 	model->pub->msg->data[1]++;
55 	LOG_DBG("Model 1 publishing..., n: %d", model->pub->msg->data[1]);
56 	k_sem_give(&publish_sem);
57 	return 0;
58 }
59 
msg_handler(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)60 static int msg_handler(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
61 			    struct net_buf_simple *buf)
62 {
63 	static uint8_t prev_num;
64 	static int64_t uptime;
65 	uint8_t num = net_buf_simple_pull_u8(buf);
66 
67 	LOG_DBG("Received msg, n: %d", num);
68 
69 	/* Ensure that payload changes. */
70 	ASSERT_TRUE(prev_num != num);
71 	prev_num = num;
72 
73 	/* Ensure that no message is received while Mesh is suspended or disabled.
74 	 * A publication may be sent just before DUT is suspended, which is ignored.
75 	 */
76 	if ((dut_status == DUT_SUSPENDED) && !(num == (NUM_PUB + 1))) {
77 		if (SUSPEND_DURATION * 1000ll <= k_uptime_delta(&uptime)) {
78 			dut_status = DUT_RUNNING;
79 			LOG_DBG("Suspend duration passed. Setting status to %d.", dut_status);
80 		} else {
81 			FAIL("Received publication while Mesh is suspended.");
82 		}
83 	}
84 
85 	if (num == NUM_PUB) {
86 		dut_status = DUT_SUSPENDED;
87 		LOG_DBG("Expected number of pubs received. Setting status to %d.", dut_status);
88 		uptime = k_uptime_get();
89 	}
90 
91 	k_sem_give(&publish_sem);
92 	return 0;
93 }
94 
95 BT_MESH_MODEL_PUB_DEFINE(model_1_pub, model_1_update, 4);
96 
97 static const struct bt_mesh_model_cb model_1_cb = {
98 	.init = model_1_init,
99 };
100 
101 static const struct bt_mesh_model_cb model_2_cb = {
102 	.init = model_2_init,
103 };
104 
105 static const struct bt_mesh_model_op model_op_1[] = {BT_MESH_MODEL_OP_END};
106 
107 static const struct bt_mesh_model_op model_op_2[] = {{TEST_MESSAGE_OP, 0, msg_handler},
108 						     BT_MESH_MODEL_OP_END};
109 
110 static struct bt_mesh_cfg_cli cfg_cli_dut;
111 static struct bt_mesh_model dut_models[] = {
112 	BT_MESH_MODEL_CFG_SRV,
113 	BT_MESH_MODEL_CFG_CLI(&cfg_cli_dut),
114 	BT_MESH_MODEL_CB(TEST_MODEL_ID_1, model_op_1, &model_1_pub, NULL, &model_1_cb),
115 };
116 
117 static struct bt_mesh_elem dut_elems[] = {
118 	BT_MESH_ELEM(0, dut_models, BT_MESH_MODEL_NONE),
119 };
120 
121 static const struct bt_mesh_comp dut_comp = {
122 	.cid = TEST_VND_COMPANY_ID,
123 	.vid = 0xeeee,
124 	.pid = 0xaaaa,
125 	.elem = dut_elems,
126 	.elem_count = ARRAY_SIZE(dut_elems),
127 };
128 
129 static struct bt_mesh_cfg_cli cfg_cli_tester;
130 static struct bt_mesh_model tester_models[] = {
131 	BT_MESH_MODEL_CFG_SRV,
132 	BT_MESH_MODEL_CFG_CLI(&cfg_cli_tester),
133 	BT_MESH_MODEL_CB(TEST_MODEL_ID_2, model_op_2, NULL, NULL, &model_2_cb),
134 };
135 
136 static struct bt_mesh_elem tester_elems[] = {
137 	BT_MESH_ELEM(0, tester_models, BT_MESH_MODEL_NONE),
138 };
139 
140 static const struct bt_mesh_comp tester_comp = {
141 	.cid = TEST_VND_COMPANY_ID,
142 	.vid = 0xbaaa,
143 	.pid = 0xb000,
144 	.elem = tester_elems,
145 	.elem_count = ARRAY_SIZE(tester_elems),
146 };
147 
model_1_init(const struct bt_mesh_model * model)148 static int model_1_init(const struct bt_mesh_model *model)
149 {
150 	bt_mesh_model_msg_init(model->pub->msg, TEST_MESSAGE_OP);
151 	net_buf_simple_add_u8(model->pub->msg, 1);
152 
153 	return 0;
154 }
155 
model_2_init(const struct bt_mesh_model * model)156 static int model_2_init(const struct bt_mesh_model *model)
157 {
158 	return 0;
159 }
160 
provision_and_configure(struct bt_mesh_test_cfg cfg,bool tester)161 static void provision_and_configure(struct bt_mesh_test_cfg cfg, bool tester)
162 {
163 	int err;
164 	uint8_t status;
165 
166 	ASSERT_OK(bt_mesh_provision(net_key, 0, 0, 0, cfg.addr, cfg.dev_key));
167 
168 	err = bt_mesh_cfg_cli_app_key_add(0, cfg.addr, 0, 0, app_key, &status);
169 	if (err || status) {
170 		FAIL("AppKey add failed (err %d, status %u)", err, status);
171 	}
172 
173 	const struct bt_mesh_test_cfg *pcfg = tester ? &tester_cfg : &dut_cfg;
174 	uint16_t model_id = tester ? TEST_MODEL_ID_2 : TEST_MODEL_ID_1;
175 
176 	err = bt_mesh_cfg_cli_mod_app_bind(0, pcfg->addr, pcfg->addr, 0, model_id, &status);
177 	if (err || status) {
178 		FAIL("Model %#4x bind failed (err %d, status %u)", model_id, err, status);
179 	}
180 }
181 
182 struct bt_mesh_cfg_cli_mod_pub pub_params = {
183 	.addr = tester_cfg.addr,
184 	.uuid = NULL,
185 	.cred_flag = false,
186 	.app_idx = 0,
187 	.ttl = 5,
188 	.period = BT_MESH_PUB_PERIOD_SEC(1),
189 	.transmit = 0,
190 };
191 
192 extern const struct bt_mesh_comp comp;
193 /* For legacy adv, pb-gatt advs are sent with a 1000ms interval. For ext adv, they are sent
194  * with a 100ms interval.
195  */
196 static struct bt_mesh_test_gatt gatt_param = {
197 #if defined(CONFIG_BT_EXT_ADV)
198 	/* (total transmit duration) / (transmit interval) */
199 	.transmits = 1500 / 100,
200 	.interval = 100,
201 #else
202 	.transmits = 2000 / 1000,
203 	.interval = 1000,
204 #endif
205 	.service = MESH_SERVICE_PROVISIONING,
206 };
207 
gatt_check_rx_count(uint8_t transmit)208 static bool gatt_check_rx_count(uint8_t transmit)
209 {
210 	static int cnt;
211 
212 	LOG_DBG("rx: cnt(%d)", cnt);
213 	cnt++;
214 
215 	if (cnt >= transmit) {
216 		cnt = 0;
217 		return true;
218 	}
219 
220 	return false;
221 }
222 
gatt_scan_cb(const bt_addr_le_t * addr,int8_t rssi,uint8_t adv_type,struct net_buf_simple * buf)223 static void gatt_scan_cb(const bt_addr_le_t *addr, int8_t rssi,
224 			  uint8_t adv_type, struct net_buf_simple *buf)
225 {
226 	if (adv_type != BT_GAP_ADV_TYPE_ADV_IND) {
227 		return;
228 	}
229 
230 	/* Ensure that no message is received while Mesh is suspended or disabled. */
231 	ASSERT_FALSE_MSG(dut_status == DUT_SUSPENDED, "Received adv while Mesh is suspended.");
232 
233 	bt_mesh_test_parse_mesh_gatt_preamble(buf);
234 
235 	if (gatt_param.service == MESH_SERVICE_PROVISIONING) {
236 		LOG_DBG("Parsing pb_gatt adv");
237 		bt_mesh_test_parse_mesh_pb_gatt_service(buf);
238 	} else {
239 		LOG_DBG("Parsing proxy adv");
240 		bt_mesh_test_parse_mesh_proxy_service(buf);
241 	}
242 
243 	if (gatt_check_rx_count(gatt_param.transmits)) {
244 		LOG_DBG("rx completed, stopping scan...");
245 		k_sem_give(&publish_sem);
246 	}
247 }
248 
suspend_state_change_cb(const bt_addr_le_t * addr,int8_t rssi,uint8_t adv_type,struct net_buf_simple * buf)249 static void suspend_state_change_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type,
250 			struct net_buf_simple *buf)
251 {
252 	uint8_t length;
253 
254 	if (adv_type != BT_GAP_ADV_TYPE_ADV_NONCONN_IND) {
255 		return;
256 	}
257 
258 	length = net_buf_simple_pull_u8(buf);
259 	ASSERT_EQUAL(buf->len, length);
260 	ASSERT_EQUAL(length, sizeof(uint8_t) + sizeof(enum dut_mesh_status));
261 	ASSERT_EQUAL(BT_DATA_MESH_MESSAGE, net_buf_simple_pull_u8(buf));
262 
263 	enum dut_mesh_status *msg_status =
264 		net_buf_simple_pull_mem(buf, sizeof(enum dut_mesh_status));
265 
266 	if ((*msg_status == DUT_RUNNING) || (*msg_status == DUT_SUSPENDED)) {
267 		dut_status = *msg_status;
268 	} else {
269 		FAIL("Received unexpected data");
270 	}
271 
272 	LOG_DBG("Received %d from DUT, setting status to %s",
273 		*msg_status, (dut_status == DUT_SUSPENDED) ? "true" : "false");
274 	k_sem_give(&publish_sem);
275 }
276 
dut_pub_common(bool disable_bt)277 static void dut_pub_common(bool disable_bt)
278 {
279 	bt_mesh_test_cfg_set(NULL, WAIT_TIME);
280 	bt_mesh_device_setup(&prov, &dut_comp);
281 	provision_and_configure(dut_cfg, 0);
282 
283 	k_sem_init(&publish_sem, 0, 1);
284 	uint8_t status;
285 	int err;
286 
287 	err = bt_mesh_cfg_cli_mod_pub_set(0, dut_cfg.addr, dut_cfg.addr, TEST_MODEL_ID_1,
288 					&pub_params, &status);
289 	if (err || status) {
290 		FAIL("Mod pub set failed (err %d, status %u)", err, status);
291 	}
292 
293 	/* Wait until node has published before suspending. */
294 	for (int i = 0; i < NUM_PUB; i++) {
295 		ASSERT_OK_MSG(k_sem_take(&publish_sem, K_SECONDS(30)), "Pub timed out");
296 	}
297 
298 	/* Allow publishing to finish before suspending. */
299 	k_sleep(K_MSEC(100));
300 
301 	ASSERT_OK_MSG(bt_mesh_suspend(), "Failed to suspend Mesh.");
302 
303 	if (disable_bt) {
304 		ASSERT_OK_MSG(bt_disable(), "Failed to disable Bluetooth.");
305 	}
306 
307 	k_sleep(K_SECONDS(SUSPEND_DURATION));
308 
309 	if (disable_bt) {
310 		ASSERT_OK_MSG(bt_enable(NULL), "Failed to enable Bluetooth.");
311 	}
312 
313 	ASSERT_OK_MSG(bt_mesh_resume(), "Failed to resume Mesh.");
314 
315 	for (int i = 0; i < NUM_PUB; i++) {
316 		ASSERT_OK_MSG(k_sem_take(&publish_sem, K_SECONDS(30)), "Pub timed out");
317 	}
318 
319 	/* Allow publishing to finish before suspending. */
320 	k_sleep(K_MSEC(100));
321 	ASSERT_OK(bt_mesh_suspend());
322 }
323 
send_start(uint16_t duration,int err,void * cb_data)324 static void send_start(uint16_t duration, int err, void *cb_data)
325 {
326 	if (err) {
327 		FAIL("Failed to send message (err %d)", err);
328 	}
329 }
330 
send_end(int err,void * cb_data)331 static void send_end(int err, void *cb_data)
332 {
333 	k_sem_give((struct k_sem *)cb_data);
334 }
335 
dut_gatt_common(bool disable_bt)336 static void dut_gatt_common(bool disable_bt)
337 {
338 	struct k_sem send_sem;
339 
340 	k_sem_init(&send_sem, 0, 1);
341 
342 	const struct bt_mesh_send_cb send_cb = {
343 		.start = send_start,
344 		.end = send_end,
345 	};
346 
347 	bt_mesh_test_cfg_set(NULL, WAIT_TIME);
348 	bt_mesh_device_setup(&prov, &comp);
349 	ASSERT_OK_MSG(bt_mesh_prov_enable(BT_MESH_PROV_GATT), "Failed to enable GATT provisioner");
350 	dut_status = DUT_RUNNING;
351 	/* Let the Tester observe pb gatt advertisements before provisioning. The node should
352 	 * advertise pb gatt service with 100 msec (ext adv) or 1000msec (legacy adv) interval.
353 	 */
354 	k_sleep(K_MSEC(1800));
355 
356 	ASSERT_OK(bt_mesh_provision(test_net_key, 0, 0, 0, dut_cfg.addr, dut_cfg.dev_key));
357 
358 	/* Let the Tester observe proxy advertisements */
359 	k_sleep(K_MSEC(6500));
360 
361 	/* Send a mesh message to notify Tester that DUT is about to be suspended. */
362 	dut_status = DUT_SUSPENDED;
363 	bt_mesh_test_send_over_adv_cb(&dut_status, sizeof(enum dut_mesh_status), &send_cb,
364 				      &send_sem);
365 	ASSERT_OK(k_sem_take(&send_sem, K_MSEC(200)));
366 
367 	ASSERT_OK_MSG(bt_mesh_suspend(), "Failed to suspend Mesh.");
368 
369 	if (disable_bt) {
370 		ASSERT_OK_MSG(bt_disable(), "Failed to disable Bluetooth.");
371 	}
372 
373 	k_sleep(K_SECONDS(SUSPEND_DURATION));
374 
375 	if (disable_bt) {
376 		ASSERT_OK_MSG(bt_enable(NULL), "Failed to enable Bluetooth.");
377 	}
378 
379 	ASSERT_OK_MSG(bt_mesh_resume(), "Failed to resume Mesh.");
380 
381 	/* Send a mesh message to notify Tester that device is resumed */
382 	dut_status = DUT_RUNNING;
383 	bt_mesh_test_send_over_adv(&dut_status, sizeof(enum dut_mesh_status));
384 
385 	/* Let the Tester observe that proxy advertisement resumes */
386 	k_sleep(K_MSEC(6000));
387 }
388 
test_dut_suspend_resume(void)389 static void test_dut_suspend_resume(void)
390 {
391 	dut_pub_common(false);
392 	PASS();
393 }
394 
test_dut_suspend_disable_resume(void)395 static void test_dut_suspend_disable_resume(void)
396 {
397 	dut_pub_common(true);
398 	PASS();
399 }
400 
test_tester_pub(void)401 static void test_tester_pub(void)
402 {
403 	bt_mesh_test_cfg_set(NULL, WAIT_TIME);
404 	bt_mesh_device_setup(&prov, &tester_comp);
405 	provision_and_configure(tester_cfg, 1);
406 	k_sem_init(&publish_sem, 0, 1);
407 	dut_status = DUT_RUNNING;
408 
409 	/* Receive messages before and after suspending. A publication may get lost
410 	 * when suspending immeditiately after publication, thus the "-1".
411 	 */
412 	for (int i = 0; i < NUM_PUB * 2 - 1; i++) {
413 		ASSERT_OK_MSG(k_sem_take(&publish_sem, K_SECONDS(30)), "Receiver timed out");
414 	}
415 
416 	PASS();
417 }
418 
test_dut_gatt_suspend_resume(void)419 static void test_dut_gatt_suspend_resume(void)
420 {
421 	dut_gatt_common(false);
422 	PASS();
423 }
424 
test_dut_gatt_suspend_disable_resume(void)425 static void test_dut_gatt_suspend_disable_resume(void)
426 {
427 	dut_gatt_common(true);
428 	PASS();
429 }
430 
test_tester_gatt(void)431 static void test_tester_gatt(void)
432 {
433 	k_sem_init(&publish_sem, 0, 1);
434 	dut_status = DUT_RUNNING;
435 	int err;
436 
437 	ASSERT_OK_MSG(bt_enable(NULL), "Bluetooth init failed");
438 
439 	/* Scan pb gatt beacons. */
440 	ASSERT_OK(bt_mesh_test_wait_for_packet(gatt_scan_cb, &publish_sem, 10));
441 
442 	/* Delay to provision DUT */
443 	k_sleep(K_MSEC(1000));
444 
445 	/* Scan gatt proxy beacons. */
446 	/* (total transmit duration) / (transmit interval) */
447 	gatt_param.transmits = 5000 / 1000;
448 	gatt_param.interval = 1000;
449 	gatt_param.service = MESH_SERVICE_PROXY;
450 	ASSERT_OK(bt_mesh_test_wait_for_packet(gatt_scan_cb, &publish_sem, 10));
451 
452 	/* Allow DUT to suspend before scanning for gatt proxy beacons */
453 	ASSERT_OK(bt_mesh_test_wait_for_packet(suspend_state_change_cb, &publish_sem, 20));
454 	ASSERT_EQUAL(dut_status, DUT_SUSPENDED);
455 	k_sleep(K_MSEC(500));
456 	err = bt_mesh_test_wait_for_packet(gatt_scan_cb, &publish_sem, 10);
457 	ASSERT_FALSE(err && err != -ETIMEDOUT);
458 
459 	/* Wait for DUT to resume Mesh and notify Tester, then scan for gatt proxy beacons */
460 	ASSERT_OK(bt_mesh_test_wait_for_packet(suspend_state_change_cb, &publish_sem, 20));
461 	ASSERT_EQUAL(dut_status, DUT_RUNNING);
462 	ASSERT_OK(bt_mesh_test_wait_for_packet(gatt_scan_cb, &publish_sem, 10));
463 
464 	PASS();
465 }
466 
467 #define TEST_CASE(role, name, description)                                                         \
468 	{                                                                                          \
469 		.test_id = "suspend_" #role "_" #name, .test_descr = description,                  \
470 		.test_tick_f = bt_mesh_test_timeout, .test_main_f = test_##role##_##name,          \
471 	}
472 
473 static const struct bst_test_instance test_suspend[] = {
474 	TEST_CASE(dut, suspend_resume,
475 			 "Suspend and resume Mesh with periodic pub"),
476 	TEST_CASE(dut, suspend_disable_resume,
477 			 "Suspend and resume Mesh (and disable/enable BT) with periodic pub"),
478 	TEST_CASE(dut, gatt_suspend_resume,
479 			 "Suspend and resume Mesh with GATT proxy advs"),
480 	TEST_CASE(dut, gatt_suspend_disable_resume,
481 			 "Suspend and resume Mesh (and disable/enable BT) with GATT proxy advs"),
482 
483 	TEST_CASE(tester, pub, "Scan and verify behavior of periodic publishing adv"),
484 	TEST_CASE(tester, gatt, "Scan and verify behavior of GATT proxy adv"),
485 
486 	BSTEST_END_MARKER};
487 
test_suspend_install(struct bst_test_list * tests)488 struct bst_test_list *test_suspend_install(struct bst_test_list *tests)
489 {
490 	tests = bst_add_tests(tests, test_suspend);
491 	return tests;
492 }
493