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, &param, 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