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