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