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