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:
parent
1aaf1e522f
commit
3fa456905d
13 changed files with 814 additions and 713 deletions
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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, ¶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,
|
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,
|
||||||
|
|
204
subsys/bluetooth/audio/audio_iso.c
Normal file
204
subsys/bluetooth/audio/audio_iso.c
Normal 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, ¶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;
|
||||||
|
}
|
||||||
|
}
|
44
subsys/bluetooth/audio/audio_iso.h
Normal file
44
subsys/bluetooth/audio/audio_iso.h
Normal 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);
|
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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(¶m, 1);
|
return bt_iso_chan_connect(¶m, 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, ¶ms[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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue