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