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