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