1 /*
2  * Copyright (c) 2024-2025 Nordic Semiconductor
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/bluetooth/hci_types.h>
8 #include <zephyr/sys/byteorder.h>
9 #include <zephyr/bluetooth/bluetooth.h>
10 #include <zephyr/bluetooth/iso.h>
11 #include <zephyr/logging/log.h>
12 #include <zephyr/sys/byteorder.h>
13 
14 #include "babblekit/flags.h"
15 #include "babblekit/testcase.h"
16 
17 LOG_MODULE_REGISTER(broadcaster, LOG_LEVEL_INF);
18 
19 static struct bt_iso_chan iso_chans[CONFIG_BT_ISO_MAX_CHAN];
20 static struct bt_iso_chan *default_chan = &iso_chans[0];
21 
22 NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT,
23 			  BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
24 			  CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
25 
26 DEFINE_FLAG_STATIC(iso_connected);
27 DEFINE_FLAG_STATIC(first_frag);
28 DEFINE_FLAG_STATIC(sdu_sent);
29 
30 extern void bt_conn_suspend_tx(bool suspend);
31 extern void bt_testing_set_iso_mtu(uint16_t mtu);
32 
send_data(struct bt_iso_chan * chan)33 static int send_data(struct bt_iso_chan *chan)
34 {
35 	static uint16_t seq;
36 	struct net_buf *buf;
37 	int err;
38 
39 	if (!IS_FLAG_SET(iso_connected)) {
40 		/* TX has been aborted */
41 		return -ENOTCONN;
42 	}
43 
44 	buf = net_buf_alloc(&tx_pool, K_NO_WAIT);
45 	TEST_ASSERT(buf != NULL, "Failed to allocate buffer");
46 
47 	net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
48 
49 	net_buf_add(buf, 40);
50 
51 	LOG_INF("Sending SDU (headroom %d)", net_buf_headroom(buf));
52 	LOG_HEXDUMP_INF(buf->data, buf->len, "SDU payload");
53 
54 	err = bt_iso_chan_send(default_chan, buf, seq++);
55 
56 	return err;
57 }
58 
iso_connected_cb(struct bt_iso_chan * chan)59 static void iso_connected_cb(struct bt_iso_chan *chan)
60 {
61 	const struct bt_iso_chan_path hci_path = {
62 		.pid = BT_ISO_DATA_PATH_HCI,
63 		.format = BT_HCI_CODING_FORMAT_TRANSPARENT,
64 	};
65 	int err;
66 
67 	err = bt_iso_setup_data_path(chan, BT_HCI_DATAPATH_DIR_HOST_TO_CTLR, &hci_path);
68 	TEST_ASSERT(err == 0, "Unable to setup ISO TX path: %d", err);
69 
70 	LOG_INF("ISO Channel %p connected", chan);
71 
72 	SET_FLAG(iso_connected);
73 }
74 
iso_disconnected_cb(struct bt_iso_chan * chan,uint8_t reason)75 static void iso_disconnected_cb(struct bt_iso_chan *chan, uint8_t reason)
76 {
77 	LOG_INF("ISO Channel %p disconnected (reason 0x%02x)", chan, reason);
78 
79 	UNSET_FLAG(iso_connected);
80 }
81 
sdu_sent_cb(struct bt_iso_chan * chan)82 static void sdu_sent_cb(struct bt_iso_chan *chan)
83 {
84 	SET_FLAG(sdu_sent);
85 }
86 
create_ext_adv(struct bt_le_ext_adv ** adv)87 static void create_ext_adv(struct bt_le_ext_adv **adv)
88 {
89 	int err;
90 
91 	LOG_INF("Creating extended advertising set with periodic advertising");
92 
93 	/* Create a non-connectable advertising set */
94 	err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN, NULL, adv);
95 	TEST_ASSERT(err == 0, "Unable to create extended advertising set: %d", err);
96 
97 	/* Set periodic advertising parameters */
98 	err = bt_le_per_adv_set_param(*adv, BT_LE_PER_ADV_PARAM(BT_GAP_PER_ADV_FAST_INT_MIN_2,
99 								BT_GAP_PER_ADV_FAST_INT_MAX_2,
100 								BT_LE_PER_ADV_OPT_NONE));
101 	TEST_ASSERT(err == 0, "Failed to set periodic advertising parameters: %d", err);
102 }
103 
start_ext_adv(struct bt_le_ext_adv * adv)104 static void start_ext_adv(struct bt_le_ext_adv *adv)
105 {
106 	int err;
107 
108 	LOG_INF("Starting extended and periodic advertising");
109 
110 	/* Start extended advertising */
111 	err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
112 	TEST_ASSERT(err == 0, "Failed to start extended advertising: %d", err);
113 
114 	/* FIXME: Temporary workaround to get around an assert in the controller
115 	 * Open issue: https://github.com/zephyrproject-rtos/zephyr/issues/72852
116 	 */
117 	k_sleep(K_MSEC(100));
118 
119 	/* Enable Periodic Advertising */
120 	err = bt_le_per_adv_start(adv);
121 	TEST_ASSERT(err == 0, "Failed to enable periodic advertising: %d", err);
122 }
123 
create_big(struct bt_le_ext_adv * adv,size_t cnt,struct bt_iso_big ** out_big)124 static void create_big(struct bt_le_ext_adv *adv, size_t cnt, struct bt_iso_big **out_big)
125 {
126 	const uint16_t latency_ms = 10U;
127 	const uint16_t sdu_interval_us = 10U * USEC_PER_MSEC;
128 
129 	struct bt_iso_chan *channels[ARRAY_SIZE(iso_chans)];
130 	struct bt_iso_big_create_param param = {
131 		.packing = BT_ISO_PACKING_SEQUENTIAL,
132 		.framing = BT_ISO_FRAMING_UNFRAMED,
133 		.interval = sdu_interval_us,
134 		.bis_channels = channels,
135 		.latency = latency_ms,
136 		.encryption = false,
137 		.num_bis = cnt,
138 	};
139 	int err;
140 
141 	for (size_t i = 0U; i < cnt; i++) {
142 		channels[i] = &iso_chans[i];
143 	}
144 
145 	LOG_INF("Creating BIG");
146 
147 	err = bt_iso_big_create(adv, &param, out_big);
148 	TEST_ASSERT(err == 0, "Failed to create BIG: %d", err);
149 
150 	WAIT_FOR_FLAG(iso_connected);
151 }
152 
153 struct bt_le_ext_adv *adv;
154 struct bt_iso_big *big;
155 
156 static struct bt_iso_chan_ops iso_ops = {
157 	.disconnected = iso_disconnected_cb,
158 	.connected = iso_connected_cb,
159 	.sent = sdu_sent_cb,
160 };
161 static struct bt_iso_chan_io_qos iso_tx = {
162 	.sdu = CONFIG_BT_ISO_TX_MTU,
163 	.phy = BT_GAP_LE_PHY_2M,
164 	.rtn = 1,
165 };
166 static struct bt_iso_chan_qos iso_qos = {
167 	.tx = &iso_tx,
168 	.rx = NULL,
169 };
170 
init(void)171 static void init(void)
172 {
173 	int err;
174 
175 	err = bt_enable(NULL);
176 	TEST_ASSERT(err == 0, "Bluetooth enable failed: %d", err);
177 }
178 
connect_iso(void)179 static void connect_iso(void)
180 {
181 	bt_testing_set_iso_mtu(10);
182 
183 	for (size_t i = 0U; i < ARRAY_SIZE(iso_chans); i++) {
184 		iso_chans[i].ops = &iso_ops;
185 		iso_chans[i].qos = &iso_qos;
186 	}
187 
188 	create_ext_adv(&adv);
189 	create_big(adv, 1U, &big);
190 	start_ext_adv(adv);
191 }
192 
disconnect_iso(void)193 static void disconnect_iso(void)
194 {
195 	int err;
196 
197 	err = bt_iso_big_terminate(big);
198 	TEST_ASSERT(err == 0, "bt_iso_big_terminate failed (%d)", err);
199 	err = bt_le_per_adv_stop(adv);
200 	TEST_ASSERT(err == 0, "bt_le_per_adv_stop failed (%d)", err);
201 	k_msleep(100);
202 	err = bt_le_ext_adv_stop(adv);
203 	TEST_ASSERT(err == 0, "bt_le_ext_adv_stop failed (%d)", err);
204 	k_msleep(100);
205 	err = bt_le_ext_adv_delete(adv);
206 	TEST_ASSERT(err == 0, "bt_le_ext_adv_delete failed (%d)", err);
207 
208 	big = NULL;
209 	adv = NULL;
210 }
211 
entrypoint_broadcaster(void)212 void entrypoint_broadcaster(void)
213 {
214 	/* Test purpose:
215 	 *
216 	 * Verifies that we are not leaking buffers when getting disconnected
217 	 * while sending fragmented ISO SDU.
218 	 *
219 	 * One device:
220 	 * - `broadcaster`: sends fragmented ISO SDUs
221 	 *
222 	 * Procedure:
223 	 * - initialize Bluetooth and a BIS
224 	 * - send a fragmented SDU
225 	 * - disconnect when the first fragment is sent
226 	 * - repeat TEST_ITERATIONS time
227 	 *
228 	 * [verdict]
229 	 * - no buffer is leaked and repeating the operation success
230 	 */
231 	int err;
232 	uint8_t TEST_ITERATIONS = 4;
233 
234 	LOG_INF("Starting ISO HCI fragmentation test 2");
235 
236 	init();
237 
238 	for (size_t i = 0; i < TEST_ITERATIONS; i++) {
239 		connect_iso();
240 
241 		/* Send an SDU */
242 		err = send_data(default_chan);
243 		TEST_ASSERT(!err, "Failed to send data w/o TS (err %d)", err);
244 
245 		/* Wait until we have sent the first SDU fragment. */
246 		WAIT_FOR_FLAG(first_frag);
247 
248 		disconnect_iso();
249 		bt_conn_suspend_tx(false);
250 
251 		k_msleep(1000);
252 	}
253 
254 	TEST_PASS_AND_EXIT("Test passed");
255 }
256 
validate_no_iso_frag(struct net_buf * buf)257 void validate_no_iso_frag(struct net_buf *buf)
258 {
259 	struct bt_hci_iso_hdr *hci_hdr = (void *)buf->data;
260 
261 	uint16_t handle = sys_le16_to_cpu(hci_hdr->handle);
262 	uint8_t flags = bt_iso_flags(handle);
263 	uint8_t pb_flag = bt_iso_flags_pb(flags);
264 
265 	TEST_ASSERT(pb_flag == BT_ISO_SINGLE, "Packet was fragmented");
266 }
267 
268 int __real_bt_send(struct net_buf *buf);
269 
__wrap_bt_send(struct net_buf * buf)270 int __wrap_bt_send(struct net_buf *buf)
271 {
272 	if (buf->data[0] == BT_HCI_H4_ISO) {
273 		struct bt_hci_iso_hdr *hci_hdr = (void *)(buf->data + 1);
274 
275 		uint16_t handle = sys_le16_to_cpu(hci_hdr->handle);
276 		uint8_t flags = bt_iso_flags(handle);
277 		uint8_t pb_flag = bt_iso_flags_pb(flags);
278 
279 		if (pb_flag == BT_ISO_START) {
280 			SET_FLAG(first_frag);
281 			bt_conn_suspend_tx(true);
282 		}
283 	}
284 
285 	return __real_bt_send(buf);
286 }
287