Bluetooth: Audio: Refactor ISO allocation endpoints
Modify how the ISO channels are allocated for the audio streams, as well as how the CIG is allocated for the unicast group. This fixes an issue where the unicast group could not be fully allocated before _after_ the bt_audio_streams had been configured by bt_audio_stream_config, thus making it impossible to create a unicast group before the connections have been established. This leaves us with 3 basic data types: 1) Streams allocated by the application 2) Endpoints that represent ASEs 3) Audio_iso which is used to couple streams and ISO channels. The Unicast Group for the unicast client will now have the same lifetime as the ISO CIG for the central. Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
parent
f6f52d3842
commit
9b15426455
12 changed files with 601 additions and 521 deletions
|
@ -1878,13 +1878,11 @@ int bt_audio_stream_reconfig(struct bt_audio_stream *stream,
|
||||||
*
|
*
|
||||||
* @param conn Connection object
|
* @param conn Connection object
|
||||||
* @param group Unicast group object
|
* @param group Unicast group object
|
||||||
* @param qos Quality of Service configuration
|
|
||||||
*
|
*
|
||||||
* @return 0 in case of success or negative value in case of error.
|
* @return 0 in case of success or negative value in case of error.
|
||||||
*/
|
*/
|
||||||
int bt_audio_stream_qos(struct bt_conn *conn,
|
int bt_audio_stream_qos(struct bt_conn *conn,
|
||||||
struct bt_audio_unicast_group *group,
|
struct bt_audio_unicast_group *group);
|
||||||
struct bt_codec_qos *qos);
|
|
||||||
|
|
||||||
/** @brief Enable Audio Stream
|
/** @brief Enable Audio Stream
|
||||||
*
|
*
|
||||||
|
@ -1998,21 +1996,41 @@ int bt_audio_stream_release(struct bt_audio_stream *stream, bool cache);
|
||||||
int bt_audio_stream_send(struct bt_audio_stream *stream, struct net_buf *buf,
|
int bt_audio_stream_send(struct bt_audio_stream *stream, struct net_buf *buf,
|
||||||
uint32_t seq_num, uint32_t ts);
|
uint32_t seq_num, uint32_t ts);
|
||||||
|
|
||||||
|
/** @brief Parameter struct for the unicast group functions
|
||||||
|
*
|
||||||
|
* Parameter struct for the bt_audio_unicast_group_create() and
|
||||||
|
* bt_audio_unicast_group_add_streams() functions.
|
||||||
|
*/
|
||||||
|
struct bt_audio_unicast_group_param {
|
||||||
|
/** Pointer to a stream object. */
|
||||||
|
struct bt_audio_stream *stream;
|
||||||
|
|
||||||
|
/** The QoS settings for the @ref bt_audio_unicast_group_param.stream. */
|
||||||
|
struct bt_codec_qos *qos;
|
||||||
|
|
||||||
|
/** @brief The direction of the @ref bt_audio_unicast_group_param.stream
|
||||||
|
*
|
||||||
|
* If two streams are being used for the same ACL connection but in
|
||||||
|
* different directions, they may use the same CIS.
|
||||||
|
*/
|
||||||
|
enum bt_audio_dir dir;
|
||||||
|
};
|
||||||
|
|
||||||
/** @brief Create audio unicast group.
|
/** @brief Create audio unicast group.
|
||||||
*
|
*
|
||||||
* Create a new audio unicast group with one or more audio streams as a
|
* Create a new audio unicast group with one or more audio streams as a
|
||||||
* unicast client. Streams in a unicast group shall share the same interval,
|
* unicast client. Streams in a unicast group shall share the same interval,
|
||||||
* framing and latency (see @ref bt_codec_qos).
|
* framing and latency (see @ref bt_codec_qos).
|
||||||
*
|
*
|
||||||
* @param[in] streams Array of stream object pointers being used for
|
* @param[in] params Array of stream parameters being used for
|
||||||
* the group.
|
* the group.
|
||||||
* @param[in] num_stream Number of streams in @p streams.
|
* @param[in] num_param Number of parameters in @p params.
|
||||||
* @param[out] unicast_group Pointer to the unicast group created
|
* @param[out] unicast_group Pointer to the unicast group created
|
||||||
*
|
*
|
||||||
* @return Zero on success or (negative) error code otherwise.
|
* @return Zero on success or (negative) error code otherwise.
|
||||||
*/
|
*/
|
||||||
int bt_audio_unicast_group_create(struct bt_audio_stream *streams[],
|
int bt_audio_unicast_group_create(struct bt_audio_unicast_group_param params[],
|
||||||
size_t num_stream,
|
size_t num_param,
|
||||||
struct bt_audio_unicast_group **unicast_group);
|
struct bt_audio_unicast_group **unicast_group);
|
||||||
|
|
||||||
/** @brief Add streams to a unicast group as a unicast client
|
/** @brief Add streams to a unicast group as a unicast client
|
||||||
|
@ -2025,34 +2043,21 @@ int bt_audio_unicast_group_create(struct bt_audio_stream *streams[],
|
||||||
* This can also be called after the streams have been stopped
|
* This can also be called after the streams have been stopped
|
||||||
* (see bt_audio_stream_ops.stopped()).
|
* (see bt_audio_stream_ops.stopped()).
|
||||||
*
|
*
|
||||||
|
* Once a stream has been added to a unicast group, it cannot be removed.
|
||||||
|
* To remove a stream from a group, the group must be deleted with
|
||||||
|
* bt_audio_unicast_group_delete(), but this will require all streams in the
|
||||||
|
* group to be released first.
|
||||||
|
*
|
||||||
* @param unicast_group Pointer to the unicast group
|
* @param unicast_group Pointer to the unicast group
|
||||||
* @param streams Array of stream object pointers being added to the
|
* @param params Array of stream parameters with streams being added
|
||||||
* group.
|
* to the group.
|
||||||
* @param num_stream Number of streams in @p streams.
|
* @param num_param Number of paramers in @p params.
|
||||||
*
|
*
|
||||||
* @return 0 in case of success or negative value in case of error.
|
* @return 0 in case of success or negative value in case of error.
|
||||||
*/
|
*/
|
||||||
int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_group,
|
int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_group,
|
||||||
struct bt_audio_stream *streams[],
|
struct bt_audio_unicast_group_param params[],
|
||||||
size_t num_stream);
|
size_t num_param);
|
||||||
|
|
||||||
/** @brief Remove streams from a unicast group as a unicast client
|
|
||||||
*
|
|
||||||
* This function can be used to remove streams from a bt_audio_unicast_group.
|
|
||||||
*
|
|
||||||
* This can be called at any time before any of the streams in the
|
|
||||||
* group has been QoS configured (see bt_audio_stream_ops.qos_set()).
|
|
||||||
*
|
|
||||||
* @param unicast_group Pointer to the unicast group
|
|
||||||
* @param streams Array of stream object pointers removed from the
|
|
||||||
* group.
|
|
||||||
* @param num_stream Number of streams in @p streams.
|
|
||||||
*
|
|
||||||
* @return 0 in case of success or negative value in case of error.
|
|
||||||
*/
|
|
||||||
int bt_audio_unicast_group_remove_streams(struct bt_audio_unicast_group *unicast_group,
|
|
||||||
struct bt_audio_stream *streams[],
|
|
||||||
size_t num_stream);
|
|
||||||
|
|
||||||
/** @brief Delete audio unicast group.
|
/** @brief Delete audio unicast group.
|
||||||
*
|
*
|
||||||
|
|
|
@ -327,6 +327,25 @@ static void audio_timer_timeout(struct k_work *work)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static enum bt_audio_dir stream_dir(const struct bt_audio_stream *stream)
|
||||||
|
{
|
||||||
|
for (size_t i = 0U; i < ARRAY_SIZE(sinks); i++) {
|
||||||
|
if (sinks[i].ep != NULL && stream->ep == sinks[i].ep) {
|
||||||
|
return BT_AUDIO_DIR_SINK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0U; i < ARRAY_SIZE(sources); i++) {
|
||||||
|
if (sources[i] != NULL && stream->ep == sources[i]) {
|
||||||
|
return BT_AUDIO_DIR_SOURCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__ASSERT(false, "Invalid stream");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void print_hex(const uint8_t *ptr, size_t len)
|
static void print_hex(const uint8_t *ptr, size_t len)
|
||||||
{
|
{
|
||||||
while (len-- != 0) {
|
while (len-- != 0) {
|
||||||
|
@ -845,14 +864,16 @@ static int configure_streams(void)
|
||||||
|
|
||||||
static int create_group(void)
|
static int create_group(void)
|
||||||
{
|
{
|
||||||
struct bt_audio_stream *streams_p[ARRAY_SIZE(streams)];
|
struct bt_audio_unicast_group_param params[ARRAY_SIZE(streams)];
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
for (size_t i = 0U; i < configured_stream_count; i++) {
|
for (size_t i = 0U; i < configured_stream_count; i++) {
|
||||||
streams_p[i] = &streams[i];
|
params[i].stream = &streams[i];
|
||||||
|
params[i].qos = &codec_configuration.qos;
|
||||||
|
params[i].dir = stream_dir(params[i].stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bt_audio_unicast_group_create(streams_p, configured_stream_count,
|
err = bt_audio_unicast_group_create(params, configured_stream_count,
|
||||||
&unicast_group);
|
&unicast_group);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
printk("Could not create unicast group (err %d)\n", err);
|
printk("Could not create unicast group (err %d)\n", err);
|
||||||
|
@ -866,10 +887,9 @@ static int set_stream_qos(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = bt_audio_stream_qos(default_conn, unicast_group,
|
err = bt_audio_stream_qos(default_conn, unicast_group);
|
||||||
&codec_configuration.qos);
|
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
printk("Unable to setup QoS: %d", err);
|
printk("Unable to setup QoS: %d\n", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ config BT_AUDIO_UNICAST_CLIENT_GROUP_COUNT
|
||||||
int "Basic Audio Unicast Group count"
|
int "Basic Audio Unicast Group count"
|
||||||
depends on BT_AUDIO_UNICAST
|
depends on BT_AUDIO_UNICAST
|
||||||
default BT_ISO_MAX_CIG
|
default BT_ISO_MAX_CIG
|
||||||
range 0 BT_ISO_MAX_CIG
|
range 1 BT_ISO_MAX_CIG
|
||||||
help
|
help
|
||||||
This option sets the number of connected audio groups to support as
|
This option sets the number of connected audio groups to support as
|
||||||
the unicast client.
|
the unicast client.
|
||||||
|
@ -84,8 +84,8 @@ config BT_AUDIO_UNICAST_CLIENT_GROUP_STREAM_COUNT
|
||||||
int "Basic Audio Unicast Group Stream count"
|
int "Basic Audio Unicast Group Stream count"
|
||||||
depends on BT_AUDIO_UNICAST_CLIENT_GROUP_COUNT > 0
|
depends on BT_AUDIO_UNICAST_CLIENT_GROUP_COUNT > 0
|
||||||
default 1
|
default 1
|
||||||
range 0 BT_ISO_MAX_CHAN if BT_ISO_MAX_CHAN < 31
|
range 1 BT_ISO_MAX_CHAN if BT_ISO_MAX_CHAN < 31
|
||||||
range 0 31
|
range 1 31
|
||||||
help
|
help
|
||||||
This option sets the maximum number of streams per unicast group
|
This option sets the maximum number of streams per unicast group
|
||||||
to support.
|
to support.
|
||||||
|
|
|
@ -80,27 +80,28 @@ static void ase_status_changed(struct bt_audio_ep *ep, uint8_t old_state,
|
||||||
static void ascs_ep_unbind_audio_iso(struct bt_audio_ep *ep)
|
static void ascs_ep_unbind_audio_iso(struct bt_audio_ep *ep)
|
||||||
{
|
{
|
||||||
struct bt_audio_iso *audio_iso = ep->iso;
|
struct bt_audio_iso *audio_iso = ep->iso;
|
||||||
struct bt_iso_chan_qos *qos;
|
struct bt_audio_stream *stream = ep->stream;
|
||||||
const uint8_t dir = ep->dir;
|
|
||||||
|
|
||||||
BT_DBG("ep %p, dir %u audio_iso %p", ep, dir, audio_iso);
|
if (audio_iso != NULL) {
|
||||||
|
struct bt_iso_chan_qos *qos;
|
||||||
|
|
||||||
if (audio_iso == NULL) {
|
qos = audio_iso->iso_chan.qos;
|
||||||
return;
|
|
||||||
|
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;
|
ep->iso = NULL;
|
||||||
qos = audio_iso->iso_chan.qos;
|
|
||||||
|
|
||||||
if (dir == BT_AUDIO_DIR_SOURCE) {
|
|
||||||
audio_iso->source_ep = NULL;
|
|
||||||
qos->tx = NULL;
|
|
||||||
} else if (dir == BT_AUDIO_DIR_SINK) {
|
|
||||||
audio_iso->sink_ep = NULL;
|
|
||||||
qos->rx = NULL;
|
|
||||||
} else {
|
|
||||||
__ASSERT(false, "Invalid dir: %u", dir);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
@ -323,23 +324,26 @@ static void ascs_iso_recv(struct bt_iso_chan *chan,
|
||||||
{
|
{
|
||||||
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
||||||
iso_chan);
|
iso_chan);
|
||||||
struct bt_audio_ep *ep = audio_iso->sink_ep;
|
struct bt_audio_stream *stream = audio_iso->sink_stream;
|
||||||
const struct bt_audio_stream_ops *ops;
|
const struct bt_audio_stream_ops *ops;
|
||||||
|
|
||||||
if (ep == NULL) {
|
if (stream == NULL) {
|
||||||
BT_ERR("Could not lookup ep by iso %p", chan);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = ep->stream->ops;
|
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 len %zu",
|
BT_DBG("stream %p ep %p len %zu",
|
||||||
chan, ep, net_buf_frags_len(buf));
|
stream, stream->ep, net_buf_frags_len(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ops != NULL && ops->recv != NULL) {
|
if (ops != NULL && ops->recv != NULL) {
|
||||||
ops->recv(ep->stream, info, buf);
|
ops->recv(stream, info, buf);
|
||||||
} else {
|
} else {
|
||||||
BT_WARN("No callback for recv set");
|
BT_WARN("No callback for recv set");
|
||||||
}
|
}
|
||||||
|
@ -349,13 +353,13 @@ 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 *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
||||||
iso_chan);
|
iso_chan);
|
||||||
struct bt_audio_ep *ep = audio_iso->source_ep;
|
struct bt_audio_stream *stream = audio_iso->source_stream;
|
||||||
struct bt_audio_stream_ops *ops = ep->stream->ops;
|
struct bt_audio_stream_ops *ops = stream->ops;
|
||||||
|
|
||||||
BT_DBG("stream %p ep %p", chan, ep);
|
BT_DBG("stream %p ep %p", stream, stream->ep);
|
||||||
|
|
||||||
if (ops != NULL && ops->sent != NULL) {
|
if (ops != NULL && ops->sent != NULL) {
|
||||||
ops->sent(ep->stream);
|
ops->sent(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,22 +367,28 @@ 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 *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
||||||
iso_chan);
|
iso_chan);
|
||||||
struct bt_audio_ep *source_ep = audio_iso->source_ep;
|
struct bt_audio_stream *source_stream = audio_iso->source_stream;
|
||||||
struct bt_audio_ep *sink_ep = audio_iso->sink_ep;
|
struct bt_audio_stream *sink_stream = audio_iso->sink_stream;
|
||||||
|
struct bt_audio_stream *stream;
|
||||||
struct bt_audio_ep *ep;
|
struct bt_audio_ep *ep;
|
||||||
|
|
||||||
if (&sink_ep->iso->iso_chan == chan) {
|
if (sink_stream->iso == chan) {
|
||||||
ep = sink_ep;
|
stream = sink_stream;
|
||||||
} else {
|
} else {
|
||||||
ep = source_ep;
|
stream = source_stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ep == NULL) {
|
if (stream == NULL) {
|
||||||
BT_ERR("Could not lookup ep by iso %p", chan);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
BT_DBG("stream %p ep %p dir %u", chan, ep, ep != NULL ? ep->dir : 0);
|
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",
|
||||||
|
@ -393,27 +403,29 @@ static void ascs_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 *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
||||||
iso_chan);
|
iso_chan);
|
||||||
struct bt_audio_ep *source_ep = audio_iso->source_ep;
|
struct bt_audio_stream *source_stream = audio_iso->source_stream;
|
||||||
struct bt_audio_ep *sink_ep = audio_iso->sink_ep;
|
struct bt_audio_stream *sink_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_stream *stream;
|
||||||
struct bt_audio_ep *ep;
|
struct bt_audio_ep *ep;
|
||||||
|
|
||||||
if (&sink_ep->iso->iso_chan == chan) {
|
if (sink_stream->iso == chan) {
|
||||||
ep = sink_ep;
|
stream = sink_stream;
|
||||||
} else {
|
} else {
|
||||||
ep = source_ep;
|
stream = source_stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ep == NULL) {
|
if (stream == NULL) {
|
||||||
BT_ERR("Could not lookup ep by iso %p", chan);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = ep->stream->ops;
|
ops = stream->ops;
|
||||||
stream = ep->stream;
|
|
||||||
|
|
||||||
BT_DBG("stream %p ep %p reason 0x%02x", chan, ep, reason);
|
BT_DBG("stream %p ep %p reason 0x%02x", stream, stream->ep, reason);
|
||||||
|
|
||||||
if (ops != NULL && ops->stopped != NULL) {
|
if (ops != NULL && ops->stopped != NULL) {
|
||||||
ops->stopped(stream);
|
ops->stopped(stream);
|
||||||
|
@ -421,6 +433,7 @@ 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) {
|
||||||
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_CODEC_CONFIGURED);
|
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_CODEC_CONFIGURED);
|
||||||
} else {
|
} else {
|
||||||
|
@ -759,46 +772,50 @@ struct bt_audio_iso *ascs_new_audio_iso(const struct bt_audio_stream *stream,
|
||||||
uint8_t cig_id,
|
uint8_t cig_id,
|
||||||
uint8_t cis_id)
|
uint8_t cis_id)
|
||||||
{
|
{
|
||||||
struct bt_conn *acl_conn = stream->conn;
|
const enum bt_audio_dir dir = stream->ep->dir;
|
||||||
struct bt_audio_iso *free_audio_iso;
|
struct bt_audio_iso *free_audio_iso = NULL;
|
||||||
|
|
||||||
BT_DBG("stream %p ascs %p", stream, ascs);
|
BT_DBG("stream %p dir %u ascs %p", stream, dir, ascs);
|
||||||
|
|
||||||
free_audio_iso = NULL;
|
/* Look through the audio_isos and find a free one, or one for the same
|
||||||
|
* ACL connection where the opposite direction is what we want, and is
|
||||||
|
* free.
|
||||||
|
*
|
||||||
|
* The returned audio_iso is either an unused one, or one where @p
|
||||||
|
* stream can be used in the opposite direction.
|
||||||
|
*/
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(ascs->isos); i++) {
|
for (size_t i = 0; i < ARRAY_SIZE(ascs->isos); i++) {
|
||||||
struct bt_audio_iso *audio_iso = &ascs->isos[i];
|
struct bt_audio_iso *audio_iso = &ascs->isos[i];
|
||||||
struct bt_audio_ep *ep;
|
const struct bt_conn *acl_conn = stream->conn;
|
||||||
|
const struct bt_audio_stream *paired_stream;
|
||||||
|
const struct bt_audio_stream *tmp_stream;
|
||||||
|
|
||||||
switch (stream->ep->dir) {
|
switch (dir) {
|
||||||
case BT_AUDIO_DIR_SINK:
|
case BT_AUDIO_DIR_SINK:
|
||||||
ep = audio_iso->sink_ep;
|
tmp_stream = audio_iso->sink_stream;
|
||||||
|
paired_stream = audio_iso->source_stream;
|
||||||
if (ep == NULL) {
|
|
||||||
if (free_audio_iso == NULL) {
|
|
||||||
free_audio_iso = audio_iso;
|
|
||||||
}
|
|
||||||
} else if (ep->cig_id == cig_id &&
|
|
||||||
ep->cis_id == cis_id &&
|
|
||||||
ep->stream->conn == acl_conn) {
|
|
||||||
return audio_iso;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case BT_AUDIO_DIR_SOURCE:
|
case BT_AUDIO_DIR_SOURCE:
|
||||||
ep = audio_iso->source_ep;
|
tmp_stream = audio_iso->source_stream;
|
||||||
|
paired_stream = audio_iso->sink_stream;
|
||||||
if (ep == NULL) {
|
|
||||||
if (free_audio_iso == NULL) {
|
|
||||||
free_audio_iso = audio_iso;
|
|
||||||
}
|
|
||||||
} else if (ep->cig_id == cig_id &&
|
|
||||||
ep->cis_id == cis_id &&
|
|
||||||
ep->stream->conn == acl_conn) {
|
|
||||||
return audio_iso;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tmp_stream == NULL) {
|
||||||
|
if (free_audio_iso == NULL) {
|
||||||
|
free_audio_iso = audio_iso;
|
||||||
|
}
|
||||||
|
} else if (paired_stream != NULL &&
|
||||||
|
paired_stream->ep->cig_id == cig_id &&
|
||||||
|
paired_stream->ep->cis_id == cis_id &&
|
||||||
|
paired_stream->ep->stream->conn == acl_conn) {
|
||||||
|
BT_DBG("Use existing audio_iso %p together with stream %p",
|
||||||
|
audio_iso, paired_stream);
|
||||||
|
|
||||||
|
return audio_iso;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return free_audio_iso;
|
return free_audio_iso;
|
||||||
|
@ -931,40 +948,39 @@ 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 void ascs_ep_bind_audio_iso(struct bt_audio_ep *ep,
|
static void ascs_ep_stream_bind_audio_iso(struct bt_audio_stream *stream,
|
||||||
struct bt_audio_iso *audio_iso)
|
struct bt_audio_iso *audio_iso)
|
||||||
{
|
{
|
||||||
|
const enum bt_audio_dir dir = stream->ep->dir;
|
||||||
struct bt_iso_chan *iso_chan;
|
struct bt_iso_chan *iso_chan;
|
||||||
struct bt_iso_chan_qos *qos;
|
struct bt_iso_chan_qos *qos;
|
||||||
const uint8_t dir = ep->dir;
|
|
||||||
|
|
||||||
BT_DBG("ep %p, dir %u audio_iso %p", ep, dir, audio_iso);
|
BT_DBG("stream %p, dir %u audio_iso %p", stream, dir, audio_iso);
|
||||||
|
|
||||||
ep->iso = audio_iso;
|
iso_chan = &audio_iso->iso_chan;
|
||||||
|
|
||||||
iso_chan = &ep->iso->iso_chan;
|
|
||||||
|
|
||||||
iso_chan->ops = &ascs_iso_ops;
|
iso_chan->ops = &ascs_iso_ops;
|
||||||
qos = iso_chan->qos = &ep->iso->iso_qos;
|
qos = iso_chan->qos = &audio_iso->iso_qos;
|
||||||
|
|
||||||
if (dir == BT_AUDIO_DIR_SOURCE) {
|
if (dir == BT_AUDIO_DIR_SOURCE) {
|
||||||
audio_iso->source_ep = ep;
|
audio_iso->source_stream = stream;
|
||||||
qos->tx = &ep->iso_io_qos;
|
qos->tx = &audio_iso->source_io_qos;
|
||||||
} else if (dir == BT_AUDIO_DIR_SINK) {
|
} else if (dir == BT_AUDIO_DIR_SINK) {
|
||||||
audio_iso->sink_ep = ep;
|
audio_iso->sink_stream = stream;
|
||||||
qos->rx = &ep->iso_io_qos;
|
qos->rx = &audio_iso->sink_io_qos;
|
||||||
} else {
|
} else {
|
||||||
__ASSERT(false, "Invalid dir: %u", dir);
|
__ASSERT(false, "Invalid dir: %u", dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
ep->stream->iso = iso_chan;
|
stream->iso = iso_chan;
|
||||||
|
stream->ep->iso = audio_iso;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
memset(ep, 0, sizeof(*ep));
|
(void)memset(ep, 0, sizeof(*ep));
|
||||||
ep->status.id = id;
|
ep->status.id = id;
|
||||||
ep->dir = ASE_DIR(id);
|
ep->dir = ASE_DIR(id);
|
||||||
}
|
}
|
||||||
|
@ -1348,9 +1364,10 @@ static int ase_stream_qos(struct bt_audio_stream *stream,
|
||||||
struct bt_audio_iso *audio_iso;
|
struct bt_audio_iso *audio_iso;
|
||||||
struct bt_audio_ep *ep;
|
struct bt_audio_ep *ep;
|
||||||
|
|
||||||
BT_DBG("stream %p qos %p", stream, qos);
|
BT_DBG("stream %p ep %p qos %p", stream, stream->ep, qos);
|
||||||
|
|
||||||
if (stream == NULL || stream->ep == NULL || qos == NULL) {
|
if (stream == NULL || stream->ep == NULL || qos == NULL) {
|
||||||
|
BT_DBG("Invalid input stream, ep or qos pointers");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1381,12 +1398,14 @@ static int ase_stream_qos(struct bt_audio_stream *stream,
|
||||||
|
|
||||||
err = unicast_server_cb->qos(stream, qos);
|
err = unicast_server_cb->qos(stream, qos);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
|
BT_DBG("Application returned error: %d", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_iso = ascs_new_audio_iso(stream, ascs, cig_id, cis_id);
|
audio_iso = ascs_new_audio_iso(stream, ascs, cig_id, cis_id);
|
||||||
if (audio_iso == NULL) {
|
if (audio_iso == NULL) {
|
||||||
|
BT_DBG("Could not allocated new audio_iso");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1394,7 +1413,7 @@ static int ase_stream_qos(struct bt_audio_stream *stream,
|
||||||
|
|
||||||
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_QOS_CONFIGURED);
|
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_QOS_CONFIGURED);
|
||||||
|
|
||||||
ascs_ep_bind_audio_iso(ep, audio_iso);
|
ascs_ep_stream_bind_audio_iso(stream, audio_iso);
|
||||||
bt_audio_stream_iso_listen(stream);
|
bt_audio_stream_iso_listen(stream);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -118,23 +118,26 @@ static void broadcast_sink_iso_recv(struct bt_iso_chan *chan,
|
||||||
{
|
{
|
||||||
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
||||||
iso_chan);
|
iso_chan);
|
||||||
struct bt_audio_ep *ep = audio_iso->sink_ep;
|
struct bt_audio_stream *stream = audio_iso->sink_stream;
|
||||||
const struct bt_audio_stream_ops *ops;
|
const struct bt_audio_stream_ops *ops;
|
||||||
|
|
||||||
if (ep == NULL) {
|
if (stream == NULL) {
|
||||||
BT_ERR("Could not lookup ep by iso %p", chan);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = ep->stream->ops;
|
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 len %zu",
|
BT_DBG("stream %p ep %p len %zu",
|
||||||
chan, ep, net_buf_frags_len(buf));
|
stream, stream->ep, net_buf_frags_len(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ops != NULL && ops->recv != NULL) {
|
if (ops != NULL && ops->recv != NULL) {
|
||||||
ops->recv(ep->stream, info, buf);
|
ops->recv(stream, info, buf);
|
||||||
} else {
|
} else {
|
||||||
BT_WARN("No callback for recv set");
|
BT_WARN("No callback for recv set");
|
||||||
}
|
}
|
||||||
|
@ -144,22 +147,25 @@ 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 *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
||||||
iso_chan);
|
iso_chan);
|
||||||
struct bt_audio_ep *ep = audio_iso->sink_ep;
|
struct bt_audio_stream *stream = audio_iso->sink_stream;
|
||||||
const struct bt_audio_stream_ops *ops;
|
const struct bt_audio_stream_ops *ops;
|
||||||
|
|
||||||
if (ep == NULL) {
|
if (stream == NULL) {
|
||||||
BT_ERR("Could not lookup ep by iso %p", chan);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = ep->stream->ops;
|
ops = stream->ops;
|
||||||
|
|
||||||
BT_DBG("stream %p ep %p", chan, ep);
|
BT_DBG("stream %p", stream);
|
||||||
|
|
||||||
broadcast_sink_set_ep_state(ep, BT_AUDIO_EP_STATE_STREAMING);
|
broadcast_sink_set_ep_state(stream->ep, BT_AUDIO_EP_STATE_STREAMING);
|
||||||
|
|
||||||
if (ops != NULL && ops->started != NULL) {
|
if (ops != NULL && ops->started != NULL) {
|
||||||
ops->started(ep->stream);
|
ops->started(stream);
|
||||||
} else {
|
} else {
|
||||||
BT_WARN("No callback for connected set");
|
BT_WARN("No callback for connected set");
|
||||||
}
|
}
|
||||||
|
@ -170,22 +176,23 @@ static void broadcast_sink_iso_disconnected(struct bt_iso_chan *chan,
|
||||||
{
|
{
|
||||||
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
||||||
iso_chan);
|
iso_chan);
|
||||||
struct bt_audio_ep *ep = audio_iso->sink_ep;
|
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_broadcast_sink *sink;
|
struct bt_audio_broadcast_sink *sink;
|
||||||
struct bt_audio_stream *stream;
|
|
||||||
|
|
||||||
if (ep == NULL) {
|
if (stream == NULL) {
|
||||||
BT_ERR("Could not lookup ep by iso %p", chan);
|
BT_ERR("Could not lookup ep by iso %p", chan);
|
||||||
return;
|
return;
|
||||||
|
} else if (stream->ep == NULL) {
|
||||||
|
BT_ERR("Stream not associated with an ep");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = ep->stream->ops;
|
ops = stream->ops;
|
||||||
stream = ep->stream;
|
|
||||||
|
|
||||||
BT_DBG("stream %p ep %p reason 0x%02x", chan, ep, reason);
|
BT_DBG("stream %p ep %p reason 0x%02x", stream, stream->ep, reason);
|
||||||
|
|
||||||
broadcast_sink_set_ep_state(ep, BT_AUDIO_EP_STATE_IDLE);
|
broadcast_sink_set_ep_state(stream->ep, BT_AUDIO_EP_STATE_IDLE);
|
||||||
|
|
||||||
if (ops != NULL && ops->stopped != NULL) {
|
if (ops != NULL && ops->stopped != NULL) {
|
||||||
ops->stopped(stream);
|
ops->stopped(stream);
|
||||||
|
@ -864,13 +871,12 @@ static void broadcast_sink_ep_init(struct bt_audio_ep *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 = iso;
|
||||||
iso->sink_ep = ep;
|
|
||||||
|
|
||||||
iso_chan = &ep->iso->iso_chan;
|
iso_chan = &iso->iso_chan;
|
||||||
|
|
||||||
iso_chan->ops = &broadcast_sink_iso_ops;
|
iso_chan->ops = &broadcast_sink_iso_ops;
|
||||||
iso_chan->qos = &ep->iso->iso_qos;
|
iso_chan->qos = &ep->iso->iso_qos;
|
||||||
iso_chan->qos->rx = &ep->iso_io_qos;
|
iso_chan->qos->rx = &iso->sink_io_qos;
|
||||||
iso_chan->qos->tx = NULL;
|
iso_chan->qos->tx = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -913,6 +919,7 @@ static int bt_audio_broadcast_sink_setup_stream(uint8_t index,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
/* 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.
|
* but the `rx` and `qos` pointers need to be set. This should be fixed.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -127,13 +127,13 @@ 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 *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
||||||
iso_chan);
|
iso_chan);
|
||||||
struct bt_audio_ep *ep = audio_iso->source_ep;
|
struct bt_audio_stream *stream = audio_iso->source_stream;
|
||||||
struct bt_audio_stream_ops *ops = ep->stream->ops;
|
struct bt_audio_stream_ops *ops = stream->ops;
|
||||||
|
|
||||||
BT_DBG("stream %p ep %p", chan, ep);
|
BT_DBG("stream %p ep %p", stream, stream->ep);
|
||||||
|
|
||||||
if (ops != NULL && ops->sent != NULL) {
|
if (ops != NULL && ops->sent != NULL) {
|
||||||
ops->sent(ep->stream);
|
ops->sent(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,22 +141,27 @@ 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 *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
||||||
iso_chan);
|
iso_chan);
|
||||||
struct bt_audio_ep *ep = audio_iso->source_ep;
|
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;
|
||||||
|
|
||||||
if (ep == NULL) {
|
if (stream == NULL) {
|
||||||
BT_ERR("Could not lookup ep by iso %p", chan);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = ep->stream->ops;
|
ops = stream->ops;
|
||||||
|
ep = stream->ep;
|
||||||
|
|
||||||
BT_DBG("stream %p ep %p", chan, ep);
|
BT_DBG("stream %p ep %p", stream, ep);
|
||||||
|
|
||||||
broadcast_source_set_ep_state(ep, BT_AUDIO_EP_STATE_STREAMING);
|
broadcast_source_set_ep_state(ep, BT_AUDIO_EP_STATE_STREAMING);
|
||||||
|
|
||||||
if (ops != NULL && ops->started != NULL) {
|
if (ops != NULL && ops->started != NULL) {
|
||||||
ops->started(ep->stream);
|
ops->started(stream);
|
||||||
} else {
|
} else {
|
||||||
BT_WARN("No callback for connected set");
|
BT_WARN("No callback for connected set");
|
||||||
}
|
}
|
||||||
|
@ -166,21 +171,22 @@ static void broadcast_source_iso_disconnected(struct bt_iso_chan *chan, uint8_t
|
||||||
{
|
{
|
||||||
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
||||||
iso_chan);
|
iso_chan);
|
||||||
struct bt_audio_ep *ep = audio_iso->source_ep;
|
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;
|
|
||||||
|
|
||||||
if (ep == NULL) {
|
if (stream == NULL) {
|
||||||
BT_ERR("Could not lookup ep by iso %p", chan);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = ep->stream->ops;
|
ops = stream->ops;
|
||||||
stream = ep->stream;
|
|
||||||
|
|
||||||
BT_DBG("stream %p ep %p reason 0x%02x", chan, ep, reason);
|
BT_DBG("stream %p ep %p reason 0x%02x", stream, stream->ep, reason);
|
||||||
|
|
||||||
broadcast_source_set_ep_state(ep, BT_AUDIO_EP_STATE_QOS_CONFIGURED);
|
broadcast_source_set_ep_state(stream->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);
|
||||||
|
@ -216,14 +222,13 @@ static void broadcast_source_ep_init(struct bt_audio_ep *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 = iso;
|
||||||
iso->source_ep = ep;
|
|
||||||
|
|
||||||
iso_chan = &ep->iso->iso_chan;
|
iso_chan = &ep->iso->iso_chan;
|
||||||
|
|
||||||
iso_chan->ops = &broadcast_source_iso_ops;
|
iso_chan->ops = &broadcast_source_iso_ops;
|
||||||
iso_chan->qos = &ep->iso->iso_qos;
|
iso_chan->qos = &iso->iso_qos;
|
||||||
iso_chan->qos->rx = NULL;
|
iso_chan->qos->rx = NULL;
|
||||||
iso_chan->qos->tx = &ep->iso_io_qos;
|
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)
|
||||||
|
@ -263,6 +268,7 @@ static int bt_audio_broadcast_source_setup_stream(uint8_t index,
|
||||||
}
|
}
|
||||||
|
|
||||||
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->rx = NULL;
|
stream->iso->qos->rx = NULL;
|
||||||
bt_audio_codec_qos_to_iso_qos(stream->iso->qos->tx, qos);
|
bt_audio_codec_qos_to_iso_qos(stream->iso->qos->tx, qos);
|
||||||
|
|
|
@ -32,8 +32,10 @@ struct bt_audio_ep;
|
||||||
struct bt_audio_iso {
|
struct bt_audio_iso {
|
||||||
struct bt_iso_chan iso_chan;
|
struct bt_iso_chan iso_chan;
|
||||||
struct bt_iso_chan_qos iso_qos;
|
struct bt_iso_chan_qos iso_qos;
|
||||||
struct bt_audio_ep *sink_ep;
|
struct bt_iso_chan_io_qos sink_io_qos;
|
||||||
struct bt_audio_ep *source_ep;
|
struct bt_iso_chan_io_qos source_io_qos;
|
||||||
|
struct bt_audio_stream *sink_stream;
|
||||||
|
struct bt_audio_stream *source_stream;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bt_audio_ep {
|
struct bt_audio_ep {
|
||||||
|
@ -48,7 +50,6 @@ struct bt_audio_ep {
|
||||||
struct bt_codec_qos qos;
|
struct bt_codec_qos qos;
|
||||||
struct bt_codec_qos_pref qos_pref;
|
struct bt_codec_qos_pref qos_pref;
|
||||||
struct bt_audio_iso *iso;
|
struct bt_audio_iso *iso;
|
||||||
struct bt_iso_chan_io_qos iso_io_qos;
|
|
||||||
struct bt_gatt_subscribe_params subscribe;
|
struct bt_gatt_subscribe_params subscribe;
|
||||||
struct bt_gatt_discover_params discover;
|
struct bt_gatt_discover_params discover;
|
||||||
|
|
||||||
|
@ -59,9 +60,10 @@ struct bt_audio_ep {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bt_audio_unicast_group {
|
struct bt_audio_unicast_group {
|
||||||
|
uint8_t index;
|
||||||
bool allocated;
|
bool allocated;
|
||||||
/* QoS used to create the CIG */
|
/* QoS used to create the CIG */
|
||||||
struct bt_codec_qos *qos;
|
const struct bt_codec_qos *qos;
|
||||||
struct bt_iso_cig *cig;
|
struct bt_iso_cig *cig;
|
||||||
/* The ISO API for CIG creation requires an array of pointers to ISO channels */
|
/* The ISO API for CIG creation requires an array of pointers to ISO channels */
|
||||||
struct bt_iso_chan *cis[UNICAST_GROUP_STREAM_CNT];
|
struct bt_iso_chan *cis[UNICAST_GROUP_STREAM_CNT];
|
||||||
|
|
|
@ -400,7 +400,7 @@ static void bt_audio_codec_qos_to_cig_param(struct bt_iso_cig_param *cig_param,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bt_audio_cig_create(struct bt_audio_unicast_group *group,
|
static int bt_audio_cig_create(struct bt_audio_unicast_group *group,
|
||||||
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;
|
struct bt_audio_stream *stream;
|
||||||
|
@ -443,7 +443,7 @@ static int bt_audio_cig_create(struct bt_audio_unicast_group *group,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bt_audio_cig_reconfigure(struct bt_audio_unicast_group *group,
|
static int bt_audio_cig_reconfigure(struct bt_audio_unicast_group *group,
|
||||||
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;
|
struct bt_audio_stream *stream;
|
||||||
|
@ -488,8 +488,7 @@ static void audio_stream_qos_cleanup(const struct bt_conn *conn,
|
||||||
}
|
}
|
||||||
|
|
||||||
int bt_audio_stream_qos(struct bt_conn *conn,
|
int bt_audio_stream_qos(struct bt_conn *conn,
|
||||||
struct bt_audio_unicast_group *group,
|
struct bt_audio_unicast_group *group)
|
||||||
struct bt_codec_qos *qos)
|
|
||||||
{
|
{
|
||||||
struct bt_audio_stream *stream;
|
struct bt_audio_stream *stream;
|
||||||
struct net_buf_simple *buf;
|
struct net_buf_simple *buf;
|
||||||
|
@ -500,7 +499,7 @@ int bt_audio_stream_qos(struct bt_conn *conn,
|
||||||
uint8_t role;
|
uint8_t role;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("conn %p group %p qos %p", conn, group, qos);
|
BT_DBG("conn %p group %p", conn, group);
|
||||||
|
|
||||||
CHECKIF(conn == NULL) {
|
CHECKIF(conn == NULL) {
|
||||||
BT_DBG("conn is NULL");
|
BT_DBG("conn is NULL");
|
||||||
|
@ -512,21 +511,11 @@ int bt_audio_stream_qos(struct bt_conn *conn,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECKIF(qos == NULL) {
|
|
||||||
BT_DBG("qos is NULL");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sys_slist_is_empty(&group->streams)) {
|
if (sys_slist_is_empty(&group->streams)) {
|
||||||
BT_DBG("group stream list is empty");
|
BT_DBG("group stream list is empty");
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECKIF(!bt_audio_valid_qos(qos)) {
|
|
||||||
BT_DBG("Invalid QoS");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
role = conn->role;
|
role = conn->role;
|
||||||
if (role != BT_HCI_ROLE_CENTRAL) {
|
if (role != BT_HCI_ROLE_CENTRAL) {
|
||||||
BT_DBG("Invalid conn role: %u, shall be central", role);
|
BT_DBG("Invalid conn role: %u, shall be central", role);
|
||||||
|
@ -576,7 +565,7 @@ int bt_audio_stream_qos(struct bt_conn *conn,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bt_audio_valid_stream_qos(stream, qos)) {
|
if (!bt_audio_valid_stream_qos(stream, stream->qos)) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -589,6 +578,14 @@ int bt_audio_stream_qos(struct bt_conn *conn,
|
||||||
__ASSERT(false, "invalid endpoint dir: %u", ep->dir);
|
__ASSERT(false, "invalid endpoint dir: %u", ep->dir);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bt_unicast_client_audio_iso_by_stream(stream) == NULL) {
|
||||||
|
/* This can only happen if the stream was somehow added
|
||||||
|
* to a group without the audio_iso being bound to it
|
||||||
|
*/
|
||||||
|
BT_ERR("Could not find audio_iso for stream %p", stream);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!conn_stream_found) {
|
if (!conn_stream_found) {
|
||||||
|
@ -596,68 +593,19 @@ int bt_audio_stream_qos(struct bt_conn *conn,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Setup streams before starting the QoS execution */
|
/* Setup endpoints before starting the QoS execution */
|
||||||
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, node) {
|
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, node) {
|
||||||
struct bt_iso_chan_qos *iso_qos;
|
|
||||||
struct bt_audio_iso *audio_iso;
|
struct bt_audio_iso *audio_iso;
|
||||||
struct bt_iso_chan_io_qos *io;
|
|
||||||
|
|
||||||
if (stream->conn != conn) {
|
if (stream->conn != conn) {
|
||||||
/* Channel not part of this ACL, skip */
|
/* Channel not part of this ACL, skip */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ep = stream->ep;
|
audio_iso = bt_unicast_client_audio_iso_by_stream(stream);
|
||||||
if (ep->iso != NULL) {
|
/* Don't need to check for audio_iso validity as we have already verified that */
|
||||||
/* already setup */
|
|
||||||
BT_DBG("Stream %p ep %p already setup with iso %p",
|
|
||||||
stream, ep, ep->iso);
|
|
||||||
|
|
||||||
continue;
|
bt_unicast_client_ep_bind_audio_iso(stream->ep, audio_iso);
|
||||||
}
|
|
||||||
|
|
||||||
audio_iso = bt_unicast_client_new_audio_iso(stream);
|
|
||||||
if (audio_iso == NULL) {
|
|
||||||
audio_stream_qos_cleanup(conn, group);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
bt_unicast_client_ep_bind_audio_iso(ep, audio_iso);
|
|
||||||
|
|
||||||
iso_qos = stream->iso->qos;
|
|
||||||
|
|
||||||
if (ep->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, qos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create or reconfigure the CIG */
|
|
||||||
if (group->cig == NULL) {
|
|
||||||
err = bt_audio_cig_create(group, qos);
|
|
||||||
if (err != 0) {
|
|
||||||
BT_DBG("bt_audio_cig_create failed: %d", err);
|
|
||||||
audio_stream_qos_cleanup(conn, group);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = bt_audio_cig_reconfigure(group, qos);
|
|
||||||
if (err != 0) {
|
|
||||||
BT_DBG("bt_audio_cig_reconfigure failed: %d", err);
|
|
||||||
audio_stream_qos_cleanup(conn, group);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Generate the control point write */
|
/* Generate the control point write */
|
||||||
|
@ -675,7 +623,7 @@ int bt_audio_stream_qos(struct bt_conn *conn,
|
||||||
|
|
||||||
op->num_ases++;
|
op->num_ases++;
|
||||||
|
|
||||||
err = bt_unicast_client_ep_qos(stream->ep, buf, qos);
|
err = bt_unicast_client_ep_qos(stream->ep, buf, stream->qos);
|
||||||
if (err) {
|
if (err) {
|
||||||
audio_stream_qos_cleanup(conn, group);
|
audio_stream_qos_cleanup(conn, group);
|
||||||
return err;
|
return err;
|
||||||
|
@ -693,13 +641,6 @@ int bt_audio_stream_qos(struct bt_conn *conn,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, node) {
|
|
||||||
stream->qos = qos;
|
|
||||||
|
|
||||||
bt_unicast_client_ep_set_state(stream->ep,
|
|
||||||
BT_AUDIO_EP_STATE_QOS_CONFIGURED);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -824,7 +765,7 @@ int bt_audio_stream_start(struct bt_audio_stream *stream)
|
||||||
uint8_t role;
|
uint8_t role;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("stream %p", stream);
|
BT_DBG("stream %p ep %p", stream, stream == NULL ? NULL : stream->ep);
|
||||||
|
|
||||||
if (stream == NULL || stream->ep == NULL || stream->conn == NULL) {
|
if (stream == NULL || stream->ep == NULL || stream->conn == NULL) {
|
||||||
BT_DBG("Invalid stream");
|
BT_DBG("Invalid stream");
|
||||||
|
@ -971,13 +912,40 @@ int bt_audio_stream_connect(struct bt_audio_stream *stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int bt_audio_unicast_group_create(struct bt_audio_stream *streams[],
|
static bool unicast_group_valid_qos(const struct bt_codec_qos *group_qos,
|
||||||
size_t num_stream,
|
const struct bt_codec_qos *stream_qos)
|
||||||
|
{
|
||||||
|
if (group_qos->framing != stream_qos->framing ||
|
||||||
|
group_qos->interval != stream_qos->interval ||
|
||||||
|
group_qos->latency != stream_qos->latency) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unicast_group_param_cleanup(struct bt_audio_unicast_group *group,
|
||||||
|
const struct bt_audio_unicast_group_param params[],
|
||||||
|
size_t num_param)
|
||||||
|
{
|
||||||
|
for (size_t i = 0U; i < num_param; i++) {
|
||||||
|
if (!sys_slist_find_and_remove(&group->streams, ¶ms[i].stream->node)) {
|
||||||
|
/* We can stop once `sys_slist_find_and_remove` fails as
|
||||||
|
* that means that the this and the following streams
|
||||||
|
* were never added to the group
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_audio_unicast_group_create(struct bt_audio_unicast_group_param params[],
|
||||||
|
size_t num_param,
|
||||||
struct bt_audio_unicast_group **out_unicast_group)
|
struct bt_audio_unicast_group **out_unicast_group)
|
||||||
{
|
{
|
||||||
|
|
||||||
struct bt_audio_unicast_group *unicast_group;
|
struct bt_audio_unicast_group *unicast_group;
|
||||||
uint8_t index;
|
const struct bt_codec_qos *group_qos = NULL;
|
||||||
|
int err;
|
||||||
|
|
||||||
CHECKIF(out_unicast_group == NULL) {
|
CHECKIF(out_unicast_group == NULL) {
|
||||||
BT_DBG("out_unicast_group is NULL");
|
BT_DBG("out_unicast_group is NULL");
|
||||||
|
@ -986,34 +954,51 @@ int bt_audio_unicast_group_create(struct bt_audio_stream *streams[],
|
||||||
/* Set out_unicast_group to NULL until the source has actually been created */
|
/* Set out_unicast_group to NULL until the source has actually been created */
|
||||||
*out_unicast_group = NULL;
|
*out_unicast_group = NULL;
|
||||||
|
|
||||||
CHECKIF(streams == NULL) {
|
CHECKIF(params == NULL) {
|
||||||
BT_DBG("streams is NULL");
|
BT_DBG("streams is NULL");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECKIF(num_stream > UNICAST_GROUP_STREAM_CNT) {
|
CHECKIF(num_param > UNICAST_GROUP_STREAM_CNT) {
|
||||||
BT_DBG("Too many streams provided: %u/%u",
|
BT_DBG("Too many streams provided: %u/%u",
|
||||||
num_stream, UNICAST_GROUP_STREAM_CNT);
|
num_param, UNICAST_GROUP_STREAM_CNT);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < num_stream; i++) {
|
for (size_t i = 0U; i < num_param; i++) {
|
||||||
CHECKIF(streams[i] == NULL) {
|
CHECKIF(params[i].stream == NULL ||
|
||||||
|
params[i].qos == NULL ||
|
||||||
|
(params[i].dir != BT_AUDIO_DIR_SINK &&
|
||||||
|
params[i].dir != BT_AUDIO_DIR_SOURCE)) {
|
||||||
|
BT_DBG("Invalid params[%zu] values", i);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (streams[i]->group != NULL) {
|
if (params[i].stream->group != NULL) {
|
||||||
BT_DBG("Channel[%u] (%p) already part of group %p",
|
BT_DBG("params[%zu] stream (%p) already part of group %p",
|
||||||
i, streams[i], streams[i]->group);
|
i, params[i].stream, params[i].stream->group);
|
||||||
return -EALREADY;
|
return -EALREADY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (group_qos == NULL) {
|
||||||
|
group_qos = params[i].qos;
|
||||||
|
} else if (!unicast_group_valid_qos(group_qos, params[i].qos)) {
|
||||||
|
BT_DBG("Stream[%zu] QoS incompatible with group QoS", i);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECKIF(!bt_audio_valid_qos(params[i].qos)) {
|
||||||
|
BT_DBG("Invalid QoS");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unicast_group = NULL;
|
unicast_group = NULL;
|
||||||
for (index = 0; index < ARRAY_SIZE(unicast_groups); index++) {
|
for (size_t i = 0U; i < ARRAY_SIZE(unicast_groups); i++) {
|
||||||
/* Find free entry */
|
/* Find free entry */
|
||||||
if (!unicast_groups[index].allocated) {
|
if (!unicast_groups[i].allocated) {
|
||||||
unicast_group = &unicast_groups[index];
|
unicast_group = &unicast_groups[i];
|
||||||
|
unicast_group->index = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1024,13 +1009,51 @@ int bt_audio_unicast_group_create(struct bt_audio_stream *streams[],
|
||||||
}
|
}
|
||||||
|
|
||||||
unicast_group->allocated = true;
|
unicast_group->allocated = true;
|
||||||
for (size_t i = 0; i < num_stream; i++) {
|
for (size_t i = 0U; i < num_param; i++) {
|
||||||
sys_slist_t *group_streams = &unicast_group->streams;
|
sys_slist_t *group_streams = &unicast_group->streams;
|
||||||
struct bt_audio_stream *stream;
|
const enum bt_audio_dir dir = params[i].dir;
|
||||||
|
struct bt_iso_chan_qos *iso_qos;
|
||||||
|
struct bt_audio_stream *stream = params[i].stream;
|
||||||
|
struct bt_audio_iso *audio_iso;
|
||||||
|
struct bt_iso_chan_io_qos *io;
|
||||||
|
|
||||||
stream = streams[i];
|
audio_iso = bt_unicast_client_new_audio_iso(unicast_group, stream, dir);
|
||||||
|
if (audio_iso == NULL) {
|
||||||
|
unicast_group_param_cleanup(unicast_group, params, num_param);
|
||||||
|
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
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->unicast_group = unicast_group;
|
||||||
|
stream->qos = params[i].qos;
|
||||||
sys_slist_append(group_streams, &stream->node);
|
sys_slist_append(group_streams, &stream->node);
|
||||||
|
|
||||||
|
BT_DBG("Added stream %p to group %p", stream, unicast_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bt_audio_cig_create(unicast_group, group_qos);
|
||||||
|
if (err != 0) {
|
||||||
|
BT_DBG("bt_audio_cig_create failed: %d", err);
|
||||||
|
unicast_group_param_cleanup(unicast_group, params, num_param);
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
*out_unicast_group = unicast_group;
|
*out_unicast_group = unicast_group;
|
||||||
|
@ -1039,35 +1062,54 @@ int bt_audio_unicast_group_create(struct bt_audio_stream *streams[],
|
||||||
}
|
}
|
||||||
|
|
||||||
int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_group,
|
int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_group,
|
||||||
struct bt_audio_stream *streams[],
|
struct bt_audio_unicast_group_param params[],
|
||||||
size_t num_stream)
|
size_t num_param)
|
||||||
{
|
{
|
||||||
|
const struct bt_codec_qos *group_qos = unicast_group->qos;
|
||||||
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;
|
||||||
|
int err;
|
||||||
|
|
||||||
CHECKIF(unicast_group == NULL) {
|
CHECKIF(unicast_group == NULL) {
|
||||||
BT_DBG("unicast_group is NULL");
|
BT_DBG("unicast_group is NULL");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECKIF(streams == NULL) {
|
CHECKIF(params == NULL) {
|
||||||
BT_DBG("streams is NULL");
|
BT_DBG("params is NULL");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECKIF(num_stream == 0) {
|
CHECKIF(num_param == 0) {
|
||||||
BT_DBG("num_stream is 0");
|
BT_DBG("num_param is 0");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < num_stream; i++) {
|
for (size_t i = 0U; i < num_param; i++) {
|
||||||
CHECKIF(streams[i] == NULL) {
|
CHECKIF(params[i].stream == NULL ||
|
||||||
|
params[i].qos == NULL ||
|
||||||
|
(params[i].dir != BT_AUDIO_DIR_SINK &&
|
||||||
|
params[i].dir != BT_AUDIO_DIR_SOURCE)) {
|
||||||
|
BT_DBG("Invalid params[%zu] values", i);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params[i].stream->group != NULL) {
|
||||||
|
BT_DBG("params[%zu] stream (%p) already part of group %p",
|
||||||
|
i, params[i].stream, params[i].stream->group);
|
||||||
|
return -EALREADY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group_qos == NULL) {
|
||||||
|
group_qos = params[i].qos;
|
||||||
|
} else if (!unicast_group_valid_qos(group_qos, params[i].qos)) {
|
||||||
|
BT_DBG("Stream[%zu] QoS incompatible with group QoS", i);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
total_stream_cnt = num_stream;
|
total_stream_cnt = num_param;
|
||||||
SYS_SLIST_FOR_EACH_CONTAINER(&unicast_group->streams, tmp_stream, node) {
|
SYS_SLIST_FOR_EACH_CONTAINER(&unicast_group->streams, tmp_stream, node) {
|
||||||
total_stream_cnt++;
|
total_stream_cnt++;
|
||||||
}
|
}
|
||||||
|
@ -1079,15 +1121,6 @@ int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_gr
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validate input */
|
|
||||||
for (size_t i = 0; i < num_stream; i++) {
|
|
||||||
if (streams[i]->group != NULL) {
|
|
||||||
BT_DBG("stream[%zu] is already part of group %p",
|
|
||||||
i, streams[i]->group);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We can just check the CIG state to see if any streams have started as
|
/* We can just check the CIG state to see if any streams have started as
|
||||||
* that would start the ISO connection procedure
|
* that would start the ISO connection procedure
|
||||||
*/
|
*/
|
||||||
|
@ -1097,67 +1130,22 @@ int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_gr
|
||||||
return -EBADMSG;
|
return -EBADMSG;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < num_stream; i++) {
|
for (size_t i = 0U; i < num_param; i++) {
|
||||||
sys_slist_t *group_streams = &unicast_group->streams;
|
sys_slist_t *group_streams = &unicast_group->streams;
|
||||||
struct bt_audio_stream *stream = streams[i];
|
struct bt_audio_stream *stream = params[i].stream;
|
||||||
|
|
||||||
stream->unicast_group = unicast_group;
|
stream->unicast_group = unicast_group;
|
||||||
sys_slist_append(group_streams, &stream->node);
|
sys_slist_append(group_streams, &stream->node);
|
||||||
|
|
||||||
|
BT_DBG("Added stream %p to group %p", stream, unicast_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
err = bt_audio_cig_reconfigure(unicast_group, group_qos);
|
||||||
}
|
if (err != 0) {
|
||||||
|
BT_DBG("bt_audio_cig_reconfigure failed: %d", err);
|
||||||
|
unicast_group_param_cleanup(unicast_group, params, num_param);
|
||||||
|
|
||||||
int bt_audio_unicast_group_remove_streams(struct bt_audio_unicast_group *unicast_group,
|
return err;
|
||||||
struct bt_audio_stream *streams[],
|
|
||||||
size_t num_stream)
|
|
||||||
{
|
|
||||||
struct bt_iso_cig *cig;
|
|
||||||
|
|
||||||
CHECKIF(unicast_group == NULL) {
|
|
||||||
BT_DBG("unicast_group is NULL");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
CHECKIF(streams == NULL) {
|
|
||||||
BT_DBG("streams is NULL");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
CHECKIF(num_stream == 0) {
|
|
||||||
BT_DBG("num_stream is 0");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validate input */
|
|
||||||
for (size_t i = 0; i < num_stream; i++) {
|
|
||||||
CHECKIF(streams[i] == NULL) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (streams[i]->group != unicast_group) {
|
|
||||||
BT_DBG("stream[%zu] group %p is not group %p",
|
|
||||||
i, streams[i]->group, unicast_group);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We can just check the CIG state to see if any streams have started as
|
|
||||||
* that would start the ISO connection procedure
|
|
||||||
*/
|
|
||||||
cig = unicast_group->cig;
|
|
||||||
if (cig != NULL) {
|
|
||||||
BT_DBG("At least one unicast group stream has configured QoS");
|
|
||||||
return -EBADMSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < num_stream; i++) {
|
|
||||||
sys_slist_t *group_streams = &unicast_group->streams;
|
|
||||||
struct bt_audio_stream *stream = streams[i];
|
|
||||||
|
|
||||||
stream->unicast_group = NULL;
|
|
||||||
(void)sys_slist_find_and_remove(group_streams,
|
|
||||||
&stream->node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1165,7 +1153,7 @@ int bt_audio_unicast_group_remove_streams(struct bt_audio_unicast_group *unicast
|
||||||
|
|
||||||
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;
|
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");
|
||||||
|
@ -1183,8 +1171,10 @@ int bt_audio_unicast_group_delete(struct bt_audio_unicast_group *unicast_group)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SYS_SLIST_FOR_EACH_CONTAINER(&unicast_group->streams, stream, node) {
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_group->streams, stream, tmp, node) {
|
||||||
stream->unicast_group = NULL;
|
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));
|
(void)memset(unicast_group, 0, sizeof(*unicast_group));
|
||||||
|
|
|
@ -47,8 +47,8 @@ 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 isos[CONFIG_BT_MAX_CONN][CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SNK_COUNT +
|
static struct bt_audio_iso audio_isos[CONFIG_BT_AUDIO_UNICAST_CLIENT_GROUP_COUNT]
|
||||||
CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SRC_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_discover_params cp_disc[CONFIG_BT_MAX_CONN];
|
static struct bt_gatt_discover_params cp_disc[CONFIG_BT_MAX_CONN];
|
||||||
|
@ -70,23 +70,26 @@ static void unicast_client_ep_iso_recv(struct bt_iso_chan *chan,
|
||||||
{
|
{
|
||||||
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
||||||
iso_chan);
|
iso_chan);
|
||||||
struct bt_audio_ep *ep = audio_iso->sink_ep;
|
struct bt_audio_stream *stream = audio_iso->sink_stream;
|
||||||
const struct bt_audio_stream_ops *ops;
|
const struct bt_audio_stream_ops *ops;
|
||||||
|
|
||||||
if (ep == NULL) {
|
if (stream == NULL) {
|
||||||
BT_ERR("Could not lookup ep by iso %p", chan);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = ep->stream->ops;
|
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 len %zu",
|
BT_DBG("stream %p ep %p len %zu",
|
||||||
chan, ep, net_buf_frags_len(buf));
|
stream, stream->ep, net_buf_frags_len(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ops != NULL && ops->recv != NULL) {
|
if (ops != NULL && ops->recv != NULL) {
|
||||||
ops->recv(ep->stream, info, buf);
|
ops->recv(stream, info, buf);
|
||||||
} else {
|
} else {
|
||||||
BT_WARN("No callback for recv set");
|
BT_WARN("No callback for recv set");
|
||||||
}
|
}
|
||||||
|
@ -96,13 +99,23 @@ 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 *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
||||||
iso_chan);
|
iso_chan);
|
||||||
struct bt_audio_ep *ep = audio_iso->source_ep;
|
struct bt_audio_stream *stream = audio_iso->source_stream;
|
||||||
struct bt_audio_stream_ops *ops = ep->stream->ops;
|
struct bt_audio_stream_ops *ops;
|
||||||
|
|
||||||
BT_DBG("stream %p ep %p", chan, ep);
|
if (stream == NULL) {
|
||||||
|
BT_ERR("Could not lookup stream by iso %p", chan);
|
||||||
|
return;
|
||||||
|
} else if (stream->ep == NULL) {
|
||||||
|
BT_ERR("Stream not associated with an ep");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ops = stream->ops;
|
||||||
|
|
||||||
|
BT_DBG("stream %p", stream);
|
||||||
|
|
||||||
if (ops != NULL && ops->sent != NULL) {
|
if (ops != NULL && ops->sent != NULL) {
|
||||||
ops->sent(ep->stream);
|
ops->sent(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,22 +123,28 @@ 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 *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
||||||
iso_chan);
|
iso_chan);
|
||||||
struct bt_audio_ep *source_ep = audio_iso->source_ep;
|
struct bt_audio_stream *source_stream = audio_iso->source_stream;
|
||||||
struct bt_audio_ep *sink_ep = audio_iso->sink_ep;
|
struct bt_audio_stream *sink_stream = audio_iso->sink_stream;
|
||||||
|
struct bt_audio_stream *stream;
|
||||||
struct bt_audio_ep *ep;
|
struct bt_audio_ep *ep;
|
||||||
|
|
||||||
if (&sink_ep->iso->iso_chan == chan) {
|
if (sink_stream->iso == chan) {
|
||||||
ep = sink_ep;
|
stream = sink_stream;
|
||||||
} else {
|
} else {
|
||||||
ep = source_ep;
|
stream = source_stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ep == NULL) {
|
if (stream == NULL) {
|
||||||
BT_ERR("Could not lookup ep by iso %p", chan);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
BT_DBG("stream %p ep %p dir %u", chan, ep, ep != NULL ? ep->dir : 0);
|
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",
|
||||||
|
@ -139,27 +158,30 @@ static void unicast_client_ep_iso_disconnected(struct bt_iso_chan *chan,
|
||||||
{
|
{
|
||||||
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
struct bt_audio_iso *audio_iso = CONTAINER_OF(chan, struct bt_audio_iso,
|
||||||
iso_chan);
|
iso_chan);
|
||||||
struct bt_audio_ep *source_ep = audio_iso->source_ep;
|
struct bt_audio_stream *source_stream = audio_iso->source_stream;
|
||||||
struct bt_audio_ep *sink_ep = audio_iso->sink_ep;
|
struct bt_audio_stream *sink_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_stream *stream;
|
||||||
struct bt_audio_ep *ep;
|
struct bt_audio_ep *ep;
|
||||||
|
|
||||||
if (&sink_ep->iso->iso_chan == chan) {
|
if (sink_stream->iso == chan) {
|
||||||
ep = sink_ep;
|
stream = sink_stream;
|
||||||
} else {
|
} else {
|
||||||
ep = source_ep;
|
stream = source_stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ep == NULL) {
|
if (stream == NULL) {
|
||||||
BT_ERR("Could not lookup ep by iso %p", chan);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = ep->stream->ops;
|
ep = stream->ep;
|
||||||
stream = ep->stream;
|
ops = stream->ops;
|
||||||
|
|
||||||
BT_DBG("stream %p ep %p reason 0x%02x", chan, 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);
|
||||||
|
@ -177,72 +199,95 @@ 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)
|
void bt_unicast_client_ep_unbind_audio_iso(struct bt_audio_ep *ep)
|
||||||
{
|
{
|
||||||
struct bt_audio_iso *audio_iso = ep->iso;
|
|
||||||
struct bt_iso_chan_qos *qos;
|
|
||||||
const uint8_t dir = ep->dir;
|
|
||||||
|
|
||||||
BT_DBG("ep %p dir %u audio_iso %p", ep, ep->dir, audio_iso);
|
|
||||||
|
|
||||||
if (audio_iso == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ep->iso = NULL;
|
ep->iso = NULL;
|
||||||
qos = audio_iso->iso_chan.qos;
|
|
||||||
|
|
||||||
if (dir == BT_AUDIO_DIR_SOURCE) {
|
|
||||||
/* If the endpoint is a source, then we need to
|
|
||||||
* reset our RX parameters
|
|
||||||
*/
|
|
||||||
audio_iso->sink_ep = NULL;
|
|
||||||
qos->rx = NULL;
|
|
||||||
} else if (dir == BT_AUDIO_DIR_SINK) {
|
|
||||||
/* If the endpoint is a sink, then we need to
|
|
||||||
* reset our TX parameters
|
|
||||||
*/
|
|
||||||
audio_iso->source_ep = NULL;
|
|
||||||
qos->tx = NULL;
|
|
||||||
} else {
|
|
||||||
__ASSERT(false, "Invalid dir: %u", dir);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_unicast_client_ep_bind_audio_iso(struct bt_audio_ep *ep,
|
void bt_unicast_client_ep_bind_audio_iso(struct bt_audio_ep *ep,
|
||||||
struct bt_audio_iso *audio_iso)
|
struct bt_audio_iso *audio_iso)
|
||||||
{
|
{
|
||||||
struct bt_iso_chan *iso_chan;
|
BT_DBG("ep %p audio_iso %p", ep, audio_iso);
|
||||||
struct bt_iso_chan_qos *qos;
|
|
||||||
const uint8_t dir = ep->dir;
|
|
||||||
|
|
||||||
ep->iso = audio_iso;
|
ep->iso = audio_iso;
|
||||||
|
}
|
||||||
|
|
||||||
iso_chan = &ep->iso->iso_chan;
|
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;
|
iso_chan->ops = &unicast_client_iso_ops;
|
||||||
qos = iso_chan->qos = &ep->iso->iso_qos;
|
qos = iso_chan->qos = &audio_iso->iso_qos;
|
||||||
|
|
||||||
BT_DBG("ep %p, audio_iso %p, iso_chan %p, dir %u",
|
|
||||||
ep, audio_iso, iso_chan, ep->dir);
|
|
||||||
|
|
||||||
if (dir == BT_AUDIO_DIR_SOURCE) {
|
if (dir == BT_AUDIO_DIR_SOURCE) {
|
||||||
/* 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
|
||||||
*/
|
*/
|
||||||
audio_iso->sink_ep = ep;
|
audio_iso->sink_stream = stream;
|
||||||
qos->rx = &ep->iso_io_qos;
|
qos->rx = &audio_iso->sink_io_qos;
|
||||||
} else if (dir == BT_AUDIO_DIR_SINK) {
|
} else if (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
|
||||||
* configure our TX parameters
|
* configure our TX parameters
|
||||||
*/
|
*/
|
||||||
audio_iso->source_ep = ep;
|
audio_iso->source_stream = stream;
|
||||||
qos->tx = &ep->iso_io_qos;
|
qos->tx = &audio_iso->source_io_qos;
|
||||||
} else {
|
} else {
|
||||||
__ASSERT(false, "Invalid dir: %u", dir);
|
__ASSERT(false, "Invalid dir: %u", dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
ep->stream->iso = iso_chan;
|
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,
|
||||||
|
@ -286,49 +331,59 @@ 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(const struct bt_audio_stream *stream)
|
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)
|
||||||
{
|
{
|
||||||
const struct bt_audio_unicast_group *unicast_group = stream->unicast_group;
|
const uint8_t group_index = unicast_group->index;
|
||||||
struct bt_conn *acl_conn = stream->conn;
|
struct bt_audio_iso *free_audio_iso = NULL;
|
||||||
const uint8_t index = bt_conn_index(acl_conn);
|
|
||||||
struct bt_audio_iso *cache = isos[index];
|
|
||||||
struct bt_audio_iso *free_audio_iso;
|
|
||||||
|
|
||||||
free_audio_iso = NULL;
|
BT_DBG("stream %p dir %u", stream, dir);
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(isos[index]); i++) {
|
|
||||||
struct bt_audio_iso *audio_iso = &cache[i];
|
|
||||||
struct bt_audio_ep *ep;
|
|
||||||
|
|
||||||
switch (stream->ep->dir) {
|
/* Look through the audio_isos and find a free one, or one for the same
|
||||||
|
* ACL connection where the opposite direction is what we want, and is
|
||||||
|
* free.
|
||||||
|
*
|
||||||
|
* The returned audio_iso is either an unused one, or one where @p
|
||||||
|
* stream can be used in the opposite direction.
|
||||||
|
*/
|
||||||
|
for (size_t i = 0U; i < ARRAY_SIZE(audio_isos[group_index]); i++) {
|
||||||
|
struct bt_audio_iso *audio_iso = &audio_isos[group_index][i];
|
||||||
|
const struct bt_conn *acl_conn = stream->conn;
|
||||||
|
const struct bt_audio_stream *paired_stream;
|
||||||
|
const struct bt_audio_stream *tmp_stream;
|
||||||
|
|
||||||
|
switch (dir) {
|
||||||
case BT_AUDIO_DIR_SINK:
|
case BT_AUDIO_DIR_SINK:
|
||||||
ep = audio_iso->source_ep;
|
tmp_stream = audio_iso->source_stream;
|
||||||
|
paired_stream = audio_iso->sink_stream;
|
||||||
if (ep == NULL) {
|
|
||||||
if (free_audio_iso == NULL) {
|
|
||||||
free_audio_iso = audio_iso;
|
|
||||||
}
|
|
||||||
} else if (ep->unicast_group == unicast_group &&
|
|
||||||
ep->stream->conn == acl_conn) {
|
|
||||||
return audio_iso;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case BT_AUDIO_DIR_SOURCE:
|
case BT_AUDIO_DIR_SOURCE:
|
||||||
ep = audio_iso->sink_ep;
|
tmp_stream = audio_iso->sink_stream;
|
||||||
|
paired_stream = audio_iso->source_stream;
|
||||||
if (ep == NULL) {
|
|
||||||
if (free_audio_iso == NULL) {
|
|
||||||
free_audio_iso = audio_iso;
|
|
||||||
}
|
|
||||||
} else if (ep->unicast_group == unicast_group &&
|
|
||||||
ep->stream->conn == acl_conn) {
|
|
||||||
return audio_iso;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return NULL;
|
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);
|
||||||
|
|
||||||
return free_audio_iso;
|
return free_audio_iso;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,20 +459,20 @@ static void unicast_client_ep_idle_state(struct bt_audio_ep *ep,
|
||||||
|
|
||||||
static void unicast_client_ep_qos_reset(struct bt_audio_ep *ep)
|
static void unicast_client_ep_qos_reset(struct bt_audio_ep *ep)
|
||||||
{
|
{
|
||||||
BT_DBG("ep %p dir %u", ep, ep->dir);
|
BT_DBG("ep %p dir %u audio_iso %p", ep, ep->dir, ep->iso);
|
||||||
|
|
||||||
if (ep->dir == BT_AUDIO_DIR_SOURCE) {
|
if (ep->dir == BT_AUDIO_DIR_SOURCE) {
|
||||||
/* If the endpoint is a source, then we need to
|
/* If the endpoint is a source, then we need to
|
||||||
* configure our RX parameters
|
* reset our RX parameters
|
||||||
*/
|
*/
|
||||||
ep->iso->iso_qos.rx = memset(&ep->iso_io_qos, 0,
|
ep->iso->iso_qos.rx = memset(&ep->iso->sink_io_qos, 0,
|
||||||
sizeof(ep->iso_io_qos));
|
sizeof(ep->iso->sink_io_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
|
||||||
* configure our TX parameters
|
* reset our TX parameters
|
||||||
*/
|
*/
|
||||||
ep->iso->iso_qos.tx = memset(&ep->iso_io_qos, 0,
|
ep->iso->iso_qos.tx = memset(&ep->iso->source_io_qos, 0,
|
||||||
sizeof(ep->iso_io_qos));
|
sizeof(ep->iso->source_io_qos));
|
||||||
} else {
|
} else {
|
||||||
__ASSERT(false, "Invalid ep->dir: %u", ep->dir);
|
__ASSERT(false, "Invalid ep->dir: %u", ep->dir);
|
||||||
}
|
}
|
||||||
|
@ -443,8 +498,12 @@ static void unicast_client_ep_config_state(struct bt_audio_ep *ep,
|
||||||
|
|
||||||
cfg = net_buf_simple_pull_mem(buf, sizeof(*cfg));
|
cfg = net_buf_simple_pull_mem(buf, sizeof(*cfg));
|
||||||
|
|
||||||
if (stream->codec == NULL || stream->codec->id != cfg->codec.id) {
|
if (stream->codec == NULL) {
|
||||||
BT_ERR("Codec configuration mismatched");
|
BT_ERR("Stream %p does not have a codec configured", stream);
|
||||||
|
return;
|
||||||
|
} else if (stream->codec->id != cfg->codec.id) {
|
||||||
|
BT_ERR("Codec configuration mismatched: %u, %u",
|
||||||
|
stream->codec->id, cfg->codec.id);
|
||||||
/* TODO: Release the stream? */
|
/* TODO: Release the stream? */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1422,18 +1481,6 @@ static void unicast_client_reset(struct bt_audio_ep *ep)
|
||||||
|
|
||||||
bt_audio_stream_reset(ep->stream);
|
bt_audio_stream_reset(ep->stream);
|
||||||
|
|
||||||
switch (ep->dir) {
|
|
||||||
case BT_AUDIO_DIR_SINK:
|
|
||||||
ep->iso->source_ep = NULL;
|
|
||||||
break;
|
|
||||||
case BT_AUDIO_DIR_SOURCE:
|
|
||||||
ep->iso->sink_ep = NULL;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BT_DBG("Invalid ep->dir %u", ep->dir);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
(void)memset(ep, 0, offsetof(struct bt_audio_ep, subscribe));
|
(void)memset(ep, 0, offsetof(struct bt_audio_ep, subscribe));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,9 +54,18 @@ void bt_unicast_client_ep_detach(struct bt_audio_ep *ep, struct bt_audio_stream
|
||||||
*
|
*
|
||||||
* @return An audio_iso struct fit for the stream, or NULL.
|
* @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_stream *stream);
|
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,
|
void bt_unicast_client_ep_bind_audio_iso(struct bt_audio_ep *ep,
|
||||||
struct bt_audio_iso *iso);
|
struct bt_audio_iso *audio_iso);
|
||||||
|
|
||||||
void bt_unicast_client_ep_unbind_audio_iso(struct bt_audio_ep *ep);
|
void bt_unicast_client_ep_unbind_audio_iso(struct bt_audio_ep *ep);
|
||||||
|
|
|
@ -37,6 +37,8 @@ static struct bt_audio_unicast_group *default_unicast_group;
|
||||||
static struct bt_codec *rcodecs[2][CONFIG_BT_AUDIO_UNICAST_CLIENT_PAC_COUNT];
|
static struct bt_codec *rcodecs[2][CONFIG_BT_AUDIO_UNICAST_CLIENT_PAC_COUNT];
|
||||||
static struct bt_audio_ep *snks[CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SNK_COUNT];
|
static struct bt_audio_ep *snks[CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SNK_COUNT];
|
||||||
static struct bt_audio_ep *srcs[CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SNK_COUNT];
|
static struct bt_audio_ep *srcs[CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SNK_COUNT];
|
||||||
|
|
||||||
|
static uint8_t stream_dir(const struct bt_audio_stream *stream);
|
||||||
#endif /* CONFIG_BT_AUDIO_UNICAST_CLIENT */
|
#endif /* CONFIG_BT_AUDIO_UNICAST_CLIENT */
|
||||||
#endif /* CONFIG_BT_AUDIO_UNICAST */
|
#endif /* CONFIG_BT_AUDIO_UNICAST */
|
||||||
|
|
||||||
|
@ -758,15 +760,20 @@ static int cmd_qos(const struct shell *sh, size_t argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (default_unicast_group == NULL) {
|
if (default_unicast_group == NULL) {
|
||||||
err = bt_audio_unicast_group_create(&default_stream, 1, &default_unicast_group);
|
struct bt_audio_unicast_group_param params = {
|
||||||
|
.stream = default_stream,
|
||||||
|
.qos = &default_preset->preset.qos,
|
||||||
|
.dir = stream_dir(default_stream)
|
||||||
|
};
|
||||||
|
|
||||||
|
err = bt_audio_unicast_group_create(¶ms, 1, &default_unicast_group);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
shell_error(sh, "Unable to create default unicast group: %d", err);
|
shell_error(sh, "Unable to create default unicast group: %d", err);
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bt_audio_stream_qos(default_conn, default_unicast_group,
|
err = bt_audio_stream_qos(default_conn, default_unicast_group);
|
||||||
&named_preset->preset.qos);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
shell_error(sh, "Unable to setup QoS");
|
shell_error(sh, "Unable to setup QoS");
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
|
|
|
@ -310,74 +310,42 @@ static size_t release_streams(size_t stream_cnt)
|
||||||
return stream_cnt;
|
return stream_cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void create_unicast_group(struct bt_audio_unicast_group **unicast_group,
|
static void create_unicast_group(struct bt_audio_unicast_group **unicast_group,
|
||||||
size_t stream_cnt)
|
size_t stream_cnt)
|
||||||
{
|
{
|
||||||
struct bt_audio_stream *streams[ARRAY_SIZE(g_streams)];
|
struct bt_audio_unicast_group_param params[ARRAY_SIZE(g_streams)];
|
||||||
int err;
|
|
||||||
|
|
||||||
for (size_t i = 0U; i < stream_cnt; i++) {
|
for (size_t i = 0U; i < stream_cnt; i++) {
|
||||||
streams[i] = &g_streams[i];
|
params[i].stream = &g_streams[i];
|
||||||
|
params[i].qos = &preset_16_2_1.qos;
|
||||||
|
params[i].dir = BT_AUDIO_DIR_SINK; /* we only configure sinks */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO)
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Require controller support for CIGs */
|
||||||
printk("Creating unicast group\n");
|
printk("Creating unicast group\n");
|
||||||
err = bt_audio_unicast_group_create(streams, 1, unicast_group);
|
err = bt_audio_unicast_group_create(¶ms, 1, unicast_group);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
FAIL("Unable to create unicast group: %d", err);
|
FAIL("Unable to create unicast group: %d", err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO */
|
||||||
/* Test removing streams from group before adding them */
|
|
||||||
if (stream_cnt > 1) {
|
|
||||||
const size_t remaining_streams = stream_cnt - 1;
|
|
||||||
|
|
||||||
err = bt_audio_unicast_group_remove_streams(*unicast_group,
|
|
||||||
&streams[1],
|
|
||||||
remaining_streams);
|
|
||||||
if (err == 0) {
|
|
||||||
FAIL("Able to remove stream not in group");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Test adding streams to group after creation */
|
|
||||||
err = bt_audio_unicast_group_add_streams(*unicast_group,
|
|
||||||
&streams[1],
|
|
||||||
remaining_streams);
|
|
||||||
if (err != 0) {
|
|
||||||
FAIL("Unable to add streams to unicast group: %d", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void delete_unicast_group(struct bt_audio_unicast_group *unicast_group,
|
static void delete_unicast_group(struct bt_audio_unicast_group *unicast_group)
|
||||||
size_t stream_cnt)
|
|
||||||
{
|
{
|
||||||
struct bt_audio_stream *streams[ARRAY_SIZE(g_streams)];
|
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO)
|
||||||
int err;
|
int err;
|
||||||
|
/* Require controller support for CIGs */
|
||||||
for (size_t i = 0U; i < stream_cnt; i++) {
|
|
||||||
streams[i] = &g_streams[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream_cnt > 1) {
|
|
||||||
const size_t remove_streams_cnt = stream_cnt - 1;
|
|
||||||
|
|
||||||
err = bt_audio_unicast_group_remove_streams(unicast_group,
|
|
||||||
&streams[1],
|
|
||||||
remove_streams_cnt);
|
|
||||||
if (err != 0) {
|
|
||||||
FAIL("Unable to remove streams from unicast group: %d",
|
|
||||||
err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = bt_audio_unicast_group_delete(unicast_group);
|
err = bt_audio_unicast_group_delete(unicast_group);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
FAIL("Unable to delete unicast group: %d", err);
|
FAIL("Unable to delete unicast group: %d", err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_main(void)
|
static void test_main(void)
|
||||||
|
@ -412,7 +380,7 @@ static void test_main(void)
|
||||||
|
|
||||||
/* Test removing streams from group after creation */
|
/* Test removing streams from group after creation */
|
||||||
printk("Deleting unicast group\n");
|
printk("Deleting unicast group\n");
|
||||||
delete_unicast_group(unicast_group, stream_cnt);
|
delete_unicast_group(unicast_group);
|
||||||
unicast_group = NULL;
|
unicast_group = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue