1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <zephyr/bluetooth/audio/audio.h>
11 #include <zephyr/bluetooth/audio/bap_lc3_preset.h>
12 #include <zephyr/bluetooth/audio/cap.h>
13 #include <zephyr/bluetooth/audio/bap.h>
14 #include <zephyr/bluetooth/bluetooth.h>
15 #include <zephyr/bluetooth/gap.h>
16 #include <zephyr/bluetooth/uuid.h>
17 #include <zephyr/kernel.h>
18 #include <zephyr/logging/log.h>
19 #include <zephyr/logging/log_core.h>
20 #include <zephyr/net_buf.h>
21 #include <zephyr/sys/byteorder.h>
22 #include <zephyr/sys/util.h>
23 #include <zephyr/types.h>
24
25 #include "cap_initiator.h"
26
27 LOG_MODULE_REGISTER(cap_initiator_broadcast, LOG_LEVEL_INF);
28
29 static struct bt_cap_stream broadcast_stream;
30 uint64_t total_broadcast_tx_iso_packet_count; /* This value is exposed to test code */
31
broadcast_stream_started_cb(struct bt_bap_stream * stream)32 static void broadcast_stream_started_cb(struct bt_bap_stream *stream)
33 {
34 struct bt_cap_stream *cap_stream = CONTAINER_OF(stream, struct bt_cap_stream, bap_stream);
35 int err;
36
37 LOG_INF("Started broadcast stream %p", stream);
38 total_broadcast_tx_iso_packet_count = 0U;
39
40 err = cap_initiator_tx_register_stream(cap_stream);
41 if (err != 0) {
42 LOG_ERR("Failed to register %p for TX: %d", stream, err);
43 }
44 }
45
broadcast_stream_stopped_cb(struct bt_bap_stream * stream,uint8_t reason)46 static void broadcast_stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
47 {
48 struct bt_cap_stream *cap_stream = CONTAINER_OF(stream, struct bt_cap_stream, bap_stream);
49 int err;
50
51 LOG_INF("Stopped broadcast stream %p with reason 0x%02X", stream, reason);
52
53 err = cap_initiator_tx_unregister_stream(cap_stream);
54 if (err != 0) {
55 LOG_ERR("Failed to unregister %p for TX: %d", stream, err);
56 }
57 }
58
broadcast_stream_sent_cb(struct bt_bap_stream * stream)59 static void broadcast_stream_sent_cb(struct bt_bap_stream *stream)
60 {
61 /* Triggered every time we have sent an HCI data packet to the controller */
62
63 if ((total_broadcast_tx_iso_packet_count % 100U) == 0U) {
64 LOG_INF("Sent %llu HCI ISO data packets", total_broadcast_tx_iso_packet_count);
65 }
66
67 total_broadcast_tx_iso_packet_count++;
68 }
69
setup_extended_adv(struct bt_le_ext_adv ** adv)70 static int setup_extended_adv(struct bt_le_ext_adv **adv)
71 {
72 int err;
73
74 /* Create a non-connectable non-scannable advertising set */
75 err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN, NULL, adv);
76 if (err != 0) {
77 LOG_ERR("Unable to create extended advertising set: %d", err);
78 return err;
79 }
80
81 /* Set periodic advertising parameters */
82 err = bt_le_per_adv_set_param(*adv, BT_LE_PER_ADV_PARAM(BT_GAP_PER_ADV_FAST_INT_MIN_2,
83 BT_GAP_PER_ADV_FAST_INT_MAX_2,
84 BT_LE_PER_ADV_OPT_NONE));
85 if (err != 0) {
86 LOG_ERR("Failed to set periodic advertising parameters: %d", err);
87 return err;
88 }
89
90 return 0;
91 }
92
broadcast_create(struct bt_cap_broadcast_source ** broadcast_source)93 static int broadcast_create(struct bt_cap_broadcast_source **broadcast_source)
94 {
95 /** For simplicity we use the mandatory configuration 16_2_1 */
96 static struct bt_bap_lc3_preset broadcast_preset_16_2_1 =
97 BT_BAP_LC3_BROADCAST_PRESET_16_2_1(BT_AUDIO_LOCATION_MONO_AUDIO,
98 BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED);
99 struct bt_cap_initiator_broadcast_stream_param stream_params = {
100 .stream = &broadcast_stream,
101 };
102 struct bt_cap_initiator_broadcast_subgroup_param subgroup_param = {
103 .codec_cfg = &broadcast_preset_16_2_1.codec_cfg,
104 .stream_params = &stream_params,
105 .stream_count = 1U,
106 };
107 const struct bt_cap_initiator_broadcast_create_param create_param = {
108 .qos = &broadcast_preset_16_2_1.qos,
109 .subgroup_params = &subgroup_param,
110 .subgroup_count = 1U,
111 };
112 int err;
113
114 LOG_INF("Creating broadcast source");
115
116 err = bt_cap_initiator_broadcast_audio_create(&create_param, broadcast_source);
117 if (err != 0) {
118 LOG_ERR("Unable to start broadcast source: %d", err);
119 return err;
120 }
121
122 return 0;
123 }
124
setup_extended_adv_data(struct bt_cap_broadcast_source * source,struct bt_le_ext_adv * adv)125 static int setup_extended_adv_data(struct bt_cap_broadcast_source *source,
126 struct bt_le_ext_adv *adv)
127 {
128 /* Broadcast Audio Streaming Endpoint advertising data */
129 NET_BUF_SIMPLE_DEFINE(ad_buf, BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE);
130 NET_BUF_SIMPLE_DEFINE(base_buf, 64);
131 struct bt_data ext_ad;
132 struct bt_data per_ad;
133 uint32_t broadcast_id;
134 int err;
135
136 #if defined(CONFIG_STATIC_BROADCAST_ID)
137 broadcast_id = CONFIG_BROADCAST_ID;
138 #else
139 err = bt_rand(&broadcast_id, BT_AUDIO_BROADCAST_ID_SIZE);
140 if (err) {
141 printk("Unable to generate broadcast ID: %d\n", err);
142 return err;
143 }
144 #endif /* CONFIG_STATIC_BROADCAST_ID */
145
146 /* Setup extended advertising data */
147 net_buf_simple_add_le16(&ad_buf, BT_UUID_BROADCAST_AUDIO_VAL);
148 net_buf_simple_add_le24(&ad_buf, broadcast_id);
149 ext_ad.type = BT_DATA_SVC_DATA16;
150 ext_ad.data_len = ad_buf.len;
151 ext_ad.data = ad_buf.data;
152 err = bt_le_ext_adv_set_data(adv, &ext_ad, 1, NULL, 0);
153 if (err != 0) {
154 LOG_ERR("Failed to set extended advertising data: %d", err);
155 return err;
156 }
157
158 /* Setup periodic advertising data */
159 err = bt_cap_initiator_broadcast_get_base(source, &base_buf);
160 if (err != 0) {
161 LOG_ERR("Failed to get encoded BASE: %d", err);
162 return err;
163 }
164
165 per_ad.type = BT_DATA_SVC_DATA16;
166 per_ad.data_len = base_buf.len;
167 per_ad.data = base_buf.data;
168 err = bt_le_per_adv_set_data(adv, &per_ad, 1);
169 if (err != 0) {
170 LOG_ERR("Failed to set periodic advertising data: %d", err);
171 return err;
172 }
173
174 return 0;
175 }
176
start_extended_adv(struct bt_le_ext_adv * adv)177 static int start_extended_adv(struct bt_le_ext_adv *adv)
178 {
179 int err;
180
181 /* Start extended advertising */
182 err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
183 if (err != 0) {
184 LOG_ERR("Failed to start extended advertising: %d", err);
185 return err;
186 }
187
188 /* Enable Periodic Advertising */
189 err = bt_le_per_adv_start(adv);
190 if (err != 0) {
191 LOG_ERR("Failed to enable periodic advertising: %d", err);
192 return err;
193 }
194
195 return 0;
196 }
197
broadcast_start(struct bt_cap_broadcast_source * broadcast_source,struct bt_le_ext_adv * adv)198 static int broadcast_start(struct bt_cap_broadcast_source *broadcast_source,
199 struct bt_le_ext_adv *adv)
200 {
201 int err;
202
203 err = bt_cap_initiator_broadcast_audio_start(broadcast_source, adv);
204 if (err != 0) {
205 LOG_ERR("Unable to start broadcast source: %d", err);
206 return err;
207 }
208
209 LOG_INF("Broadcast source started");
210
211 return 0;
212 }
213
init_cap_initiator(void)214 static int init_cap_initiator(void)
215 {
216 static struct bt_bap_stream_ops broadcast_stream_ops = {
217 .started = broadcast_stream_started_cb,
218 .stopped = broadcast_stream_stopped_cb,
219 .sent = broadcast_stream_sent_cb,
220 };
221
222 bt_cap_stream_ops_register(&broadcast_stream, &broadcast_stream_ops);
223
224 cap_initiator_tx_init();
225
226 return 0;
227 }
228
cap_initiator_broadcast(void)229 int cap_initiator_broadcast(void)
230 {
231 struct bt_cap_broadcast_source *broadcast_source;
232 struct bt_le_ext_adv *adv;
233 int err;
234
235 err = init_cap_initiator();
236 if (err != 0) {
237 return err;
238 }
239
240 LOG_INF("CAP initiator broadcast initialized");
241
242 err = setup_extended_adv(&adv);
243 if (err != 0) {
244 return err;
245 }
246
247 err = broadcast_create(&broadcast_source);
248 if (err != 0) {
249 return err;
250 }
251
252 err = broadcast_start(broadcast_source, adv);
253 if (err != 0) {
254 return err;
255 }
256
257 err = setup_extended_adv_data(broadcast_source, adv);
258 if (err != 0) {
259 return err;
260 }
261
262 err = start_extended_adv(adv);
263 if (err != 0) {
264 return err;
265 }
266
267 return 0;
268 }
269