1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <autoconf.h>
8 #include <stdbool.h>
9 #include <stddef.h>
10 #include <stdint.h>
11
12 #include <zephyr/bluetooth/addr.h>
13 #include <zephyr/bluetooth/att.h>
14 #include <zephyr/bluetooth/audio/audio.h>
15 #include <zephyr/bluetooth/audio/bap_lc3_preset.h>
16 #include <zephyr/bluetooth/audio/cap.h>
17 #include <zephyr/bluetooth/audio/bap.h>
18 #include <zephyr/bluetooth/audio/csip.h>
19 #include <zephyr/bluetooth/bluetooth.h>
20 #include <zephyr/bluetooth/conn.h>
21 #include <zephyr/bluetooth/gap.h>
22 #include <zephyr/bluetooth/gatt.h>
23 #include <zephyr/bluetooth/hci.h>
24 #include <zephyr/bluetooth/hci_types.h>
25 #include <zephyr/bluetooth/iso.h>
26 #include <zephyr/bluetooth/uuid.h>
27 #include <zephyr/kernel.h>
28 #include <zephyr/kernel/thread_stack.h>
29 #include <zephyr/logging/log.h>
30 #include <zephyr/logging/log_core.h>
31 #include <zephyr/net_buf.h>
32 #include <zephyr/sys/byteorder.h>
33 #include <zephyr/sys/util.h>
34 #include <zephyr/sys/util_macro.h>
35
36 #include "cap_initiator.h"
37
38 LOG_MODULE_REGISTER(cap_initiator_unicast, LOG_LEVEL_INF);
39
40 #define SEM_TIMEOUT K_SECONDS(5)
41
42 /* We use the same config for both sink and source streams
43 * For simplicity we use the mandatory configuration 16_2_1
44 */
45 static struct bt_bap_lc3_preset unicast_preset_16_2_1 = BT_BAP_LC3_UNICAST_PRESET_16_2_1(
46 BT_AUDIO_LOCATION_MONO_AUDIO, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED);
47 static struct bt_bap_unicast_group *unicast_group;
48 uint64_t total_rx_iso_packet_count; /* This value is exposed to test code */
49 uint64_t total_unicast_tx_iso_packet_count; /* This value is exposed to test code */
50
51 /** Struct to contain information for a specific peer (CAP) device */
52 struct peer_config {
53 /** Stream for the source endpoint */
54 struct bt_cap_stream source_stream;
55 /** Stream for the sink endpoint */
56 struct bt_cap_stream sink_stream;
57 /** Semaphore to help wait for a release operation if the source stream is not idle */
58 struct k_sem source_stream_sem;
59 /** Semaphore to help wait for a release operation if the sink stream is not idle */
60 struct k_sem sink_stream_sem;
61 /** Reference to the endpoint for the source stream */
62 struct bt_bap_ep *source_ep;
63 /** Reference to the endpoint for the sink stream */
64 struct bt_bap_ep *sink_ep;
65 /** ACL connection object for the peer device */
66 struct bt_conn *conn;
67 /** Current sequence number for TX */
68 uint16_t tx_seq_num;
69 };
70
71 /* TODO: Expand to multiple ACL connections */
72 static struct peer_config peer;
73
74 static K_SEM_DEFINE(sem_proc, 0, 1);
75 static K_SEM_DEFINE(sem_state_change, 0, 1);
76 static K_SEM_DEFINE(sem_mtu_exchanged, 0, 1);
77 static K_SEM_DEFINE(sem_security_changed, 0, 1);
78
is_tx_stream(struct bt_bap_stream * stream)79 static bool is_tx_stream(struct bt_bap_stream *stream)
80 {
81 struct bt_bap_ep_info ep_info;
82 int err;
83
84 err = bt_bap_ep_get_info(stream->ep, &ep_info);
85 if (err != 0) {
86 LOG_ERR("Failed to get ep info: %d", err);
87
88 return false;
89 }
90
91 return ep_info.dir == BT_AUDIO_DIR_SINK;
92 }
93
unicast_stream_configured_cb(struct bt_bap_stream * stream,const struct bt_bap_qos_cfg_pref * pref)94 static void unicast_stream_configured_cb(struct bt_bap_stream *stream,
95 const struct bt_bap_qos_cfg_pref *pref)
96 {
97 LOG_INF("Configured stream %p", stream);
98
99 /* TODO: The preference should be used/taken into account when
100 * setting the QoS
101 */
102
103 LOG_INF("Remote preferences: unframed %s, phy %u, rtn %u, latency %u, pd_min %u, pd_max "
104 "%u, pref_pd_min %u, pref_pd_max %u",
105 pref->unframed_supported ? "supported" : "not supported", pref->phy, pref->rtn,
106 pref->latency, pref->pd_min, pref->pd_max, pref->pref_pd_min, pref->pref_pd_max);
107 }
108
unicast_stream_qos_set_cb(struct bt_bap_stream * stream)109 static void unicast_stream_qos_set_cb(struct bt_bap_stream *stream)
110 {
111 LOG_INF("QoS set stream %p", stream);
112 }
113
unicast_stream_enabled_cb(struct bt_bap_stream * stream)114 static void unicast_stream_enabled_cb(struct bt_bap_stream *stream)
115 {
116 LOG_INF("Enabled stream %p", stream);
117 }
118
unicast_stream_started_cb(struct bt_bap_stream * stream)119 static void unicast_stream_started_cb(struct bt_bap_stream *stream)
120 {
121 LOG_INF("Started stream %p", stream);
122 total_rx_iso_packet_count = 0U;
123 total_unicast_tx_iso_packet_count = 0U;
124
125 if (is_tx_stream(stream)) {
126 struct bt_cap_stream *cap_stream =
127 CONTAINER_OF(stream, struct bt_cap_stream, bap_stream);
128 int err;
129
130 err = cap_initiator_tx_register_stream(cap_stream);
131 if (err != 0) {
132 LOG_ERR("Failed to register %p for TX: %d", stream, err);
133 }
134 }
135 }
136
unicast_stream_metadata_updated_cb(struct bt_bap_stream * stream)137 static void unicast_stream_metadata_updated_cb(struct bt_bap_stream *stream)
138 {
139 LOG_INF("Metadata updated stream %p", stream);
140 }
141
unicast_stream_disabled_cb(struct bt_bap_stream * stream)142 static void unicast_stream_disabled_cb(struct bt_bap_stream *stream)
143 {
144 LOG_INF("Disabled stream %p", stream);
145 }
146
unicast_stream_stopped_cb(struct bt_bap_stream * stream,uint8_t reason)147 static void unicast_stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
148 {
149 LOG_INF("Stopped stream %p with reason 0x%02X", stream, reason);
150
151 if (is_tx_stream(stream)) {
152 struct bt_cap_stream *cap_stream =
153 CONTAINER_OF(stream, struct bt_cap_stream, bap_stream);
154 int err;
155
156 err = cap_initiator_tx_unregister_stream(cap_stream);
157 if (err != 0) {
158 LOG_ERR("Failed to unregister %p for TX: %d", stream, err);
159 }
160 }
161 }
162
unicast_stream_released_cb(struct bt_bap_stream * stream)163 static void unicast_stream_released_cb(struct bt_bap_stream *stream)
164 {
165 LOG_INF("Released stream %p", stream);
166
167 if (stream == &peer.source_stream.bap_stream) {
168 k_sem_give(&peer.source_stream_sem);
169 } else if (stream == &peer.sink_stream.bap_stream) {
170 k_sem_give(&peer.sink_stream_sem);
171 }
172 }
173
unicast_stream_recv_cb(struct bt_bap_stream * stream,const struct bt_iso_recv_info * info,struct net_buf * buf)174 static void unicast_stream_recv_cb(struct bt_bap_stream *stream,
175 const struct bt_iso_recv_info *info, struct net_buf *buf)
176 {
177 /* Triggered every time we receive an HCI data packet from the controller.
178 * A call to this does not indicate valid data
179 * (see the `info->flags` for which flags to check),
180 */
181
182 if ((total_rx_iso_packet_count % 100U) == 0U) {
183 LOG_INF("Received %llu HCI ISO data packets", total_rx_iso_packet_count);
184 }
185
186 total_rx_iso_packet_count++;
187 }
188
unicast_stream_sent_cb(struct bt_bap_stream * stream)189 static void unicast_stream_sent_cb(struct bt_bap_stream *stream)
190 {
191 /* Triggered every time we have sent an HCI data packet to the controller */
192
193 if ((total_unicast_tx_iso_packet_count % 100U) == 0U) {
194 LOG_INF("Sent %llu HCI ISO data packets", total_unicast_tx_iso_packet_count);
195 }
196
197 total_unicast_tx_iso_packet_count++;
198 }
199
200 static struct bt_bap_stream_ops unicast_stream_ops = {
201 .configured = unicast_stream_configured_cb,
202 .qos_set = unicast_stream_qos_set_cb,
203 .enabled = unicast_stream_enabled_cb,
204 .started = unicast_stream_started_cb,
205 .metadata_updated = unicast_stream_metadata_updated_cb,
206 .disabled = unicast_stream_disabled_cb,
207 .stopped = unicast_stream_stopped_cb,
208 .released = unicast_stream_released_cb,
209 .recv = unicast_stream_recv_cb,
210 .sent = unicast_stream_sent_cb,
211 };
212
log_codec_cb(struct bt_data * data,void * user_data)213 static bool log_codec_cb(struct bt_data *data, void *user_data)
214 {
215 const char *str = (const char *)user_data;
216
217 LOG_DBG("\t%s: type 0x%02x value_len %u", str, data->type, data->data_len);
218 LOG_HEXDUMP_DBG(data->data, data->data_len, "\t\tdata");
219
220 return true;
221 }
222
log_codec(const struct bt_audio_codec_cap * codec_cap,enum bt_audio_dir dir)223 static void log_codec(const struct bt_audio_codec_cap *codec_cap, enum bt_audio_dir dir)
224 {
225 LOG_INF("codec id 0x%02x cid 0x%04x vid 0x%04x count %u", codec_cap->id, codec_cap->cid,
226 codec_cap->vid, codec_cap->data_len);
227
228 if (codec_cap->id == BT_HCI_CODING_FORMAT_LC3) {
229 bt_audio_data_parse(codec_cap->data, codec_cap->data_len, log_codec_cb, "data");
230 } else { /* If not LC3, we cannot assume it's LTV */
231 LOG_HEXDUMP_DBG(codec_cap->data, codec_cap->data_len, "data");
232 }
233
234 bt_audio_data_parse(codec_cap->meta, codec_cap->meta_len, log_codec_cb, "meta");
235 }
236
add_remote_sink(struct bt_bap_ep * ep)237 static void add_remote_sink(struct bt_bap_ep *ep)
238 {
239 if (peer.sink_ep == NULL) {
240 LOG_INF("Sink ep: %p", (void *)ep);
241 peer.sink_ep = ep;
242 return;
243 }
244 }
245
add_remote_source(struct bt_bap_ep * ep)246 static void add_remote_source(struct bt_bap_ep *ep)
247 {
248 if (peer.source_ep == NULL) {
249 LOG_INF("Source ep: %p", (void *)ep);
250 peer.source_ep = ep;
251 return;
252 }
253 }
254
discover_cb(struct bt_conn * conn,int err,enum bt_audio_dir dir)255 static void discover_cb(struct bt_conn *conn, int err, enum bt_audio_dir dir)
256 {
257 if (dir == BT_AUDIO_DIR_SINK) {
258 if (err != 0) {
259 LOG_ERR("Discovery sinks failed: %d", err);
260 } else {
261 LOG_INF("Discover sinks complete");
262 }
263 } else if (dir == BT_AUDIO_DIR_SOURCE) {
264 if (err != 0) {
265 LOG_ERR("Discovery sources failed: %d", err);
266 } else {
267 LOG_INF("Discover sources complete");
268 }
269 }
270
271 k_sem_give(&sem_proc);
272 }
273
pac_record_cb(struct bt_conn * conn,enum bt_audio_dir dir,const struct bt_audio_codec_cap * codec_cap)274 static void pac_record_cb(struct bt_conn *conn, enum bt_audio_dir dir,
275 const struct bt_audio_codec_cap *codec_cap)
276 {
277 log_codec(codec_cap, dir);
278 }
279
endpoint_cb(struct bt_conn * conn,enum bt_audio_dir dir,struct bt_bap_ep * ep)280 static void endpoint_cb(struct bt_conn *conn, enum bt_audio_dir dir, struct bt_bap_ep *ep)
281 {
282 if (dir == BT_AUDIO_DIR_SOURCE) {
283 add_remote_source(ep);
284 } else if (dir == BT_AUDIO_DIR_SINK) {
285 add_remote_sink(ep);
286 }
287 }
288
discover_sinks(void)289 static int discover_sinks(void)
290 {
291 int err;
292
293 LOG_INF("Discovering sink ASEs");
294 k_sem_reset(&sem_proc);
295
296 bt_cap_stream_ops_register(&peer.sink_stream, &unicast_stream_ops);
297
298 err = bt_bap_unicast_client_discover(peer.conn, BT_AUDIO_DIR_SINK);
299 if (err != 0) {
300 LOG_ERR("Failed to discover sink: %d", err);
301 return err;
302 }
303
304 err = k_sem_take(&sem_proc, SEM_TIMEOUT);
305 if (err != 0) {
306 LOG_ERR("Timeout on sinks discover: %d", err);
307 return err;
308 }
309
310 return err;
311 }
312
discover_sources(void)313 static int discover_sources(void)
314 {
315 int err;
316
317 LOG_INF("Discovering source ASEs");
318 k_sem_reset(&sem_proc);
319
320 bt_cap_stream_ops_register(&peer.source_stream, &unicast_stream_ops);
321
322 err = bt_bap_unicast_client_discover(peer.conn, BT_AUDIO_DIR_SOURCE);
323 if (err != 0) {
324 LOG_ERR("Failed to discover sources: %d", err);
325 return err;
326 }
327
328 err = k_sem_take(&sem_proc, SEM_TIMEOUT);
329 if (err != 0) {
330 LOG_ERR("Timeout on sources discover: %d", err);
331 return err;
332 }
333
334 return 0;
335 }
336
unicast_group_create(void)337 static int unicast_group_create(void)
338 {
339 struct bt_bap_unicast_group_stream_param source_stream_param = {
340 .qos = &unicast_preset_16_2_1.qos,
341 .stream = &peer.source_stream.bap_stream,
342 };
343 struct bt_bap_unicast_group_stream_param sink_stream_param = {
344 .qos = &unicast_preset_16_2_1.qos,
345 .stream = &peer.sink_stream.bap_stream,
346 };
347 struct bt_bap_unicast_group_stream_pair_param pair_params = {0};
348 struct bt_bap_unicast_group_param group_param = {0};
349 int err;
350
351 if (peer.source_ep != NULL) {
352 pair_params.rx_param = &source_stream_param;
353 }
354
355 if (peer.sink_ep != NULL) {
356 pair_params.tx_param = &sink_stream_param;
357 }
358
359 group_param.params_count = 1U;
360 group_param.params = &pair_params;
361
362 err = bt_bap_unicast_group_create(&group_param, &unicast_group);
363 if (err != 0) {
364 LOG_ERR("Failed to create group: %d", err);
365 return err;
366 }
367
368 LOG_INF("Created group");
369
370 return err;
371 }
372
unicast_group_delete(void)373 static int unicast_group_delete(void)
374 {
375 int err;
376
377 err = bt_bap_unicast_group_delete(unicast_group);
378 if (err != 0) {
379 LOG_ERR("Failed to delete group: %d", err);
380 return err;
381 }
382 unicast_group = NULL;
383
384 LOG_INF("Deleted group");
385
386 return err;
387 }
388
cap_discovery_complete_cb(struct bt_conn * conn,int err,const struct bt_csip_set_coordinator_set_member * member,const struct bt_csip_set_coordinator_csis_inst * csis_inst)389 static void cap_discovery_complete_cb(struct bt_conn *conn, int err,
390 const struct bt_csip_set_coordinator_set_member *member,
391 const struct bt_csip_set_coordinator_csis_inst *csis_inst)
392 {
393 if (err != 0) {
394 LOG_ERR("CAS discovery completed with error: %d", err);
395
396 return;
397 }
398
399 if (IS_ENABLED(CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER) && csis_inst != NULL) {
400 LOG_INF("Found CAS with CSIS %p", csis_inst);
401 /* TODO: Do set member discovery */
402 } else {
403 LOG_INF("Found CAS");
404 }
405
406 k_sem_give(&sem_proc);
407 }
408
unicast_start_complete_cb(int err,struct bt_conn * conn)409 static void unicast_start_complete_cb(int err, struct bt_conn *conn)
410 {
411 if (err != 0) {
412 LOG_ERR("Failed to start (failing conn %p): %d", (void *)conn, err);
413 return;
414 }
415
416 k_sem_give(&sem_proc);
417 }
418
discover_cas(void)419 static int discover_cas(void)
420 {
421 int err;
422
423 LOG_INF("Discovering CAS");
424 k_sem_reset(&sem_proc);
425
426 err = bt_cap_initiator_unicast_discover(peer.conn);
427 if (err != 0) {
428 LOG_ERR("Failed to discover CAS: %d", err);
429 return err;
430 }
431
432 err = k_sem_take(&sem_proc, SEM_TIMEOUT);
433 if (err != 0) {
434 LOG_ERR("Timeout on CAS discover: %d", err);
435 return err;
436 }
437
438 return err;
439 }
440
unicast_audio_start(void)441 static int unicast_audio_start(void)
442 {
443 /* TODO: Expand to start multiple streams on multiple CAP acceptors */
444 struct bt_cap_unicast_audio_start_stream_param stream_param[2] = {0};
445 struct bt_cap_unicast_audio_start_param param = {0};
446 int err;
447
448 LOG_INF("Starting streams");
449
450 if (peer.sink_ep != NULL) {
451 stream_param[param.count].member.member = peer.conn;
452 stream_param[param.count].stream = &peer.sink_stream;
453 stream_param[param.count].ep = peer.sink_ep;
454 stream_param[param.count].codec_cfg = &unicast_preset_16_2_1.codec_cfg;
455 param.count++;
456 }
457
458 if (peer.source_ep != NULL) {
459 stream_param[param.count].member.member = peer.conn;
460 stream_param[param.count].stream = &peer.source_stream;
461 stream_param[param.count].ep = peer.source_ep;
462 stream_param[param.count].codec_cfg = &unicast_preset_16_2_1.codec_cfg;
463 param.count++;
464 }
465
466 param.type = BT_CAP_SET_TYPE_AD_HOC;
467 param.stream_params = stream_param;
468
469 err = bt_cap_initiator_unicast_audio_start(¶m);
470 if (err != 0) {
471 LOG_ERR("Failed to start unicast audio: %d", err);
472 return err;
473 }
474
475 return err;
476 }
477
att_mtu_updated_cb(struct bt_conn * conn,uint16_t tx,uint16_t rx)478 static void att_mtu_updated_cb(struct bt_conn *conn, uint16_t tx, uint16_t rx)
479 {
480 LOG_INF("MTU exchanged: %u/%u", tx, rx);
481 k_sem_give(&sem_mtu_exchanged);
482 }
483
start_scan(void)484 static void start_scan(void)
485 {
486 int err;
487
488 err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
489 if (err != 0) {
490 LOG_ERR("Scanning failed to start: %d", err);
491 return;
492 }
493
494 LOG_INF("Scanning successfully started");
495 }
496
connected_cb(struct bt_conn * conn,uint8_t err)497 static void connected_cb(struct bt_conn *conn, uint8_t err)
498 {
499 char addr[BT_ADDR_LE_STR_LEN];
500
501 (void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
502
503 if (err != 0) {
504 LOG_ERR("Failed to connect to %s: %u", addr, err);
505
506 bt_conn_unref(peer.conn);
507 peer.conn = NULL;
508
509 start_scan();
510 return;
511 }
512
513 if (conn != peer.conn) {
514 return;
515 }
516
517 LOG_INF("Connected: %s", addr);
518 k_sem_give(&sem_state_change);
519 }
520
disconnected_cb(struct bt_conn * conn,uint8_t reason)521 static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
522 {
523 char addr[BT_ADDR_LE_STR_LEN];
524
525 if (conn != peer.conn) {
526 return;
527 }
528
529 (void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
530
531 LOG_INF("Disconnected: %s, reason 0x%02x %s", addr, reason, bt_hci_err_to_str(reason));
532
533 bt_conn_unref(peer.conn);
534 peer.conn = NULL;
535
536 k_sem_give(&sem_state_change);
537 }
538
security_changed_cb(struct bt_conn * conn,bt_security_t level,enum bt_security_err sec_err)539 static void security_changed_cb(struct bt_conn *conn, bt_security_t level,
540 enum bt_security_err sec_err)
541 {
542 if (sec_err == 0) {
543 LOG_INF("Security changed: %u", level);
544 k_sem_give(&sem_security_changed);
545 } else {
546 LOG_ERR("Failed to set security level: %s(%d)",
547 bt_security_err_to_str(sec_err), sec_err);
548
549 if (sec_err == BT_SECURITY_ERR_PIN_OR_KEY_MISSING) {
550 int err;
551
552 LOG_INF("Removing old key");
553 err = bt_unpair(BT_ID_DEFAULT, bt_conn_get_dst(conn));
554 if (err != 0) {
555 LOG_ERR("Failed to remove old key: %d", err);
556 }
557 }
558 }
559 }
560
561 BT_CONN_CB_DEFINE(conn_callbacks) = {
562 .connected = connected_cb,
563 .disconnected = disconnected_cb,
564 .security_changed = security_changed_cb,
565 };
566
check_audio_support_and_connect_cb(struct bt_data * data,void * user_data)567 static bool check_audio_support_and_connect_cb(struct bt_data *data, void *user_data)
568 {
569 char addr_str[BT_ADDR_LE_STR_LEN];
570 bt_addr_le_t *addr = user_data;
571 const struct bt_uuid *uuid;
572 uint16_t uuid_val;
573 int err;
574
575 LOG_DBG("[AD]: %u data_len %u", data->type, data->data_len);
576
577 if (data->type != BT_DATA_SVC_DATA16) {
578 return true; /* Continue parsing to next AD data type */
579 }
580
581 if (data->data_len < sizeof(uuid_val)) {
582 LOG_DBG("AD invalid size %u", data->data_len);
583 return true; /* Continue parsing to next AD data type */
584 }
585
586 /* We are looking for the CAS service data */
587 uuid_val = sys_get_le16(data->data);
588 uuid = BT_UUID_DECLARE_16(uuid_val);
589 if (bt_uuid_cmp(uuid, BT_UUID_CAS) != 0) {
590 return true; /* Continue parsing to next AD data type */
591 }
592
593 bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
594 LOG_INF("Attempt to connect to %s", addr_str);
595
596 err = bt_le_scan_stop();
597 if (err != 0) {
598 LOG_ERR("Failed to stop scan: %d", err);
599 return false;
600 }
601
602 err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &peer.conn);
603 if (err != 0) {
604 LOG_WRN("Create conn to failed: %d, restarting scan", err);
605 start_scan();
606 }
607
608 return false; /* Stop parsing */
609 }
610
scan_recv_cb(const struct bt_le_scan_recv_info * info,struct net_buf_simple * buf)611 static void scan_recv_cb(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf)
612 {
613 if (peer.conn != NULL) {
614 /* Already connected */
615 return;
616 }
617
618 /* Check for connectable, extended advertising */
619 if (((info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0) &&
620 ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE)) != 0) {
621 /* Check for TMAS support in advertising data */
622 bt_data_parse(buf, check_audio_support_and_connect_cb, (void *)info->addr);
623 }
624 }
625
scan_and_connect(void)626 static int scan_and_connect(void)
627 {
628 int err;
629
630 start_scan();
631
632 err = k_sem_take(&sem_state_change, K_FOREVER);
633 if (err != 0) {
634 LOG_ERR("Failed to take sem_state_change: %d", err);
635 return err;
636 }
637
638 return 0;
639 }
640
exchange_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_exchange_params * params)641 static void exchange_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params)
642 {
643 if (err == BT_ATT_ERR_SUCCESS) {
644 LOG_INF("MTU exchange done");
645 k_sem_give(&sem_proc);
646 } else {
647 LOG_ERR("MTU exchange failed: err %u", err);
648 }
649 }
650
exchange_mtu(void)651 static int exchange_mtu(void)
652 {
653 int err;
654
655 if (!IS_ENABLED(CONFIG_BT_GATT_AUTO_UPDATE_MTU)) {
656 static struct bt_gatt_exchange_params exchange_params = {
657 .func = exchange_cb,
658 };
659
660 LOG_INF("Exchanging MTU");
661
662 k_sem_reset(&sem_proc);
663
664 err = bt_gatt_exchange_mtu(peer.conn, &exchange_params);
665 if (err != 0) {
666 LOG_ERR("Failed to exchange MTU: %d", err);
667 }
668
669 err = k_sem_take(&sem_proc, SEM_TIMEOUT);
670 if (err != 0) {
671 LOG_ERR("Timeout on MTU exchange request: %d", err);
672 return err;
673 }
674 }
675
676 LOG_INF("Waiting for MTU exchange");
677 err = k_sem_take(&sem_mtu_exchanged, SEM_TIMEOUT);
678 if (err != 0) {
679 LOG_ERR("Timeout on MTU exchange: %d", err);
680 return err;
681 }
682
683 return 0;
684 }
685
update_security(void)686 static int update_security(void)
687 {
688 int err;
689
690 err = bt_conn_set_security(peer.conn, BT_SECURITY_L2);
691 if (err != 0) {
692 LOG_ERR("Failed to set security: %d", err);
693 return err;
694 }
695
696 LOG_INF("Waiting for security change");
697 err = k_sem_take(&sem_security_changed, SEM_TIMEOUT);
698 if (err != 0) {
699 LOG_ERR("Timeout on security: %d", err);
700 return err;
701 }
702
703 return 0;
704 }
705
init_cap_initiator(void)706 static int init_cap_initiator(void)
707 {
708 static struct bt_bap_unicast_client_cb unicast_client_cbs = {
709 .discover = discover_cb,
710 .pac_record = pac_record_cb,
711 .endpoint = endpoint_cb,
712 };
713 static struct bt_cap_initiator_cb cap_cb = {
714 .unicast_discovery_complete = cap_discovery_complete_cb,
715 .unicast_start_complete = unicast_start_complete_cb,
716 };
717 static struct bt_gatt_cb gatt_callbacks = {
718 .att_mtu_updated = att_mtu_updated_cb,
719 };
720 static struct bt_le_scan_cb scan_callbacks = {
721 .recv = scan_recv_cb,
722 };
723 int err;
724
725 err = bt_cap_initiator_register_cb(&cap_cb);
726 if (err != 0) {
727 LOG_ERR("Failed to register CAP callbacks: %d", err);
728
729 return err;
730 }
731
732 err = bt_bap_unicast_client_register_cb(&unicast_client_cbs);
733 if (err != 0) {
734 LOG_ERR("Failed to register BAP unicast client callbacks: %d", err);
735
736 return err;
737 }
738
739 bt_gatt_cb_register(&gatt_callbacks);
740 bt_le_scan_cb_register(&scan_callbacks);
741
742 k_sem_init(&peer.source_stream_sem, 0, 1);
743 k_sem_init(&peer.sink_stream_sem, 0, 1);
744
745 if (IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK)) {
746 cap_initiator_tx_init();
747 }
748
749 return 0;
750 }
751
reset_cap_initiator(void)752 static int reset_cap_initiator(void)
753 {
754 int err;
755
756 LOG_INF("Resetting");
757
758 if (peer.conn != NULL) {
759 err = bt_conn_disconnect(peer.conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
760 if (err != 0) {
761 return err;
762 }
763
764 err = k_sem_take(&sem_state_change, K_FOREVER);
765 if (err != 0) {
766 LOG_ERR("Timeout on disconnect: %d", err);
767 return err;
768 }
769 }
770
771 if (peer.source_stream.bap_stream.ep != NULL) {
772 err = k_sem_take(&peer.source_stream_sem, SEM_TIMEOUT);
773 if (err != 0) {
774 LOG_ERR("Timeout on source_stream_sem: %d", err);
775 return err;
776 }
777 }
778
779 if (peer.sink_stream.bap_stream.ep != NULL) {
780 err = k_sem_take(&peer.sink_stream_sem, SEM_TIMEOUT);
781 if (err != 0) {
782 LOG_ERR("Timeout on sink_stream_sem: %d", err);
783 return err;
784 }
785 }
786
787 if (unicast_group != NULL) {
788 int err;
789
790 err = unicast_group_delete();
791 if (err != 0) {
792 return err;
793 }
794
795 return err;
796 }
797
798 peer.source_ep = NULL;
799 peer.sink_ep = NULL;
800 k_sem_reset(&sem_proc);
801 k_sem_reset(&sem_state_change);
802 k_sem_reset(&sem_mtu_exchanged);
803
804 return 0;
805 }
806
cap_initiator_unicast(void)807 int cap_initiator_unicast(void)
808 {
809 int err;
810
811 err = init_cap_initiator();
812 if (err != 0) {
813 return err;
814 }
815
816 LOG_INF("CAP initiator unicast initialized");
817
818 while (true) {
819 err = reset_cap_initiator();
820 if (err != 0) {
821 LOG_ERR("Failed to reset");
822
823 return err;
824 }
825
826 /* Start scanning for and connecting to CAP Acceptors. CAP Acceptors are identified
827 * by their advertising data
828 */
829 err = scan_and_connect();
830 if (err != 0) {
831 continue;
832 }
833
834 /* BAP mandates support for an MTU of at least 65 octets. Because of that, we
835 * should always exchange the MTU before accessing BAP related services to ensure
836 * correctness
837 */
838 err = exchange_mtu();
839 if (err != 0) {
840 continue;
841 }
842
843 /* LE Audio services require encryption with LE Secure Connections, so we increase
844 * security before attempting to do any LE Audio operations
845 */
846 err = update_security();
847 if (err != 0) {
848 continue;
849 }
850
851 /* All remote CAP Acceptors shall have the Common Audio Service (CAS) so we start by
852 * discovering that on the remote device to determine if they are really CAP
853 * Acceptors. If they are only a BAP Unicast Server we ignore them.
854 */
855 err = discover_cas();
856 if (err != 0) {
857 continue;
858 }
859
860 /* Discover sink ASEs and capabilities. This may not result in any endpoints if they
861 * remote device is only a source (e.g. a microphone)
862 */
863 err = discover_sinks();
864 if (err != 0) {
865 continue;
866 }
867
868 /* Discover source ASEs and capabilities. This may not result in any endpoints if
869 * they remote device is only a sink (e.g. a speaker)
870 */
871 err = discover_sources();
872 if (err != 0) {
873 continue;
874 }
875
876 /* Create a unicast group (Connected Isochronous Group (CIG)) based on what we have
877 * found on the remote device
878 */
879 err = unicast_group_create();
880 if (err != 0) {
881 continue;
882 }
883
884 /* Execute the start operation which will take one or more streams into the
885 * streaming state
886 */
887 err = unicast_audio_start();
888 if (err != 0) {
889 continue;
890 }
891
892 /* Reset if disconnected */
893 err = k_sem_take(&sem_state_change, K_FOREVER);
894 if (err != 0) {
895 LOG_ERR("Failed to take sem_state_change: err %d", err);
896
897 return err;
898 }
899 }
900
901 return 0;
902 }
903