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_BAP_CONN_PARAM_RELAXED,
603 &peer.conn);
604 if (err != 0) {
605 LOG_WRN("Create conn to failed: %d, restarting scan", err);
606 start_scan();
607 }
608
609 return false; /* Stop parsing */
610 }
611
scan_recv_cb(const struct bt_le_scan_recv_info * info,struct net_buf_simple * buf)612 static void scan_recv_cb(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf)
613 {
614 if (peer.conn != NULL) {
615 /* Already connected */
616 return;
617 }
618
619 /* Check for connectable, extended advertising */
620 if (((info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0) &&
621 ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE)) != 0) {
622 /* Check for TMAS support in advertising data */
623 bt_data_parse(buf, check_audio_support_and_connect_cb, (void *)info->addr);
624 }
625 }
626
scan_and_connect(void)627 static int scan_and_connect(void)
628 {
629 int err;
630
631 start_scan();
632
633 err = k_sem_take(&sem_state_change, K_FOREVER);
634 if (err != 0) {
635 LOG_ERR("Failed to take sem_state_change: %d", err);
636 return err;
637 }
638
639 return 0;
640 }
641
exchange_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_exchange_params * params)642 static void exchange_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params)
643 {
644 if (err == BT_ATT_ERR_SUCCESS) {
645 LOG_INF("MTU exchange done");
646 k_sem_give(&sem_proc);
647 } else {
648 LOG_ERR("MTU exchange failed: err %u", err);
649 }
650 }
651
exchange_mtu(void)652 static int exchange_mtu(void)
653 {
654 int err;
655
656 if (!IS_ENABLED(CONFIG_BT_GATT_AUTO_UPDATE_MTU)) {
657 static struct bt_gatt_exchange_params exchange_params = {
658 .func = exchange_cb,
659 };
660
661 LOG_INF("Exchanging MTU");
662
663 k_sem_reset(&sem_proc);
664
665 err = bt_gatt_exchange_mtu(peer.conn, &exchange_params);
666 if (err != 0) {
667 LOG_ERR("Failed to exchange MTU: %d", err);
668 }
669
670 err = k_sem_take(&sem_proc, SEM_TIMEOUT);
671 if (err != 0) {
672 LOG_ERR("Timeout on MTU exchange request: %d", err);
673 return err;
674 }
675 }
676
677 LOG_INF("Waiting for MTU exchange");
678 err = k_sem_take(&sem_mtu_exchanged, SEM_TIMEOUT);
679 if (err != 0) {
680 LOG_ERR("Timeout on MTU exchange: %d", err);
681 return err;
682 }
683
684 return 0;
685 }
686
update_security(void)687 static int update_security(void)
688 {
689 int err;
690
691 err = bt_conn_set_security(peer.conn, BT_SECURITY_L2);
692 if (err != 0) {
693 LOG_ERR("Failed to set security: %d", err);
694 return err;
695 }
696
697 LOG_INF("Waiting for security change");
698 err = k_sem_take(&sem_security_changed, SEM_TIMEOUT);
699 if (err != 0) {
700 LOG_ERR("Timeout on security: %d", err);
701 return err;
702 }
703
704 return 0;
705 }
706
init_cap_initiator(void)707 static int init_cap_initiator(void)
708 {
709 static struct bt_bap_unicast_client_cb unicast_client_cbs = {
710 .discover = discover_cb,
711 .pac_record = pac_record_cb,
712 .endpoint = endpoint_cb,
713 };
714 static struct bt_cap_initiator_cb cap_cb = {
715 .unicast_discovery_complete = cap_discovery_complete_cb,
716 .unicast_start_complete = unicast_start_complete_cb,
717 };
718 static struct bt_gatt_cb gatt_callbacks = {
719 .att_mtu_updated = att_mtu_updated_cb,
720 };
721 static struct bt_le_scan_cb scan_callbacks = {
722 .recv = scan_recv_cb,
723 };
724 int err;
725
726 err = bt_cap_initiator_register_cb(&cap_cb);
727 if (err != 0) {
728 LOG_ERR("Failed to register CAP callbacks: %d", err);
729
730 return err;
731 }
732
733 err = bt_bap_unicast_client_register_cb(&unicast_client_cbs);
734 if (err != 0) {
735 LOG_ERR("Failed to register BAP unicast client callbacks: %d", err);
736
737 return err;
738 }
739
740 bt_gatt_cb_register(&gatt_callbacks);
741 bt_le_scan_cb_register(&scan_callbacks);
742
743 k_sem_init(&peer.source_stream_sem, 0, 1);
744 k_sem_init(&peer.sink_stream_sem, 0, 1);
745
746 if (IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK)) {
747 cap_initiator_tx_init();
748 }
749
750 return 0;
751 }
752
reset_cap_initiator(void)753 static int reset_cap_initiator(void)
754 {
755 int err;
756
757 LOG_INF("Resetting");
758
759 if (peer.conn != NULL) {
760 err = bt_conn_disconnect(peer.conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
761 if (err != 0) {
762 return err;
763 }
764
765 err = k_sem_take(&sem_state_change, K_FOREVER);
766 if (err != 0) {
767 LOG_ERR("Timeout on disconnect: %d", err);
768 return err;
769 }
770 }
771
772 if (peer.source_stream.bap_stream.ep != NULL) {
773 err = k_sem_take(&peer.source_stream_sem, SEM_TIMEOUT);
774 if (err != 0) {
775 LOG_ERR("Timeout on source_stream_sem: %d", err);
776 return err;
777 }
778 }
779
780 if (peer.sink_stream.bap_stream.ep != NULL) {
781 err = k_sem_take(&peer.sink_stream_sem, SEM_TIMEOUT);
782 if (err != 0) {
783 LOG_ERR("Timeout on sink_stream_sem: %d", err);
784 return err;
785 }
786 }
787
788 if (unicast_group != NULL) {
789 int err;
790
791 err = unicast_group_delete();
792 if (err != 0) {
793 return err;
794 }
795
796 return err;
797 }
798
799 peer.source_ep = NULL;
800 peer.sink_ep = NULL;
801 k_sem_reset(&sem_proc);
802 k_sem_reset(&sem_state_change);
803 k_sem_reset(&sem_mtu_exchanged);
804
805 return 0;
806 }
807
cap_initiator_unicast(void)808 int cap_initiator_unicast(void)
809 {
810 int err;
811
812 err = init_cap_initiator();
813 if (err != 0) {
814 return err;
815 }
816
817 LOG_INF("CAP initiator unicast initialized");
818
819 while (true) {
820 err = reset_cap_initiator();
821 if (err != 0) {
822 LOG_ERR("Failed to reset");
823
824 return err;
825 }
826
827 /* Start scanning for and connecting to CAP Acceptors. CAP Acceptors are identified
828 * by their advertising data
829 */
830 err = scan_and_connect();
831 if (err != 0) {
832 continue;
833 }
834
835 /* BAP mandates support for an MTU of at least 65 octets. Because of that, we
836 * should always exchange the MTU before accessing BAP related services to ensure
837 * correctness
838 */
839 err = exchange_mtu();
840 if (err != 0) {
841 continue;
842 }
843
844 /* LE Audio services require encryption with LE Secure Connections, so we increase
845 * security before attempting to do any LE Audio operations
846 */
847 err = update_security();
848 if (err != 0) {
849 continue;
850 }
851
852 /* All remote CAP Acceptors shall have the Common Audio Service (CAS) so we start by
853 * discovering that on the remote device to determine if they are really CAP
854 * Acceptors. If they are only a BAP Unicast Server we ignore them.
855 */
856 err = discover_cas();
857 if (err != 0) {
858 continue;
859 }
860
861 /* Discover sink ASEs and capabilities. This may not result in any endpoints if they
862 * remote device is only a source (e.g. a microphone)
863 */
864 err = discover_sinks();
865 if (err != 0) {
866 continue;
867 }
868
869 /* Discover source ASEs and capabilities. This may not result in any endpoints if
870 * they remote device is only a sink (e.g. a speaker)
871 */
872 err = discover_sources();
873 if (err != 0) {
874 continue;
875 }
876
877 /* Create a unicast group (Connected Isochronous Group (CIG)) based on what we have
878 * found on the remote device
879 */
880 err = unicast_group_create();
881 if (err != 0) {
882 continue;
883 }
884
885 /* Execute the start operation which will take one or more streams into the
886 * streaming state
887 */
888 err = unicast_audio_start();
889 if (err != 0) {
890 continue;
891 }
892
893 /* Reset if disconnected */
894 err = k_sem_take(&sem_state_change, K_FOREVER);
895 if (err != 0) {
896 LOG_ERR("Failed to take sem_state_change: err %d", err);
897
898 return err;
899 }
900 }
901
902 return 0;
903 }
904