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, ¶m, 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