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/util.h>
30 #include <zephyr/sys/util_macro.h>
31
32 #include "cap_acceptor.h"
33
34 LOG_MODULE_REGISTER(cap_acceptor, LOG_LEVEL_INF);
35
36 #define SUPPORTED_DURATION (BT_AUDIO_CODEC_CAP_DURATION_7_5 | BT_AUDIO_CODEC_CAP_DURATION_10)
37 #define MAX_CHAN_PER_STREAM BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(2)
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 if (dir == BT_AUDIO_DIR_SINK && peer.sink_stream.bap_stream.ep == NULL) {
145 return &peer.sink_stream;
146 } else if (dir == BT_AUDIO_DIR_SOURCE && peer.source_stream.bap_stream.ep == NULL) {
147 return &peer.source_stream;
148 }
149
150 return NULL;
151 }
152
stream_released(const struct bt_cap_stream * cap_stream)153 void stream_released(const struct bt_cap_stream *cap_stream)
154 {
155 if (cap_stream == &peer.source_stream) {
156 k_sem_give(&peer.source_stream_sem);
157 } else if (cap_stream == &peer.sink_stream) {
158 k_sem_give(&peer.sink_stream_sem);
159 }
160 }
161
reset_cap_acceptor(void)162 static int reset_cap_acceptor(void)
163 {
164 int err;
165
166 LOG_INF("Resetting");
167
168 if (peer.conn != NULL) {
169 err = bt_conn_disconnect(peer.conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
170 if (err != 0) {
171 return err;
172 }
173
174 err = k_sem_take(&sem_state_change, K_FOREVER);
175 if (err != 0) {
176 LOG_ERR("Timeout on disconnect: %d", err);
177 return err;
178 }
179 }
180
181 if (adv != NULL) {
182 err = bt_le_ext_adv_stop(adv);
183 if (err != 0) {
184 LOG_ERR("Failed to stop advertiser: %d", err);
185 return err;
186 }
187
188 err = bt_le_ext_adv_delete(adv);
189 if (err != 0) {
190 LOG_ERR("Failed to delete advertiser: %d", err);
191 return err;
192 }
193
194 adv = NULL;
195 }
196
197 if (peer.source_stream.bap_stream.ep != NULL) {
198 err = k_sem_take(&peer.source_stream_sem, SEM_TIMEOUT);
199 if (err != 0) {
200 LOG_ERR("Timeout on source_stream_sem: %d", err);
201 return err;
202 }
203 }
204
205 if (peer.sink_stream.bap_stream.ep != NULL) {
206 err = k_sem_take(&peer.sink_stream_sem, SEM_TIMEOUT);
207 if (err != 0) {
208 LOG_ERR("Timeout on sink_stream_sem: %d", err);
209 return err;
210 }
211 }
212
213 k_sem_reset(&sem_state_change);
214
215 return 0;
216 }
217
218 /** Register the PAC records for PACS */
register_pac(enum bt_audio_dir dir,enum bt_audio_context context,struct bt_pacs_cap * cap)219 static int register_pac(enum bt_audio_dir dir, enum bt_audio_context context,
220 struct bt_pacs_cap *cap)
221 {
222 int err;
223
224 err = bt_pacs_cap_register(dir, cap);
225 if (err != 0) {
226 LOG_ERR("Failed to register capabilities: %d", err);
227
228 return err;
229 }
230
231 err = bt_pacs_set_location(dir, BT_AUDIO_LOCATION_MONO_AUDIO);
232 if (err != 0) {
233 LOG_ERR("Failed to set location: %d", err);
234
235 return err;
236 }
237
238 err = bt_pacs_set_supported_contexts(dir, context);
239 if (err != 0 && err != -EALREADY) {
240 LOG_ERR("Failed to set supported contexts: %d", err);
241
242 return err;
243 }
244
245 err = bt_pacs_set_available_contexts(dir, context);
246 if (err != 0 && err != -EALREADY) {
247 LOG_ERR("Failed to set available contexts: %d", err);
248
249 return err;
250 }
251
252 return 0;
253 }
254
init_cap_acceptor(void)255 static int init_cap_acceptor(void)
256 {
257 static const struct bt_audio_codec_cap lc3_codec_cap = BT_AUDIO_CODEC_CAP_LC3(
258 SUPPORTED_FREQ, SUPPORTED_DURATION, MAX_CHAN_PER_STREAM, MIN_SDU, MAX_SDU,
259 FRAMES_PER_SDU, (SINK_CONTEXT | SOURCE_CONTEXT));
260 const struct bt_pacs_register_param pacs_param = {
261 .snk_pac = true,
262 .snk_loc = true,
263 .src_pac = true,
264 .src_loc = true,
265 };
266 int err;
267
268 err = bt_enable(NULL);
269 if (err != 0) {
270 LOG_ERR("Bluetooth enable failed: %d", err);
271
272 return 0;
273 }
274
275 LOG_INF("Bluetooth initialized");
276
277 err = bt_pacs_register(&pacs_param);
278 if (err) {
279 LOG_ERR("Could not register PACS (err %d)", err);
280 return 0;
281 }
282
283 if (IS_ENABLED(CONFIG_BT_PAC_SNK)) {
284 static struct bt_pacs_cap sink_cap = {
285 .codec_cap = &lc3_codec_cap,
286 };
287 int err;
288
289 err = register_pac(BT_AUDIO_DIR_SINK, SINK_CONTEXT, &sink_cap);
290 if (err != 0) {
291 LOG_ERR("Failed to register sink capabilities: %d", err);
292
293 return -ENOEXEC;
294 }
295 }
296
297 if (IS_ENABLED(CONFIG_BT_PAC_SRC)) {
298 static struct bt_pacs_cap source_cap = {
299 .codec_cap = &lc3_codec_cap,
300 };
301 int err;
302
303 err = register_pac(BT_AUDIO_DIR_SOURCE, SOURCE_CONTEXT, &source_cap);
304 if (err != 0) {
305 LOG_ERR("Failed to register sink capabilities: %d", err);
306
307 return -ENOEXEC;
308 }
309 }
310
311 return 0;
312 }
313
main(void)314 int main(void)
315 {
316 int err;
317
318 err = init_cap_acceptor();
319 if (err != 0) {
320 return 0;
321 }
322
323 if (IS_ENABLED(CONFIG_BT_BAP_UNICAST_SERVER)) {
324 err = init_cap_acceptor_unicast(&peer);
325 if (err != 0) {
326 return 0;
327 }
328 }
329
330 if (IS_ENABLED(CONFIG_BT_BAP_BROADCAST_SINK)) {
331 err = init_cap_acceptor_broadcast();
332 if (err != 0) {
333 return 0;
334 }
335 }
336
337 LOG_INF("CAP Acceptor initialized");
338
339 while (true) {
340 err = reset_cap_acceptor();
341 if (err != 0) {
342 LOG_ERR("Failed to reset");
343
344 break;
345 }
346
347 /* Start advertising as a CAP Acceptor, which includes setting the required
348 * advertising data based on the roles we support. The Common Audio Service data is
349 * always advertised, as CAP Initiators and CAP Commanders will use this to identify
350 * our device as a CAP Acceptor.
351 */
352 err = advertise();
353 if (err != 0) {
354 continue;
355 }
356
357 /* After advertising we expect CAP Initiators to connect to us and setup streams,
358 * and eventually disconnect again. As a CAP Acceptor we just need to react to their
359 * requests and not do anything else.
360 */
361
362 /* Reset if disconnected */
363 err = k_sem_take(&sem_state_change, K_FOREVER);
364 if (err != 0) {
365 LOG_ERR("Failed to take sem_state_change: err %d", err);
366
367 break;
368 }
369 }
370
371 return 0;
372 }
373