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