1 /*
2 * Copyright (c) 2023 Nordic Semiconductor
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "babblekit/testcase.h"
8 #include "babblekit/flags.h"
9 #include "common.h"
10
11 #include <zephyr/bluetooth/bluetooth.h>
12 #include <zephyr/bluetooth/iso.h>
13 #include <zephyr/sys/printk.h>
14
15 #define ENQUEUE_COUNT 2
16
17 extern enum bst_result_t bst_result;
18 static struct bt_iso_chan iso_chans[CONFIG_BT_ISO_MAX_CHAN];
19 static struct bt_iso_chan *default_chan = &iso_chans[0];
20 static struct bt_iso_cig *cig;
21 static uint16_t seq_num;
22 static volatile size_t enqueue_cnt;
23 static uint32_t latency_ms = 10U; /* 10ms */
24 static uint32_t interval_us = 10U * USEC_PER_MSEC; /* 10 ms */
25 NET_BUF_POOL_FIXED_DEFINE(tx_pool, ENQUEUE_COUNT, BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
26 CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
27
28 BUILD_ASSERT(CONFIG_BT_ISO_MAX_CHAN > 1, "CONFIG_BT_ISO_MAX_CHAN shall be at least 2");
29
30 DEFINE_FLAG_STATIC(flag_iso_connected);
31
send_data_cb(struct k_work * work)32 static void send_data_cb(struct k_work *work)
33 {
34 static uint8_t buf_data[CONFIG_BT_ISO_TX_MTU];
35 static size_t len_to_send = 1;
36 static bool data_initialized;
37 struct net_buf *buf;
38 int ret;
39
40 if (!IS_FLAG_SET(flag_iso_connected)) {
41 /* TX has been aborted */
42 return;
43 }
44
45 if (!data_initialized) {
46 for (int i = 0; i < ARRAY_SIZE(buf_data); i++) {
47 buf_data[i] = (uint8_t)i;
48 }
49
50 data_initialized = true;
51 }
52
53 buf = net_buf_alloc(&tx_pool, K_FOREVER);
54 net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
55
56 net_buf_add_mem(buf, buf_data, len_to_send);
57
58 ret = bt_iso_chan_send(default_chan, buf, seq_num++);
59 if (ret < 0) {
60 printk("Failed to send ISO data (%d)\n", ret);
61 net_buf_unref(buf);
62
63 /* Reschedule for next interval */
64 k_work_reschedule(k_work_delayable_from_work(work), K_USEC(interval_us));
65
66 return;
67 }
68
69 len_to_send++;
70 if (len_to_send > ARRAY_SIZE(buf_data)) {
71 len_to_send = 1;
72 }
73
74 enqueue_cnt--;
75 if (enqueue_cnt > 0U) {
76 /* If we have more buffers available, we reschedule the workqueue item immediately
77 * to trigger another encode + TX, but without blocking this call for too long
78 */
79 k_work_reschedule(k_work_delayable_from_work(work), K_NO_WAIT);
80 }
81 }
82 K_WORK_DELAYABLE_DEFINE(iso_send_work, send_data_cb);
83
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)84 static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
85 struct net_buf_simple *ad)
86 {
87 int err;
88
89 err = bt_le_scan_stop();
90 if (err) {
91 TEST_FAIL("Failed to stop scanning (err %d)", err);
92
93 return;
94 }
95
96 err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT,
97 &default_conn);
98 if (err) {
99 TEST_FAIL("Failed to create connection (err %d)", err);
100
101 return;
102 }
103 }
104
iso_connected(struct bt_iso_chan * chan)105 static void iso_connected(struct bt_iso_chan *chan)
106 {
107 printk("ISO Channel %p connected\n", chan);
108
109 seq_num = 0U;
110 enqueue_cnt = ENQUEUE_COUNT;
111
112 if (chan == default_chan) {
113 /* Start send timer */
114 k_work_schedule(&iso_send_work, K_MSEC(0));
115
116 SET_FLAG(flag_iso_connected);
117 }
118 }
119
iso_disconnected(struct bt_iso_chan * chan,uint8_t reason)120 static void iso_disconnected(struct bt_iso_chan *chan, uint8_t reason)
121 {
122 printk("ISO Channel %p disconnected (reason 0x%02x)\n", chan, reason);
123
124 if (chan == default_chan) {
125 k_work_cancel_delayable(&iso_send_work);
126
127 UNSET_FLAG(flag_iso_connected);
128 }
129 }
130
sdu_sent_cb(struct bt_iso_chan * chan)131 static void sdu_sent_cb(struct bt_iso_chan *chan)
132 {
133 int err;
134
135 enqueue_cnt++;
136
137 if (!IS_FLAG_SET(flag_iso_connected)) {
138 /* TX has been aborted */
139 return;
140 }
141
142 err = k_work_schedule(&iso_send_work, K_NO_WAIT);
143 if (err < 0) {
144 TEST_FAIL("Failed to schedule TX for chan %p: %d", chan, err);
145 }
146 }
147
init(void)148 static void init(void)
149 {
150 static struct bt_iso_chan_ops iso_ops = {
151 .connected = iso_connected,
152 .disconnected = iso_disconnected,
153 .sent = sdu_sent_cb,
154 };
155 static struct bt_iso_chan_io_qos iso_tx = {
156 .sdu = CONFIG_BT_ISO_TX_MTU,
157 .phy = BT_GAP_LE_PHY_2M,
158 .rtn = 1,
159 .path = NULL,
160 };
161 static struct bt_iso_chan_qos iso_qos = {
162 .tx = &iso_tx,
163 .rx = NULL,
164 };
165 int err;
166
167 err = bt_enable(NULL);
168 if (err != 0) {
169 TEST_FAIL("Bluetooth enable failed (err %d)", err);
170
171 return;
172 }
173
174 for (size_t i = 0U; i < ARRAY_SIZE(iso_chans); i++) {
175 iso_chans[i].ops = &iso_ops;
176 iso_chans[i].qos = &iso_qos;
177 #if defined(CONFIG_BT_SMP)
178 iso_chans[i].required_sec_level = BT_SECURITY_L2;
179 #endif /* CONFIG_BT_SMP */
180 }
181 }
182
set_cig_defaults(struct bt_iso_cig_param * param)183 static void set_cig_defaults(struct bt_iso_cig_param *param)
184 {
185 param->cis_channels = &default_chan;
186 param->num_cis = 1U;
187 param->sca = BT_GAP_SCA_UNKNOWN;
188 param->packing = BT_ISO_PACKING_SEQUENTIAL;
189 param->framing = BT_ISO_FRAMING_UNFRAMED;
190 param->c_to_p_latency = latency_ms; /* ms */
191 param->p_to_c_latency = latency_ms; /* ms */
192 param->c_to_p_interval = interval_us; /* us */
193 param->p_to_c_interval = interval_us; /* us */
194
195 }
196
create_cig(size_t iso_channels)197 static void create_cig(size_t iso_channels)
198 {
199 struct bt_iso_chan *channels[ARRAY_SIZE(iso_chans)];
200 struct bt_iso_cig_param param;
201 int err;
202
203 for (size_t i = 0U; i < iso_channels; i++) {
204 channels[i] = &iso_chans[i];
205 }
206
207 set_cig_defaults(¶m);
208 param.num_cis = iso_channels;
209 param.cis_channels = channels;
210
211 err = bt_iso_cig_create(¶m, &cig);
212 if (err != 0) {
213 TEST_FAIL("Failed to create CIG (%d)", err);
214
215 return;
216 }
217 }
218
reconfigure_cig_interval(struct bt_iso_cig_param * param)219 static int reconfigure_cig_interval(struct bt_iso_cig_param *param)
220 {
221 int err;
222
223 /* Test modifying CIG parameter without any CIS */
224 param->num_cis = 0U;
225 param->c_to_p_interval = 7500; /* us */
226 param->p_to_c_interval = param->c_to_p_interval;
227 err = bt_iso_cig_reconfigure(cig, param);
228 if (err != 0) {
229 TEST_FAIL("Failed to reconfigure CIG to new interval (%d)", err);
230
231 return err;
232 }
233
234 err = bt_iso_cig_reconfigure(cig, param);
235 if (err != 0) {
236 TEST_FAIL("Failed to reconfigure CIG to same interval (%d)", err);
237
238 return err;
239 }
240
241 /* Test modifying to different values for both intervals */
242 param->c_to_p_interval = 5000; /* us */
243 param->p_to_c_interval = 2500; /* us */
244 err = bt_iso_cig_reconfigure(cig, param);
245 if (err != 0) {
246 TEST_FAIL("Failed to reconfigure CIG to new interval (%d)", err);
247
248 return err;
249 }
250
251 return 0;
252 }
253
reconfigure_cig_latency(struct bt_iso_cig_param * param)254 static int reconfigure_cig_latency(struct bt_iso_cig_param *param)
255 {
256 int err;
257
258 /* Test modifying CIG latency without any CIS */
259 param->num_cis = 0U;
260 param->c_to_p_latency = 20; /* ms */
261 param->p_to_c_latency = param->c_to_p_latency;
262 err = bt_iso_cig_reconfigure(cig, param);
263 if (err != 0) {
264 TEST_FAIL("Failed to reconfigure CIG latency (%d)", err);
265
266 return err;
267 }
268
269 param->c_to_p_latency = 30; /* ms */
270 param->p_to_c_latency = 40; /* ms */
271 err = bt_iso_cig_reconfigure(cig, param);
272 if (err != 0) {
273 TEST_FAIL("Failed to reconfigure CIG for different latencies (%d)", err);
274
275 return err;
276 }
277
278 return 0;
279 }
280
reconfigure_cig(void)281 static void reconfigure_cig(void)
282 {
283 struct bt_iso_chan *channels[2];
284 struct bt_iso_cig_param param;
285 int err;
286
287 for (size_t i = 0U; i < ARRAY_SIZE(channels); i++) {
288 channels[i] = &iso_chans[i];
289 }
290
291 set_cig_defaults(¶m);
292
293 /* Test modifying existing CIS */
294 default_chan->qos->tx->rtn++;
295
296 err = bt_iso_cig_reconfigure(cig, ¶m);
297 if (err != 0) {
298 TEST_FAIL("Failed to reconfigure CIS to new RTN (%d)", err);
299
300 return;
301 }
302
303 /* Test modifying interval parameter */
304 err = reconfigure_cig_interval(¶m);
305 if (err != 0) {
306 return;
307 }
308
309 /* Test modifying latency parameter */
310 err = reconfigure_cig_latency(¶m);
311 if (err != 0) {
312 return;
313 }
314
315 /* Add CIS to the CIG and restore all other parameters */
316 set_cig_defaults(¶m);
317 param.cis_channels = &channels[1];
318
319 err = bt_iso_cig_reconfigure(cig, ¶m);
320 if (err != 0) {
321 TEST_FAIL("Failed to reconfigure CIG with new CIS and original parameters (%d)",
322 err);
323
324 return;
325 }
326 }
327
connect_acl(void)328 static void connect_acl(void)
329 {
330 int err;
331
332 err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
333 if (err != 0) {
334 TEST_FAIL("Scanning failed to start (err %d)", err);
335
336 return;
337 }
338
339 WAIT_FOR_FLAG(flag_connected);
340 }
341
connect_cis(void)342 static void connect_cis(void)
343 {
344 const struct bt_iso_connect_param connect_param = {
345 .acl = default_conn,
346 .iso_chan = default_chan,
347 };
348 int err;
349
350 err = bt_iso_chan_connect(&connect_param, 1);
351 if (err) {
352 TEST_FAIL("Failed to connect ISO (%d)", err);
353
354 return;
355 }
356
357 WAIT_FOR_FLAG(flag_iso_connected);
358 }
359
disconnect_cis(void)360 static void disconnect_cis(void)
361 {
362 int err;
363
364 err = bt_iso_chan_disconnect(default_chan);
365 if (err) {
366 TEST_FAIL("Failed to disconnect ISO (err %d)", err);
367
368 return;
369 }
370
371 WAIT_FOR_FLAG_UNSET(flag_iso_connected);
372 }
373
disconnect_acl(void)374 static void disconnect_acl(void)
375 {
376 int err;
377
378 err = bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
379 if (err) {
380 TEST_FAIL("Failed to disconnect ACL (err %d)", err);
381
382 return;
383 }
384
385 WAIT_FOR_FLAG_UNSET(flag_connected);
386 }
387
terminate_cig(void)388 static void terminate_cig(void)
389 {
390 int err;
391
392 err = bt_iso_cig_terminate(cig);
393 if (err != 0) {
394 TEST_FAIL("Failed to terminate CIG (%d)", err);
395
396 return;
397 }
398
399 cig = NULL;
400 }
401
reset_bluetooth(void)402 static void reset_bluetooth(void)
403 {
404 int err;
405
406 printk("Resetting Bluetooth\n");
407
408 err = bt_disable();
409 if (err != 0) {
410 TEST_FAIL("Failed to disable (%d)", err);
411
412 return;
413 }
414
415 /* After a disable, all CIGs and BIGs are removed */
416 cig = NULL;
417
418 err = bt_enable(NULL);
419 if (err != 0) {
420 TEST_FAIL("Failed to re-enable (%d)", err);
421
422 return;
423 }
424 }
425
test_main(void)426 static void test_main(void)
427 {
428 init();
429 create_cig(1);
430 reconfigure_cig();
431 connect_acl();
432 connect_cis();
433
434 while (seq_num < 100U) {
435 k_sleep(K_USEC(interval_us));
436 }
437
438 disconnect_cis();
439 disconnect_acl();
440 terminate_cig();
441
442 TEST_PASS("Test passed");
443 }
444
test_main_disable(void)445 static void test_main_disable(void)
446 {
447 init();
448
449 /* Setup and connect before disabling */
450 create_cig(ARRAY_SIZE(iso_chans));
451 connect_acl();
452 connect_cis();
453
454 /* Reset BT to see if we can set it up again */
455 reset_bluetooth();
456
457 /* Set everything up again to see if everything still works as expected */
458 create_cig(ARRAY_SIZE(iso_chans));
459 connect_acl();
460 connect_cis();
461
462 while (seq_num < 100U) {
463 k_sleep(K_USEC(interval_us));
464 }
465
466 disconnect_cis();
467 disconnect_acl();
468 terminate_cig();
469
470 TEST_PASS("Disable test passed");
471 }
472
473 static const struct bst_test_instance test_def[] = {
474 {
475 .test_id = "central",
476 .test_descr = "Central",
477 .test_main_f = test_main,
478 },
479 {
480 .test_id = "central_disable",
481 .test_descr = "CIS central that tests bt_disable for ISO",
482 .test_main_f = test_main_disable,
483 },
484 BSTEST_END_MARKER,
485 };
486
test_main_cis_central_install(struct bst_test_list * tests)487 struct bst_test_list *test_main_cis_central_install(struct bst_test_list *tests)
488 {
489 return bst_add_tests(tests, test_def);
490 }
491