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