Bluetooth: audio: Factor out bt_audio_iso pool

This adds common bt_audio_iso pool that will be used across all the
roles/profiles. The pool range is dependent on the CONFIG_BT_ISO_MAX_CHAN
which is the maximum number of ISO connections the host can maintain.

Signed-off-by: Mariusz Skamra <mariusz.skamra@codecoup.pl>
This commit is contained in:
Mariusz Skamra 2022-10-21 14:35:10 +02:00 committed by Fabio Baltieri
commit 3fa456905d
13 changed files with 814 additions and 713 deletions

View file

@ -1297,8 +1297,6 @@ struct bt_audio_stream {
const struct bt_codec *codec; const struct bt_codec *codec;
/** QoS Configuration */ /** QoS Configuration */
struct bt_codec_qos *qos; struct bt_codec_qos *qos;
/** ISO channel reference */
struct bt_iso_chan *iso;
/** Audio stream operations */ /** Audio stream operations */
struct bt_audio_stream_ops *ops; struct bt_audio_stream_ops *ops;

View file

@ -1,7 +1,10 @@
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
zephyr_library() zephyr_library()
zephyr_library_sources(audio.c) zephyr_library_sources(
audio.c
audio_iso.c
)
if (CONFIG_BT_VOCS OR CONFIG_BT_VOCS_CLIENT) if (CONFIG_BT_VOCS OR CONFIG_BT_VOCS_CLIENT)
zephyr_library_sources(vocs.c) zephyr_library_sources(vocs.c)

View file

@ -32,6 +32,7 @@
#include "../host/conn_internal.h" #include "../host/conn_internal.h"
#include "audio_internal.h" #include "audio_internal.h"
#include "audio_iso.h"
#include "endpoint.h" #include "endpoint.h"
#include "unicast_server.h" #include "unicast_server.h"
#include "pacs_internal.h" #include "pacs_internal.h"
@ -54,10 +55,6 @@ struct bt_ascs_ase {
struct bt_ascs { struct bt_ascs {
struct bt_conn *conn; struct bt_conn *conn;
struct bt_ascs_ase ases[ASE_COUNT]; struct bt_ascs_ase ases[ASE_COUNT];
/* A single iso_channel may be used for 1 or 2 ases.
* Controlled by the client.
*/
struct bt_audio_iso isos[ASE_COUNT];
}; };
static struct bt_ascs sessions[CONFIG_BT_MAX_CONN]; static struct bt_ascs sessions[CONFIG_BT_MAX_CONN];
@ -70,33 +67,6 @@ static void ase_status_changed(struct bt_audio_ep *ep, uint8_t old_state,
k_work_submit(&ep->work); k_work_submit(&ep->work);
} }
static void ascs_ep_unbind_audio_iso(struct bt_audio_ep *ep)
{
struct bt_audio_iso *audio_iso = ep->iso;
struct bt_audio_stream *stream = ep->stream;
if (audio_iso != NULL) {
struct bt_iso_chan_qos *qos;
qos = audio_iso->iso_chan.qos;
BT_ASSERT_MSG(stream != NULL, "Stream was NULL");
stream->iso = NULL;
if (audio_iso->sink_stream == stream) {
audio_iso->sink_stream = NULL;
qos->rx = NULL;
} else if (audio_iso->source_stream == stream) {
audio_iso->source_stream = NULL;
qos->tx = NULL;
} else {
BT_ERR("Stream %p not linked to audio_iso %p", stream, audio_iso);
}
}
ep->iso = NULL;
}
void ascs_ep_set_state(struct bt_audio_ep *ep, uint8_t state) void ascs_ep_set_state(struct bt_audio_ep *ep, uint8_t state)
{ {
struct bt_audio_stream *stream; struct bt_audio_stream *stream;
@ -290,12 +260,6 @@ void ascs_ep_set_state(struct bt_audio_ep *ep, uint8_t state)
break; break;
} }
} }
if (state_changed &&
state == BT_AUDIO_EP_STATE_CODEC_CONFIGURED &&
old_state != BT_AUDIO_EP_STATE_IDLE) {
ascs_ep_unbind_audio_iso(ep);
}
} }
static void ascs_codec_data_add(struct net_buf_simple *buf, const char *prefix, static void ascs_codec_data_add(struct net_buf_simple *buf, const char *prefix,
@ -438,29 +402,26 @@ static void ascs_iso_recv(struct bt_iso_chan *chan,
const struct bt_iso_recv_info *info, const struct bt_iso_recv_info *info,
struct net_buf *buf) struct net_buf *buf)
{ {
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan);
iso_chan);
struct bt_audio_stream *stream = audio_iso->sink_stream;
const struct bt_audio_stream_ops *ops; const struct bt_audio_stream_ops *ops;
struct bt_audio_stream *stream;
struct bt_audio_ep *ep;
if (stream == NULL) { ep = iso->rx.ep;
BT_ERR("Could not lookup stream by iso %p", chan); if (ep == NULL) {
return; BT_ERR("iso %p not bound with ep", chan);
} else if (stream->ep == NULL) {
BT_ERR("Stream not associated with an ep");
return; return;
} }
/* Since 2 streams can share the same CIS, the CIS may be connected and if (ep->status.state != BT_AUDIO_EP_STATE_STREAMING) {
* capable of transferring data, without the bt_audio_stream being in BT_DBG("ep %p is not in the streaming state: %s",
* the streaming state. In that case we simply ignore the data. ep, bt_audio_ep_state_str(ep->status.state));
*/ return;
if (stream->ep->status.state != BT_AUDIO_EP_STATE_STREAMING) { }
if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) {
BT_DBG("Stream %p is not in the streaming state: %u",
stream, stream->ep->status.state);
}
stream = ep->stream;
if (stream == NULL) {
BT_ERR("No stream for ep %p", ep);
return; return;
} }
@ -468,7 +429,7 @@ static void ascs_iso_recv(struct bt_iso_chan *chan,
if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) { if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) {
BT_DBG("stream %p ep %p len %zu", BT_DBG("stream %p ep %p len %zu",
stream, stream->ep, net_buf_frags_len(buf)); stream, ep, net_buf_frags_len(buf));
} }
if (ops != NULL && ops->recv != NULL) { if (ops != NULL && ops->recv != NULL) {
@ -480,13 +441,27 @@ static void ascs_iso_recv(struct bt_iso_chan *chan,
static void ascs_iso_sent(struct bt_iso_chan *chan) static void ascs_iso_sent(struct bt_iso_chan *chan)
{ {
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan);
iso_chan); const struct bt_audio_stream_ops *ops;
struct bt_audio_stream *stream = audio_iso->source_stream; struct bt_audio_stream *stream;
struct bt_audio_stream_ops *ops = stream->ops; struct bt_audio_ep *ep;
ep = iso->tx.ep;
if (ep == NULL) {
BT_ERR("iso %p not bound with ep", chan);
return;
}
stream = ep->stream;
if (stream == NULL) {
BT_ERR("No stream for ep %p", ep);
return;
}
ops = stream->ops;
if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) { if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) {
BT_DBG("stream %p ep %p", stream, stream->ep); BT_DBG("stream %p ep %p", stream, ep);
} }
if (ops != NULL && ops->sent != NULL) { if (ops != NULL && ops->sent != NULL) {
@ -509,39 +484,14 @@ static int ase_stream_start(struct bt_audio_stream *stream)
return err; return err;
} }
static void ascs_iso_connected(struct bt_iso_chan *chan) static void ascs_ep_iso_connected(struct bt_audio_ep *ep)
{ {
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
iso_chan);
struct bt_audio_stream *source_stream = audio_iso->source_stream;
struct bt_audio_stream *sink_stream = audio_iso->sink_stream;
struct bt_audio_stream *stream; struct bt_audio_stream *stream;
struct bt_audio_ep *ep;
int err; int err;
if (sink_stream != NULL && sink_stream->iso == chan) {
stream = sink_stream;
} else if (source_stream != NULL && source_stream->iso == chan) {
stream = source_stream;
} else {
stream = NULL;
}
if (stream == NULL) {
BT_ERR("Could not lookup stream by iso %p", chan);
return;
} else if (stream->ep == NULL) {
BT_ERR("Stream not associated with an ep");
return;
}
ep = stream->ep;
BT_DBG("stream %p ep %p dir %u", stream, ep, ep->dir);
if (ep->status.state != BT_AUDIO_EP_STATE_ENABLING) { if (ep->status.state != BT_AUDIO_EP_STATE_ENABLING) {
BT_DBG("endpoint not in enabling state: %s", BT_DBG("ep %p not in enabling state: %s",
bt_audio_ep_state_str(ep->status.state)); ep, bt_audio_ep_state_str(ep->status.state));
return; return;
} }
@ -549,41 +499,50 @@ static void ascs_iso_connected(struct bt_iso_chan *chan)
return; return;
} }
stream = ep->stream;
if (stream == NULL) {
BT_ERR("No stream for ep %p", ep);
return;
}
err = ase_stream_start(stream); err = ase_stream_start(stream);
if (err) { if (err) {
BT_ERR("Could not start stream %d", err); BT_ERR("Could not start stream %d", err);
} }
} }
static void ascs_iso_disconnected(struct bt_iso_chan *chan, uint8_t reason) static void ascs_iso_connected(struct bt_iso_chan *chan)
{ {
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan);
iso_chan);
struct bt_audio_stream *source_stream = audio_iso->source_stream;
struct bt_audio_stream *sink_stream = audio_iso->sink_stream;
const struct bt_audio_stream_ops *ops;
struct bt_audio_stream *stream;
struct bt_audio_ep *ep;
if (sink_stream != NULL && sink_stream->iso == chan) { if (iso->rx.ep == NULL && iso->tx.ep == NULL) {
stream = sink_stream; BT_ERR("iso %p not bound with ep", chan);
} else if (source_stream != NULL && source_stream->iso == chan) { return;
stream = source_stream;
} else {
stream = NULL;
} }
if (iso->rx.ep != NULL) {
ascs_ep_iso_connected(iso->rx.ep);
}
if (iso->tx.ep != NULL) {
ascs_ep_iso_connected(iso->tx.ep);
}
}
static void ascs_ep_iso_disconnected(struct bt_audio_ep *ep, uint8_t reason)
{
const struct bt_audio_stream_ops *ops;
struct bt_audio_stream *stream;
stream = ep->stream;
if (stream == NULL) { if (stream == NULL) {
BT_ERR("Could not lookup stream by iso %p", chan); BT_ERR("No stream for ep %p", ep);
return;
} else if (stream->ep == NULL) {
BT_ERR("Stream not associated with an ep");
return; return;
} }
ops = stream->ops; ops = stream->ops;
BT_DBG("stream %p ep %p reason 0x%02x", stream, stream->ep, reason); BT_DBG("stream %p ep %p reason 0x%02x", stream, ep, reason);
if (ops != NULL && ops->stopped != NULL) { if (ops != NULL && ops->stopped != NULL) {
ops->stopped(stream); ops->stopped(stream);
@ -591,8 +550,9 @@ static void ascs_iso_disconnected(struct bt_iso_chan *chan, uint8_t reason)
BT_WARN("No callback for stopped set"); BT_WARN("No callback for stopped set");
} }
ep = stream->ep;
if (ep->status.state == BT_AUDIO_EP_STATE_RELEASING) { if (ep->status.state == BT_AUDIO_EP_STATE_RELEASING) {
bt_audio_iso_unbind_ep(ep->iso, ep);
/* Trigger a call to ase_process to handle the cleanup */ /* Trigger a call to ase_process to handle the cleanup */
k_work_submit(&ep->work); k_work_submit(&ep->work);
} else { } else {
@ -614,6 +574,24 @@ static void ascs_iso_disconnected(struct bt_iso_chan *chan, uint8_t reason)
} }
} }
static void ascs_iso_disconnected(struct bt_iso_chan *chan, uint8_t reason)
{
struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan);
if (iso->rx.ep == NULL && iso->tx.ep == NULL) {
BT_ERR("iso %p not bound with ep", chan);
return;
}
if (iso->rx.ep != NULL) {
ascs_ep_iso_disconnected(iso->rx.ep, reason);
}
if (iso->tx.ep != NULL) {
ascs_ep_iso_disconnected(iso->tx.ep, reason);
}
}
static struct bt_iso_chan_ops ascs_iso_ops = { static struct bt_iso_chan_ops ascs_iso_ops = {
.recv = ascs_iso_recv, .recv = ascs_iso_recv,
.sent = ascs_iso_sent, .sent = ascs_iso_sent,
@ -858,57 +836,54 @@ BT_CONN_CB_DEFINE(conn_cb) = {
.disconnected = disconnected, .disconnected = disconnected,
}; };
static void audio_iso_init(struct bt_audio_iso *audio_iso) struct audio_iso_find_params {
struct bt_conn *acl;
uint8_t cig_id;
uint8_t cis_id;
};
static bool audio_iso_find_func(struct bt_audio_iso *iso, void *user_data)
{ {
/* Setup points for both sink and source struct audio_iso_find_params *params = user_data;
* This is due to the limitation in the ISO API where pointers like const struct bt_audio_ep *ep;
* the `qos->tx` shall be initialized before the CIS is connected if
* ever want to use it for TX, and ditto for RX. They cannot be
* initialized after the CIS has been connected
*/
audio_iso->iso_chan.ops = &ascs_iso_ops;
audio_iso->iso_chan.qos = &audio_iso->iso_qos;
audio_iso->iso_chan.qos->tx = &audio_iso->source_io_qos; if (iso->rx.ep != NULL) {
audio_iso->iso_chan.qos->tx->path = &audio_iso->source_path; ep = iso->rx.ep;
audio_iso->iso_chan.qos->tx->path->cc = audio_iso->source_path_cc; } else if (iso->tx.ep != NULL) {
ep = iso->tx.ep;
audio_iso->iso_chan.qos->rx = &audio_iso->sink_io_qos; } else {
audio_iso->iso_chan.qos->rx->path = &audio_iso->sink_path; return false;
audio_iso->iso_chan.qos->rx->path->cc = audio_iso->sink_path_cc;
}
static struct bt_audio_iso *audio_iso_get_or_new(struct bt_ascs *ascs, uint8_t cig_id,
uint8_t cis_id)
{
struct bt_audio_iso *free_audio_iso = NULL;
BT_DBG("ascs %p cig_id 0x%02x cis_id 0x%02x", ascs, cis_id, cis_id);
for (size_t i = 0; i < ARRAY_SIZE(ascs->isos); i++) {
struct bt_audio_iso *audio_iso = &ascs->isos[i];
const struct bt_audio_ep *ep;
if (audio_iso->sink_stream == NULL && audio_iso->source_stream == NULL) {
free_audio_iso = audio_iso;
continue;
} else if (audio_iso->sink_stream && audio_iso->sink_stream->ep) {
ep = audio_iso->sink_stream->ep;
} else if (audio_iso->source_stream && audio_iso->source_stream->ep) {
ep = audio_iso->source_stream->ep;
} else {
/* XXX: Stream not associated with endpoint. Should we assert??? */
continue;
}
if (ep->cig_id == cig_id && ep->cis_id == cis_id) {
return audio_iso;
}
} }
audio_iso_init(free_audio_iso); return ep->stream->conn == params->acl &&
ep->cig_id == params->cig_id &&
ep->cis_id == params->cis_id;
}
return free_audio_iso; static struct bt_audio_iso *audio_iso_get_or_new(struct bt_ascs *ascs,
uint8_t cig_id,
uint8_t cis_id)
{
struct bt_audio_iso *iso;
struct audio_iso_find_params params = {
.acl = ascs->conn,
.cig_id = cig_id,
.cis_id = cis_id,
};
iso = bt_audio_iso_find(audio_iso_find_func, &params);
if (iso) {
return iso;
}
iso = bt_audio_iso_new();
if (!iso) {
return NULL;
}
bt_audio_iso_init(iso, &ascs_iso_ops);
return iso;
} }
static void ase_stream_add(struct bt_ascs *ascs, struct bt_ascs_ase *ase, static void ase_stream_add(struct bt_ascs *ascs, struct bt_ascs_ase *ase,
@ -918,7 +893,6 @@ static void ase_stream_add(struct bt_ascs *ascs, struct bt_ascs_ase *ase,
ase->ep.stream = stream; ase->ep.stream = stream;
stream->conn = ascs->conn; stream->conn = ascs->conn;
stream->ep = &ase->ep; stream->ep = &ase->ep;
stream->iso = &ase->ep.iso->iso_chan;
} }
static struct bt_ascs *ascs_get(struct bt_conn *conn) static struct bt_ascs *ascs_get(struct bt_conn *conn)
@ -938,7 +912,6 @@ static void ase_process(struct k_work *work)
{ {
struct bt_audio_ep *ep = CONTAINER_OF(work, struct bt_audio_ep, work); struct bt_audio_ep *ep = CONTAINER_OF(work, struct bt_audio_ep, work);
struct bt_ascs_ase *ase = CONTAINER_OF(ep, struct bt_ascs_ase, ep); struct bt_ascs_ase *ase = CONTAINER_OF(ep, struct bt_ascs_ase, ep);
const struct bt_audio_iso *audio_iso = ep->iso;
struct bt_audio_stream *stream = ep->stream; struct bt_audio_stream *stream = ep->stream;
const uint8_t ep_state = ep->status.state; const uint8_t ep_state = ep->status.state;
struct bt_conn *conn = ase->ascs->conn; struct bt_conn *conn = ase->ascs->conn;
@ -958,10 +931,8 @@ static void ase_process(struct k_work *work)
"stream is NULL"); "stream is NULL");
if (ep_state == BT_AUDIO_EP_STATE_RELEASING) { if (ep_state == BT_AUDIO_EP_STATE_RELEASING) {
if (ep->iso == NULL ||
if (audio_iso == NULL || ep->iso->chan.state == BT_ISO_STATE_DISCONNECTED) {
audio_iso->iso_chan.state == BT_ISO_STATE_DISCONNECTED) {
ascs_ep_unbind_audio_iso(ep);
bt_audio_stream_detach(stream); bt_audio_stream_detach(stream);
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_IDLE); ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_IDLE);
} else { } else {
@ -980,8 +951,8 @@ static void ase_process(struct k_work *work)
* the CIS is connected * the CIS is connected
*/ */
if (ep->dir == BT_AUDIO_DIR_SINK && if (ep->dir == BT_AUDIO_DIR_SINK &&
audio_iso != NULL && ep->iso != NULL &&
audio_iso->iso_chan.state == BT_ISO_STATE_CONNECTED) { ep->iso->chan.state == BT_ISO_STATE_CONNECTED) {
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_STREAMING); ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_STREAMING);
} }
} }
@ -1001,37 +972,6 @@ static uint8_t ase_attr_cb(const struct bt_gatt_attr *attr, uint16_t handle,
return BT_GATT_ITER_CONTINUE; return BT_GATT_ITER_CONTINUE;
} }
static int ascs_ep_stream_bind_audio_iso(struct bt_audio_stream *stream,
struct bt_audio_iso *audio_iso)
{
const enum bt_audio_dir dir = stream->ep->dir;
BT_DBG("stream %p, dir %u audio_iso %p", stream, dir, audio_iso);
if (dir == BT_AUDIO_DIR_SOURCE) {
if (audio_iso->source_stream == NULL) {
audio_iso->source_stream = stream;
} else if (audio_iso->source_stream != stream) {
BT_WARN("Bound with source_stream %p already", audio_iso->source_stream);
return -EADDRINUSE;
}
} else if (dir == BT_AUDIO_DIR_SINK) {
if (audio_iso->sink_stream == NULL) {
audio_iso->sink_stream = stream;
} else if (audio_iso->sink_stream != stream) {
BT_WARN("Bound with sink_stream %p already", audio_iso->sink_stream);
return -EADDRINUSE;
}
} else {
__ASSERT(false, "Invalid dir: %u", dir);
}
stream->iso = &audio_iso->iso_chan;
stream->ep->iso = audio_iso;
return 0;
}
void ascs_ep_init(struct bt_audio_ep *ep, uint8_t id) void ascs_ep_init(struct bt_audio_ep *ep, uint8_t id)
{ {
BT_DBG("ep %p id 0x%02x", ep, id); BT_DBG("ep %p id 0x%02x", ep, id);
@ -1444,9 +1384,8 @@ static int ase_stream_qos(struct bt_audio_stream *stream,
uint8_t cig_id, uint8_t cig_id,
uint8_t cis_id) uint8_t cis_id)
{ {
struct bt_audio_iso *audio_iso; struct bt_audio_iso *iso;
struct bt_audio_ep *ep; struct bt_audio_ep *ep;
int err;
BT_DBG("stream %p ep %p qos %p", stream, stream->ep, qos); BT_DBG("stream %p ep %p qos %p", stream, stream->ep, qos);
@ -1486,17 +1425,22 @@ static int ase_stream_qos(struct bt_audio_stream *stream,
} }
} }
audio_iso = audio_iso_get_or_new(ascs, cig_id, cis_id); iso = audio_iso_get_or_new(ascs, cig_id, cis_id);
if (audio_iso == NULL) { if (iso == NULL) {
BT_ERR("Could not allocate audio_iso"); BT_ERR("Could not allocate audio_iso");
return -ENOMEM; return -ENOMEM;
} }
err = ascs_ep_stream_bind_audio_iso(stream, audio_iso); if (bt_audio_iso_get_ep(iso, ep->dir) != NULL) {
if (err < 0) { BT_ERR("iso %p already in use in dir %u",
return err; &iso->chan, ep->dir);
bt_audio_iso_unref(iso);
return -EALREADY;
} }
bt_audio_iso_bind_ep(iso, ep);
bt_audio_iso_unref(iso);
stream->qos = qos; stream->qos = qos;
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_QOS_CONFIGURED); ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_QOS_CONFIGURED);
@ -1567,10 +1511,10 @@ static void ase_qos(struct bt_ascs_ase *ase, const struct bt_ascs_qos *qos)
* the CIS ID in the QoS procedure). * the CIS ID in the QoS procedure).
*/ */
if (ep->dir == BT_AUDIO_DIR_SINK) { if (ep->dir == BT_AUDIO_DIR_SINK) {
bt_audio_codec_to_iso_path(&ep->iso->sink_path, bt_audio_codec_to_iso_path(&ep->iso->rx.path,
stream->codec); stream->codec);
} else { } else {
bt_audio_codec_to_iso_path(&ep->iso->source_path, bt_audio_codec_to_iso_path(&ep->iso->tx.path,
stream->codec); stream->codec);
} }
} }
@ -1971,7 +1915,6 @@ static ssize_t ascs_enable(struct bt_ascs *ascs, struct net_buf_simple *buf)
static void ase_start(struct bt_ascs_ase *ase) static void ase_start(struct bt_ascs_ase *ase)
{ {
struct bt_audio_stream *stream;
struct bt_audio_ep *ep; struct bt_audio_ep *ep;
BT_DBG("ase %p", ase); BT_DBG("ase %p", ase);
@ -2001,11 +1944,10 @@ static void ase_start(struct bt_ascs_ase *ase)
ep->receiver_ready = true; ep->receiver_ready = true;
stream = ep->stream; if (ep->iso->chan.state == BT_ISO_STATE_CONNECTED) {
if (stream->iso->state == BT_ISO_STATE_CONNECTED) {
int err; int err;
err = ase_stream_start(stream); err = ase_stream_start(ep->stream);
if (err) { if (err) {
BT_ERR("Start failed: %d", err); BT_ERR("Start failed: %d", err);
ascs_cp_rsp_add(ASE_ID(ase), BT_ASCS_START_OP, err, ascs_cp_rsp_add(ASE_ID(ase), BT_ASCS_START_OP, err,

View file

@ -0,0 +1,204 @@
/* audio_iso.c - Audio ISO handling */
/*
* Copyright (c) 2022 Codecoup
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "audio_iso.h"
#include "endpoint.h"
/* TODO: Optimize the ISO_POOL_SIZE */
#define ISO_POOL_SIZE CONFIG_BT_ISO_MAX_CHAN
static struct bt_audio_iso iso_pool[ISO_POOL_SIZE];
struct bt_audio_iso *bt_audio_iso_new(void)
{
struct bt_audio_iso *iso = NULL;
for (size_t i = 0; i < ARRAY_SIZE(iso_pool); i++) {
if (atomic_cas(&iso_pool[i].ref, 0, 1)) {
iso = &iso_pool[i];
break;
}
}
if (!iso) {
return NULL;
}
(void)memset(iso, 0, offsetof(struct bt_audio_iso, ref));
return iso;
}
struct bt_audio_iso *bt_audio_iso_ref(struct bt_audio_iso *iso)
{
atomic_val_t old;
__ASSERT_NO_MSG(iso != NULL);
/* Reference counter must be checked to avoid incrementing ref from
* zero, then we should return NULL instead.
* Loop on clear-and-set in case someone has modified the reference
* count since the read, and start over again when that happens.
*/
do {
old = atomic_get(&iso->ref);
if (!old) {
return NULL;
}
} while (!atomic_cas(&iso->ref, old, old + 1));
return iso;
}
void bt_audio_iso_unref(struct bt_audio_iso *iso)
{
atomic_val_t old;
__ASSERT_NO_MSG(iso != NULL);
old = atomic_dec(&iso->ref);
__ASSERT(old > 0, "iso reference counter is 0");
}
void bt_audio_iso_foreach(bt_audio_iso_func_t func, void *user_data)
{
for (size_t i = 0; i < ARRAY_SIZE(iso_pool); i++) {
struct bt_audio_iso *iso = bt_audio_iso_ref(&iso_pool[i]);
bool iter;
if (!iso) {
continue;
}
iter = func(iso, user_data);
bt_audio_iso_unref(iso);
if (!iter) {
return;
}
}
}
struct bt_audio_iso_find_param {
struct bt_audio_iso *iso;
bt_audio_iso_func_t func;
void *user_data;
};
static bool bt_audio_iso_find_cb(struct bt_audio_iso *iso, void *user_data)
{
struct bt_audio_iso_find_param *param = user_data;
bool found;
found = param->func(iso, param->user_data);
if (found) {
param->iso = bt_audio_iso_ref(iso);
}
return !found;
}
struct bt_audio_iso *bt_audio_iso_find(bt_audio_iso_func_t func,
void *user_data)
{
struct bt_audio_iso_find_param param = {
.iso = NULL,
.func = func,
.user_data = user_data,
};
bt_audio_iso_foreach(bt_audio_iso_find_cb, &param);
return param.iso;
}
void bt_audio_iso_init(struct bt_audio_iso *iso, struct bt_iso_chan_ops *ops)
{
iso->chan.ops = ops;
iso->chan.qos = &iso->qos;
/* Setup points for both Tx and Rx
* This is due to the limitation in the ISO API where pointers like
* the `qos->tx` shall be initialized before the CIS is connected if
* ever want to use it for TX, and ditto for RX. They cannot be
* initialized after the CIS has been connected
*/
iso->chan.qos->rx = &iso->rx.qos;
iso->chan.qos->rx->path = &iso->rx.path;
iso->chan.qos->rx->path->cc = iso->rx.cc;
iso->chan.qos->tx = &iso->tx.qos;
iso->chan.qos->tx->path = &iso->tx.path;
iso->chan.qos->tx->path->cc = iso->tx.cc;
}
void bt_audio_iso_bind_ep(struct bt_audio_iso *iso, struct bt_audio_ep *ep)
{
struct bt_iso_chan_qos *qos;
__ASSERT_NO_MSG(ep != NULL);
__ASSERT_NO_MSG(iso != NULL);
__ASSERT(ep->iso == NULL, "ep %p bound with iso %p already", ep, ep->iso);
__ASSERT(ep->dir == BT_AUDIO_DIR_SINK || ep->dir == BT_AUDIO_DIR_SOURCE,
"invalid dir: %u", ep->dir);
qos = iso->chan.qos;
if (ep->dir == BT_AUDIO_DIR_SINK) {
__ASSERT(iso->rx.ep == NULL,
"iso %p bound with ep %p", iso, iso->rx.ep);
iso->rx.ep = ep;
} else {
__ASSERT(iso->tx.ep == NULL,
"iso %p bound with ep %p", iso, iso->tx.ep);
iso->tx.ep = ep;
}
ep->iso = bt_audio_iso_ref(iso);
}
void bt_audio_iso_unbind_ep(struct bt_audio_iso *iso, struct bt_audio_ep *ep)
{
struct bt_iso_chan_qos *qos;
__ASSERT_NO_MSG(ep != NULL);
__ASSERT_NO_MSG(iso != NULL);
__ASSERT(ep->iso, "ep %p not bound with iso", iso, ep);
__ASSERT(ep->dir == BT_AUDIO_DIR_SINK || ep->dir == BT_AUDIO_DIR_SOURCE,
"Invalid dir: %u", ep->dir);
qos = iso->chan.qos;
if (ep->dir == BT_AUDIO_DIR_SINK) {
__ASSERT(iso->rx.ep == ep,
"iso %p not bound with ep %p", iso, ep);
iso->rx.ep = NULL;
} else {
__ASSERT(iso->tx.ep == ep,
"iso %p not bound with ep %p", iso, ep);
iso->tx.ep = NULL;
}
bt_audio_iso_unref(ep->iso);
ep->iso = NULL;
}
struct bt_audio_ep *bt_audio_iso_get_ep(struct bt_audio_iso *iso,
enum bt_audio_dir dir)
{
__ASSERT(dir == BT_AUDIO_DIR_SINK || dir == BT_AUDIO_DIR_SOURCE,
"invalid dir: %u", dir);
if (dir == BT_AUDIO_DIR_SINK) {
return iso->rx.ep;
} else {
return iso->tx.ep;
}
}

View file

@ -0,0 +1,44 @@
/* @file
* @brief Internal APIs for Audio ISO handling
*
* Copyright (c) 2022 Codecoup
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/iso.h>
#include <zephyr/bluetooth/audio/audio.h>
struct bt_audio_iso_ep {
struct bt_audio_ep *ep;
struct bt_iso_chan_path path;
struct bt_iso_chan_io_qos qos;
uint8_t cc[CONFIG_BT_CODEC_MAX_DATA_COUNT * CONFIG_BT_CODEC_MAX_DATA_LEN];
};
struct bt_audio_iso {
struct bt_iso_chan chan;
struct bt_iso_chan_qos qos;
struct bt_audio_iso_ep rx;
struct bt_audio_iso_ep tx;
/* Must be at the end so that everything else in the structure can be
* memset to zero without affecting the ref.
*/
atomic_t ref;
};
typedef bool (*bt_audio_iso_func_t)(struct bt_audio_iso *iso, void *user_data);
struct bt_audio_iso *bt_audio_iso_new(void);
struct bt_audio_iso *bt_audio_iso_ref(struct bt_audio_iso *iso);
void bt_audio_iso_unref(struct bt_audio_iso *iso);
void bt_audio_iso_foreach(bt_audio_iso_func_t func, void *user_data);
struct bt_audio_iso *bt_audio_iso_find(bt_audio_iso_func_t func,
void *user_data);
void bt_audio_iso_init(struct bt_audio_iso *iso, struct bt_iso_chan_ops *ops);
void bt_audio_iso_bind_ep(struct bt_audio_iso *iso, struct bt_audio_ep *ep);
void bt_audio_iso_unbind_ep(struct bt_audio_iso *iso, struct bt_audio_ep *ep);
struct bt_audio_ep *bt_audio_iso_get_ep(struct bt_audio_iso *iso,
enum bt_audio_dir dir);

View file

@ -18,6 +18,7 @@
#include "../host/conn_internal.h" #include "../host/conn_internal.h"
#include "../host/iso_internal.h" #include "../host/iso_internal.h"
#include "audio_iso.h"
#include "endpoint.h" #include "endpoint.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_BROADCAST_SINK) #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_BROADCAST_SINK)
@ -36,8 +37,6 @@
*/ */
#define INVALID_BROADCAST_ID 0xFFFFFFFF #define INVALID_BROADCAST_ID 0xFFFFFFFF
static struct bt_audio_iso broadcast_sink_iso
[CONFIG_BT_AUDIO_BROADCAST_SNK_COUNT][BROADCAST_SNK_STREAM_CNT];
static struct bt_audio_ep broadcast_sink_eps static struct bt_audio_ep broadcast_sink_eps
[CONFIG_BT_AUDIO_BROADCAST_SNK_COUNT][BROADCAST_SNK_STREAM_CNT]; [CONFIG_BT_AUDIO_BROADCAST_SNK_COUNT][BROADCAST_SNK_STREAM_CNT];
static struct bt_audio_broadcast_sink broadcast_sinks[CONFIG_BT_AUDIO_BROADCAST_SNK_COUNT]; static struct bt_audio_broadcast_sink broadcast_sinks[CONFIG_BT_AUDIO_BROADCAST_SNK_COUNT];
@ -120,16 +119,19 @@ static void broadcast_sink_iso_recv(struct bt_iso_chan *chan,
const struct bt_iso_recv_info *info, const struct bt_iso_recv_info *info,
struct net_buf *buf) struct net_buf *buf)
{ {
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan);
iso_chan);
struct bt_audio_stream *stream = audio_iso->sink_stream;
const struct bt_audio_stream_ops *ops; const struct bt_audio_stream_ops *ops;
struct bt_audio_stream *stream;
struct bt_audio_ep *ep = iso->rx.ep;
if (stream == NULL) { if (ep == NULL) {
BT_ERR("Could not lookup stream by iso %p", chan); BT_ERR("iso %p not bound with ep", chan);
return; return;
} else if (stream->ep == NULL) { }
BT_ERR("Stream not associated with an ep");
stream = ep->stream;
if (stream == NULL) {
BT_ERR("No stream for ep %p", ep);
return; return;
} }
@ -137,7 +139,7 @@ static void broadcast_sink_iso_recv(struct bt_iso_chan *chan,
if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) { if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) {
BT_DBG("stream %p ep %p len %zu", BT_DBG("stream %p ep %p len %zu",
stream, stream->ep, net_buf_frags_len(buf)); stream, ep, net_buf_frags_len(buf));
} }
if (ops != NULL && ops->recv != NULL) { if (ops != NULL && ops->recv != NULL) {
@ -149,16 +151,19 @@ static void broadcast_sink_iso_recv(struct bt_iso_chan *chan,
static void broadcast_sink_iso_connected(struct bt_iso_chan *chan) static void broadcast_sink_iso_connected(struct bt_iso_chan *chan)
{ {
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan);
iso_chan);
struct bt_audio_stream *stream = audio_iso->sink_stream;
const struct bt_audio_stream_ops *ops; const struct bt_audio_stream_ops *ops;
struct bt_audio_stream *stream;
struct bt_audio_ep *ep = iso->rx.ep;
if (stream == NULL) { if (ep == NULL) {
BT_ERR("Could not lookup stream by iso %p", chan); BT_ERR("iso %p not bound with ep", chan);
return; return;
} else if (stream->ep == NULL) { }
BT_ERR("Stream not associated with an ep");
stream = ep->stream;
if (stream == NULL) {
BT_ERR("No stream for ep %p", ep);
return; return;
} }
@ -166,7 +171,7 @@ static void broadcast_sink_iso_connected(struct bt_iso_chan *chan)
BT_DBG("stream %p", stream); BT_DBG("stream %p", stream);
broadcast_sink_set_ep_state(stream->ep, BT_AUDIO_EP_STATE_STREAMING); broadcast_sink_set_ep_state(ep, BT_AUDIO_EP_STATE_STREAMING);
if (ops != NULL && ops->started != NULL) { if (ops != NULL && ops->started != NULL) {
ops->started(stream); ops->started(stream);
@ -178,25 +183,28 @@ static void broadcast_sink_iso_connected(struct bt_iso_chan *chan)
static void broadcast_sink_iso_disconnected(struct bt_iso_chan *chan, static void broadcast_sink_iso_disconnected(struct bt_iso_chan *chan,
uint8_t reason) uint8_t reason)
{ {
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan);
iso_chan);
struct bt_audio_stream *stream = audio_iso->sink_stream;
const struct bt_audio_stream_ops *ops; const struct bt_audio_stream_ops *ops;
struct bt_audio_stream *stream;
struct bt_audio_ep *ep = iso->rx.ep;
struct bt_audio_broadcast_sink *sink; struct bt_audio_broadcast_sink *sink;
if (stream == NULL) { if (ep == NULL) {
BT_ERR("Could not lookup ep by iso %p", chan); BT_ERR("iso %p not bound with ep", chan);
return; return;
} else if (stream->ep == NULL) { }
BT_ERR("Stream not associated with an ep");
stream = ep->stream;
if (stream == NULL) {
BT_ERR("No stream for ep %p", ep);
return; return;
} }
ops = stream->ops; ops = stream->ops;
BT_DBG("stream %p ep %p reason 0x%02x", stream, stream->ep, reason); BT_DBG("stream %p ep %p reason 0x%02x", stream, ep, reason);
broadcast_sink_set_ep_state(stream->ep, BT_AUDIO_EP_STATE_IDLE); broadcast_sink_set_ep_state(ep, BT_AUDIO_EP_STATE_IDLE);
if (ops != NULL && ops->stopped != NULL) { if (ops != NULL && ops->stopped != NULL) {
ops->stopped(stream); ops->stopped(stream);
@ -874,23 +882,13 @@ bool bt_audio_ep_is_broadcast_snk(const struct bt_audio_ep *ep)
return false; return false;
} }
static void broadcast_sink_ep_init(struct bt_audio_ep *ep, static void broadcast_sink_ep_init(struct bt_audio_ep *ep)
struct bt_audio_iso *iso)
{ {
struct bt_iso_chan *iso_chan;
BT_DBG("ep %p", ep); BT_DBG("ep %p", ep);
(void)memset(ep, 0, sizeof(*ep)); (void)memset(ep, 0, sizeof(*ep));
ep->dir = BT_AUDIO_DIR_SINK; ep->dir = BT_AUDIO_DIR_SINK;
ep->iso = iso; ep->iso = NULL;
iso_chan = &iso->iso_chan;
iso_chan->ops = &broadcast_sink_iso_ops;
iso_chan->qos = &ep->iso->iso_qos;
iso_chan->qos->rx = &iso->sink_io_qos;
iso_chan->qos->tx = NULL;
} }
static struct bt_audio_ep *broadcast_sink_new_ep(uint8_t index) static struct bt_audio_ep *broadcast_sink_new_ep(uint8_t index)
@ -900,11 +898,7 @@ static struct bt_audio_ep *broadcast_sink_new_ep(uint8_t index)
/* If ep->stream is NULL the endpoint is unallocated */ /* If ep->stream is NULL the endpoint is unallocated */
if (ep->stream == NULL) { if (ep->stream == NULL) {
/* Initialize - It is up to the caller to allocate the broadcast_sink_ep_init(ep);
* stream pointer.
*/
broadcast_sink_ep_init(ep,
&broadcast_sink_iso[index][i]);
return ep; return ep;
} }
} }
@ -916,8 +910,8 @@ static int bt_audio_broadcast_sink_setup_stream(uint8_t index,
struct bt_audio_stream *stream, struct bt_audio_stream *stream,
struct bt_codec *codec) struct bt_codec *codec)
{ {
static struct bt_iso_chan_io_qos sink_chan_io_qos;
static struct bt_codec_qos codec_qos; static struct bt_codec_qos codec_qos;
struct bt_audio_iso *iso;
struct bt_audio_ep *ep; struct bt_audio_ep *ep;
if (stream->group != NULL) { if (stream->group != NULL) {
@ -931,18 +925,22 @@ static int bt_audio_broadcast_sink_setup_stream(uint8_t index,
return -ENOMEM; return -ENOMEM;
} }
iso = bt_audio_iso_new();
if (iso == NULL) {
BT_DBG("Could not allocate iso");
return -ENOMEM;
}
bt_audio_iso_init(iso, &broadcast_sink_iso_ops);
bt_audio_iso_bind_ep(iso, ep);
bt_audio_codec_qos_to_iso_qos(iso->chan.qos->rx, &codec_qos);
bt_audio_codec_to_iso_path(iso->chan.qos->rx->path, codec);
bt_audio_iso_unref(iso);
bt_audio_stream_attach(NULL, stream, ep, codec); bt_audio_stream_attach(NULL, stream, ep, codec);
ep->iso->sink_stream = stream;
/* TODO: The values of sink_chan_io_qos and codec_qos are not used,
* but the `rx` and `qos` pointers need to be set. This should be fixed.
*/
stream->iso->qos->rx = &sink_chan_io_qos;
stream->iso->qos->rx->path = &ep->iso->sink_path;
stream->iso->qos->rx->path->cc = ep->iso->sink_path_cc;
stream->iso->qos->tx = NULL;
stream->qos = &codec_qos; stream->qos = &codec_qos;
bt_audio_codec_qos_to_iso_qos(stream->iso->qos->rx, &codec_qos);
bt_audio_codec_to_iso_path(stream->iso->qos->rx->path, codec);
return 0; return 0;
} }
@ -953,13 +951,13 @@ static void broadcast_sink_cleanup_streams(struct bt_audio_broadcast_sink *sink)
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&sink->streams, stream, next, _node) { SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&sink->streams, stream, next, _node) {
if (stream->ep != NULL) { if (stream->ep != NULL) {
bt_audio_iso_unbind_ep(stream->ep->iso, stream->ep);
stream->ep->stream = NULL; stream->ep->stream = NULL;
stream->ep = NULL; stream->ep = NULL;
} }
stream->qos = NULL; stream->qos = NULL;
stream->codec = NULL; stream->codec = NULL;
stream->iso = NULL;
stream->group = NULL; stream->group = NULL;
sys_slist_remove(&sink->streams, NULL, &stream->_node); sys_slist_remove(&sink->streams, NULL, &stream->_node);
@ -1083,7 +1081,7 @@ int bt_audio_broadcast_sink_sync(struct bt_audio_broadcast_sink *sink,
return err; return err;
} }
sink->bis[i] = &stream->ep->iso->iso_chan; sink->bis[i] = bt_audio_stream_iso_chan_get(stream);
sys_slist_append(&sink->streams, &stream->_node); sys_slist_append(&sink->streams, &stream->_node);
sink->stream_count++; sink->stream_count++;
} }

View file

@ -19,10 +19,9 @@
#define LOG_MODULE_NAME bt_audio_broadcast_source #define LOG_MODULE_NAME bt_audio_broadcast_source
#include "common/log.h" #include "common/log.h"
#include "audio_iso.h"
#include "endpoint.h" #include "endpoint.h"
static struct bt_audio_iso broadcast_source_iso
[CONFIG_BT_AUDIO_BROADCAST_SRC_COUNT][BROADCAST_STREAM_CNT];
static struct bt_audio_ep broadcast_source_eps static struct bt_audio_ep broadcast_source_eps
[CONFIG_BT_AUDIO_BROADCAST_SRC_COUNT][BROADCAST_STREAM_CNT]; [CONFIG_BT_AUDIO_BROADCAST_SRC_COUNT][BROADCAST_STREAM_CNT];
static struct bt_audio_broadcast_source broadcast_sources[CONFIG_BT_AUDIO_BROADCAST_SRC_COUNT]; static struct bt_audio_broadcast_source broadcast_sources[CONFIG_BT_AUDIO_BROADCAST_SRC_COUNT];
@ -85,10 +84,23 @@ static void broadcast_source_set_ep_state(struct bt_audio_ep *ep, uint8_t state)
static void broadcast_source_iso_sent(struct bt_iso_chan *chan) static void broadcast_source_iso_sent(struct bt_iso_chan *chan)
{ {
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan);
iso_chan); const struct bt_audio_stream_ops *ops;
struct bt_audio_stream *stream = audio_iso->source_stream; struct bt_audio_stream *stream;
struct bt_audio_stream_ops *ops = stream->ops; struct bt_audio_ep *ep = iso->tx.ep;
if (ep == NULL) {
BT_ERR("iso %p not bound with ep", chan);
return;
}
stream = ep->stream;
if (stream == NULL) {
BT_ERR("No stream for ep %p", ep);
return;
}
ops = stream->ops;
if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) { if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) {
BT_DBG("stream %p ep %p", stream, stream->ep); BT_DBG("stream %p ep %p", stream, stream->ep);
@ -101,22 +113,23 @@ static void broadcast_source_iso_sent(struct bt_iso_chan *chan)
static void broadcast_source_iso_connected(struct bt_iso_chan *chan) static void broadcast_source_iso_connected(struct bt_iso_chan *chan)
{ {
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan);
iso_chan);
struct bt_audio_stream *stream = audio_iso->source_stream;
const struct bt_audio_stream_ops *ops; const struct bt_audio_stream_ops *ops;
struct bt_audio_ep *ep; struct bt_audio_stream *stream;
struct bt_audio_ep *ep = iso->tx.ep;
if (stream == NULL) { if (ep == NULL) {
BT_ERR("Could not lookup stream by iso %p", chan); BT_ERR("iso %p not bound with ep", chan);
return; return;
} else if (stream->ep == NULL) { }
BT_ERR("Stream not associated with an ep");
stream = ep->stream;
if (stream == NULL) {
BT_ERR("No stream for ep %p", ep);
return; return;
} }
ops = stream->ops; ops = stream->ops;
ep = stream->ep;
BT_DBG("stream %p ep %p", stream, ep); BT_DBG("stream %p ep %p", stream, ep);
@ -131,16 +144,19 @@ static void broadcast_source_iso_connected(struct bt_iso_chan *chan)
static void broadcast_source_iso_disconnected(struct bt_iso_chan *chan, uint8_t reason) static void broadcast_source_iso_disconnected(struct bt_iso_chan *chan, uint8_t reason)
{ {
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan);
iso_chan);
struct bt_audio_stream *stream = audio_iso->source_stream;
const struct bt_audio_stream_ops *ops; const struct bt_audio_stream_ops *ops;
struct bt_audio_stream *stream;
struct bt_audio_ep *ep = iso->tx.ep;
if (stream == NULL) { if (ep == NULL) {
BT_ERR("Could not lookup stream by iso %p", chan); BT_ERR("iso %p not bound with ep", chan);
return; return;
} else if (stream->ep == NULL) { }
BT_ERR("Stream not associated with an ep");
stream = ep->stream;
if (stream == NULL) {
BT_ERR("No stream for ep %p", ep);
return; return;
} }
@ -148,7 +164,7 @@ static void broadcast_source_iso_disconnected(struct bt_iso_chan *chan, uint8_t
BT_DBG("stream %p ep %p reason 0x%02x", stream, stream->ep, reason); BT_DBG("stream %p ep %p reason 0x%02x", stream, stream->ep, reason);
broadcast_source_set_ep_state(stream->ep, BT_AUDIO_EP_STATE_QOS_CONFIGURED); broadcast_source_set_ep_state(ep, BT_AUDIO_EP_STATE_QOS_CONFIGURED);
if (ops != NULL && ops->stopped != NULL) { if (ops != NULL && ops->stopped != NULL) {
ops->stopped(stream); ops->stopped(stream);
@ -174,23 +190,13 @@ bool bt_audio_ep_is_broadcast_src(const struct bt_audio_ep *ep)
return false; return false;
} }
static void broadcast_source_ep_init(struct bt_audio_ep *ep, static void broadcast_source_ep_init(struct bt_audio_ep *ep)
struct bt_audio_iso *iso)
{ {
struct bt_iso_chan *iso_chan;
BT_DBG("ep %p", ep); BT_DBG("ep %p", ep);
(void)memset(ep, 0, sizeof(*ep)); (void)memset(ep, 0, sizeof(*ep));
ep->dir = BT_AUDIO_DIR_SOURCE; ep->dir = BT_AUDIO_DIR_SOURCE;
ep->iso = iso; ep->iso = NULL;
iso_chan = &ep->iso->iso_chan;
iso_chan->ops = &broadcast_source_iso_ops;
iso_chan->qos = &iso->iso_qos;
iso_chan->qos->rx = NULL;
iso_chan->qos->tx = &iso->source_io_qos;
} }
static struct bt_audio_ep *broadcast_source_new_ep(uint8_t index) static struct bt_audio_ep *broadcast_source_new_ep(uint8_t index)
@ -200,10 +206,7 @@ static struct bt_audio_ep *broadcast_source_new_ep(uint8_t index)
/* If ep->stream is NULL the endpoint is unallocated */ /* If ep->stream is NULL the endpoint is unallocated */
if (ep->stream == NULL) { if (ep->stream == NULL) {
/* Initialize - It is up to the caller to allocate the broadcast_source_ep_init(ep);
* stream pointer.
*/
broadcast_source_ep_init(ep, &broadcast_source_iso[index][i]);
return ep; return ep;
} }
} }
@ -216,6 +219,7 @@ static int bt_audio_broadcast_source_setup_stream(uint8_t index,
struct bt_codec *codec, struct bt_codec *codec,
struct bt_codec_qos *qos) struct bt_codec_qos *qos)
{ {
struct bt_audio_iso *iso;
struct bt_audio_ep *ep; struct bt_audio_ep *ep;
if (stream->group != NULL) { if (stream->group != NULL) {
@ -229,14 +233,22 @@ static int bt_audio_broadcast_source_setup_stream(uint8_t index,
return -ENOMEM; return -ENOMEM;
} }
iso = bt_audio_iso_new();
if (iso == NULL) {
BT_DBG("Could not allocate iso");
return -ENOMEM;
}
bt_audio_iso_init(iso, &broadcast_source_iso_ops);
bt_audio_iso_bind_ep(iso, ep);
bt_audio_codec_qos_to_iso_qos(iso->chan.qos->tx, qos);
bt_audio_codec_to_iso_path(iso->chan.qos->tx->path, codec);
bt_audio_iso_unref(iso);
bt_audio_stream_attach(NULL, stream, ep, codec); bt_audio_stream_attach(NULL, stream, ep, codec);
ep->iso->source_stream = stream;
stream->qos = qos; stream->qos = qos;
stream->iso->qos->tx->path = &ep->iso->source_path;
stream->iso->qos->tx->path->cc = ep->iso->source_path_cc;
stream->iso->qos->rx = NULL;
bt_audio_codec_qos_to_iso_qos(stream->iso->qos->tx, qos);
bt_audio_codec_to_iso_path(stream->iso->qos->tx->path, codec);
return 0; return 0;
} }
@ -379,11 +391,11 @@ static void broadcast_source_cleanup(struct bt_audio_broadcast_source *source)
struct bt_audio_stream *stream, *next; struct bt_audio_stream *stream, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&source->streams, stream, next, _node) { SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&source->streams, stream, next, _node) {
bt_audio_iso_unbind_ep(stream->ep->iso, stream->ep);
stream->ep->stream = NULL; stream->ep->stream = NULL;
stream->ep = NULL; stream->ep = NULL;
stream->codec = NULL; stream->codec = NULL;
stream->qos = NULL; stream->qos = NULL;
stream->iso = NULL;
stream->group = NULL; stream->group = NULL;
sys_slist_remove(&source->streams, NULL, &stream->_node); sys_slist_remove(&source->streams, NULL, &stream->_node);
@ -458,7 +470,7 @@ int bt_audio_broadcast_source_create(struct bt_audio_stream *streams[],
return err; return err;
} }
source->bis[i] = &stream->ep->iso->iso_chan; source->bis[i] = bt_audio_stream_iso_chan_get(stream);
sys_slist_append(&source->streams, &stream->_node); sys_slist_append(&source->streams, &stream->_node);
source->stream_count++; source->stream_count++;
} }

View file

@ -32,24 +32,6 @@ struct bt_audio_broadcast_source;
struct bt_audio_broadcast_sink; struct bt_audio_broadcast_sink;
struct bt_audio_ep; struct bt_audio_ep;
struct bt_audio_iso {
struct bt_iso_chan iso_chan;
struct bt_iso_chan_qos iso_qos;
struct bt_iso_chan_io_qos sink_io_qos;
struct bt_iso_chan_io_qos source_io_qos;
struct bt_iso_chan_path sink_path;
/* TODO: The sink/source path CC will basically be a duplicate of
* bt_codec.data, but since struct bt_codec stores the information as an
* array of bt_codec_data, and ISO expect a uint8_t array, we need to
* duplicate the data. This should be optimized.
*/
uint8_t sink_path_cc[CONFIG_BT_CODEC_MAX_DATA_COUNT * CONFIG_BT_CODEC_MAX_DATA_LEN];
struct bt_iso_chan_path source_path;
uint8_t source_path_cc[CONFIG_BT_CODEC_MAX_DATA_COUNT * CONFIG_BT_CODEC_MAX_DATA_LEN];
struct bt_audio_stream *sink_stream;
struct bt_audio_stream *source_stream;
};
struct bt_audio_ep { struct bt_audio_ep {
uint8_t dir; uint8_t dir;
uint8_t cig_id; uint8_t cig_id;

View file

@ -20,6 +20,7 @@
#include "../host/conn_internal.h" #include "../host/conn_internal.h"
#include "../host/iso_internal.h" #include "../host/iso_internal.h"
#include "audio_iso.h"
#include "endpoint.h" #include "endpoint.h"
#include "unicast_client_internal.h" #include "unicast_client_internal.h"
#include "unicast_server.h" #include "unicast_server.h"
@ -83,10 +84,15 @@ void bt_audio_stream_attach(struct bt_conn *conn,
stream->codec = codec; stream->codec = codec;
stream->ep = ep; stream->ep = ep;
ep->stream = stream; ep->stream = stream;
}
if (stream->iso == NULL) { struct bt_iso_chan *bt_audio_stream_iso_chan_get(struct bt_audio_stream *stream)
stream->iso = &ep->iso->iso_chan; {
if (stream != NULL && stream->ep != NULL && stream->ep->iso != NULL) {
return &stream->ep->iso->chan;
} }
return NULL;
} }
#if defined(CONFIG_BT_AUDIO_UNICAST) || defined(CONFIG_BT_AUDIO_BROADCAST_SOURCE) #if defined(CONFIG_BT_AUDIO_UNICAST) || defined(CONFIG_BT_AUDIO_BROADCAST_SOURCE)
@ -109,7 +115,8 @@ int bt_audio_stream_send(struct bt_audio_stream *stream, struct net_buf *buf,
/* TODO: Add checks for broadcast sink */ /* TODO: Add checks for broadcast sink */
return bt_iso_chan_send(stream->iso, buf, seq_num, ts); return bt_iso_chan_send(bt_audio_stream_iso_chan_get(stream),
buf, seq_num, ts);
} }
#endif /* CONFIG_BT_AUDIO_UNICAST || CONFIG_BT_AUDIO_BROADCAST_SOURCE */ #endif /* CONFIG_BT_AUDIO_UNICAST || CONFIG_BT_AUDIO_BROADCAST_SOURCE */
@ -133,7 +140,7 @@ static int bt_audio_stream_iso_accept(const struct bt_iso_accept_info *info,
if (c && c->ep->cig_id == info->cig_id && if (c && c->ep->cig_id == info->cig_id &&
c->ep->cis_id == info->cis_id) { c->ep->cis_id == info->cis_id) {
*iso_chan = enabling[i]->iso; *iso_chan = &enabling[i]->ep->iso->chan;
enabling[i] = NULL; enabling[i] = NULL;
BT_DBG("iso_chan %p", *iso_chan); BT_DBG("iso_chan %p", *iso_chan);
@ -278,7 +285,9 @@ void bt_audio_stream_detach(struct bt_audio_stream *stream)
int bt_audio_stream_disconnect(struct bt_audio_stream *stream) int bt_audio_stream_disconnect(struct bt_audio_stream *stream)
{ {
BT_DBG("stream %p iso %p", stream, stream->iso); struct bt_iso_chan *iso_chan = bt_audio_stream_iso_chan_get(stream);
BT_DBG("stream %p iso %p", stream, iso_chan);
if (stream == NULL) { if (stream == NULL) {
return -EINVAL; return -EINVAL;
@ -294,11 +303,11 @@ int bt_audio_stream_disconnect(struct bt_audio_stream *stream)
} }
#endif /* CONFIG_BT_AUDIO_UNICAST_SERVER */ #endif /* CONFIG_BT_AUDIO_UNICAST_SERVER */
if (stream->iso == NULL || stream->iso->iso == NULL) { if (iso_chan == NULL || iso_chan->iso == NULL) {
return -ENOTCONN; return -ENOTCONN;
} }
return bt_iso_chan_disconnect(stream->iso); return bt_iso_chan_disconnect(iso_chan);
} }
void bt_audio_stream_reset(struct bt_audio_stream *stream) void bt_audio_stream_reset(struct bt_audio_stream *stream)
@ -388,27 +397,15 @@ static int bt_audio_cig_create(struct bt_audio_unicast_group *group,
const struct bt_codec_qos *qos) const struct bt_codec_qos *qos)
{ {
struct bt_iso_cig_param param; struct bt_iso_cig_param param;
struct bt_audio_stream *stream;
uint8_t cis_count; uint8_t cis_count;
int err; int err;
BT_DBG("group %p qos %p", group, qos); BT_DBG("group %p qos %p", group, qos);
cis_count = 0; cis_count = 0;
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) { for (size_t i = 0; i < ARRAY_SIZE(group->cis); i++) {
if (stream->iso != NULL) { if (group->cis[i] != NULL) {
bool already_added = false; cis_count++;
for (size_t i = 0U; i < cis_count; i++) {
if (group->cis[i] == stream->iso) {
already_added = true;
break;
}
}
if (!already_added) {
group->cis[cis_count++] = stream->iso;
}
} }
} }
@ -431,15 +428,16 @@ static int bt_audio_cig_reconfigure(struct bt_audio_unicast_group *group,
const struct bt_codec_qos *qos) const struct bt_codec_qos *qos)
{ {
struct bt_iso_cig_param param; struct bt_iso_cig_param param;
struct bt_audio_stream *stream;
uint8_t cis_count; uint8_t cis_count;
int err; int err;
BT_DBG("group %p qos %p", group, qos); BT_DBG("group %p qos %p", group, qos);
cis_count = 0U; cis_count = 0U;
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) { for (size_t i = 0; i < ARRAY_SIZE(group->cis); i++) {
group->cis[cis_count++] = stream->iso; if (group->cis[i] != NULL) {
cis_count++;
}
} }
param.num_cis = cis_count; param.num_cis = cis_count;
@ -468,7 +466,7 @@ static void audio_stream_qos_cleanup(const struct bt_conn *conn,
continue; continue;
} }
bt_unicast_client_ep_unbind_audio_iso(stream->ep); bt_audio_iso_unbind_ep(stream->ep->iso, stream->ep);
} }
} }
@ -558,7 +556,7 @@ int bt_audio_stream_qos(struct bt_conn *conn,
return -EINVAL; return -EINVAL;
} }
if (bt_unicast_client_audio_iso_by_stream(stream) == NULL) { if (ep->iso == NULL) {
/* This can only happen if the stream was somehow added /* This can only happen if the stream was somehow added
* to a group without the audio_iso being bound to it * to a group without the audio_iso being bound to it
*/ */
@ -572,21 +570,6 @@ int bt_audio_stream_qos(struct bt_conn *conn,
return -EINVAL; return -EINVAL;
} }
/* Setup endpoints before starting the QoS execution */
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
struct bt_audio_iso *audio_iso;
if (stream->conn != conn) {
/* Channel not part of this ACL, skip */
continue;
}
audio_iso = bt_unicast_client_audio_iso_by_stream(stream);
/* Don't need to check for audio_iso validity as we have already verified that */
bt_unicast_client_ep_bind_audio_iso(stream->ep, audio_iso);
}
/* Generate the control point write */ /* Generate the control point write */
buf = bt_unicast_client_ep_create_pdu(BT_ASCS_QOS_OP); buf = bt_unicast_client_ep_create_pdu(BT_ASCS_QOS_OP);
@ -707,17 +690,20 @@ int bt_audio_cig_terminate(struct bt_audio_unicast_group *group)
int bt_audio_stream_connect(struct bt_audio_stream *stream) int bt_audio_stream_connect(struct bt_audio_stream *stream)
{ {
struct bt_iso_connect_param param; struct bt_iso_connect_param param;
struct bt_iso_chan *iso_chan;
BT_DBG("stream %p iso %p", stream, stream ? stream->iso : NULL); iso_chan = bt_audio_stream_iso_chan_get(stream);
if (stream == NULL || stream->iso == NULL) { BT_DBG("stream %p iso %p", stream, iso_chan);
if (stream == NULL || iso_chan == NULL) {
return -EINVAL; return -EINVAL;
} }
param.acl = stream->conn; param.acl = stream->conn;
param.iso_chan = stream->iso; param.iso_chan = iso_chan;
switch (stream->iso->state) { switch (iso_chan->state) {
case BT_ISO_STATE_DISCONNECTED: case BT_ISO_STATE_DISCONNECTED:
return bt_iso_chan_connect(&param, 1); return bt_iso_chan_connect(&param, 1);
case BT_ISO_STATE_CONNECTING: case BT_ISO_STATE_CONNECTING:
@ -740,19 +726,178 @@ static bool unicast_group_valid_qos(const struct bt_codec_qos *group_qos,
return true; return true;
} }
static void unicast_group_param_cleanup(struct bt_audio_unicast_group *group, static struct bt_audio_iso *get_new_iso(struct bt_audio_unicast_group *group,
const struct bt_audio_unicast_group_param params[], struct bt_conn *acl,
size_t num_param) enum bt_audio_dir dir)
{ {
for (size_t i = 0U; i < num_param; i++) { struct bt_audio_stream *stream;
if (!sys_slist_find_and_remove(&group->streams, &params[i].stream->_node)) {
/* We can stop once `sys_slist_find_and_remove` fails as /* Check if there's already an ISO that can be used for this direction */
* that means that the this and the following streams SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
* were never added to the group __ASSERT(stream->ep, "stream->ep is NULL");
*/ __ASSERT(stream->ep->iso, "ep->iso is NULL");
if (stream->conn != acl) {
continue;
}
if (bt_audio_iso_get_ep(stream->ep->iso, dir) == NULL) {
return bt_audio_iso_ref(stream->ep->iso);
}
}
return bt_unicast_client_new_audio_iso();
}
static int unicast_group_add_iso(struct bt_audio_unicast_group *group,
struct bt_audio_iso *iso)
{
struct bt_iso_chan **chan_slot = NULL;
__ASSERT_NO_MSG(group != NULL);
__ASSERT_NO_MSG(iso != NULL);
/* Append iso channel to the group->cis array */
for (size_t i = 0; i < ARRAY_SIZE(group->cis); i++) {
/* Return if already there */
if (group->cis[i] == &iso->chan) {
return 0;
}
if (chan_slot == NULL && group->cis[i] == NULL) {
chan_slot = &group->cis[i];
}
}
if (chan_slot == NULL) {
return -ENOMEM;
}
*chan_slot = &iso->chan;
return 0;
}
static void unicast_group_del_iso(struct bt_audio_unicast_group *group,
struct bt_audio_iso *iso)
{
struct bt_audio_stream *stream;
__ASSERT_NO_MSG(group != NULL);
__ASSERT_NO_MSG(iso != NULL);
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
if (stream->ep->iso == iso) {
/* still in use by some other stream */
return; return;
} }
} }
for (size_t i = 0; i < ARRAY_SIZE(group->cis); i++) {
if (group->cis[i] == &iso->chan) {
group->cis[i] = NULL;
return;
}
}
}
static int unicast_group_add_stream(struct bt_audio_unicast_group *group,
struct bt_audio_stream *stream,
struct bt_codec_qos *qos,
enum bt_audio_dir dir)
{
struct bt_audio_iso *iso;
int err;
__ASSERT_NO_MSG(group != NULL);
__ASSERT_NO_MSG(stream != NULL);
__ASSERT_NO_MSG(stream->ep != NULL);
__ASSERT_NO_MSG(stream->ep->iso != NULL);
iso = get_new_iso(group, stream->conn, dir);
if (iso == NULL) {
return -ENOMEM;
}
err = unicast_group_add_iso(group, iso);
if (err < 0) {
bt_audio_iso_unref(iso);
return err;
}
/* iso initialized already */
bt_audio_iso_bind_ep(iso, stream->ep);
if (dir == BT_AUDIO_DIR_SINK) {
/* If the endpoint is a sink, then we need to
* configure our TX parameters
*/
bt_audio_codec_qos_to_iso_qos(iso->chan.qos->tx, qos);
} else {
/* If the endpoint is a source, then we need to
* configure our RX parameters
*/
bt_audio_codec_qos_to_iso_qos(iso->chan.qos->rx, qos);
}
bt_audio_iso_unref(iso);
stream->qos = qos;
stream->unicast_group = group;
sys_slist_append(&group->streams, &stream->_node);
BT_DBG("Added stream %p to group %p", stream, group);
return 0;
}
static void unicast_group_del_stream(struct bt_audio_unicast_group *group,
struct bt_audio_stream *stream)
{
__ASSERT_NO_MSG(group != NULL);
__ASSERT_NO_MSG(stream != NULL);
if (sys_slist_find_and_remove(&group->streams, &stream->_node)) {
unicast_group_del_iso(group, stream->ep->iso);
stream->unicast_group = NULL;
bt_audio_iso_unbind_ep(stream->ep->iso, stream->ep);
}
}
static struct bt_audio_unicast_group *unicast_group_alloc(void)
{
struct bt_audio_unicast_group *group = NULL;
for (size_t i = 0U; i < ARRAY_SIZE(unicast_groups); i++) {
if (!unicast_groups[i].allocated) {
group = &unicast_groups[i];
(void)memset(group, 0, sizeof(*group));
group->allocated = true;
group->index = i;
break;
}
}
return group;
}
static void unicast_group_free(struct bt_audio_unicast_group *group)
{
struct bt_audio_stream *stream, *next;
__ASSERT_NO_MSG(group != NULL);
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&group->streams, stream, next, _node) {
stream->unicast_group = NULL;
bt_audio_iso_unbind_ep(stream->ep->iso, stream->ep);
sys_slist_remove(&group->streams, NULL, &stream->_node);
}
group->allocated = false;
} }
int bt_audio_unicast_group_create(struct bt_audio_unicast_group_param params[], int bt_audio_unicast_group_create(struct bt_audio_unicast_group_param params[],
@ -809,65 +954,27 @@ int bt_audio_unicast_group_create(struct bt_audio_unicast_group_param params[],
} }
} }
unicast_group = NULL; unicast_group = unicast_group_alloc();
for (size_t i = 0U; i < ARRAY_SIZE(unicast_groups); i++) {
/* Find free entry */
if (!unicast_groups[i].allocated) {
unicast_group = &unicast_groups[i];
unicast_group->index = i;
break;
}
}
if (unicast_group == NULL) { if (unicast_group == NULL) {
BT_DBG("Could not allocate any more unicast groups"); BT_DBG("Could not allocate any more unicast groups");
return -ENOMEM; return -ENOMEM;
} }
unicast_group->allocated = true;
for (size_t i = 0U; i < num_param; i++) { for (size_t i = 0U; i < num_param; i++) {
sys_slist_t *group_streams = &unicast_group->streams; err = unicast_group_add_stream(unicast_group, params[i].stream,
const enum bt_audio_dir dir = params[i].dir; params[i].qos, params[i].dir);
struct bt_iso_chan_qos *iso_qos; if (err < 0) {
struct bt_audio_stream *stream = params[i].stream; BT_DBG("unicast_group_add_stream failed: %d", err);
struct bt_audio_iso *audio_iso; unicast_group_free(unicast_group);
struct bt_iso_chan_io_qos *io;
audio_iso = bt_unicast_client_new_audio_iso(unicast_group, stream, dir); return err;
if (audio_iso == NULL) {
unicast_group_param_cleanup(unicast_group, params, num_param);
return -ENOMEM;
} }
bt_unicast_client_stream_bind_audio_iso(stream, audio_iso, dir);
iso_qos = &audio_iso->iso_qos;
if (dir == BT_AUDIO_DIR_SINK) {
/* If the endpoint is a sink, then we need to
* configure our TX parameters
*/
io = iso_qos->tx;
} else {
/* If the endpoint is a source, then we need to
* configure our RX parameters
*/
io = iso_qos->rx;
}
bt_audio_codec_qos_to_iso_qos(io, params[i].qos);
stream->unicast_group = unicast_group;
stream->qos = params[i].qos;
sys_slist_append(group_streams, &stream->_node);
BT_DBG("Added stream %p to group %p", stream, unicast_group);
} }
err = bt_audio_cig_create(unicast_group, group_qos); err = bt_audio_cig_create(unicast_group, group_qos);
if (err != 0) { if (err != 0) {
BT_DBG("bt_audio_cig_create failed: %d", err); BT_DBG("bt_audio_cig_create failed: %d", err);
unicast_group_param_cleanup(unicast_group, params, num_param); unicast_group_free(unicast_group);
return err; return err;
} }
@ -885,6 +992,7 @@ int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_gr
struct bt_audio_stream *tmp_stream; struct bt_audio_stream *tmp_stream;
size_t total_stream_cnt; size_t total_stream_cnt;
struct bt_iso_cig *cig; struct bt_iso_cig *cig;
size_t num_added;
int err; int err;
CHECKIF(unicast_group == NULL) { CHECKIF(unicast_group == NULL) {
@ -946,31 +1054,36 @@ int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_gr
return -EBADMSG; return -EBADMSG;
} }
for (size_t i = 0U; i < num_param; i++) { for (num_added = 0U; num_added < num_param; num_added++) {
sys_slist_t *group_streams = &unicast_group->streams; err = unicast_group_add_stream(unicast_group,
struct bt_audio_stream *stream = params[i].stream; params[num_added].stream,
params[num_added].qos,
stream->unicast_group = unicast_group; params[num_added].dir);
sys_slist_append(group_streams, &stream->_node); if (err < 0) {
BT_DBG("unicast_group_add_stream failed: %d", err);
BT_DBG("Added stream %p to group %p", stream, unicast_group); goto fail;
}
} }
err = bt_audio_cig_reconfigure(unicast_group, group_qos); err = bt_audio_cig_reconfigure(unicast_group, group_qos);
if (err != 0) { if (err != 0) {
BT_DBG("bt_audio_cig_reconfigure failed: %d", err); BT_DBG("bt_audio_cig_reconfigure failed: %d", err);
unicast_group_param_cleanup(unicast_group, params, num_param); goto fail;
return err;
} }
return 0; return 0;
fail:
/* Restore group by removing the newly added streams */
while (num_added--) {
unicast_group_del_stream(unicast_group, params[num_added].stream);
}
return err;
} }
int bt_audio_unicast_group_delete(struct bt_audio_unicast_group *unicast_group) int bt_audio_unicast_group_delete(struct bt_audio_unicast_group *unicast_group)
{ {
struct bt_audio_stream *stream, *tmp;
CHECKIF(unicast_group == NULL) { CHECKIF(unicast_group == NULL) {
BT_DBG("unicast_group is NULL"); BT_DBG("unicast_group is NULL");
return -EINVAL; return -EINVAL;
@ -987,13 +1100,7 @@ int bt_audio_unicast_group_delete(struct bt_audio_unicast_group *unicast_group)
} }
} }
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_group->streams, stream, tmp, _node) { unicast_group_free(unicast_group);
stream->unicast_group = NULL;
sys_slist_remove(&unicast_group->streams, NULL, &stream->_node);
bt_unicast_client_stream_unbind_audio_iso(stream);
}
(void)memset(unicast_group, 0, sizeof(*unicast_group));
return 0; return 0;
} }

