From d5499e6b87f1e66cfc439507def593f8c2bfaf06 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Mon, 18 Jul 2022 10:34:13 +0200 Subject: [PATCH] Bluetooth: Audio: Add CAP stream Add a new stream object, bt_cap_stream, which is an extension of the BAP bt_audio_stream. The purpose of this stream is that we can extend the data stored in the BAP stream for CAP usage, as well as making it more explicit what type of stream should be used for CAP. The callbacks will be extended for CAP in specific use cases, e.g. when starting one or more unicast audio streams. Signed-off-by: Emil Gydesen --- include/zephyr/bluetooth/audio/cap.h | 19 ++- subsys/bluetooth/audio/CMakeLists.txt | 1 + subsys/bluetooth/audio/Kconfig.cap | 3 + subsys/bluetooth/audio/cap_stream.c | 163 ++++++++++++++++++++++++++ 4 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 subsys/bluetooth/audio/cap_stream.c diff --git a/include/zephyr/bluetooth/audio/cap.h b/include/zephyr/bluetooth/audio/cap.h index 2782b5fac12..41771d8d65e 100644 --- a/include/zephyr/bluetooth/audio/cap.h +++ b/include/zephyr/bluetooth/audio/cap.h @@ -136,6 +136,21 @@ union bt_cap_set_member { struct bt_csis_client_set_member *csip; }; +struct bt_cap_stream { + struct bt_audio_stream bap_stream; + struct bt_audio_stream_ops *ops; +}; + +/** @brief Register Audio operations for a Common Audio Profile stream. + * + * Register Audio operations for a stream. + * + * @param stream Stream object. + * @param ops Stream operations structure. + */ +void bt_cap_stream_ops_register(struct bt_cap_stream *stream, + struct bt_audio_stream_ops *ops); + struct bt_cap_unicast_audio_start_param { /** The type of the set. */ enum bt_cap_set_type type; @@ -151,7 +166,7 @@ struct bt_cap_unicast_audio_start_param { * stream[i] will be associated with members[i] if not already * initialized, else the stream will be verified against the member. */ - struct bt_audio_stream **streams; + struct bt_cap_stream **streams; /** * @brief Codec configuration. @@ -233,7 +248,7 @@ struct bt_cap_broadcast_audio_start_param { size_t count; /** Streams for broadcast source. */ - struct bt_audio_stream **streams; + struct bt_cap_stream **streams; /** * @brief Codec configuration. diff --git a/subsys/bluetooth/audio/CMakeLists.txt b/subsys/bluetooth/audio/CMakeLists.txt index 3bfb897f564..a7c4aaf6ba2 100644 --- a/subsys/bluetooth/audio/CMakeLists.txt +++ b/subsys/bluetooth/audio/CMakeLists.txt @@ -57,5 +57,6 @@ zephyr_library_sources_ifdef(CONFIG_BT_BASS bass.c) zephyr_library_sources_ifdef(CONFIG_BT_BASS_CLIENT bass_client.c) zephyr_library_sources_ifdef(CONFIG_BT_HAS has.c) zephyr_library_sources_ifdef(CONFIG_BT_HAS_CLIENT has_client.c) +zephyr_library_sources_ifdef(CONFIG_BT_CAP cap_stream.c) zephyr_library_sources_ifdef(CONFIG_BT_CAP_ACCEPTOR cap_acceptor.c) zephyr_library_sources_ifdef(CONFIG_BT_CAP_INITIATOR cap_initiator.c) diff --git a/subsys/bluetooth/audio/Kconfig.cap b/subsys/bluetooth/audio/Kconfig.cap index 71d7f586670..7232de3fefc 100644 --- a/subsys/bluetooth/audio/Kconfig.cap +++ b/subsys/bluetooth/audio/Kconfig.cap @@ -5,6 +5,9 @@ # SPDX-License-Identifier: Apache-2.0 # +config BT_CAP + def_bool BT_CAP_ACCEPTOR || BT_CAP_INITIATOR + config BT_CAP_ACCEPTOR bool "Common Audio Profile Acceptor Role Support [EXPERIMENTAL]" depends on BT_AUDIO_UNICAST_SERVER || (BT_AUDIO_BROADCAST_SINK && BT_BASS) diff --git a/subsys/bluetooth/audio/cap_stream.c b/subsys/bluetooth/audio/cap_stream.c new file mode 100644 index 00000000000..a40f6c5ae56 --- /dev/null +++ b/subsys/bluetooth/audio/cap_stream.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#if defined(CONFIG_BT_AUDIO_UNICAST) +static void cap_stream_configured_cb(struct bt_audio_stream *bap_stream, + const struct bt_codec_qos_pref *pref) +{ + struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream, + struct bt_cap_stream, + bap_stream); + struct bt_audio_stream_ops *ops = cap_stream->ops; + + if (ops != NULL && ops->configured != NULL) { + ops->configured(bap_stream, pref); + } +} + +static void cap_stream_qos_set_cb(struct bt_audio_stream *bap_stream) +{ + struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream, + struct bt_cap_stream, + bap_stream); + struct bt_audio_stream_ops *ops = cap_stream->ops; + + if (ops != NULL && ops->qos_set != NULL) { + ops->qos_set(bap_stream); + } +} + +static void cap_stream_enabled_cb(struct bt_audio_stream *bap_stream) +{ + struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream, + struct bt_cap_stream, + bap_stream); + struct bt_audio_stream_ops *ops = cap_stream->ops; + + if (ops != NULL && ops->enabled != NULL) { + ops->enabled(bap_stream); + } +} + +static void cap_stream_metadata_updated_cb(struct bt_audio_stream *bap_stream) +{ + struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream, + struct bt_cap_stream, + bap_stream); + struct bt_audio_stream_ops *ops = cap_stream->ops; + + if (ops != NULL && ops->metadata_updated != NULL) { + ops->metadata_updated(bap_stream); + } +} + +static void cap_stream_disabled_cb(struct bt_audio_stream *bap_stream) +{ + struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream, + struct bt_cap_stream, + bap_stream); + struct bt_audio_stream_ops *ops = cap_stream->ops; + + if (ops != NULL && ops->disabled != NULL) { + ops->disabled(bap_stream); + } +} + +static void cap_stream_released_cb(struct bt_audio_stream *bap_stream) +{ + struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream, + struct bt_cap_stream, + bap_stream); + struct bt_audio_stream_ops *ops = cap_stream->ops; + + if (ops != NULL && ops->released != NULL) { + ops->released(bap_stream); + } +} + +#endif /* CONFIG_BT_AUDIO_UNICAST */ + +static void cap_stream_started_cb(struct bt_audio_stream *bap_stream) +{ + struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream, + struct bt_cap_stream, + bap_stream); + struct bt_audio_stream_ops *ops = cap_stream->ops; + + if (ops != NULL && ops->started != NULL) { + ops->started(bap_stream); + } +} + +static void cap_stream_stopped_cb(struct bt_audio_stream *bap_stream) +{ + struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream, + struct bt_cap_stream, + bap_stream); + struct bt_audio_stream_ops *ops = cap_stream->ops; + + if (ops != NULL && ops->stopped != NULL) { + ops->stopped(bap_stream); + } +} + +#if defined(CONFIG_BT_AUDIO_UNICAST) || defined(CONFIG_BT_AUDIO_BROADCAST_SINK) +static void cap_stream_recv_cb(struct bt_audio_stream *bap_stream, + const struct bt_iso_recv_info *info, + struct net_buf *buf) +{ + struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream, + struct bt_cap_stream, + bap_stream); + struct bt_audio_stream_ops *ops = cap_stream->ops; + + if (ops != NULL && ops->recv != NULL) { + ops->recv(bap_stream, info, buf); + } +} +#endif /* CONFIG_BT_AUDIO_UNICAST || CONFIG_BT_AUDIO_BROADCAST_SINK */ + +#if defined(CONFIG_BT_AUDIO_UNICAST) || defined(CONFIG_BT_AUDIO_BROADCAST_SOURCE) +static void cap_stream_sent_cb(struct bt_audio_stream *bap_stream) +{ + struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream, + struct bt_cap_stream, + bap_stream); + struct bt_audio_stream_ops *ops = cap_stream->ops; + + if (ops != NULL && ops->sent != NULL) { + ops->sent(bap_stream); + } +} +#endif /* CONFIG_BT_AUDIO_UNICAST || CONFIG_BT_AUDIO_BROADCAST_SOURCE */ + +static struct bt_audio_stream_ops bap_stream_ops = { +#if defined(CONFIG_BT_AUDIO_UNICAST) + .configured = cap_stream_configured_cb, + .qos_set = cap_stream_qos_set_cb, + .enabled = cap_stream_enabled_cb, + .metadata_updated = cap_stream_metadata_updated_cb, + .disabled = cap_stream_disabled_cb, + .released = cap_stream_released_cb, +#endif /* CONFIG_BT_AUDIO_UNICAST */ + .started = cap_stream_started_cb, + .stopped = cap_stream_stopped_cb, +#if defined(CONFIG_BT_AUDIO_UNICAST) || defined(CONFIG_BT_AUDIO_BROADCAST_SINK) + .recv = cap_stream_recv_cb, +#endif /* CONFIG_BT_AUDIO_UNICAST || CONFIG_BT_AUDIO_BROADCAST_SINK */ +#if defined(CONFIG_BT_AUDIO_UNICAST) || defined(CONFIG_BT_AUDIO_BROADCAST_SOURCE) + .sent = cap_stream_sent_cb, +#endif /* CONFIG_BT_AUDIO_UNICAST || CONFIG_BT_AUDIO_BROADCAST_SOURCE */ +}; + +void bt_cap_stream_ops_register(struct bt_cap_stream *stream, + struct bt_audio_stream_ops *ops) +{ + stream->ops = ops; + bt_audio_stream_cb_register(&stream->bap_stream, &bap_stream_ops); +}