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