1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <autoconf.h>
8 #include <errno.h>
9 #include <stdbool.h>
10 #include <stddef.h>
11 #include <stdint.h>
12
13 #include <zephyr/bluetooth/addr.h>
14 #include <zephyr/bluetooth/audio/audio.h>
15 #include <zephyr/bluetooth/audio/bap.h>
16 #include <zephyr/bluetooth/audio/cap.h>
17 #include <zephyr/bluetooth/audio/lc3.h>
18 #include <zephyr/bluetooth/audio/pacs.h>
19 #include <zephyr/bluetooth/bluetooth.h>
20 #include <zephyr/bluetooth/byteorder.h>
21 #include <zephyr/bluetooth/conn.h>
22 #include <zephyr/bluetooth/gap.h>
23 #include <zephyr/bluetooth/hci.h>
24 #include <zephyr/bluetooth/hci_types.h>
25 #include <zephyr/bluetooth/uuid.h>
26 #include <zephyr/kernel.h>
27 #include <zephyr/logging/log.h>
28 #include <zephyr/logging/log_core.h>
29 #include <zephyr/sys/__assert.h>
30 #include <zephyr/sys/util.h>
31 #include <zephyr/sys/util_macro.h>
32
33 #include "cap_acceptor.h"
34
35 LOG_MODULE_REGISTER(cap_acceptor, LOG_LEVEL_INF);
36
37 #define SUPPORTED_DURATION (BT_AUDIO_CODEC_CAP_DURATION_7_5 | BT_AUDIO_CODEC_CAP_DURATION_10)
38 #define SUPPORTED_FREQ BT_AUDIO_CODEC_CAP_FREQ_ANY
39 #define SEM_TIMEOUT K_SECONDS(5)
40 #define MAX_SDU 155U
41 #define MIN_SDU 30U
42 #define FRAMES_PER_SDU 2
43
44 static const struct bt_data ad[] = {
45 BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
46 BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
47 BT_DATA_BYTES(BT_DATA_UUID16_SOME,
48 BT_UUID_16_ENCODE(BT_UUID_ASCS_VAL),
49 BT_UUID_16_ENCODE(BT_UUID_CAS_VAL)),
50 BT_DATA_BYTES(BT_DATA_SVC_DATA16,
51 BT_UUID_16_ENCODE(BT_UUID_CAS_VAL),
52 BT_AUDIO_UNICAST_ANNOUNCEMENT_TARGETED),
53 IF_ENABLED(CONFIG_BT_BAP_UNICAST_SERVER,
54 (BT_DATA_BYTES(BT_DATA_SVC_DATA16,
55 BT_UUID_16_ENCODE(BT_UUID_ASCS_VAL),
56 BT_AUDIO_UNICAST_ANNOUNCEMENT_TARGETED,
57 BT_BYTES_LIST_LE16(SINK_CONTEXT),
58 BT_BYTES_LIST_LE16(SOURCE_CONTEXT),
59 0x00, /* Metadata length */),
60 ))
61 IF_ENABLED(CONFIG_BT_BAP_SCAN_DELEGATOR,
62 (BT_DATA_BYTES(BT_DATA_SVC_DATA16,
63 BT_UUID_16_ENCODE(BT_UUID_BASS_VAL)),
64 ))
65 };
66
67 static struct bt_le_ext_adv *adv;
68 static struct peer_config peer;
69
70 static K_SEM_DEFINE(sem_state_change, 0, 1);
71
connected_cb(struct bt_conn * conn,uint8_t err)72 static void connected_cb(struct bt_conn *conn, uint8_t err)
73 {
74 char addr[BT_ADDR_LE_STR_LEN];
75
76 (void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
77 LOG_INF("Connected: %s", addr);
78
79 peer.conn = bt_conn_ref(conn);
80 k_sem_give(&sem_state_change);
81 }
82
disconnected_cb(struct bt_conn * conn,uint8_t reason)83 static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
84 {
85 char addr[BT_ADDR_LE_STR_LEN];
86
87 if (conn != peer.conn) {
88 return;
89 }
90
91 (void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
92 LOG_INF("Disconnected: %s, reason 0x%02x %s", addr, reason, bt_hci_err_to_str(reason));
93
94 bt_conn_unref(peer.conn);
95 peer.conn = NULL;
96 k_sem_give(&sem_state_change);
97 }
98
99 BT_CONN_CB_DEFINE(conn_callbacks) = {
100 .connected = connected_cb,
101 .disconnected = disconnected_cb,
102 };
103
advertise(void)104 static int advertise(void)
105 {
106 int err;
107
108 err = bt_le_ext_adv_create(BT_BAP_ADV_PARAM_CONN_QUICK, NULL, &adv);
109 if (err) {
110 LOG_ERR("Failed to create advertising set: %d", err);
111
112 return err;
113 }
114
115 err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
116 if (err) {
117 LOG_ERR("Failed to set advertising data: %d", err);
118
119 return err;
120 }
121
122 err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
123 if (err) {
124 LOG_ERR("Failed to start advertising set: %d", err);
125
126 return err;
127 }
128
129 LOG_INF("Advertising successfully started");
130
131 /* Wait for connection*/
132 err = k_sem_take(&sem_state_change, K_FOREVER);
133 if (err != 0) {
134 LOG_ERR("Failed to take sem_state_change: err %d", err);
135
136 return err;
137 }
138
139 return 0;
140 }
141
stream_alloc(enum bt_audio_dir dir)142 struct bt_cap_stream *stream_alloc(enum bt_audio_dir dir)
143 {
144 struct bt_cap_stream *ret_stream;
145
146 if (dir == BT_AUDIO_DIR_SINK) {
147 ret_stream = NULL;
148 ARRAY_FOR_EACH_PTR(peer.sink_streams, stream) {
149 if (stream->bap_stream.ep == NULL) {
150 ret_stream = stream;
151 }
152 }
153 } else if (dir == BT_AUDIO_DIR_SOURCE && peer.source_stream.bap_stream.ep == NULL) {
154 ret_stream = &peer.source_stream;
155 } else {
156 ret_stream = NULL;
157 }
158
159 return ret_stream;
160 }
161
stream_released(const struct bt_cap_stream * cap_stream)162 void stream_released(const struct bt_cap_stream *cap_stream)
163 {
164 if (cap_stream == &peer.source_stream) {
165 k_sem_give(&peer.source_stream_sem);
166 } else if (IS_ARRAY_ELEMENT(peer.sink_streams, cap_stream)) {
167 k_sem_give(&peer.sink_stream_sem);
168 } else {
169 __ASSERT(false, "Invalid stream: %p", cap_stream);
170 }
171 }
172
reset_cap_acceptor(void)173 static int reset_cap_acceptor(void)
174 {
175 int err;
176
177 LOG_INF("Resetting");
178
179 if (peer.conn != NULL) {
180 err = bt_conn_disconnect(peer.conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
181 if (err != 0) {
182 return err;
183 }
184
185 err = k_sem_take(&sem_state_change, K_FOREVER);
186 if (err != 0) {
187 LOG_ERR("Timeout on disconnect: %d", err);
188 return err;
189 }
190 }
191
192 if (adv != NULL) {
193 err = bt_le_ext_adv_stop(adv);
194 if (err != 0) {
195 LOG_ERR("Failed to stop advertiser: %d", err);
196 return err;
197 }
198
199 err = bt_le_ext_adv_delete(adv);
200 if (err != 0) {
201 LOG_ERR("Failed to delete advertiser: %d", err);
202 return err;
203 }
204
205 adv = NULL;
206 }
207
208 if (peer.source_stream.bap_stream.ep != NULL) {
209 err = k_sem_take(&peer.source_stream_sem, SEM_TIMEOUT);
210 if (err != 0) {
211 LOG_ERR("Timeout on source_stream_sem: %d", err);
212 return err;
213 }
214 }
215
216 ARRAY_FOR_EACH_PTR(peer.sink_streams, stream) {
217 if (stream->bap_stream.ep != NULL) {
218 err = k_sem_take(&peer.sink_stream_sem, SEM_TIMEOUT);
219 if (err != 0) {
220 LOG_ERR("Timeout on sink_stream_sem: %d", err);
221 return err;
222 }
223 }
224 }
225
226 k_sem_reset(&sem_state_change);
227
228 return 0;
229 }
230
231 /** Register the PAC records for PACS */
register_pac(enum bt_audio_dir dir,enum bt_audio_context context,struct bt_pacs_cap * cap,enum bt_audio_location locations)232 static int register_pac(enum bt_audio_dir dir, enum bt_audio_context context,
233 struct bt_pacs_cap *cap, enum bt_audio_location locations)
234 {
235 int err;
236
237 err = bt_pacs_cap_register(dir, cap);
238 if (err != 0) {
239 LOG_ERR("Failed to register capabilities: %d", err);
240
241 return err;
242 }
243
244 err = bt_pacs_set_location(dir, locations);
245 if (err != 0) {
246 LOG_ERR("Failed to set location: %d", err);
247
248 return err;
249 }
250
251 err = bt_pacs_set_supported_contexts(dir, context);
252 if (err != 0 && err != -EALREADY) {
253 LOG_ERR("Failed to set supported contexts: %d", err);
254
255 return err;
256 }
257
258 err = bt_pacs_set_available_contexts(dir, context);
259 if (err != 0 && err != -EALREADY) {
260 LOG_ERR("Failed to set available contexts: %d", err);
261
262 return err;
263 }
264
265 return 0;
266 }
267
init_cap_acceptor(void)268 static int init_cap_acceptor(void)
269 {
270 const struct bt_pacs_register_param pacs_param = {
271 .snk_pac = true,
272 .snk_loc = true,
273 .src_pac = true,
274 .src_loc = true,
275 };
276 int err;
277
278 err = bt_enable(NULL);
279 if (err != 0) {
280 LOG_ERR("Bluetooth enable failed: %d", err);
281
282 return 0;
283 }
284
285 LOG_INF("Bluetooth initialized");
286
287 err = bt_pacs_register(&pacs_param);
288 if (err) {
289 LOG_ERR("Could not register PACS (err %d)", err);
290 return 0;
291 }
292
293 if (IS_ENABLED(CONFIG_BT_PAC_SNK)) {
294 static const struct bt_audio_codec_cap lc3_codec_cap_sink =
295 BT_AUDIO_CODEC_CAP_LC3(SUPPORTED_FREQ, SUPPORTED_DURATION,
296 BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(2), MIN_SDU,
297 MAX_SDU, FRAMES_PER_SDU, SINK_CONTEXT);
298 static struct bt_pacs_cap sink_cap = {
299 .codec_cap = &lc3_codec_cap_sink,
300 };
301 int err;
302
303 err = register_pac(BT_AUDIO_DIR_SINK, SINK_CONTEXT, &sink_cap,
304 BT_AUDIO_LOCATION_FRONT_LEFT | BT_AUDIO_LOCATION_FRONT_RIGHT);
305 if (err != 0) {
306 LOG_ERR("Failed to register sink capabilities: %d", err);
307
308 return -ENOEXEC;
309 }
310 }
311
312 if (IS_ENABLED(CONFIG_BT_PAC_SRC)) {
313 static const struct bt_audio_codec_cap lc3_codec_cap_source =
314 BT_AUDIO_CODEC_CAP_LC3(SUPPORTED_FREQ, SUPPORTED_DURATION,
315 BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(1), MIN_SDU,
316 MAX_SDU, FRAMES_PER_SDU, SOURCE_CONTEXT);
317 static struct bt_pacs_cap source_cap = {
318 .codec_cap = &lc3_codec_cap_source,
319 };
320 int err;
321
322 err = register_pac(BT_AUDIO_DIR_SOURCE, SOURCE_CONTEXT, &source_cap,
323 BT_AUDIO_LOCATION_FRONT_CENTER);
324 if (err != 0) {
325 LOG_ERR("Failed to register sink capabilities: %d", err);
326
327 return -ENOEXEC;
328 }
329 }
330
331 return 0;
332 }
333
main(void)334 int main(void)
335 {
336 int err;
337
338 err = init_cap_acceptor();
339 if (err != 0) {
340 return 0;
341 }
342
343 if (IS_ENABLED(CONFIG_BT_BAP_UNICAST_SERVER)) {
344 err = init_cap_acceptor_unicast(&peer);
345 if (err != 0) {
346 return 0;
347 }
348 }
349
350 if (IS_ENABLED(CONFIG_BT_BAP_BROADCAST_SINK)) {
351 err = init_cap_acceptor_broadcast();
352 if (err != 0) {
353 return 0;
354 }
355 }
356
357 LOG_INF("CAP Acceptor initialized");
358
359 while (true) {
360 err = reset_cap_acceptor();
361 if (err != 0) {
362 LOG_ERR("Failed to reset");
363
364 break;
365 }
366
367 /* Start advertising as a CAP Acceptor, which includes setting the required
368 * advertising data based on the roles we support. The Common Audio Service data is
369 * always advertised, as CAP Initiators and CAP Commanders will use this to identify
370 * our device as a CAP Acceptor.
371 */
372 err = advertise();
373 if (err != 0) {
374 continue;
375 }
376
377 /* After advertising we expect CAP Initiators to connect to us and setup streams,
378 * and eventually disconnect again. As a CAP Acceptor we just need to react to their
379 * requests and not do anything else.
380 */
381
382 /* Reset if disconnected */
383 err = k_sem_take(&sem_state_change, K_FOREVER);
384 if (err != 0) {
385 LOG_ERR("Failed to take sem_state_change: err %d", err);
386
387 break;
388 }
389 }
390
391 return 0;
392 }
393