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