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 
12 #include "babblekit/flags.h"
13 #include "babblekit/testcase.h"
14 
15 LOG_MODULE_REGISTER(broadcaster, LOG_LEVEL_INF);
16 
17 static struct bt_iso_chan iso_chans[CONFIG_BT_ISO_MAX_CHAN];
18 static struct bt_iso_chan *default_chan = &iso_chans[0];
19 
20 NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT,
21 			  BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
22 			  CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
23 
24 DEFINE_FLAG_STATIC(iso_connected);
25 DEFINE_FLAG_STATIC(sdu_sent);
26 
send_data(struct bt_iso_chan * chan,bool ts)27 static int send_data(struct bt_iso_chan *chan, bool ts)
28 {
29 	static uint16_t seq;
30 	struct net_buf *buf;
31 	int err;
32 
33 	if (!IS_FLAG_SET(iso_connected)) {
34 		/* TX has been aborted */
35 		return -ENOTCONN;
36 	}
37 
38 	buf = net_buf_alloc(&tx_pool, K_NO_WAIT);
39 	TEST_ASSERT(buf != NULL, "Failed to allocate buffer");
40 
41 	net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
42 
43 	net_buf_add_le32(buf, 0xdeadbeef);
44 	net_buf_add_u8(buf, 0x11);
45 
46 	LOG_INF("Sending SDU with%s timestamp (headroom %d)", ts ? "" : "out",
47 		net_buf_headroom(buf));
48 	LOG_HEXDUMP_INF(buf->data, buf->len, "SDU payload");
49 
50 	if (ts) {
51 		err = bt_iso_chan_send_ts(default_chan, buf, seq++, 0x00eeeee);
52 	} else {
53 		err = bt_iso_chan_send(default_chan, buf, seq++);
54 	}
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 	LOG_INF("ISO Channel %p connected", chan);
62 
63 	SET_FLAG(iso_connected);
64 }
65 
iso_disconnected_cb(struct bt_iso_chan * chan,uint8_t reason)66 static void iso_disconnected_cb(struct bt_iso_chan *chan, uint8_t reason)
67 {
68 	LOG_INF("ISO Channel %p disconnected (reason 0x%02x)", chan, reason);
69 
70 	UNSET_FLAG(iso_connected);
71 }
72 
sdu_sent_cb(struct bt_iso_chan * chan)73 static void sdu_sent_cb(struct bt_iso_chan *chan)
74 {
75 	SET_FLAG(sdu_sent);
76 }
77 
create_ext_adv(struct bt_le_ext_adv ** adv)78 static void create_ext_adv(struct bt_le_ext_adv **adv)
79 {
80 	int err;
81 
82 	LOG_INF("Creating extended advertising set with periodic advertising");
83 
84 	/* Create a non-connectable advertising set */
85 	err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN, NULL, adv);
86 	TEST_ASSERT(err == 0, "Unable to create extended advertising set: %d", err);
87 
88 	/* Set periodic advertising parameters */
89 	err = bt_le_per_adv_set_param(*adv, BT_LE_PER_ADV_PARAM(BT_GAP_PER_ADV_FAST_INT_MIN_2,
90 								BT_GAP_PER_ADV_FAST_INT_MAX_2,
91 								BT_LE_PER_ADV_OPT_NONE));
92 	TEST_ASSERT(err == 0, "Failed to set periodic advertising parameters: %d", err);
93 }
94 
start_ext_adv(struct bt_le_ext_adv * adv)95 static void start_ext_adv(struct bt_le_ext_adv *adv)
96 {
97 	int err;
98 
99 	LOG_INF("Starting extended and periodic advertising");
100 
101 	/* Start extended advertising */
102 	err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
103 	TEST_ASSERT(err == 0, "Failed to start extended advertising: %d", err);
104 
105 	/* FIXME: Temporary workaround to get around an assert in the controller
106 	 * Open issue: https://github.com/zephyrproject-rtos/zephyr/issues/72852
107 	 */
108 	k_sleep(K_MSEC(100));
109 
110 	/* Enable Periodic Advertising */
111 	err = bt_le_per_adv_start(adv);
112 	TEST_ASSERT(err == 0, "Failed to enable periodic advertising: %d", err);
113 }
114 
create_big(struct bt_le_ext_adv * adv,size_t cnt,struct bt_iso_big ** out_big)115 static void create_big(struct bt_le_ext_adv *adv, size_t cnt, struct bt_iso_big **out_big)
116 {
117 	const uint16_t latency_ms = 10U;
118 	const uint16_t sdu_interval_us = 10U * USEC_PER_MSEC;
119 
120 	struct bt_iso_chan *channels[ARRAY_SIZE(iso_chans)];
121 	struct bt_iso_big_create_param param = {
122 		.packing = BT_ISO_PACKING_SEQUENTIAL,
123 		.framing = BT_ISO_FRAMING_UNFRAMED,
124 		.interval = sdu_interval_us,
125 		.bis_channels = channels,
126 		.latency = latency_ms,
127 		.encryption = false,
128 		.num_bis = cnt,
129 	};
130 	int err;
131 
132 	for (size_t i = 0U; i < cnt; i++) {
133 		channels[i] = &iso_chans[i];
134 	}
135 
136 	LOG_INF("Creating BIG");
137 
138 	err = bt_iso_big_create(adv, &param, out_big);
139 	TEST_ASSERT(err == 0, "Failed to create BIG: %d", err);
140 
141 	WAIT_FOR_FLAG(iso_connected);
142 }
143 
init(void)144 static void init(void)
145 {
146 	struct bt_le_ext_adv *adv;
147 	struct bt_iso_big *big;
148 
149 	static struct bt_iso_chan_ops iso_ops = {
150 		.disconnected = iso_disconnected_cb,
151 		.connected = iso_connected_cb,
152 		.sent = sdu_sent_cb,
153 	};
154 	static struct bt_iso_chan_io_qos iso_tx = {
155 		.sdu = CONFIG_BT_ISO_TX_MTU,
156 		.phy = BT_GAP_LE_PHY_2M,
157 		.rtn = 1,
158 		.path = NULL,
159 	};
160 	static struct bt_iso_chan_qos iso_qos = {
161 		.tx = &iso_tx,
162 		.rx = NULL,
163 	};
164 	int err;
165 
166 	err = bt_enable(NULL);
167 	TEST_ASSERT(err == 0, "Bluetooth enable failed: %d", err);
168 
169 	for (size_t i = 0U; i < ARRAY_SIZE(iso_chans); i++) {
170 		iso_chans[i].ops = &iso_ops;
171 		iso_chans[i].qos = &iso_qos;
172 	}
173 
174 	create_ext_adv(&adv);
175 	create_big(adv, 1U, &big);
176 	start_ext_adv(adv);
177 }
178 
entrypoint_broadcaster(void)179 void entrypoint_broadcaster(void)
180 {
181 	/* Test purpose:
182 	 *
183 	 * Verifies that we are able to send an ISO SDU that exactly fits the
184 	 * configured TX MTU, without any HCI fragmentation.
185 	 *
186 	 * One device:
187 	 * - `broadcaster`: sends two ISO SDUs
188 	 *
189 	 * Procedure:
190 	 * - initialize Bluetooth and a BIS
191 	 * - send an SDU without timestamp
192 	 * - send an SDU with timestamp
193 	 *
194 	 * [verdict]
195 	 * - no fragmentation is observed on the HCI layer
196 	 */
197 	int err;
198 
199 	LOG_INF("Starting ISO HCI fragmentation test");
200 
201 	init();
202 
203 	/* Send an SDU without timestamp */
204 	err = send_data(default_chan, 0);
205 	TEST_ASSERT(!err, "Failed to send data w/o TS (err %d)", err);
206 
207 	/* Wait until we have sent the SDU.
208 	 * Using linker wrapping, we verify that no fragmentation happens.
209 	 */
210 	WAIT_FOR_FLAG(sdu_sent);
211 
212 	/* Send an SDU with timestamp */
213 	err = send_data(default_chan, 1);
214 	TEST_ASSERT(!err, "Failed to send data w/ TS (err %d)", err);
215 
216 	/* Wait until we have sent the SDU.
217 	 * Using linker wrapping, we verify that no fragmentation happens.
218 	 */
219 	WAIT_FOR_FLAG(sdu_sent);
220 
221 	TEST_PASS_AND_EXIT("Test passed");
222 }
223 
validate_no_iso_frag(struct net_buf * buf)224 void validate_no_iso_frag(struct net_buf *buf)
225 {
226 	struct bt_hci_iso_hdr *hci_hdr = (void *)buf->data;
227 
228 	uint16_t handle = sys_le16_to_cpu(hci_hdr->handle);
229 	uint8_t flags = bt_iso_flags(handle);
230 	uint8_t pb_flag = bt_iso_flags_pb(flags);
231 
232 	TEST_ASSERT(pb_flag == BT_ISO_SINGLE, "Packet was fragmented");
233 }
234 
235 int __real_bt_send(struct net_buf *buf);
236 
__wrap_bt_send(struct net_buf * buf)237 int __wrap_bt_send(struct net_buf *buf)
238 {
239 	LOG_HEXDUMP_DBG(buf->data, buf->len, "h->c");
240 
241 	if (bt_buf_get_type(buf) == BT_BUF_ISO_OUT) {
242 		validate_no_iso_frag(buf);
243 	}
244 
245 	return __real_bt_send(buf);
246 }
247