1 /*
2 * Copyright (c) 2021-2025 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <stddef.h>
7 #include <stdint.h>
8
9 #include <zephyr/bluetooth/bluetooth.h>
10 #include <zephyr/bluetooth/gap.h>
11 #include <zephyr/bluetooth/hci_types.h>
12 #include <zephyr/bluetooth/iso.h>
13 #include <zephyr/sys/byteorder.h>
14
15 #define BIG_SDU_INTERVAL_US (10000)
16 #define BUF_ALLOC_TIMEOUT_US (BIG_SDU_INTERVAL_US * 2U) /* milliseconds */
17 #define BIG_TERMINATE_TIMEOUT_US (60 * USEC_PER_SEC) /* microseconds */
18
19 #define BIS_ISO_CHAN_COUNT 2
20 NET_BUF_POOL_FIXED_DEFINE(bis_tx_pool, BIS_ISO_CHAN_COUNT,
21 BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
22 CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
23
24 static K_SEM_DEFINE(sem_big_cmplt, 0, BIS_ISO_CHAN_COUNT);
25 static K_SEM_DEFINE(sem_big_term, 0, BIS_ISO_CHAN_COUNT);
26 static K_SEM_DEFINE(sem_iso_data, CONFIG_BT_ISO_TX_BUF_COUNT,
27 CONFIG_BT_ISO_TX_BUF_COUNT);
28
29 #define INITIAL_TIMEOUT_COUNTER (BIG_TERMINATE_TIMEOUT_US / BIG_SDU_INTERVAL_US)
30
31 static uint16_t seq_num;
32
iso_connected(struct bt_iso_chan * chan)33 static void iso_connected(struct bt_iso_chan *chan)
34 {
35 const struct bt_iso_chan_path hci_path = {
36 .pid = BT_ISO_DATA_PATH_HCI,
37 .format = BT_HCI_CODING_FORMAT_TRANSPARENT,
38 };
39 int err;
40
41 printk("ISO Channel %p connected\n", chan);
42
43 seq_num = 0U;
44
45 err = bt_iso_setup_data_path(chan, BT_HCI_DATAPATH_DIR_HOST_TO_CTLR, &hci_path);
46 if (err != 0) {
47 printk("Failed to setup ISO TX data path: %d\n", err);
48 }
49
50 k_sem_give(&sem_big_cmplt);
51 }
52
iso_disconnected(struct bt_iso_chan * chan,uint8_t reason)53 static void iso_disconnected(struct bt_iso_chan *chan, uint8_t reason)
54 {
55 printk("ISO Channel %p disconnected with reason 0x%02x\n",
56 chan, reason);
57 k_sem_give(&sem_big_term);
58 }
59
iso_sent(struct bt_iso_chan * chan)60 static void iso_sent(struct bt_iso_chan *chan)
61 {
62 k_sem_give(&sem_iso_data);
63 }
64
65 static struct bt_iso_chan_ops iso_ops = {
66 .connected = iso_connected,
67 .disconnected = iso_disconnected,
68 .sent = iso_sent,
69 };
70
71 static struct bt_iso_chan_io_qos iso_tx_qos = {
72 .sdu = sizeof(uint32_t), /* bytes */
73 .rtn = 1,
74 .phy = BT_GAP_LE_PHY_2M,
75 };
76
77 static struct bt_iso_chan_qos bis_iso_qos = {
78 .tx = &iso_tx_qos,
79 };
80
81 static struct bt_iso_chan bis_iso_chan[] = {
82 { .ops = &iso_ops, .qos = &bis_iso_qos, },
83 { .ops = &iso_ops, .qos = &bis_iso_qos, },
84 };
85
86 static struct bt_iso_chan *bis[] = {
87 &bis_iso_chan[0],
88 &bis_iso_chan[1],
89 };
90
91 static struct bt_iso_big_create_param big_create_param = {
92 .num_bis = BIS_ISO_CHAN_COUNT,
93 .bis_channels = bis,
94 .interval = BIG_SDU_INTERVAL_US, /* in microseconds */
95 .latency = 10, /* in milliseconds */
96 .packing = (IS_ENABLED(CONFIG_ISO_PACKING_INTERLEAVED) ?
97 BT_ISO_PACKING_INTERLEAVED :
98 BT_ISO_PACKING_SEQUENTIAL),
99 .framing = 0, /* 0 - unframed, 1 - framed */
100 };
101
102 static const struct bt_data ad[] = {
103 BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
104 };
105
main(void)106 int main(void)
107 {
108 /* Some controllers work best while Extended Advertising interval to be a multiple
109 * of the ISO Interval minus 10 ms (max. advertising random delay). This is
110 * required to place the AUX_ADV_IND PDUs in a non-overlapping interval with the
111 * Broadcast ISO radio events.
112 * For 10ms SDU interval a extended advertising interval of 60 - 10 = 50 is suitable
113 */
114 const uint16_t adv_interval_ms = 60U;
115 const uint16_t ext_adv_interval_ms = adv_interval_ms - 10U;
116 const struct bt_le_adv_param *ext_adv_param = BT_LE_ADV_PARAM(
117 BT_LE_ADV_OPT_EXT_ADV, BT_GAP_MS_TO_ADV_INTERVAL(ext_adv_interval_ms),
118 BT_GAP_MS_TO_ADV_INTERVAL(ext_adv_interval_ms), NULL);
119 const struct bt_le_per_adv_param *per_adv_param = BT_LE_PER_ADV_PARAM(
120 BT_GAP_MS_TO_PER_ADV_INTERVAL(adv_interval_ms),
121 BT_GAP_MS_TO_PER_ADV_INTERVAL(adv_interval_ms), BT_LE_PER_ADV_OPT_NONE);
122 uint32_t timeout_counter = INITIAL_TIMEOUT_COUNTER;
123 struct bt_le_ext_adv *adv;
124 struct bt_iso_big *big;
125 int err;
126
127 uint32_t iso_send_count = 0;
128 uint8_t iso_data[sizeof(iso_send_count)] = { 0 };
129
130 printk("Starting ISO Broadcast Demo\n");
131
132 /* Initialize the Bluetooth Subsystem */
133 err = bt_enable(NULL);
134 if (err) {
135 printk("Bluetooth init failed (err %d)\n", err);
136 return 0;
137 }
138
139 /* Create a non-connectable advertising set */
140 err = bt_le_ext_adv_create(ext_adv_param, NULL, &adv);
141 if (err) {
142 printk("Failed to create advertising set (err %d)\n", err);
143 return 0;
144 }
145
146 /* Set advertising data to have complete local name set */
147 err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
148 if (err) {
149 printk("Failed to set advertising data (err %d)\n", err);
150 return 0;
151 }
152
153 /* Set periodic advertising parameters */
154 err = bt_le_per_adv_set_param(adv, per_adv_param);
155 if (err) {
156 printk("Failed to set periodic advertising parameters"
157 " (err %d)\n", err);
158 return 0;
159 }
160
161 /* Enable Periodic Advertising */
162 err = bt_le_per_adv_start(adv);
163 if (err) {
164 printk("Failed to enable periodic advertising (err %d)\n", err);
165 return 0;
166 }
167
168 /* Start extended advertising */
169 err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
170 if (err) {
171 printk("Failed to start extended advertising (err %d)\n", err);
172 return 0;
173 }
174
175 /* Create BIG */
176 err = bt_iso_big_create(adv, &big_create_param, &big);
177 if (err) {
178 printk("Failed to create BIG (err %d)\n", err);
179 return 0;
180 }
181
182 for (uint8_t chan = 0U; chan < BIS_ISO_CHAN_COUNT; chan++) {
183 printk("Waiting for BIG complete chan %u...\n", chan);
184 err = k_sem_take(&sem_big_cmplt, K_FOREVER);
185 if (err) {
186 printk("failed (err %d)\n", err);
187 return 0;
188 }
189 printk("BIG create complete chan %u.\n", chan);
190 }
191
192 while (true) {
193 for (uint8_t chan = 0U; chan < BIS_ISO_CHAN_COUNT; chan++) {
194 struct net_buf *buf;
195 int ret;
196
197 buf = net_buf_alloc(&bis_tx_pool, K_USEC(BUF_ALLOC_TIMEOUT_US));
198 if (!buf) {
199 printk("Data buffer allocate timeout on channel"
200 " %u\n", chan);
201 return 0;
202 }
203
204 ret = k_sem_take(&sem_iso_data, K_USEC(BUF_ALLOC_TIMEOUT_US));
205 if (ret) {
206 printk("k_sem_take for ISO data sent failed\n");
207 net_buf_unref(buf);
208 return 0;
209 }
210
211 net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
212 sys_put_le32(iso_send_count, iso_data);
213 net_buf_add_mem(buf, iso_data, sizeof(iso_data));
214 ret = bt_iso_chan_send(&bis_iso_chan[chan], buf, seq_num);
215 if (ret < 0) {
216 printk("Unable to broadcast data on channel %u"
217 " : %d", chan, ret);
218 net_buf_unref(buf);
219 return 0;
220 }
221
222 }
223
224 if ((iso_send_count % CONFIG_ISO_PRINT_INTERVAL) == 0) {
225 printk("Sending value %u\n", iso_send_count);
226 }
227
228 iso_send_count++;
229 seq_num++;
230
231 timeout_counter--;
232 if (!timeout_counter) {
233 timeout_counter = INITIAL_TIMEOUT_COUNTER;
234
235 printk("BIG Terminate...");
236 err = bt_iso_big_terminate(big);
237 if (err) {
238 printk("failed (err %d)\n", err);
239 return 0;
240 }
241 printk("done.\n");
242
243 for (uint8_t chan = 0U; chan < BIS_ISO_CHAN_COUNT;
244 chan++) {
245 printk("Waiting for BIG terminate complete"
246 " chan %u...\n", chan);
247 err = k_sem_take(&sem_big_term, K_FOREVER);
248 if (err) {
249 printk("failed (err %d)\n", err);
250 return 0;
251 }
252 printk("BIG terminate complete chan %u.\n",
253 chan);
254 }
255
256 printk("Create BIG...");
257 err = bt_iso_big_create(adv, &big_create_param, &big);
258 if (err) {
259 printk("failed (err %d)\n", err);
260 return 0;
261 }
262 printk("done.\n");
263
264 for (uint8_t chan = 0U; chan < BIS_ISO_CHAN_COUNT;
265 chan++) {
266 printk("Waiting for BIG complete chan %u...\n",
267 chan);
268 err = k_sem_take(&sem_big_cmplt, K_FOREVER);
269 if (err) {
270 printk("failed (err %d)\n", err);
271 return 0;
272 }
273 printk("BIG create complete chan %u.\n", chan);
274 }
275 }
276 }
277 }
278