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