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