View file

@ -55,3 +55,5 @@ bool bt_audio_valid_stream_qos(const struct bt_audio_stream *stream,
const struct bt_codec_qos *qos); const struct bt_codec_qos *qos);
int bt_audio_stream_iso_listen(struct bt_audio_stream *stream); int bt_audio_stream_iso_listen(struct bt_audio_stream *stream);
struct bt_iso_chan *bt_audio_stream_iso_chan_get(struct bt_audio_stream *stream);

View file

@ -21,6 +21,7 @@
#include "../host/hci_core.h" #include "../host/hci_core.h"
#include "../host/conn_internal.h" #include "../host/conn_internal.h"
#include "audio_iso.h"
#include "endpoint.h" #include "endpoint.h"
#include "pacs_internal.h" #include "pacs_internal.h"
#include "unicast_client_internal.h" #include "unicast_client_internal.h"
@ -49,8 +50,6 @@ static const struct bt_uuid *cp_uuid = BT_UUID_ASCS_ASE_CP;
static struct bt_audio_ep snks[CONFIG_BT_MAX_CONN][CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SNK_COUNT]; static struct bt_audio_ep snks[CONFIG_BT_MAX_CONN][CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SNK_COUNT];
static struct bt_audio_ep srcs[CONFIG_BT_MAX_CONN][CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SRC_COUNT]; static struct bt_audio_ep srcs[CONFIG_BT_MAX_CONN][CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SRC_COUNT];
static struct bt_audio_iso audio_isos[CONFIG_BT_AUDIO_UNICAST_CLIENT_GROUP_COUNT]
[CONFIG_BT_AUDIO_UNICAST_CLIENT_GROUP_STREAM_COUNT];
static struct bt_gatt_subscribe_params cp_subscribe[CONFIG_BT_MAX_CONN]; static struct bt_gatt_subscribe_params cp_subscribe[CONFIG_BT_MAX_CONN];
static struct bt_gatt_subscribe_params snk_loc_subscribe[CONFIG_BT_MAX_CONN]; static struct bt_gatt_subscribe_params snk_loc_subscribe[CONFIG_BT_MAX_CONN];
@ -80,29 +79,25 @@ static void unicast_client_ep_iso_recv(struct bt_iso_chan *chan,
const struct bt_iso_recv_info *info, const struct bt_iso_recv_info *info,
struct net_buf *buf) struct net_buf *buf)
{ {
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan);
iso_chan);
struct bt_audio_stream *stream = audio_iso->sink_stream;
const struct bt_audio_stream_ops *ops; const struct bt_audio_stream_ops *ops;
struct bt_audio_stream *stream;
struct bt_audio_ep *ep = iso->rx.ep;
if (stream == NULL) { if (ep == NULL) {
BT_ERR("Could not lookup stream by iso %p", chan); BT_ERR("iso %p not bound with ep", chan);
return;
} else if (stream->ep == NULL) {
BT_ERR("Stream not associated with an ep");
return; return;
} }
/* Since 2 streams can share the same CIS, the CIS may be connected and if (ep->status.state != BT_AUDIO_EP_STATE_STREAMING) {
* capable of transferring data, without the bt_audio_stream being in BT_DBG("ep %p is not in the streaming state: %s",
* the streaming state. In that case we simply ignore the data. ep, bt_audio_ep_state_str(ep->status.state));
*/ return;
if (stream->ep->status.state != BT_AUDIO_EP_STATE_STREAMING) { }
if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) {
BT_DBG("Stream %p is not in the streaming state: %u",
stream, stream->ep->status.state);
}
stream = ep->stream;
if (stream == NULL) {
BT_ERR("No stream for ep %p", ep);
return; return;
} }
@ -110,7 +105,7 @@ static void unicast_client_ep_iso_recv(struct bt_iso_chan *chan,
if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) { if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) {
BT_DBG("stream %p ep %p len %zu", BT_DBG("stream %p ep %p len %zu",
stream, stream->ep, net_buf_frags_len(buf)); stream, ep, net_buf_frags_len(buf));
} }
if (ops != NULL && ops->recv != NULL) { if (ops != NULL && ops->recv != NULL) {
@ -122,100 +117,82 @@ static void unicast_client_ep_iso_recv(struct bt_iso_chan *chan,
static void unicast_client_ep_iso_sent(struct bt_iso_chan *chan) static void unicast_client_ep_iso_sent(struct bt_iso_chan *chan)
{ {
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan);
iso_chan); struct bt_audio_stream *stream;
struct bt_audio_stream *stream = audio_iso->source_stream; struct bt_audio_ep *ep = iso->tx.ep;
struct bt_audio_stream_ops *ops;
if (stream == NULL) { if (ep == NULL) {
BT_ERR("Could not lookup stream by iso %p", chan); BT_ERR("iso %p not bound with ep", chan);
return;
} else if (stream->ep == NULL) {
BT_ERR("Stream not associated with an ep");
return; return;
} }
ops = stream->ops; stream = ep->stream;
if (stream == NULL) {
BT_ERR("No stream for ep %p", ep);
return;
}
if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) { if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) {
BT_DBG("stream %p ep %p", stream, stream->ep); BT_DBG("stream %p ep %p", stream, ep);
} }
if (ops != NULL && ops->sent != NULL) { if (stream->ops != NULL && stream->ops->sent != NULL) {
ops->sent(stream); stream->ops->sent(stream);
} }
} }
static void unicast_client_ep_iso_connected(struct bt_iso_chan *chan) static void unicast_client_ep_iso_connected(struct bt_iso_chan *chan)
{ {
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan);
iso_chan);
struct bt_audio_stream *source_stream = audio_iso->source_stream;
struct bt_audio_stream *sink_stream = audio_iso->sink_stream;
struct bt_audio_stream *stream; struct bt_audio_stream *stream;
struct bt_audio_ep *ep; struct bt_audio_ep *ep;
if (sink_stream != NULL && sink_stream->iso == chan) { /* FIXME: Handle both EP's */
stream = sink_stream; ep = iso->tx.ep != NULL ? iso->tx.ep : iso->rx.ep;
} else if (source_stream != NULL && source_stream->iso == chan) { if (ep == NULL) {
stream = source_stream; BT_ERR("iso %p not bound with ep", chan);
} else {
stream = NULL;
}
if (stream == NULL) {
BT_ERR("Could not lookup stream by iso %p", chan);
return;
} else if (stream->ep == NULL) {
BT_ERR("Stream not associated with an ep");
return; return;
} }
ep = stream->ep;
BT_DBG("stream %p ep %p dir %u", stream, ep, ep->dir);
if (ep->status.state != BT_AUDIO_EP_STATE_ENABLING) { if (ep->status.state != BT_AUDIO_EP_STATE_ENABLING) {
BT_DBG("endpoint not in enabling state: %s", BT_DBG("endpoint not in enabling state: %s",
bt_audio_ep_state_str(ep->status.state)); bt_audio_ep_state_str(ep->status.state));
return; return;
} }
stream = ep->stream;
if (stream == NULL) {
BT_ERR("No stream for ep %p", ep);
return;
}
BT_DBG("stream %p ep %p dir %u", stream, ep, ep->dir);
} }
static void unicast_client_ep_iso_disconnected(struct bt_iso_chan *chan, static void unicast_client_ep_iso_disconnected(struct bt_iso_chan *chan,
uint8_t reason) uint8_t reason)
{ {
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan);
iso_chan);
struct bt_audio_stream *source_stream = audio_iso->source_stream;
struct bt_audio_stream *sink_stream = audio_iso->sink_stream;
const struct bt_audio_stream_ops *ops;
struct bt_audio_stream *stream; struct bt_audio_stream *stream;
struct bt_audio_ep *ep; struct bt_audio_ep *ep;
if (sink_stream != NULL && sink_stream->iso == chan) { /* FIXME: Handle both EP's */
stream = sink_stream; ep = iso->tx.ep != NULL ? iso->tx.ep : iso->rx.ep;
} else if (source_stream != NULL && source_stream->iso == chan) { if (ep == NULL) {
stream = source_stream; BT_ERR("iso %p not bound with ep", chan);
} else { return;
stream = NULL;
} }
stream = ep->stream;
if (stream == NULL) { if (stream == NULL) {
BT_ERR("Could not lookup stream by iso %p", chan);
return;
} else if (stream->ep == NULL) {
BT_ERR("Stream not associated with an ep"); BT_ERR("Stream not associated with an ep");
return; return;
} }
ep = stream->ep;
ops = stream->ops;
BT_DBG("stream %p ep %p reason 0x%02x", stream, ep, reason); BT_DBG("stream %p ep %p reason 0x%02x", stream, ep, reason);
if (ops != NULL && ops->stopped != NULL) { if (stream->ops != NULL && stream->ops->stopped != NULL) {
ops->stopped(stream); stream->ops->stopped(stream);
} else { } else {
BT_WARN("No callback for stopped set"); BT_WARN("No callback for stopped set");
} }
@ -228,101 +205,6 @@ static struct bt_iso_chan_ops unicast_client_iso_ops = {
.disconnected = unicast_client_ep_iso_disconnected, .disconnected = unicast_client_ep_iso_disconnected,
}; };
struct bt_audio_iso *bt_unicast_client_audio_iso_by_stream(const struct bt_audio_stream *stream)
{
uint8_t group_index;
if (stream == NULL || stream->unicast_group == NULL) {
return NULL;
}
group_index = stream->unicast_group->index;
for (size_t i = 0U; i < ARRAY_SIZE(audio_isos[group_index]); i++) {
struct bt_audio_iso *audio_iso = &audio_isos[group_index][i];
if (audio_iso->sink_stream == stream ||
audio_iso->source_stream == stream) {
return audio_iso;
}
}
return NULL;
}
void bt_unicast_client_ep_unbind_audio_iso(struct bt_audio_ep *ep)
{
ep->iso = NULL;
}
void bt_unicast_client_ep_bind_audio_iso(struct bt_audio_ep *ep,
struct bt_audio_iso *audio_iso)
{
BT_DBG("ep %p audio_iso %p", ep, audio_iso);
ep->iso = audio_iso;
}
void bt_unicast_client_stream_bind_audio_iso(struct bt_audio_stream *stream,
struct bt_audio_iso *audio_iso,
enum bt_audio_dir dir)
{
struct bt_iso_chan *iso_chan;
struct bt_iso_chan_qos *qos;
iso_chan = &audio_iso->iso_chan;
iso_chan->ops = &unicast_client_iso_ops;
qos = iso_chan->qos = &audio_iso->iso_qos;
if (dir == BT_AUDIO_DIR_SOURCE) {
/* If the endpoint is a source, then we need to
* configure our RX parameters
*/
audio_iso->sink_stream = stream;
qos->rx = &audio_iso->sink_io_qos;
qos->rx->path = &audio_iso->sink_path;
qos->rx->path->cc = audio_iso->sink_path_cc;
} else if (dir == BT_AUDIO_DIR_SINK) {
/* If the endpoint is a sink, then we need to
* configure our TX parameters
*/
audio_iso->source_stream = stream;
qos->tx = &audio_iso->source_io_qos;
qos->tx->path = &audio_iso->source_path;
qos->tx->path->cc = audio_iso->source_path_cc;
} else {
__ASSERT(false, "Invalid dir: %u", dir);
}
stream->iso = iso_chan;
}
void bt_unicast_client_stream_unbind_audio_iso(struct bt_audio_stream *stream)
{
BT_DBG("stream %p", stream);
if (stream->iso != NULL) {
struct bt_audio_iso *audio_iso = CONTAINER_OF(stream->iso,
struct bt_audio_iso,
iso_chan);
struct bt_iso_chan_qos *qos;
qos = audio_iso->iso_chan.qos;
stream->iso = NULL;
if (audio_iso->sink_stream == stream) {
audio_iso->sink_stream = NULL;
qos->rx = NULL;
} else if (audio_iso->source_stream == stream) {
audio_iso->source_stream = NULL;
qos->tx = NULL;
} else {
BT_ERR("Stream %p not linked to audio_iso %p", stream, audio_iso);
}
}
}
static void unicast_client_ep_init(struct bt_audio_ep *ep, uint16_t handle, static void unicast_client_ep_init(struct bt_audio_ep *ep, uint16_t handle,
uint8_t dir) uint8_t dir)
{ {
@ -364,60 +246,20 @@ static struct bt_audio_ep *unicast_client_ep_find(struct bt_conn *conn,
return NULL; return NULL;
} }
struct bt_audio_iso *bt_unicast_client_new_audio_iso( struct bt_audio_iso *bt_unicast_client_new_audio_iso(void)
const struct bt_audio_unicast_group *unicast_group,
const struct bt_audio_stream *stream,
enum bt_audio_dir dir)
{ {
const uint8_t group_index = unicast_group->index; struct bt_audio_iso *audio_iso;
struct bt_audio_iso *free_audio_iso = NULL;
BT_DBG("stream %p dir %u", stream, dir); audio_iso = bt_audio_iso_new();
if (audio_iso == NULL) {
/* Look through the audio_isos and find a free one, or one for the same return NULL;
* ACL connection where the opposite direction is what we want, and is
* free.
*
* The returned audio_iso is either an unused one, or one where @p
* stream can be used in the opposite direction.
*/
for (size_t i = 0U; i < ARRAY_SIZE(audio_isos[group_index]); i++) {
struct bt_audio_iso *audio_iso = &audio_isos[group_index][i];
const struct bt_conn *acl_conn = stream->conn;
const struct bt_audio_stream *paired_stream;
const struct bt_audio_stream *tmp_stream;
switch (dir) {
case BT_AUDIO_DIR_SINK:
tmp_stream = audio_iso->source_stream;
paired_stream = audio_iso->sink_stream;
break;
case BT_AUDIO_DIR_SOURCE:
tmp_stream = audio_iso->sink_stream;
paired_stream = audio_iso->source_stream;
break;
default:
return NULL;
}
if (tmp_stream == NULL) {
if (paired_stream == NULL) {
if (free_audio_iso == NULL) {
free_audio_iso = audio_iso;
}
} else if (paired_stream->unicast_group == unicast_group &&
acl_conn != NULL &&
paired_stream->conn == acl_conn) {
BT_DBG("Use existing audio_iso %p", audio_iso);
return audio_iso;
}
}
} }
BT_DBG("New audio_iso %p", free_audio_iso); bt_audio_iso_init(audio_iso, &unicast_client_iso_ops);
return free_audio_iso; BT_DBG("New audio_iso %p", audio_iso);
return audio_iso;
} }
static struct bt_audio_ep *unicast_client_ep_new(struct bt_conn *conn, static struct bt_audio_ep *unicast_client_ep_new(struct bt_conn *conn,
@ -501,12 +343,12 @@ static void unicast_client_ep_qos_update(struct bt_audio_ep *ep,
/* If the endpoint is a source, then we need to /* If the endpoint is a source, then we need to
* reset our RX parameters * reset our RX parameters
*/ */
iso_io_qos = ep->iso->iso_qos.rx; iso_io_qos = &ep->iso->rx.qos;
} else if (ep->dir == BT_AUDIO_DIR_SINK) { } else if (ep->dir == BT_AUDIO_DIR_SINK) {
/* If the endpoint is a sink, then we need to /* If the endpoint is a sink, then we need to
* reset our TX parameters * reset our TX parameters
*/ */
iso_io_qos = ep->iso->iso_qos.tx; iso_io_qos = &ep->iso->tx.qos;
} else { } else {
__ASSERT(false, "Invalid ep->dir: %u", ep->dir); __ASSERT(false, "Invalid ep->dir: %u", ep->dir);
return; return;
@ -628,7 +470,7 @@ static void unicast_client_ep_qos_state(struct bt_audio_ep *ep,
stream->qos->pd); stream->qos->pd);
/* Disconnect ISO if connected */ /* Disconnect ISO if connected */
if (stream->iso->state == BT_ISO_STATE_CONNECTED) { if (ep->iso->chan.state == BT_ISO_STATE_CONNECTED) {
bt_audio_stream_disconnect(stream); bt_audio_stream_disconnect(stream);
} else { } else {
/* We setup the data path here, as this is the earliest where /* We setup the data path here, as this is the earliest where
@ -639,13 +481,13 @@ static void unicast_client_ep_qos_state(struct bt_audio_ep *ep,
/* If the endpoint is a source, then we need to /* If the endpoint is a source, then we need to
* configure our RX parameters * configure our RX parameters
*/ */
bt_audio_codec_to_iso_path(&ep->iso->sink_path, bt_audio_codec_to_iso_path(&ep->iso->rx.path,
stream->codec); stream->codec);
} else { } else {
/* If the endpoint is a sink, then we need to /* If the endpoint is a sink, then we need to
* configure our TX parameters * configure our TX parameters
*/ */
bt_audio_codec_to_iso_path(&ep->iso->source_path, bt_audio_codec_to_iso_path(&ep->iso->tx.path,
stream->codec); stream->codec);
} }
} }
@ -1385,7 +1227,7 @@ int bt_unicast_client_ep_qos(struct bt_audio_ep *ep, struct net_buf_simple *buf,
return -EINVAL; return -EINVAL;
} }
conn_iso = &ep->iso->iso_chan.iso->iso; conn_iso = &ep->iso->chan.iso->iso;
BT_DBG("id 0x%02x cig 0x%02x cis 0x%02x interval %u framing 0x%02x " BT_DBG("id 0x%02x cig 0x%02x cis 0x%02x interval %u framing 0x%02x "
"phy 0x%02x sdu %u rtn %u latency %u pd %u", ep->status.id, "phy 0x%02x sdu %u rtn %u latency %u pd %u", ep->status.id,
@ -1723,7 +1565,7 @@ int bt_unicast_client_start(struct bt_audio_stream *stream)
BT_DBG("bt_audio_stream_connect failed: %d", err); BT_DBG("bt_audio_stream_connect failed: %d", err);
return err; return err;
} else if (err == -EALREADY) { } else if (err == -EALREADY) {
BT_DBG("ISO %p already connected", stream->iso); BT_DBG("ISO %p already connected", bt_audio_stream_iso_chan_get(stream));
} }
/* When initiated by the client, valid only if Direction field /* When initiated by the client, valid only if Direction field

View file

@ -33,32 +33,4 @@ int bt_unicast_client_ep_qos(struct bt_audio_ep *ep, struct net_buf_simple *buf,
int bt_unicast_client_ep_send(struct bt_conn *conn, struct bt_audio_ep *ep, int bt_unicast_client_ep_send(struct bt_conn *conn, struct bt_audio_ep *ep,
struct net_buf_simple *buf); struct net_buf_simple *buf);
/** struct bt_audio_iso *bt_unicast_client_new_audio_iso(void);
* @brief Get a new a audio_iso that is a match for the provided stream
*
* This will first attempt to find a audio_iso where the stream's direction
* is not yet used, and where the opposing direction is for the same stream
* group and ACL connection.
*
* If no such one is available, it will use the first completely free audio_iso
* available, or return NULL if not such one is found.
*
* @param stream Pointer to a stream used to find a corresponding audio_iso
*
* @return An audio_iso struct fit for the stream, or NULL.
*/
struct bt_audio_iso *bt_unicast_client_new_audio_iso(
const struct bt_audio_unicast_group *unicast_group,
const struct bt_audio_stream *stream,
enum bt_audio_dir dir);
struct bt_audio_iso *bt_unicast_client_audio_iso_by_stream(const struct bt_audio_stream *stream);
void bt_unicast_client_stream_bind_audio_iso(struct bt_audio_stream *stream,
struct bt_audio_iso *audio_iso,
enum bt_audio_dir dir);
void bt_unicast_client_stream_unbind_audio_iso(struct bt_audio_stream *stream);
void bt_unicast_client_ep_bind_audio_iso(struct bt_audio_ep *ep,
struct bt_audio_iso *audio_iso);
void bt_unicast_client_ep_unbind_audio_iso(struct bt_audio_ep *ep);

View file

@ -1814,20 +1814,15 @@ static int cmd_send(const struct shell *sh, size_t argc, char *argv[])
int ret, len; int ret, len;
struct net_buf *buf; struct net_buf *buf;
if (default_stream->iso->qos->tx == NULL) {
shell_error(sh, "Stream %p cannot send", default_stream);
return -ENOEXEC;
}
if (argc > 1) { if (argc > 1) {
len = hex2bin(argv[1], strlen(argv[1]), data, sizeof(data)); len = hex2bin(argv[1], strlen(argv[1]), data, sizeof(data));
if (len > default_stream->iso->qos->tx->sdu) { if (len > default_preset->preset.qos.sdu) {
shell_print(sh, "Unable to send: len %d > %u MTU", shell_print(sh, "Unable to send: len %d > %u MTU",
len, default_stream->iso->qos->tx->sdu); len, default_preset->preset.qos.sdu);
return -ENOEXEC; return -ENOEXEC;
} }
} else { } else {
len = MIN(default_stream->iso->qos->tx->sdu, sizeof(data)); len = MIN(default_preset->preset.qos.sdu, sizeof(data));
memset(data, 0xff, len); memset(data, 0xff, len);
} }