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