Skip to content

Commit f7cb0c5

Browse files
committed
samples: Bluetooth: CAP Handover
Add a CAP Handover sample that uses the CAP handover API as the CAP Initiator and CAP commander. Signed-off-by: Emil Gydesen <[email protected]>
1 parent 0f1f838 commit f7cb0c5

35 files changed

+2116
-36
lines changed

samples/bluetooth/cap_acceptor/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ config SAMPLE_UNICAST
2020

2121
config SAMPLE_BROADCAST
2222
bool "Whether or not to search for CAP acceptors for unicast audio"
23-
default y if !SAMPLE_UNICAST
23+
default y
2424
select BT_BAP_SCAN_DELEGATOR
2525
select BT_OBSERVER
2626
select BT_ISO_SYNC_RECEIVER

samples/bluetooth/cap_acceptor/prj.conf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ CONFIG_BT_ISO_MAX_CHAN=2
3333

3434
# Support long Metadata size for audio configuration
3535
CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE=24
36+
37+
CONFIG_BT_BAP_SCAN_DELEGATOR_LOG_LEVEL_DBG=y

samples/bluetooth/cap_acceptor/src/cap_acceptor.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
struct peer_config {
1919
/** Stream for the source endpoint */
2020
struct bt_cap_stream source_stream;
21-
/** Stream for the sink endpoint */
22-
struct bt_cap_stream sink_stream;
21+
/** Streams for the sink endpoint */
22+
struct bt_cap_stream sink_streams[CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT];
2323
/** Semaphore to help wait for a release operation if the source stream is not idle */
2424
struct k_sem source_stream_sem;
2525
/** Semaphore to help wait for a release operation if the sink stream is not idle */

samples/bluetooth/cap_acceptor/src/cap_acceptor_broadcast.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ static int check_start_scan(void)
100100
static void broadcast_stream_started_cb(struct bt_bap_stream *bap_stream)
101101
{
102102
LOG_INF("Started bap_stream %p", bap_stream);
103-
total_broadcast_rx_iso_packet_count = 0U;
104103

105104
atomic_clear_bit(flags, FLAG_BROADCAST_SYNCING);
106105
atomic_set_bit(flags, FLAG_BROADCAST_SYNCED);
@@ -482,6 +481,8 @@ static int bis_sync_req_cb(struct bt_conn *conn,
482481
}
483482

484483
if (broadcast_sink.requested_bis_sync == new_bis_sync_req) {
484+
LOG_INF("New request (0x%08x) is the same as last request; ignoring",
485+
bis_sync_req[0]);
485486
return 0; /* no op */
486487
}
487488

@@ -491,6 +492,8 @@ static int bis_sync_req_cb(struct bt_conn *conn,
491492
*/
492493
int err;
493494

495+
LOG_INF("Already synced. Stopping current sync and attempt resyncing");
496+
494497
/* The stream stopped callback will be called as part of this,
495498
* and we do not need to wait for any events from the
496499
* controller. Thus, when this returns, the broadcast sink is stopped

samples/bluetooth/cap_acceptor/src/cap_acceptor_unicast.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ static void unicast_stream_enabled_cb(struct bt_bap_stream *bap_stream)
296296
static void unicast_stream_started_cb(struct bt_bap_stream *bap_stream)
297297
{
298298
LOG_INF("Started bap_stream %p", bap_stream);
299-
total_unicast_rx_iso_packet_count = 0U;
300299
}
301300

302301
static void unicast_stream_metadata_updated_cb(struct bt_bap_stream *bap_stream)
@@ -438,7 +437,10 @@ int init_cap_acceptor_unicast(struct peer_config *peer)
438437
}
439438

440439
bt_cap_stream_ops_register(&peer->source_stream, &unicast_stream_ops);
441-
bt_cap_stream_ops_register(&peer->sink_stream, &unicast_stream_ops);
440+
441+
ARRAY_FOR_EACH_PTR(peer->sink_streams, stream) {
442+
bt_cap_stream_ops_register(stream, &unicast_stream_ops);
443+
}
442444

443445
if (IS_ENABLED(CONFIG_BT_ASCS_ASE_SRC)) {
444446
static bool thread_started;
@@ -459,5 +461,8 @@ int init_cap_acceptor_unicast(struct peer_config *peer)
459461
k_sem_init(&peer->source_stream_sem, 0, 1);
460462
k_sem_init(&peer->sink_stream_sem, 0, 1);
461463

464+
total_unicast_rx_iso_packet_count = 0U;
465+
total_unicast_tx_iso_packet_count = 0U;
466+
462467
return 0;
463468
}

samples/bluetooth/cap_acceptor/src/main.c

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <zephyr/kernel.h>
2727
#include <zephyr/logging/log.h>
2828
#include <zephyr/logging/log_core.h>
29+
#include <zephyr/sys/__assert.h>
2930
#include <zephyr/sys/util.h>
3031
#include <zephyr/sys/util_macro.h>
3132

@@ -140,8 +141,12 @@ static int advertise(void)
140141

141142
struct bt_cap_stream *stream_alloc(enum bt_audio_dir dir)
142143
{
143-
if (dir == BT_AUDIO_DIR_SINK && peer.sink_stream.bap_stream.ep == NULL) {
144-
return &peer.sink_stream;
144+
if (dir == BT_AUDIO_DIR_SINK) {
145+
ARRAY_FOR_EACH_PTR(peer.sink_streams, stream) {
146+
if (stream->bap_stream.ep == NULL) {
147+
return stream;
148+
}
149+
}
145150
} else if (dir == BT_AUDIO_DIR_SOURCE && peer.source_stream.bap_stream.ep == NULL) {
146151
return &peer.source_stream;
147152
}
@@ -153,8 +158,10 @@ void stream_released(const struct bt_cap_stream *cap_stream)
153158
{
154159
if (cap_stream == &peer.source_stream) {
155160
k_sem_give(&peer.source_stream_sem);
156-
} else if (cap_stream == &peer.sink_stream) {
161+
} else if (IS_ARRAY_ELEMENT(peer.sink_streams, cap_stream)) {
157162
k_sem_give(&peer.sink_stream_sem);
163+
} else {
164+
__ASSERT(false, "Invalid stream: %p", cap_stream);
158165
}
159166
}
160167

@@ -201,11 +208,13 @@ static int reset_cap_acceptor(void)
201208
}
202209
}
203210

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;
211+
ARRAY_FOR_EACH_PTR(peer.sink_streams, stream) {
212+
if (stream->bap_stream.ep != NULL) {
213+
err = k_sem_take(&peer.sink_stream_sem, SEM_TIMEOUT);
214+
if (err != 0) {
215+
LOG_ERR("Timeout on sink_stream_sem: %d", err);
216+
return err;
217+
}
209218
}
210219
}
211220

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
5+
project(cap_handover)
6+
7+
target_sources(app PRIVATE
8+
src/main.c
9+
src/cap_stream_tx.c
10+
)
11+
12+
zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright (c) 2024 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
mainmenu "Bluetooth: Common Audio Profile initiator sample"
5+
6+
config SAMPLE_STATIC_BROADCAST_ID
7+
bool "Use static broadcast ID"
8+
default y
9+
help
10+
Enabling this option will make the application use static broadcast ID, as opposed to a
11+
randomly generated one.
12+
13+
config SAMPLE_BROADCAST_ID
14+
hex "The static broadcast ID to use"
15+
range 0x000000 0xFFFFFF
16+
depends on STATIC_BROADCAST_ID
17+
default 0x123456
18+
help
19+
This is the 3-octet broadcast ID advertised if static broadcast IDs are enabled.
20+
21+
source "Kconfig.zephyr"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2023 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
source "share/sysbuild/Kconfig"
5+
6+
config NET_CORE_BOARD
7+
string
8+
default "nrf5340dk/nrf5340/cpunet" if "$(BOARD)" = "nrf5340dk"
9+
default "nrf5340_audio_dk/nrf5340/cpunet" if "$(BOARD)" = "nrf5340_audio_dk"
10+
default "nrf5340bsim/nrf5340/cpunet" if $(BOARD_TARGET_STRING) = "NRF5340BSIM_NRF5340_CPUAPP"
11+
12+
config NET_CORE_IMAGE_HCI_IPC
13+
bool "HCI IPC image on network core"
14+
default y
15+
depends on NET_CORE_BOARD != ""
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
.. zephyr:code-sample:: bluetooth_cap_handover
2+
:name: Common Audio Profile (CAP) Handover
3+
:relevant-api: bluetooth bt_bap bt_cap bt_conn
4+
5+
Connects to a CAP acceptor and performs CAP handover procedures
6+
7+
Overview
8+
********
9+
10+
Application demonstrating the CAP handover functionality.
11+
Starts by scanning for a CAP Acceptor then sets up unicast audio, and then switches between unicast
12+
and broadcast using the CAP handover procedures.
13+
14+
This sample can be found under :zephyr_file:`samples/bluetooth/cap_handover` in the Zephyr tree.
15+
16+
Check the :zephyr:code-sample-category:`bluetooth` samples for general information.
17+
18+
Requirements
19+
************
20+
21+
* BlueZ running on the host, or
22+
* A board with Bluetooth Low Energy 5.2 support
23+
24+
Building and Running
25+
********************
26+
27+
When building targeting an nrf52 series board with the Zephyr Bluetooth Controller,
28+
use ``-DEXTRA_CONF_FILE=overlay-bt_ll_sw_split.conf`` to enable the required ISO
29+
feature support.
30+
31+
Building for an nrf5340dk
32+
-------------------------
33+
34+
You can build both the application core image and an appropriate controller image for the network
35+
core with:
36+
37+
.. zephyr-app-commands::
38+
:zephyr-app: samples/bluetooth/cap_handover/
39+
:board: nrf5340dk/nrf5340/cpuapp
40+
:goals: build
41+
:west-args: --sysbuild
42+
43+
If you prefer to only build the application core image, you can do so by doing instead:
44+
45+
.. zephyr-app-commands::
46+
:zephyr-app: samples/bluetooth/cap_handover/
47+
:board: nrf5340dk/nrf5340/cpuapp
48+
:goals: build
49+
50+
In that case you can pair this application core image with the
51+
:zephyr:code-sample:`bluetooth_hci_ipc` sample
52+
:zephyr_file:`samples/bluetooth/hci_ipc/nrf5340_cpunet_iso-bt_ll_sw_split.conf` configuration.
53+
54+
Building for a simulated nrf5340bsim
55+
------------------------------------
56+
57+
Similarly to how you would for real HW, you can do:
58+
59+
.. zephyr-app-commands::
60+
:zephyr-app: samples/bluetooth/cap_handover/
61+
:board: nrf5340bsim/nrf5340/cpuapp
62+
:goals: build
63+
:west-args: --sysbuild
64+
65+
Note this will produce a Linux executable in :file:`./build/zephyr/zephyr.exe`.
66+
For more information, check :ref:`this board documentation <nrf5340bsim>`.
67+
68+
Building for a simulated nrf52_bsim
69+
-----------------------------------
70+
71+
.. zephyr-app-commands::
72+
:zephyr-app: samples/bluetooth/cap_handover/
73+
:board: nrf52_bsim
74+
:goals: build
75+
:gen-args: -DEXTRA_CONF_FILE=overlay-bt_ll_sw_split.conf

0 commit comments

Comments
 (0)