diff --git a/include/zephyr/bluetooth/audio/audio.h b/include/zephyr/bluetooth/audio/audio.h index c8d74688ed8..7d10ea9a044 100644 --- a/include/zephyr/bluetooth/audio/audio.h +++ b/include/zephyr/bluetooth/audio/audio.h @@ -1297,8 +1297,6 @@ struct bt_audio_stream { const struct bt_codec *codec; /** QoS Configuration */ struct bt_codec_qos *qos; - /** ISO channel reference */ - struct bt_iso_chan *iso; /** Audio stream operations */ struct bt_audio_stream_ops *ops; diff --git a/subsys/bluetooth/audio/CMakeLists.txt b/subsys/bluetooth/audio/CMakeLists.txt index fa2028c8a49..98ab7308452 100644 --- a/subsys/bluetooth/audio/CMakeLists.txt +++ b/subsys/bluetooth/audio/CMakeLists.txt @@ -1,7 +1,10 @@ # SPDX-License-Identifier: Apache-2.0 zephyr_library() -zephyr_library_sources(audio.c) +zephyr_library_sources( + audio.c + audio_iso.c +) if (CONFIG_BT_VOCS OR CONFIG_BT_VOCS_CLIENT) zephyr_library_sources(vocs.c) diff --git a/subsys/bluetooth/audio/ascs.c b/subsys/bluetooth/audio/ascs.c index 3210356aaac..c4fbb6bae00 100644 --- a/subsys/bluetooth/audio/ascs.c +++ b/subsys/bluetooth/audio/ascs.c @@ -32,6 +32,7 @@ #include "../host/conn_internal.h" #include "audio_internal.h" +#include "audio_iso.h" #include "endpoint.h" #include "unicast_server.h" #include "pacs_internal.h" @@ -54,10 +55,6 @@ struct bt_ascs_ase { struct bt_ascs { struct bt_conn *conn; 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]; @@ -70,33 +67,6 @@ static void ase_status_changed(struct bt_audio_ep *ep, uint8_t old_state, 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) { struct bt_audio_stream *stream; @@ -290,12 +260,6 @@ void ascs_ep_set_state(struct bt_audio_ep *ep, uint8_t state) 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, @@ -438,29 +402,26 @@ static void ascs_iso_recv(struct bt_iso_chan *chan, const struct bt_iso_recv_info *info, struct net_buf *buf) { - struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, - iso_chan); - struct bt_audio_stream *stream = audio_iso->sink_stream; + struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan); const struct bt_audio_stream_ops *ops; + struct bt_audio_stream *stream; + struct bt_audio_ep *ep; - 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"); + ep = iso->rx.ep; + if (ep == NULL) { + BT_ERR("iso %p not bound with ep", chan); return; } - /* Since 2 streams can share the same CIS, the CIS may be connected and - * capable of transferring data, without the bt_audio_stream being in - * the streaming state. In that case we simply ignore the data. - */ - 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); - } + if (ep->status.state != BT_AUDIO_EP_STATE_STREAMING) { + BT_DBG("ep %p is not in the streaming state: %s", + ep, bt_audio_ep_state_str(ep->status.state)); + return; + } + stream = ep->stream; + if (stream == NULL) { + BT_ERR("No stream for ep %p", ep); return; } @@ -468,7 +429,7 @@ static void ascs_iso_recv(struct bt_iso_chan *chan, if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) { 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) { @@ -480,13 +441,27 @@ static void ascs_iso_recv(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, - iso_chan); - struct bt_audio_stream *stream = audio_iso->source_stream; - struct bt_audio_stream_ops *ops = stream->ops; + struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan); + const struct bt_audio_stream_ops *ops; + struct bt_audio_stream *stream; + 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)) { - BT_DBG("stream %p ep %p", stream, stream->ep); + BT_DBG("stream %p ep %p", stream, ep); } if (ops != NULL && ops->sent != NULL) { @@ -509,39 +484,14 @@ static int ase_stream_start(struct bt_audio_stream *stream) 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_ep *ep; 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) { - BT_DBG("endpoint not in enabling state: %s", - bt_audio_ep_state_str(ep->status.state)); + BT_DBG("ep %p not in enabling state: %s", + ep, bt_audio_ep_state_str(ep->status.state)); return; } @@ -549,41 +499,50 @@ static void ascs_iso_connected(struct bt_iso_chan *chan) return; } + stream = ep->stream; + if (stream == NULL) { + BT_ERR("No stream for ep %p", ep); + return; + } + err = ase_stream_start(stream); if (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, - 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; + struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan); - 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 (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_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) { - 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("No stream for ep %p", ep); return; } 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) { 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"); } - ep = stream->ep; 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 */ k_work_submit(&ep->work); } 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 = { .recv = ascs_iso_recv, .sent = ascs_iso_sent, @@ -858,57 +836,54 @@ BT_CONN_CB_DEFINE(conn_cb) = { .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 - * 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 - */ - audio_iso->iso_chan.ops = &ascs_iso_ops; - audio_iso->iso_chan.qos = &audio_iso->iso_qos; + struct audio_iso_find_params *params = user_data; + const struct bt_audio_ep *ep; - audio_iso->iso_chan.qos->tx = &audio_iso->source_io_qos; - audio_iso->iso_chan.qos->tx->path = &audio_iso->source_path; - audio_iso->iso_chan.qos->tx->path->cc = audio_iso->source_path_cc; - - audio_iso->iso_chan.qos->rx = &audio_iso->sink_io_qos; - audio_iso->iso_chan.qos->rx->path = &audio_iso->sink_path; - 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; - } + if (iso->rx.ep != NULL) { + ep = iso->rx.ep; + } else if (iso->tx.ep != NULL) { + ep = iso->tx.ep; + } else { + return false; } - 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, ¶ms); + 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, @@ -918,7 +893,6 @@ static void ase_stream_add(struct bt_ascs *ascs, struct bt_ascs_ase *ase, ase->ep.stream = stream; stream->conn = ascs->conn; stream->ep = &ase->ep; - stream->iso = &ase->ep.iso->iso_chan; } 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_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; const uint8_t ep_state = ep->status.state; struct bt_conn *conn = ase->ascs->conn; @@ -958,10 +931,8 @@ static void ase_process(struct k_work *work) "stream is NULL"); if (ep_state == BT_AUDIO_EP_STATE_RELEASING) { - - if (audio_iso == NULL || - audio_iso->iso_chan.state == BT_ISO_STATE_DISCONNECTED) { - ascs_ep_unbind_audio_iso(ep); + if (ep->iso == NULL || + ep->iso->chan.state == BT_ISO_STATE_DISCONNECTED) { bt_audio_stream_detach(stream); ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_IDLE); } else { @@ -980,8 +951,8 @@ static void ase_process(struct k_work *work) * the CIS is connected */ if (ep->dir == BT_AUDIO_DIR_SINK && - audio_iso != NULL && - audio_iso->iso_chan.state == BT_ISO_STATE_CONNECTED) { + ep->iso != NULL && + ep->iso->chan.state == BT_ISO_STATE_CONNECTED) { 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; } -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) { 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 cis_id) { - struct bt_audio_iso *audio_iso; + struct bt_audio_iso *iso; struct bt_audio_ep *ep; - int err; 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); - if (audio_iso == NULL) { + iso = audio_iso_get_or_new(ascs, cig_id, cis_id); + if (iso == NULL) { BT_ERR("Could not allocate audio_iso"); return -ENOMEM; } - err = ascs_ep_stream_bind_audio_iso(stream, audio_iso); - if (err < 0) { - return err; + if (bt_audio_iso_get_ep(iso, ep->dir) != NULL) { + BT_ERR("iso %p already in use in dir %u", + &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; 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). */ 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); } else { - bt_audio_codec_to_iso_path(&ep->iso->source_path, + bt_audio_codec_to_iso_path(&ep->iso->tx.path, 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) { - struct bt_audio_stream *stream; struct bt_audio_ep *ep; BT_DBG("ase %p", ase); @@ -2001,11 +1944,10 @@ static void ase_start(struct bt_ascs_ase *ase) ep->receiver_ready = true; - stream = ep->stream; - if (stream->iso->state == BT_ISO_STATE_CONNECTED) { + if (ep->iso->chan.state == BT_ISO_STATE_CONNECTED) { int err; - err = ase_stream_start(stream); + err = ase_stream_start(ep->stream); if (err) { BT_ERR("Start failed: %d", err); ascs_cp_rsp_add(ASE_ID(ase), BT_ASCS_START_OP, err, diff --git a/subsys/bluetooth/audio/audio_iso.c b/subsys/bluetooth/audio/audio_iso.c new file mode 100644 index 00000000000..283d59067ae --- /dev/null +++ b/subsys/bluetooth/audio/audio_iso.c @@ -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, ¶m); + + 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; + } +} diff --git a/subsys/bluetooth/audio/audio_iso.h b/subsys/bluetooth/audio/audio_iso.h new file mode 100644 index 00000000000..3392fcc8efa --- /dev/null +++ b/subsys/bluetooth/audio/audio_iso.h @@ -0,0 +1,44 @@ +/* @file + * @brief Internal APIs for Audio ISO handling + * + * Copyright (c) 2022 Codecoup + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +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); diff --git a/subsys/bluetooth/audio/broadcast_sink.c b/subsys/bluetooth/audio/broadcast_sink.c index d1707526c64..b6347ddf4da 100644 --- a/subsys/bluetooth/audio/broadcast_sink.c +++ b/subsys/bluetooth/audio/broadcast_sink.c @@ -18,6 +18,7 @@ #include "../host/conn_internal.h" #include "../host/iso_internal.h" +#include "audio_iso.h" #include "endpoint.h" #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_BROADCAST_SINK) @@ -36,8 +37,6 @@ */ #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 [CONFIG_BT_AUDIO_BROADCAST_SNK_COUNT][BROADCAST_SNK_STREAM_CNT]; 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, struct net_buf *buf) { - struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, - iso_chan); - struct bt_audio_stream *stream = audio_iso->sink_stream; + struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan); const struct bt_audio_stream_ops *ops; + struct bt_audio_stream *stream; + struct bt_audio_ep *ep = iso->rx.ep; - if (stream == NULL) { - BT_ERR("Could not lookup stream by iso %p", chan); + if (ep == NULL) { + BT_ERR("iso %p not bound with ep", chan); 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; } @@ -137,7 +139,7 @@ static void broadcast_sink_iso_recv(struct bt_iso_chan *chan, if (IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_STREAM_DATA)) { 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) { @@ -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) { - struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, - iso_chan); - struct bt_audio_stream *stream = audio_iso->sink_stream; + struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan); const struct bt_audio_stream_ops *ops; + struct bt_audio_stream *stream; + struct bt_audio_ep *ep = iso->rx.ep; - if (stream == NULL) { - BT_ERR("Could not lookup stream by iso %p", chan); + if (ep == NULL) { + BT_ERR("iso %p not bound with ep", chan); 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; } @@ -166,7 +171,7 @@ static void broadcast_sink_iso_connected(struct bt_iso_chan *chan) 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) { 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, uint8_t reason) { - struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, - iso_chan); - struct bt_audio_stream *stream = audio_iso->sink_stream; + struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan); 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; - if (stream == NULL) { - BT_ERR("Could not lookup ep by iso %p", chan); + if (ep == NULL) { + BT_ERR("iso %p not bound with ep", chan); 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; } 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) { ops->stopped(stream); @@ -874,23 +882,13 @@ bool bt_audio_ep_is_broadcast_snk(const struct bt_audio_ep *ep) return false; } -static void broadcast_sink_ep_init(struct bt_audio_ep *ep, - struct bt_audio_iso *iso) +static void broadcast_sink_ep_init(struct bt_audio_ep *ep) { - struct bt_iso_chan *iso_chan; - BT_DBG("ep %p", ep); (void)memset(ep, 0, sizeof(*ep)); ep->dir = BT_AUDIO_DIR_SINK; - ep->iso = iso; - - 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; + ep->iso = NULL; } 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 == NULL) { - /* Initialize - It is up to the caller to allocate the - * stream pointer. - */ - broadcast_sink_ep_init(ep, - &broadcast_sink_iso[index][i]); + broadcast_sink_ep_init(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_codec *codec) { - static struct bt_iso_chan_io_qos sink_chan_io_qos; static struct bt_codec_qos codec_qos; + struct bt_audio_iso *iso; struct bt_audio_ep *ep; if (stream->group != NULL) { @@ -931,18 +925,22 @@ static int bt_audio_broadcast_sink_setup_stream(uint8_t index, 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); - 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; - 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; } @@ -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) { if (stream->ep != NULL) { + bt_audio_iso_unbind_ep(stream->ep->iso, stream->ep); stream->ep->stream = NULL; stream->ep = NULL; } stream->qos = NULL; stream->codec = NULL; - stream->iso = NULL; stream->group = NULL; 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; } - 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); sink->stream_count++; } diff --git a/subsys/bluetooth/audio/broadcast_source.c b/subsys/bluetooth/audio/broadcast_source.c index 76ef1074868..1ea5d6bce2a 100644 --- a/subsys/bluetooth/audio/broadcast_source.c +++ b/subsys/bluetooth/audio/broadcast_source.c @@ -19,10 +19,9 @@ #define LOG_MODULE_NAME bt_audio_broadcast_source #include "common/log.h" +#include "audio_iso.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 [CONFIG_BT_AUDIO_BROADCAST_SRC_COUNT][BROADCAST_STREAM_CNT]; 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) { - struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, - iso_chan); - struct bt_audio_stream *stream = audio_iso->source_stream; - struct bt_audio_stream_ops *ops = stream->ops; + struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan); + const struct bt_audio_stream_ops *ops; + struct bt_audio_stream *stream; + 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)) { 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) { - struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, - iso_chan); - struct bt_audio_stream *stream = audio_iso->source_stream; + struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan); 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) { - BT_ERR("Could not lookup stream by iso %p", chan); + if (ep == NULL) { + BT_ERR("iso %p not bound with ep", chan); 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; } ops = stream->ops; - ep = 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) { - struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, - iso_chan); - struct bt_audio_stream *stream = audio_iso->source_stream; + struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan); const struct bt_audio_stream_ops *ops; + struct bt_audio_stream *stream; + struct bt_audio_ep *ep = iso->tx.ep; - if (stream == NULL) { - BT_ERR("Could not lookup stream by iso %p", chan); + if (ep == NULL) { + BT_ERR("iso %p not bound with ep", chan); 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; } @@ -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); - 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) { ops->stopped(stream); @@ -174,23 +190,13 @@ bool bt_audio_ep_is_broadcast_src(const struct bt_audio_ep *ep) return false; } -static void broadcast_source_ep_init(struct bt_audio_ep *ep, - struct bt_audio_iso *iso) +static void broadcast_source_ep_init(struct bt_audio_ep *ep) { - struct bt_iso_chan *iso_chan; - BT_DBG("ep %p", ep); (void)memset(ep, 0, sizeof(*ep)); ep->dir = BT_AUDIO_DIR_SOURCE; - ep->iso = iso; - - 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; + ep->iso = NULL; } 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 == NULL) { - /* Initialize - It is up to the caller to allocate the - * stream pointer. - */ - broadcast_source_ep_init(ep, &broadcast_source_iso[index][i]); + broadcast_source_ep_init(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_qos *qos) { + struct bt_audio_iso *iso; struct bt_audio_ep *ep; if (stream->group != NULL) { @@ -229,14 +233,22 @@ static int bt_audio_broadcast_source_setup_stream(uint8_t index, 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); - ep->iso->source_stream = stream; 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; } @@ -379,11 +391,11 @@ static void broadcast_source_cleanup(struct bt_audio_broadcast_source *source) struct bt_audio_stream *stream, *next; 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 = NULL; stream->codec = NULL; stream->qos = NULL; - stream->iso = NULL; stream->group = NULL; 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; } - 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); source->stream_count++; } diff --git a/subsys/bluetooth/audio/endpoint.h b/subsys/bluetooth/audio/endpoint.h index 89cb5aab991..1562ac45ab6 100644 --- a/subsys/bluetooth/audio/endpoint.h +++ b/subsys/bluetooth/audio/endpoint.h @@ -32,24 +32,6 @@ struct bt_audio_broadcast_source; struct bt_audio_broadcast_sink; 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 { uint8_t dir; uint8_t cig_id; diff --git a/subsys/bluetooth/audio/stream.c b/subsys/bluetooth/audio/stream.c index 331e093cc7f..495da902a99 100644 --- a/subsys/bluetooth/audio/stream.c +++ b/subsys/bluetooth/audio/stream.c @@ -20,6 +20,7 @@ #include "../host/conn_internal.h" #include "../host/iso_internal.h" +#include "audio_iso.h" #include "endpoint.h" #include "unicast_client_internal.h" #include "unicast_server.h" @@ -83,10 +84,15 @@ void bt_audio_stream_attach(struct bt_conn *conn, stream->codec = codec; stream->ep = ep; ep->stream = stream; +} - if (stream->iso == NULL) { - stream->iso = &ep->iso->iso_chan; +struct bt_iso_chan *bt_audio_stream_iso_chan_get(struct bt_audio_stream *stream) +{ + 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) @@ -109,7 +115,8 @@ int bt_audio_stream_send(struct bt_audio_stream *stream, struct net_buf *buf, /* 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 */ @@ -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 && c->ep->cis_id == info->cis_id) { - *iso_chan = enabling[i]->iso; + *iso_chan = &enabling[i]->ep->iso->chan; enabling[i] = NULL; 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) { - 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) { return -EINVAL; @@ -294,11 +303,11 @@ int bt_audio_stream_disconnect(struct bt_audio_stream *stream) } #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 bt_iso_chan_disconnect(stream->iso); + return bt_iso_chan_disconnect(iso_chan); } 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) { struct bt_iso_cig_param param; - struct bt_audio_stream *stream; uint8_t cis_count; int err; BT_DBG("group %p qos %p", group, qos); cis_count = 0; - SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) { - if (stream->iso != NULL) { - bool already_added = false; - - 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; - } + for (size_t i = 0; i < ARRAY_SIZE(group->cis); i++) { + if (group->cis[i] != NULL) { + cis_count++; } } @@ -431,15 +428,16 @@ static int bt_audio_cig_reconfigure(struct bt_audio_unicast_group *group, const struct bt_codec_qos *qos) { struct bt_iso_cig_param param; - struct bt_audio_stream *stream; uint8_t cis_count; int err; BT_DBG("group %p qos %p", group, qos); cis_count = 0U; - SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) { - group->cis[cis_count++] = stream->iso; + for (size_t i = 0; i < ARRAY_SIZE(group->cis); i++) { + if (group->cis[i] != NULL) { + cis_count++; + } } param.num_cis = cis_count; @@ -468,7 +466,7 @@ static void audio_stream_qos_cleanup(const struct bt_conn *conn, 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; } - if (bt_unicast_client_audio_iso_by_stream(stream) == NULL) { + if (ep->iso == NULL) { /* This can only happen if the stream was somehow added * 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; } - /* 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 */ 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) { 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; } 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: return bt_iso_chan_connect(¶m, 1); case BT_ISO_STATE_CONNECTING: @@ -740,19 +726,178 @@ static bool unicast_group_valid_qos(const struct bt_codec_qos *group_qos, return true; } -static void unicast_group_param_cleanup(struct bt_audio_unicast_group *group, - const struct bt_audio_unicast_group_param params[], - size_t num_param) +static struct bt_audio_iso *get_new_iso(struct bt_audio_unicast_group *group, + struct bt_conn *acl, + enum bt_audio_dir dir) { - for (size_t i = 0U; i < num_param; i++) { - if (!sys_slist_find_and_remove(&group->streams, ¶ms[i].stream->_node)) { - /* We can stop once `sys_slist_find_and_remove` fails as - * that means that the this and the following streams - * were never added to the group - */ + struct bt_audio_stream *stream; + + /* Check if there's already an ISO that can be used for this direction */ + SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) { + __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; } } + + 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[], @@ -809,65 +954,27 @@ int bt_audio_unicast_group_create(struct bt_audio_unicast_group_param params[], } } - unicast_group = NULL; - 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; - } - } - + unicast_group = unicast_group_alloc(); if (unicast_group == NULL) { BT_DBG("Could not allocate any more unicast groups"); return -ENOMEM; } - unicast_group->allocated = true; for (size_t i = 0U; i < num_param; i++) { - sys_slist_t *group_streams = &unicast_group->streams; - const enum bt_audio_dir dir = params[i].dir; - struct bt_iso_chan_qos *iso_qos; - struct bt_audio_stream *stream = params[i].stream; - struct bt_audio_iso *audio_iso; - struct bt_iso_chan_io_qos *io; + err = unicast_group_add_stream(unicast_group, params[i].stream, + params[i].qos, params[i].dir); + if (err < 0) { + BT_DBG("unicast_group_add_stream failed: %d", err); + unicast_group_free(unicast_group); - audio_iso = bt_unicast_client_new_audio_iso(unicast_group, stream, dir); - if (audio_iso == NULL) { - unicast_group_param_cleanup(unicast_group, params, num_param); - - return -ENOMEM; + return err; } - - 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); if (err != 0) { 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; } @@ -885,6 +992,7 @@ int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_gr struct bt_audio_stream *tmp_stream; size_t total_stream_cnt; struct bt_iso_cig *cig; + size_t num_added; int err; CHECKIF(unicast_group == NULL) { @@ -946,31 +1054,36 @@ int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_gr return -EBADMSG; } - for (size_t i = 0U; i < num_param; i++) { - sys_slist_t *group_streams = &unicast_group->streams; - struct bt_audio_stream *stream = params[i].stream; - - stream->unicast_group = unicast_group; - sys_slist_append(group_streams, &stream->_node); - - BT_DBG("Added stream %p to group %p", stream, unicast_group); + for (num_added = 0U; num_added < num_param; num_added++) { + err = unicast_group_add_stream(unicast_group, + params[num_added].stream, + params[num_added].qos, + params[num_added].dir); + if (err < 0) { + BT_DBG("unicast_group_add_stream failed: %d", err); + goto fail; + } } err = bt_audio_cig_reconfigure(unicast_group, group_qos); if (err != 0) { BT_DBG("bt_audio_cig_reconfigure failed: %d", err); - unicast_group_param_cleanup(unicast_group, params, num_param); - - return err; + goto fail; } 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) { - struct bt_audio_stream *stream, *tmp; - CHECKIF(unicast_group == NULL) { BT_DBG("unicast_group is NULL"); 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) { - 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)); + unicast_group_free(unicast_group); return 0; } diff --git a/subsys/bluetooth/audio/stream.h b/subsys/bluetooth/audio/stream.h index 3516da3326c..e56d3a965ee 100644 --- a/subsys/bluetooth/audio/stream.h +++ b/subsys/bluetooth/audio/stream.h @@ -55,3 +55,5 @@ bool bt_audio_valid_stream_qos(const struct bt_audio_stream *stream, const struct bt_codec_qos *qos); 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); diff --git a/subsys/bluetooth/audio/unicast_client.c b/subsys/bluetooth/audio/unicast_client.c index 327af18abb3..bced8d56ab7 100644 --- a/subsys/bluetooth/audio/unicast_client.c +++ b/subsys/bluetooth/audio/unicast_client.c @@ -21,6 +21,7 @@ #include "../host/hci_core.h" #include "../host/conn_internal.h" +#include "audio_iso.h" #include "endpoint.h" #include "pacs_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 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 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, struct net_buf *buf) { - struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, - iso_chan); - struct bt_audio_stream *stream = audio_iso->sink_stream; + struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan); const struct bt_audio_stream_ops *ops; + struct bt_audio_stream *stream; + struct bt_audio_ep *ep = iso->rx.ep; - 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"); + if (ep == NULL) { + BT_ERR("iso %p not bound with ep", chan); return; } - /* Since 2 streams can share the same CIS, the CIS may be connected and - * capable of transferring data, without the bt_audio_stream being in - * the streaming state. In that case we simply ignore the data. - */ - 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); - } + if (ep->status.state != BT_AUDIO_EP_STATE_STREAMING) { + BT_DBG("ep %p is not in the streaming state: %s", + ep, bt_audio_ep_state_str(ep->status.state)); + return; + } + stream = ep->stream; + if (stream == NULL) { + BT_ERR("No stream for ep %p", ep); 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)) { 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) { @@ -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) { - struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso, - iso_chan); - struct bt_audio_stream *stream = audio_iso->source_stream; - struct bt_audio_stream_ops *ops; + struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan); + struct bt_audio_stream *stream; + struct bt_audio_ep *ep = iso->tx.ep; - 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"); + if (ep == NULL) { + BT_ERR("iso %p not bound with ep", chan); 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)) { - BT_DBG("stream %p ep %p", stream, stream->ep); + BT_DBG("stream %p ep %p", stream, ep); } - if (ops != NULL && ops->sent != NULL) { - ops->sent(stream); + if (stream->ops != NULL && stream->ops->sent != NULL) { + stream->ops->sent(stream); } } 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, - 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_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan); struct bt_audio_stream *stream; struct bt_audio_ep *ep; - 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"); + /* FIXME: Handle both EP's */ + ep = iso->tx.ep != NULL ? iso->tx.ep : iso->rx.ep; + if (ep == NULL) { + BT_ERR("iso %p not bound with ep", chan); 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) { BT_DBG("endpoint not in enabling state: %s", bt_audio_ep_state_str(ep->status.state)); 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, uint8_t reason) { - 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; - const struct bt_audio_stream_ops *ops; + struct bt_audio_iso *iso = CONTAINER_OF(chan, struct bt_audio_iso, chan); struct bt_audio_stream *stream; struct bt_audio_ep *ep; - 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; + /* FIXME: Handle both EP's */ + ep = iso->tx.ep != NULL ? iso->tx.ep : iso->rx.ep; + if (ep == NULL) { + BT_ERR("iso %p not bound with ep", chan); + return; } + stream = ep->stream; 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; - ops = stream->ops; - BT_DBG("stream %p ep %p reason 0x%02x", stream, ep, reason); - if (ops != NULL && ops->stopped != NULL) { - ops->stopped(stream); + if (stream->ops != NULL && stream->ops->stopped != NULL) { + stream->ops->stopped(stream); } else { 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, }; -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, uint8_t dir) { @@ -364,60 +246,20 @@ static struct bt_audio_ep *unicast_client_ep_find(struct bt_conn *conn, return 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_new_audio_iso(void) { - const uint8_t group_index = unicast_group->index; - struct bt_audio_iso *free_audio_iso = NULL; + struct bt_audio_iso *audio_iso; - BT_DBG("stream %p dir %u", stream, dir); - - /* Look through the audio_isos and find a free one, or one for the same - * 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; - } - } + audio_iso = bt_audio_iso_new(); + if (audio_iso == NULL) { + return NULL; } - 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, @@ -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 * 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) { /* If the endpoint is a sink, then we need to * reset our TX parameters */ - iso_io_qos = ep->iso->iso_qos.tx; + iso_io_qos = &ep->iso->tx.qos; } else { __ASSERT(false, "Invalid ep->dir: %u", ep->dir); return; @@ -628,7 +470,7 @@ static void unicast_client_ep_qos_state(struct bt_audio_ep *ep, stream->qos->pd); /* 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); } else { /* 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 * 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); } else { /* If the endpoint is a sink, then we need to * 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); } } @@ -1385,7 +1227,7 @@ int bt_unicast_client_ep_qos(struct bt_audio_ep *ep, struct net_buf_simple *buf, 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 " "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); return err; } 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 diff --git a/subsys/bluetooth/audio/unicast_client_internal.h b/subsys/bluetooth/audio/unicast_client_internal.h index 3b548bc8f0d..d8c0cabe9a2 100644 --- a/subsys/bluetooth/audio/unicast_client_internal.h +++ b/subsys/bluetooth/audio/unicast_client_internal.h @@ -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, struct net_buf_simple *buf); -/** - * @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); +struct bt_audio_iso *bt_unicast_client_new_audio_iso(void); diff --git a/subsys/bluetooth/shell/audio.c b/subsys/bluetooth/shell/audio.c index 988cce46ec5..7475539cdc5 100644 --- a/subsys/bluetooth/shell/audio.c +++ b/subsys/bluetooth/shell/audio.c @@ -1814,20 +1814,15 @@ static int cmd_send(const struct shell *sh, size_t argc, char *argv[]) int ret, len; 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) { 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", - len, default_stream->iso->qos->tx->sdu); + len, default_preset->preset.qos.sdu); return -ENOEXEC; } } else { - len = MIN(default_stream->iso->qos->tx->sdu, sizeof(data)); + len = MIN(default_preset->preset.qos.sdu, sizeof(data)); memset(data, 0xff, len); }