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 <emil.gydesen@nordicsemi.no>
This commit is contained in:
Emil Gydesen 2022-07-18 10:34:13 +02:00 committed by Carles Cufí
commit d5499e6b87
4 changed files with 184 additions and 2 deletions

View file

@ -136,6 +136,21 @@ union bt_cap_set_member {
struct bt_csis_client_set_member *csip; 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 { struct bt_cap_unicast_audio_start_param {
/** The type of the set. */ /** The type of the set. */
enum bt_cap_set_type type; 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 * stream[i] will be associated with members[i] if not already
* initialized, else the stream will be verified against the member. * initialized, else the stream will be verified against the member.
*/ */
struct bt_audio_stream **streams; struct bt_cap_stream **streams;
/** /**
* @brief Codec configuration. * @brief Codec configuration.
@ -233,7 +248,7 @@ struct bt_cap_broadcast_audio_start_param {
size_t count; size_t count;
/** Streams for broadcast source. */ /** Streams for broadcast source. */
struct bt_audio_stream **streams; struct bt_cap_stream **streams;
/** /**
* @brief Codec configuration. * @brief Codec configuration.

View file

@ -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_BASS_CLIENT bass_client.c)
zephyr_library_sources_ifdef(CONFIG_BT_HAS has.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_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_ACCEPTOR cap_acceptor.c)
zephyr_library_sources_ifdef(CONFIG_BT_CAP_INITIATOR cap_initiator.c) zephyr_library_sources_ifdef(CONFIG_BT_CAP_INITIATOR cap_initiator.c)

View file

@ -5,6 +5,9 @@
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# #
config BT_CAP
def_bool BT_CAP_ACCEPTOR || BT_CAP_INITIATOR
config BT_CAP_ACCEPTOR config BT_CAP_ACCEPTOR
bool "Common Audio Profile Acceptor Role Support [EXPERIMENTAL]" bool "Common Audio Profile Acceptor Role Support [EXPERIMENTAL]"
depends on BT_AUDIO_UNICAST_SERVER || (BT_AUDIO_BROADCAST_SINK && BT_BASS) depends on BT_AUDIO_UNICAST_SERVER || (BT_AUDIO_BROADCAST_SINK && BT_BASS)

View file

@ -0,0 +1,163 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/audio/cap.h>
#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);
}