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