1 /*
2 * Copyright (c) 2024 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/logging/log.h>
12
13 #include "babblekit/flags.h"
14 #include "babblekit/sync.h"
15 #include "babblekit/testcase.h"
16
17 LOG_MODULE_REGISTER(bis_broadcaster, LOG_LEVEL_INF);
18
19 #define LATENCY_MS 10U /* 10ms */
20 #define SDU_INTERVAL_US 10U * USEC_PER_MSEC /* 10 ms */
21
22 extern enum bst_result_t bst_result;
23 static struct bt_iso_chan iso_chans[CONFIG_BT_ISO_MAX_CHAN];
24 static struct bt_iso_chan *default_chan = &iso_chans[0];
25 static uint16_t seq_num;
26 NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT,
27 BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
28 CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
29
30 static DEFINE_FLAG(flag_iso_connected);
31
32 static void send_data_cb(struct k_work *work);
33 K_WORK_DELAYABLE_DEFINE(iso_send_work, send_data_cb);
34
send_data(struct bt_iso_chan * chan)35 static void send_data(struct bt_iso_chan *chan)
36 {
37 static uint8_t buf_data[CONFIG_BT_ISO_TX_MTU];
38 static size_t len_to_send = 1U;
39 static bool data_initialized;
40 struct net_buf *buf;
41 int ret;
42
43 if (!IS_FLAG_SET(flag_iso_connected)) {
44 /* TX has been aborted */
45 return;
46 }
47
48 if (!data_initialized) {
49 for (int i = 0; i < ARRAY_SIZE(buf_data); i++) {
50 buf_data[i] = (uint8_t)i;
51 }
52
53 data_initialized = true;
54 }
55
56 buf = net_buf_alloc(&tx_pool, K_NO_WAIT);
57 TEST_ASSERT(buf != NULL, "Failed to allocate buffer");
58
59 net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
60
61 net_buf_add_mem(buf, buf_data, len_to_send);
62
63 ret = bt_iso_chan_send(default_chan, buf, seq_num++);
64 if (ret < 0) {
65 LOG_DBG("Failed to send ISO data: %d", ret);
66 net_buf_unref(buf);
67
68 /* Reschedule for next interval */
69 k_work_reschedule(&iso_send_work, K_USEC(SDU_INTERVAL_US));
70
71 return;
72 }
73
74 len_to_send++;
75 if (len_to_send > ARRAY_SIZE(buf_data)) {
76 len_to_send = 1;
77 }
78 }
79
send_data_cb(struct k_work * work)80 static void send_data_cb(struct k_work *work)
81 {
82 const uint16_t tx_pool_cnt = tx_pool.uninit_count;
83
84 /* Send/enqueue as many as we can */
85 for (uint16_t i = 0U; i < tx_pool_cnt; i++) {
86 send_data(default_chan);
87 }
88 }
89
iso_connected_cb(struct bt_iso_chan * chan)90 static void iso_connected_cb(struct bt_iso_chan *chan)
91 {
92 LOG_INF("ISO Channel %p connected", chan);
93
94 if (chan == default_chan) {
95 seq_num = 0U;
96
97 SET_FLAG(flag_iso_connected);
98 }
99 }
100
iso_disconnected_cb(struct bt_iso_chan * chan,uint8_t reason)101 static void iso_disconnected_cb(struct bt_iso_chan *chan, uint8_t reason)
102 {
103 LOG_INF("ISO Channel %p disconnected (reason 0x%02x)", chan, reason);
104
105 if (chan == default_chan) {
106 k_work_cancel_delayable(&iso_send_work);
107
108 UNSET_FLAG(flag_iso_connected);
109 }
110 }
111
sdu_sent_cb(struct bt_iso_chan * chan)112 static void sdu_sent_cb(struct bt_iso_chan *chan)
113 {
114 if (!IS_FLAG_SET(flag_iso_connected)) {
115 /* TX has been aborted */
116 return;
117 }
118
119 send_data(chan);
120 }
121
init(void)122 static void init(void)
123 {
124 static struct bt_iso_chan_ops iso_ops = {
125 .disconnected = iso_disconnected_cb,
126 .connected = iso_connected_cb,
127 .sent = sdu_sent_cb,
128 };
129 static struct bt_iso_chan_io_qos iso_tx = {
130 .sdu = CONFIG_BT_ISO_TX_MTU,
131 .phy = BT_GAP_LE_PHY_2M,
132 .rtn = 1,
133 .path = NULL,
134 };
135 static struct bt_iso_chan_qos iso_qos = {
136 .tx = &iso_tx,
137 .rx = NULL,
138 };
139 int err;
140
141 err = bt_enable(NULL);
142 TEST_ASSERT(err == 0, "Bluetooth enable failed: %d", err);
143
144 for (size_t i = 0U; i < ARRAY_SIZE(iso_chans); i++) {
145 iso_chans[i].ops = &iso_ops;
146 iso_chans[i].qos = &iso_qos;
147 }
148
149 bk_sync_init();
150 }
151
create_ext_adv(struct bt_le_ext_adv ** adv)152 static void create_ext_adv(struct bt_le_ext_adv **adv)
153 {
154 int err;
155
156 LOG_INF("Creating extended advertising set with periodic advertising");
157
158 /* Create a non-connectable advertising set */
159 err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN, NULL, adv);
160 TEST_ASSERT(err == 0, "Unable to create extended advertising set: %d", err);
161
162 /* Set periodic advertising parameters */
163 err = bt_le_per_adv_set_param(*adv, BT_LE_PER_ADV_PARAM(BT_GAP_PER_ADV_FAST_INT_MIN_2,
164 BT_GAP_PER_ADV_FAST_INT_MAX_2,
165 BT_LE_PER_ADV_OPT_NONE));
166 TEST_ASSERT(err == 0, "Failed to set periodic advertising parameters: %d", err);
167 }
168
start_ext_adv(struct bt_le_ext_adv * adv)169 static void start_ext_adv(struct bt_le_ext_adv *adv)
170 {
171 int err;
172
173 LOG_INF("Starting extended and periodic advertising");
174
175 /* Start extended advertising */
176 err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
177 TEST_ASSERT(err == 0, "Failed to start extended advertising: %d", err);
178
179 /* FIXME: Temporary workaround to get around an assert in the controller
180 * Open issue: https://github.com/zephyrproject-rtos/zephyr/issues/72852
181 */
182 k_sleep(K_MSEC(100));
183
184 /* Enable Periodic Advertising */
185 err = bt_le_per_adv_start(adv);
186 TEST_ASSERT(err == 0, "Failed to enable periodic advertising: %d", err);
187 }
188
create_big(struct bt_le_ext_adv * adv,size_t cnt,struct bt_iso_big ** out_big)189 static void create_big(struct bt_le_ext_adv *adv, size_t cnt, struct bt_iso_big **out_big)
190 {
191 struct bt_iso_chan *channels[ARRAY_SIZE(iso_chans)];
192 struct bt_iso_big_create_param param = {
193 .packing = BT_ISO_PACKING_SEQUENTIAL,
194 .framing = BT_ISO_FRAMING_UNFRAMED,
195 .interval = SDU_INTERVAL_US,
196 .bis_channels = channels,
197 .latency = LATENCY_MS,
198 .encryption = false,
199 .num_bis = cnt,
200 };
201 int err;
202
203 for (size_t i = 0U; i < cnt; i++) {
204 channels[i] = &iso_chans[i];
205 }
206
207 LOG_INF("Creating BIG");
208
209 err = bt_iso_big_create(adv, ¶m, out_big);
210 TEST_ASSERT(err == 0, "Failed to create BIG: %d", err);
211
212 WAIT_FOR_FLAG(flag_iso_connected);
213 }
214
start_tx(void)215 static void start_tx(void)
216 {
217 const uint16_t tx_pool_cnt = tx_pool.uninit_count;
218
219 LOG_INF("Starting TX");
220
221 /* Send/enqueue as many as we can */
222 for (uint16_t i = 0U; i < tx_pool_cnt; i++) {
223 send_data(default_chan);
224 }
225 }
226
terminate_big(struct bt_iso_big * big)227 static void terminate_big(struct bt_iso_big *big)
228 {
229 int err;
230
231 LOG_INF("Terminating BIG");
232
233 err = bt_iso_big_terminate(big);
234 TEST_ASSERT(err == 0, "Failed to terminate BIG: %d", err);
235
236 big = NULL;
237 }
238
reset_bluetooth(void)239 static void reset_bluetooth(void)
240 {
241 int err;
242
243 LOG_INF("Resetting Bluetooth");
244
245 err = bt_disable();
246 TEST_ASSERT(err == 0, "Failed to disable: %d", err);
247
248 err = bt_enable(NULL);
249 TEST_ASSERT(err == 0, "Failed to re-enable: %d", err);
250 }
251
test_main(void)252 static void test_main(void)
253 {
254 struct bt_le_ext_adv *adv;
255 struct bt_iso_big *big;
256
257 init();
258
259 /* Create advertising set and BIG and start it and starting TXing */
260 create_ext_adv(&adv);
261 create_big(adv, 1U, &big);
262 start_ext_adv(adv);
263 start_tx();
264
265 /* Wait for receiver to tell us to terminate */
266 bk_sync_wait();
267
268 terminate_big(big);
269 big = NULL;
270
271 TEST_PASS("Test passed");
272 }
273
test_main_disable(void)274 static void test_main_disable(void)
275 {
276 struct bt_le_ext_adv *adv;
277 struct bt_iso_big *big;
278
279 init();
280
281 /* Create advertising set and BIG */
282 create_ext_adv(&adv);
283 create_big(adv, ARRAY_SIZE(iso_chans), &big);
284
285 /* Reset BT to see if we can set it up again */
286 reset_bluetooth();
287
288 /* After a disable, all advertising sets and BIGs are removed */
289 big = NULL;
290 adv = NULL;
291
292 /* Set everything up again to see if everything still works as expected */
293 create_ext_adv(&adv);
294 create_big(adv, ARRAY_SIZE(iso_chans), &big);
295 start_ext_adv(adv);
296 start_tx();
297
298 /* Wait for receiver to tell us to terminate */
299 bk_sync_wait();
300
301 terminate_big(big);
302 big = NULL;
303
304 TEST_PASS("Disable test passed");
305 }
306
307 static const struct bst_test_instance test_def[] = {
308 {
309 .test_id = "broadcaster",
310 .test_descr = "Minimal BIS broadcaster that broadcast ISO data",
311 .test_pre_init_f = test_init,
312 .test_tick_f = test_tick,
313 .test_main_f = test_main,
314 },
315 {
316 .test_id = "broadcaster_disable",
317 .test_descr = "BIS broadcaster that tests bt_disable for ISO",
318 .test_pre_init_f = test_init,
319 .test_tick_f = test_tick,
320 .test_main_f = test_main_disable,
321 },
322 BSTEST_END_MARKER,
323 };
324
test_main_bis_broadcaster_install(struct bst_test_list * tests)325 struct bst_test_list *test_main_bis_broadcaster_install(struct bst_test_list *tests)
326 {
327 return bst_add_tests(tests, test_def);
328 }
329