2022-01-07 17:23:45 +01:00
|
|
|
/* Bluetooth Audio Stream */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2020 Intel Corporation
|
|
|
|
* Copyright (c) 2021-2022 Nordic Semiconductor ASA
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
includes: prefer <zephyr/kernel.h> over <zephyr/zephyr.h>
As of today <zephyr/zephyr.h> is 100% equivalent to <zephyr/kernel.h>.
This patch proposes to then include <zephyr/kernel.h> instead of
<zephyr/zephyr.h> since it is more clear that you are including the
Kernel APIs and (probably) nothing else. <zephyr/zephyr.h> sounds like a
catch-all header that may be confusing. Most applications need to
include a bunch of other things to compile, e.g. driver headers or
subsystem headers like BT, logging, etc.
The idea of a catch-all header in Zephyr is probably not feasible
anyway. Reason is that Zephyr is not a library, like it could be for
example `libpython`. Zephyr provides many utilities nowadays: a kernel,
drivers, subsystems, etc and things will likely grow. A catch-all header
would be massive, difficult to keep up-to-date. It is also likely that
an application will only build a small subset. Note that subsystem-level
headers may use a catch-all approach to make things easier, though.
NOTE: This patch is **NOT** removing the header, just removing its usage
in-tree. I'd advocate for its deprecation (add a #warning on it), but I
understand many people will have concerns.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2022-08-25 09:58:46 +02:00
|
|
|
#include <zephyr/kernel.h>
|
2022-05-06 11:12:04 +02:00
|
|
|
#include <zephyr/sys/byteorder.h>
|
|
|
|
#include <zephyr/sys/check.h>
|
|
|
|
|
|
|
|
#include <zephyr/bluetooth/bluetooth.h>
|
|
|
|
#include <zephyr/bluetooth/conn.h>
|
|
|
|
#include <zephyr/bluetooth/gatt.h>
|
2022-11-02 14:31:13 +01:00
|
|
|
#include <zephyr/bluetooth/hci.h>
|
2022-05-06 11:12:04 +02:00
|
|
|
#include <zephyr/bluetooth/iso.h>
|
|
|
|
#include <zephyr/bluetooth/audio/audio.h>
|
2022-01-07 17:23:45 +01:00
|
|
|
|
|
|
|
#include "../host/conn_internal.h"
|
|
|
|
#include "../host/iso_internal.h"
|
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
#include "audio_iso.h"
|
2022-01-07 17:23:45 +01:00
|
|
|
#include "endpoint.h"
|
2022-01-10 13:57:08 +01:00
|
|
|
#include "unicast_client_internal.h"
|
2022-04-15 13:16:08 +02:00
|
|
|
#include "unicast_server.h"
|
2022-01-07 17:23:45 +01:00
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
#include <zephyr/logging/log.h>
|
|
|
|
|
|
|
|
LOG_MODULE_REGISTER(bt_audio_stream, CONFIG_BT_AUDIO_STREAM_LOG_LEVEL);
|
2022-01-07 17:23:45 +01:00
|
|
|
|
2022-06-03 15:12:05 +02:00
|
|
|
static uint8_t pack_bt_codec_cc(const struct bt_codec *codec, uint8_t cc[])
|
|
|
|
{
|
|
|
|
uint8_t len;
|
|
|
|
|
|
|
|
len = 0U;
|
|
|
|
for (size_t i = 0U; i < codec->data_count; i++) {
|
|
|
|
const struct bt_data *data = &codec->data[i].data;
|
|
|
|
|
|
|
|
/* We assume that data_len and data has previously been verified
|
|
|
|
* and that based on the Kconfigs we can assume that the length
|
|
|
|
* will always fit in `cc`
|
|
|
|
*/
|
2022-10-21 17:57:11 +08:00
|
|
|
cc[len++] = data->data_len + 1;
|
|
|
|
cc[len++] = data->type;
|
2022-06-03 15:12:05 +02:00
|
|
|
(void)memcpy(cc + len, data->data, data->data_len);
|
|
|
|
len += data->data_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bt_audio_codec_to_iso_path(struct bt_iso_chan_path *path,
|
|
|
|
const struct bt_codec *codec)
|
|
|
|
{
|
|
|
|
path->pid = codec->path_id;
|
|
|
|
path->format = codec->id;
|
|
|
|
path->cid = codec->cid;
|
|
|
|
path->vid = codec->vid;
|
|
|
|
path->delay = 0; /* TODO: Add to bt_codec? Use presentation delay? */
|
|
|
|
path->cc_len = pack_bt_codec_cc(codec, path->cc);
|
|
|
|
}
|
|
|
|
|
2022-02-25 12:17:02 +01:00
|
|
|
void bt_audio_codec_qos_to_iso_qos(struct bt_iso_chan_io_qos *io,
|
2022-06-03 15:12:05 +02:00
|
|
|
const struct bt_codec_qos *codec_qos)
|
2022-03-17 17:18:26 +01:00
|
|
|
{
|
2022-06-03 15:12:05 +02:00
|
|
|
io->sdu = codec_qos->sdu;
|
|
|
|
io->phy = codec_qos->phy;
|
|
|
|
io->rtn = codec_qos->rtn;
|
2022-03-17 17:18:26 +01:00
|
|
|
}
|
2022-01-07 17:23:45 +01:00
|
|
|
|
|
|
|
void bt_audio_stream_attach(struct bt_conn *conn,
|
|
|
|
struct bt_audio_stream *stream,
|
|
|
|
struct bt_audio_ep *ep,
|
|
|
|
struct bt_codec *codec)
|
|
|
|
{
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("conn %p stream %p ep %p codec %p", conn, stream, ep, codec);
|
2022-01-07 17:23:45 +01:00
|
|
|
|
2022-03-07 19:50:22 +01:00
|
|
|
if (conn != NULL) {
|
|
|
|
__ASSERT(stream->conn == NULL || stream->conn == conn,
|
2022-09-26 17:42:26 +02:00
|
|
|
"stream->conn %p already attached", stream->conn);
|
2022-03-07 19:50:22 +01:00
|
|
|
stream->conn = bt_conn_ref(conn);
|
|
|
|
}
|
2022-01-07 17:23:45 +01:00
|
|
|
stream->codec = codec;
|
|
|
|
stream->ep = ep;
|
|
|
|
ep->stream = stream;
|
2022-10-21 14:35:10 +02:00
|
|
|
}
|
2022-01-07 17:23:45 +01:00
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
struct bt_iso_chan *bt_audio_stream_iso_chan_get(struct bt_audio_stream *stream)
|
|
|
|
{
|
|
|
|
if (stream != NULL && stream->ep != NULL && stream->ep->iso != NULL) {
|
|
|
|
return &stream->ep->iso->chan;
|
2022-01-07 17:23:45 +01:00
|
|
|
}
|
2022-10-21 14:35:10 +02:00
|
|
|
|
|
|
|
return NULL;
|
2022-01-07 17:23:45 +01:00
|
|
|
}
|
|
|
|
|
2022-09-05 11:29:06 +02:00
|
|
|
void bt_audio_stream_cb_register(struct bt_audio_stream *stream,
|
|
|
|
struct bt_audio_stream_ops *ops)
|
|
|
|
{
|
|
|
|
stream->ops = ops;
|
|
|
|
}
|
|
|
|
|
2022-03-17 17:18:26 +01:00
|
|
|
#if defined(CONFIG_BT_AUDIO_UNICAST) || defined(CONFIG_BT_AUDIO_BROADCAST_SOURCE)
|
2022-09-05 11:29:06 +02:00
|
|
|
bool bt_audio_valid_qos(const struct bt_codec_qos *qos)
|
|
|
|
{
|
|
|
|
if (qos->interval < BT_ISO_SDU_INTERVAL_MIN ||
|
|
|
|
qos->interval > BT_ISO_SDU_INTERVAL_MAX) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Interval not within allowed range: %u (%u-%u)", qos->interval,
|
|
|
|
BT_ISO_SDU_INTERVAL_MIN, BT_ISO_SDU_INTERVAL_MAX);
|
2022-09-05 11:29:06 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qos->framing > BT_CODEC_QOS_FRAMED) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid Framing 0x%02x", qos->framing);
|
2022-09-05 11:29:06 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qos->phy != BT_CODEC_QOS_1M &&
|
|
|
|
qos->phy != BT_CODEC_QOS_2M &&
|
|
|
|
qos->phy != BT_CODEC_QOS_CODED) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid PHY 0x%02x", qos->phy);
|
2022-09-05 11:29:06 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qos->sdu > BT_ISO_MAX_SDU) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid SDU %u", qos->sdu);
|
2022-09-05 11:29:06 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qos->latency < BT_ISO_LATENCY_MIN ||
|
|
|
|
qos->latency > BT_ISO_LATENCY_MAX) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid Latency %u", qos->latency);
|
2022-09-05 11:29:06 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-06-22 15:02:02 +02:00
|
|
|
int bt_audio_stream_send(struct bt_audio_stream *stream, struct net_buf *buf,
|
2022-10-19 19:13:58 +02:00
|
|
|
uint16_t seq_num, uint32_t ts)
|
2022-03-17 17:18:26 +01:00
|
|
|
{
|
2022-03-16 14:38:04 +01:00
|
|
|
struct bt_audio_ep *ep;
|
|
|
|
|
2022-03-17 17:18:26 +01:00
|
|
|
if (stream == NULL || stream->ep == NULL) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-03-16 14:38:04 +01:00
|
|
|
ep = stream->ep;
|
|
|
|
|
|
|
|
if (ep->status.state != BT_AUDIO_EP_STATE_STREAMING) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Channel %p not ready for streaming (state: %s)", stream,
|
|
|
|
bt_audio_ep_state_str(ep->status.state));
|
2022-03-17 17:18:26 +01:00
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Add checks for broadcast sink */
|
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
return bt_iso_chan_send(bt_audio_stream_iso_chan_get(stream),
|
|
|
|
buf, seq_num, ts);
|
2022-03-17 17:18:26 +01:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_BT_AUDIO_UNICAST || CONFIG_BT_AUDIO_BROADCAST_SOURCE */
|
|
|
|
|
|
|
|
#if defined(CONFIG_BT_AUDIO_UNICAST)
|
|
|
|
#if defined(CONFIG_BT_AUDIO_UNICAST_CLIENT)
|
|
|
|
static struct bt_audio_unicast_group unicast_groups[UNICAST_GROUP_CNT];
|
|
|
|
#endif /* CONFIG_BT_AUDIO_UNICAST_CLIENT */
|
|
|
|
|
2022-02-24 12:57:43 +01:00
|
|
|
#if defined(CONFIG_BT_AUDIO_UNICAST_SERVER)
|
|
|
|
static struct bt_audio_stream *enabling[CONFIG_BT_ISO_MAX_CHAN];
|
|
|
|
|
2022-01-07 17:23:45 +01:00
|
|
|
static int bt_audio_stream_iso_accept(const struct bt_iso_accept_info *info,
|
|
|
|
struct bt_iso_chan **iso_chan)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("acl %p", info->acl);
|
2022-01-07 17:23:45 +01:00
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(enabling); i++) {
|
|
|
|
struct bt_audio_stream *c = enabling[i];
|
|
|
|
|
|
|
|
if (c && c->ep->cig_id == info->cig_id &&
|
|
|
|
c->ep->cis_id == info->cis_id) {
|
2022-10-21 14:35:10 +02:00
|
|
|
*iso_chan = &enabling[i]->ep->iso->chan;
|
2022-01-07 17:23:45 +01:00
|
|
|
enabling[i] = NULL;
|
2022-06-10 10:56:12 +02:00
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("iso_chan %p", *iso_chan);
|
2022-06-10 10:56:12 +02:00
|
|
|
|
2022-01-07 17:23:45 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_ERR("No channel listening");
|
2022-01-07 17:23:45 +01:00
|
|
|
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bt_iso_server iso_server = {
|
|
|
|
.sec_level = BT_SECURITY_L2,
|
|
|
|
.accept = bt_audio_stream_iso_accept,
|
|
|
|
};
|
|
|
|
|
|
|
|
int bt_audio_stream_iso_listen(struct bt_audio_stream *stream)
|
|
|
|
{
|
|
|
|
static bool server;
|
|
|
|
int err, i;
|
|
|
|
struct bt_audio_stream **free_stream = NULL;
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("stream %p conn %p", stream, stream->conn);
|
2022-01-07 17:23:45 +01:00
|
|
|
|
|
|
|
if (server) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = bt_iso_server_register(&iso_server);
|
|
|
|
if (err) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_ERR("bt_iso_server_register: %d", err);
|
2022-01-07 17:23:45 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
server = true;
|
|
|
|
|
|
|
|
done:
|
|
|
|
for (i = 0; i < ARRAY_SIZE(enabling); i++) {
|
|
|
|
if (enabling[i] == stream) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (enabling[i] == NULL && free_stream == NULL) {
|
|
|
|
free_stream = &enabling[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (free_stream != NULL) {
|
|
|
|
*free_stream = stream;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_ERR("Unable to listen: no slot left");
|
2022-01-07 17:23:45 +01:00
|
|
|
|
|
|
|
return -ENOSPC;
|
|
|
|
}
|
2022-02-24 12:57:43 +01:00
|
|
|
#endif /* CONFIG_BT_AUDIO_UNICAST_SERVER */
|
2022-01-07 17:23:45 +01:00
|
|
|
|
|
|
|
static bool bt_audio_stream_is_broadcast(const struct bt_audio_stream *stream)
|
|
|
|
{
|
2022-01-10 15:06:08 +01:00
|
|
|
return (IS_ENABLED(CONFIG_BT_AUDIO_BROADCAST_SOURCE) &&
|
2022-01-10 15:34:37 +01:00
|
|
|
bt_audio_ep_is_broadcast_src(stream->ep)) ||
|
|
|
|
(IS_ENABLED(CONFIG_BT_AUDIO_BROADCAST_SINK) &&
|
|
|
|
bt_audio_ep_is_broadcast_snk(stream->ep));
|
2022-01-07 17:23:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool bt_audio_valid_stream_qos(const struct bt_audio_stream *stream,
|
|
|
|
const struct bt_codec_qos *qos)
|
|
|
|
{
|
|
|
|
const struct bt_codec_qos_pref *qos_pref = &stream->ep->qos_pref;
|
|
|
|
|
|
|
|
if (qos_pref->latency < qos->latency) {
|
2022-03-02 13:13:38 +01:00
|
|
|
/* Latency is a preferred value. Print debug info but do not fail. */
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Latency %u higher than preferred max %u", qos->latency, qos_pref->latency);
|
2022-01-07 17:23:45 +01:00
|
|
|
}
|
|
|
|
|
2022-03-08 11:01:55 +01:00
|
|
|
if (!IN_RANGE(qos->pd, qos_pref->pd_min, qos_pref->pd_max)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Presentation Delay not within range: min %u max %u pd %u",
|
2022-01-07 17:23:45 +01:00
|
|
|
qos_pref->pd_min, qos_pref->pd_max, qos->pd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bt_audio_stream_detach(struct bt_audio_stream *stream)
|
|
|
|
{
|
|
|
|
const bool is_broadcast = bt_audio_stream_is_broadcast(stream);
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("stream %p", stream);
|
2022-01-07 17:23:45 +01:00
|
|
|
|
2022-03-07 19:50:22 +01:00
|
|
|
if (stream->conn != NULL) {
|
|
|
|
bt_conn_unref(stream->conn);
|
|
|
|
stream->conn = NULL;
|
|
|
|
}
|
2022-01-07 17:23:45 +01:00
|
|
|
stream->codec = NULL;
|
|
|
|
stream->ep->stream = NULL;
|
|
|
|
stream->ep = NULL;
|
|
|
|
|
|
|
|
if (!is_broadcast) {
|
|
|
|
bt_audio_stream_disconnect(stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int bt_audio_stream_disconnect(struct bt_audio_stream *stream)
|
|
|
|
{
|
2022-10-21 14:35:10 +02:00
|
|
|
struct bt_iso_chan *iso_chan = bt_audio_stream_iso_chan_get(stream);
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("stream %p iso %p", stream, iso_chan);
|
2022-01-07 17:23:45 +01:00
|
|
|
|
|
|
|
if (stream == NULL) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-02-24 12:57:43 +01:00
|
|
|
#if defined(CONFIG_BT_AUDIO_UNICAST_SERVER)
|
2022-01-07 17:23:45 +01:00
|
|
|
/* Stop listening */
|
2022-02-24 12:57:43 +01:00
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(enabling); i++) {
|
2022-01-07 17:23:45 +01:00
|
|
|
if (enabling[i] == stream) {
|
|
|
|
enabling[i] = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-02-24 12:57:43 +01:00
|
|
|
#endif /* CONFIG_BT_AUDIO_UNICAST_SERVER */
|
2022-01-07 17:23:45 +01:00
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
if (iso_chan == NULL || iso_chan->iso == NULL) {
|
2022-01-07 17:23:45 +01:00
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
return bt_iso_chan_disconnect(iso_chan);
|
2022-01-07 17:23:45 +01:00
|
|
|
}
|
|
|
|
|
2022-01-10 13:57:08 +01:00
|
|
|
void bt_audio_stream_reset(struct bt_audio_stream *stream)
|
|
|
|
{
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("stream %p", stream);
|
2022-01-10 13:57:08 +01:00
|
|
|
|
2022-03-29 14:05:21 +02:00
|
|
|
if (stream == NULL) {
|
2022-01-10 13:57:08 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-29 14:05:21 +02:00
|
|
|
bt_audio_stream_detach(stream);
|
2022-01-10 13:57:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(CONFIG_BT_AUDIO_UNICAST_CLIENT)
|
|
|
|
|
|
|
|
int bt_audio_stream_config(struct bt_conn *conn,
|
|
|
|
struct bt_audio_stream *stream,
|
|
|
|
struct bt_audio_ep *ep,
|
|
|
|
struct bt_codec *codec)
|
|
|
|
{
|
|
|
|
uint8_t role;
|
2022-03-29 17:37:54 +02:00
|
|
|
int err;
|
2022-01-10 13:57:08 +01:00
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("conn %p stream %p, ep %p codec %p codec id 0x%02x "
|
2022-01-10 13:57:08 +01:00
|
|
|
"codec cid 0x%04x codec vid 0x%04x", conn, stream, ep,
|
|
|
|
codec, codec ? codec->id : 0, codec ? codec->cid : 0,
|
|
|
|
codec ? codec->vid : 0);
|
|
|
|
|
|
|
|
CHECKIF(conn == NULL || stream == NULL || codec == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("NULL value(s) supplied)");
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-09-26 17:42:26 +02:00
|
|
|
if (stream->conn != NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Stream already configured for conn %p", stream->conn);
|
2022-09-26 17:42:26 +02:00
|
|
|
return -EALREADY;
|
|
|
|
}
|
|
|
|
|
2022-01-10 13:57:08 +01:00
|
|
|
role = conn->role;
|
|
|
|
if (role != BT_HCI_ROLE_CENTRAL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid conn role: %u, shall be central", role);
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (ep->status.state) {
|
|
|
|
/* Valid only if ASE_State field = 0x00 (Idle) */
|
|
|
|
case BT_AUDIO_EP_STATE_IDLE:
|
|
|
|
/* or 0x01 (Codec Configured) */
|
|
|
|
case BT_AUDIO_EP_STATE_CODEC_CONFIGURED:
|
|
|
|
/* or 0x02 (QoS Configured) */
|
|
|
|
case BT_AUDIO_EP_STATE_QOS_CONFIGURED:
|
|
|
|
break;
|
|
|
|
default:
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_ERR("Invalid state: %s", bt_audio_ep_state_str(ep->status.state));
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
bt_audio_stream_attach(conn, stream, ep, codec);
|
|
|
|
|
2022-03-29 17:37:54 +02:00
|
|
|
err = bt_unicast_client_config(stream, codec);
|
|
|
|
if (err != 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Failed to configure stream: %d", err);
|
2022-03-29 17:37:54 +02:00
|
|
|
return err;
|
2022-01-10 13:57:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-31 14:36:29 +02:00
|
|
|
static void bt_audio_codec_qos_to_cig_param(struct bt_iso_cig_param *cig_param,
|
|
|
|
const struct bt_codec_qos *qos)
|
|
|
|
{
|
|
|
|
cig_param->framing = qos->framing;
|
|
|
|
cig_param->packing = BT_ISO_PACKING_SEQUENTIAL; /* TODO: Add to QoS struct */
|
|
|
|
cig_param->interval = qos->interval;
|
|
|
|
cig_param->latency = qos->latency;
|
|
|
|
cig_param->sca = BT_GAP_SCA_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
2022-01-10 13:57:08 +01:00
|
|
|
static int bt_audio_cig_create(struct bt_audio_unicast_group *group,
|
2022-05-24 13:59:45 +02:00
|
|
|
const struct bt_codec_qos *qos)
|
2022-01-10 13:57:08 +01:00
|
|
|
{
|
|
|
|
struct bt_iso_cig_param param;
|
|
|
|
uint8_t cis_count;
|
|
|
|
int err;
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("group %p qos %p", group, qos);
|
2022-01-10 13:57:08 +01:00
|
|
|
|
|
|
|
cis_count = 0;
|
2022-10-21 14:35:10 +02:00
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(group->cis); i++) {
|
|
|
|
if (group->cis[i] != NULL) {
|
|
|
|
cis_count++;
|
2022-05-04 15:48:23 +02:00
|
|
|
}
|
2022-01-10 13:57:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
param.num_cis = cis_count;
|
|
|
|
param.cis_channels = group->cis;
|
2022-03-31 14:36:29 +02:00
|
|
|
bt_audio_codec_qos_to_cig_param(¶m, qos);
|
2022-01-10 13:57:08 +01:00
|
|
|
|
|
|
|
err = bt_iso_cig_create(¶m, &group->cig);
|
|
|
|
if (err != 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_ERR("bt_iso_cig_create failed: %d", err);
|
2022-01-10 13:57:08 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
group->qos = qos;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-31 14:36:29 +02:00
|
|
|
static int bt_audio_cig_reconfigure(struct bt_audio_unicast_group *group,
|
2022-05-24 13:59:45 +02:00
|
|
|
const struct bt_codec_qos *qos)
|
2022-03-31 14:36:29 +02:00
|
|
|
{
|
|
|
|
struct bt_iso_cig_param param;
|
|
|
|
uint8_t cis_count;
|
|
|
|
int err;
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("group %p qos %p", group, qos);
|
2022-03-31 14:36:29 +02:00
|
|
|
|
|
|
|
cis_count = 0U;
|
2022-10-21 14:35:10 +02:00
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(group->cis); i++) {
|
|
|
|
if (group->cis[i] != NULL) {
|
|
|
|
cis_count++;
|
|
|
|
}
|
2022-03-31 14:36:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
param.num_cis = cis_count;
|
|
|
|
param.cis_channels = group->cis;
|
|
|
|
bt_audio_codec_qos_to_cig_param(¶m, qos);
|
|
|
|
|
|
|
|
err = bt_iso_cig_reconfigure(group->cig, ¶m);
|
|
|
|
if (err != 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_ERR("bt_iso_cig_create failed: %d", err);
|
2022-03-31 14:36:29 +02:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
group->qos = qos;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-02-22 09:13:09 +01:00
|
|
|
static void audio_stream_qos_cleanup(const struct bt_conn *conn,
|
|
|
|
struct bt_audio_unicast_group *group)
|
|
|
|
{
|
|
|
|
struct bt_audio_stream *stream;
|
|
|
|
|
2022-05-24 12:17:11 +02:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
|
2022-02-22 09:13:09 +01:00
|
|
|
if (stream->conn != conn && stream->ep != NULL) {
|
|
|
|
/* Channel not part of this ACL, skip */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
bt_audio_iso_unbind_ep(stream->ep->iso, stream->ep);
|
2022-02-22 09:13:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-10 13:57:08 +01:00
|
|
|
int bt_audio_stream_qos(struct bt_conn *conn,
|
2022-05-24 13:59:45 +02:00
|
|
|
struct bt_audio_unicast_group *group)
|
2022-01-10 13:57:08 +01:00
|
|
|
{
|
|
|
|
struct bt_audio_stream *stream;
|
|
|
|
struct net_buf_simple *buf;
|
|
|
|
struct bt_ascs_qos_op *op;
|
|
|
|
struct bt_audio_ep *ep;
|
|
|
|
bool conn_stream_found;
|
|
|
|
bool cig_connected;
|
|
|
|
uint8_t role;
|
|
|
|
int err;
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("conn %p group %p", conn, group);
|
2022-01-10 13:57:08 +01:00
|
|
|
|
|
|
|
CHECKIF(conn == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("conn is NULL");
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECKIF(group == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("group is NULL");
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sys_slist_is_empty(&group->streams)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("group stream list is empty");
|
2022-01-10 13:57:08 +01:00
|
|
|
return -ENOEXEC;
|
|
|
|
}
|
|
|
|
|
|
|
|
role = conn->role;
|
|
|
|
if (role != BT_HCI_ROLE_CENTRAL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid conn role: %u, shall be central", role);
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Used to determine if a stream for the supplied connection pointer
|
|
|
|
* was actually found
|
|
|
|
*/
|
|
|
|
conn_stream_found = false;
|
|
|
|
|
|
|
|
/* User to determine if any stream in the group is in
|
|
|
|
* the connected state
|
|
|
|
*/
|
|
|
|
cig_connected = false;
|
|
|
|
|
|
|
|
/* Validate streams before starting the QoS execution */
|
2022-05-24 12:17:11 +02:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
|
2022-05-04 15:34:48 +02:00
|
|
|
if (stream->conn != conn) {
|
|
|
|
/* Channel not part of this ACL, skip */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
conn_stream_found = true;
|
|
|
|
|
2022-02-22 09:13:09 +01:00
|
|
|
ep = stream->ep;
|
|
|
|
if (ep == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("stream->ep is NULL");
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Can only be done if all the streams are in the codec
|
|
|
|
* configured state or the QoS configured state
|
|
|
|
*/
|
2022-02-22 09:13:09 +01:00
|
|
|
switch (ep->status.state) {
|
2022-01-10 13:57:08 +01:00
|
|
|
case BT_AUDIO_EP_STATE_CODEC_CONFIGURED:
|
|
|
|
case BT_AUDIO_EP_STATE_QOS_CONFIGURED:
|
|
|
|
break;
|
|
|
|
default:
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid state: %s",
|
|
|
|
bt_audio_ep_state_str(stream->ep->status.state));
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
if (!bt_audio_valid_stream_qos(stream, stream->qos)) {
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-02-22 09:13:09 +01:00
|
|
|
/* Verify ep->dir */
|
|
|
|
switch (ep->dir) {
|
|
|
|
case BT_AUDIO_DIR_SINK:
|
|
|
|
case BT_AUDIO_DIR_SOURCE:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
__ASSERT(false, "invalid endpoint dir: %u", ep->dir);
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2022-05-24 13:59:45 +02:00
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
if (ep->iso == NULL) {
|
2022-05-24 13:59:45 +02:00
|
|
|
/* This can only happen if the stream was somehow added
|
|
|
|
* to a group without the audio_iso being bound to it
|
|
|
|
*/
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_ERR("Could not find audio_iso for stream %p", stream);
|
2022-05-24 13:59:45 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2022-02-22 09:13:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!conn_stream_found) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("No streams in the group %p for conn %p", group, conn);
|
2022-02-22 09:13:09 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-01-10 13:57:08 +01:00
|
|
|
/* Generate the control point write */
|
|
|
|
buf = bt_unicast_client_ep_create_pdu(BT_ASCS_QOS_OP);
|
|
|
|
|
|
|
|
op = net_buf_simple_add(buf, sizeof(*op));
|
|
|
|
|
|
|
|
(void)memset(op, 0, sizeof(*op));
|
|
|
|
ep = NULL; /* Needed to find the control point handle */
|
2022-05-24 12:17:11 +02:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
|
2022-01-10 13:57:08 +01:00
|
|
|
if (stream->conn != conn) {
|
|
|
|
/* Channel not part of this ACL, skip */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
op->num_ases++;
|
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
err = bt_unicast_client_ep_qos(stream->ep, buf, stream->qos);
|
2022-01-10 13:57:08 +01:00
|
|
|
if (err) {
|
2022-02-22 09:13:09 +01:00
|
|
|
audio_stream_qos_cleanup(conn, group);
|
2022-01-10 13:57:08 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ep == NULL) {
|
|
|
|
ep = stream->ep;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = bt_unicast_client_ep_send(conn, ep, buf);
|
|
|
|
if (err != 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Could not send config QoS: %d", err);
|
2022-02-22 09:13:09 +01:00
|
|
|
audio_stream_qos_cleanup(conn, group);
|
2022-01-10 13:57:08 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bt_audio_stream_enable(struct bt_audio_stream *stream,
|
2022-02-24 11:22:29 +01:00
|
|
|
struct bt_codec_data *meta,
|
|
|
|
size_t meta_count)
|
2022-01-10 13:57:08 +01:00
|
|
|
{
|
|
|
|
uint8_t role;
|
|
|
|
int err;
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("stream %p", stream);
|
2022-01-10 13:57:08 +01:00
|
|
|
|
|
|
|
if (stream == NULL || stream->ep == NULL || stream->conn == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid stream");
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
role = stream->conn->role;
|
|
|
|
if (role != BT_HCI_ROLE_CENTRAL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid conn role: %u, shall be central", role);
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Valid for an ASE only if ASE_State field = 0x02 (QoS Configured) */
|
|
|
|
if (stream->ep->status.state != BT_AUDIO_EP_STATE_QOS_CONFIGURED) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_ERR("Invalid state: %s", bt_audio_ep_state_str(stream->ep->status.state));
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
2022-02-24 11:22:29 +01:00
|
|
|
err = bt_unicast_client_enable(stream, meta, meta_count);
|
2022-01-10 13:57:08 +01:00
|
|
|
if (err != 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Failed to enable stream: %d", err);
|
2022-01-10 13:57:08 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2022-03-29 17:37:54 +02:00
|
|
|
return 0;
|
2022-01-10 13:57:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int bt_audio_stream_stop(struct bt_audio_stream *stream)
|
|
|
|
{
|
|
|
|
struct bt_audio_ep *ep;
|
|
|
|
uint8_t role;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (stream == NULL || stream->ep == NULL || stream->conn == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid stream");
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
role = stream->conn->role;
|
|
|
|
if (role != BT_HCI_ROLE_CENTRAL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid conn role: %u, shall be central", role);
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep = stream->ep;
|
|
|
|
|
|
|
|
switch (ep->status.state) {
|
|
|
|
/* Valid only if ASE_State field = 0x03 (Disabling) */
|
|
|
|
case BT_AUDIO_EP_STATE_DISABLING:
|
|
|
|
break;
|
|
|
|
default:
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_ERR("Invalid state: %s", bt_audio_ep_state_str(ep->status.state));
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = bt_unicast_client_stop(stream);
|
|
|
|
if (err != 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Stopping stream failed: %d", err);
|
2022-01-10 13:57:08 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2022-03-29 17:37:54 +02:00
|
|
|
return 0;
|
2022-01-10 13:57:08 +01:00
|
|
|
}
|
|
|
|
|
2022-03-31 14:36:29 +02:00
|
|
|
int bt_audio_cig_terminate(struct bt_audio_unicast_group *group)
|
2022-01-10 13:57:08 +01:00
|
|
|
{
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("group %p", group);
|
2022-01-10 13:57:08 +01:00
|
|
|
|
2022-03-31 14:36:29 +02:00
|
|
|
return bt_iso_cig_terminate(group->cig);
|
2022-01-10 13:57:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int bt_audio_stream_connect(struct bt_audio_stream *stream)
|
|
|
|
{
|
|
|
|
struct bt_iso_connect_param param;
|
2022-10-21 14:35:10 +02:00
|
|
|
struct bt_iso_chan *iso_chan;
|
|
|
|
|
|
|
|
iso_chan = bt_audio_stream_iso_chan_get(stream);
|
2022-01-10 13:57:08 +01:00
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("stream %p iso %p", stream, iso_chan);
|
2022-01-10 13:57:08 +01:00
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
if (stream == NULL || iso_chan == NULL) {
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
param.acl = stream->conn;
|
2022-10-21 14:35:10 +02:00
|
|
|
param.iso_chan = iso_chan;
|
2022-01-10 13:57:08 +01:00
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
switch (iso_chan->state) {
|
2022-02-22 15:30:30 +01:00
|
|
|
case BT_ISO_STATE_DISCONNECTED:
|
2022-01-10 13:57:08 +01:00
|
|
|
return bt_iso_chan_connect(¶m, 1);
|
2022-02-22 15:30:30 +01:00
|
|
|
case BT_ISO_STATE_CONNECTING:
|
|
|
|
case BT_ISO_STATE_CONNECTED:
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EALREADY;
|
|
|
|
default:
|
|
|
|
return bt_iso_chan_connect(¶m, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
static bool unicast_group_valid_qos(const struct bt_codec_qos *group_qos,
|
|
|
|
const struct bt_codec_qos *stream_qos)
|
2022-01-10 13:57:08 +01:00
|
|
|
{
|
2022-05-24 13:59:45 +02:00
|
|
|
if (group_qos->framing != stream_qos->framing ||
|
|
|
|
group_qos->interval != stream_qos->interval ||
|
|
|
|
group_qos->latency != stream_qos->latency) {
|
|
|
|
return false;
|
|
|
|
}
|
2022-01-10 13:57:08 +01:00
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
static struct bt_audio_iso *get_new_iso(struct bt_audio_unicast_group *group,
|
|
|
|
struct bt_conn *acl,
|
|
|
|
enum bt_audio_dir dir)
|
2022-05-24 13:59:45 +02:00
|
|
|
{
|
2022-10-21 14:35:10 +02:00
|
|
|
struct bt_audio_stream *stream;
|
|
|
|
|
|
|
|
/* Check if there's already an ISO that can be used for this direction */
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
|
|
|
|
__ASSERT(stream->ep, "stream->ep is NULL");
|
|
|
|
__ASSERT(stream->ep->iso, "ep->iso is NULL");
|
|
|
|
|
|
|
|
if (stream->conn != acl) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-12-14 13:09:52 +01:00
|
|
|
if (bt_audio_iso_get_ep(true, stream->ep->iso, dir) == NULL) {
|
2022-10-21 14:35:10 +02:00
|
|
|
return bt_audio_iso_ref(stream->ep->iso);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return bt_unicast_client_new_audio_iso();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int unicast_group_add_iso(struct bt_audio_unicast_group *group,
|
|
|
|
struct bt_audio_iso *iso)
|
|
|
|
{
|
|
|
|
struct bt_iso_chan **chan_slot = NULL;
|
|
|
|
|
|
|
|
__ASSERT_NO_MSG(group != NULL);
|
|
|
|
__ASSERT_NO_MSG(iso != NULL);
|
|
|
|
|
|
|
|
/* Append iso channel to the group->cis array */
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(group->cis); i++) {
|
|
|
|
/* Return if already there */
|
|
|
|
if (group->cis[i] == &iso->chan) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan_slot == NULL && group->cis[i] == NULL) {
|
|
|
|
chan_slot = &group->cis[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan_slot == NULL) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
*chan_slot = &iso->chan;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void unicast_group_del_iso(struct bt_audio_unicast_group *group,
|
|
|
|
struct bt_audio_iso *iso)
|
|
|
|
{
|
|
|
|
struct bt_audio_stream *stream;
|
|
|
|
|
|
|
|
__ASSERT_NO_MSG(group != NULL);
|
|
|
|
__ASSERT_NO_MSG(iso != NULL);
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
|
|
|
|
if (stream->ep->iso == iso) {
|
|
|
|
/* still in use by some other stream */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(group->cis); i++) {
|
|
|
|
if (group->cis[i] == &iso->chan) {
|
|
|
|
group->cis[i] = NULL;
|
2022-05-24 13:59:45 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
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);
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Added stream %p to group %p", stream, group);
|
2022-10-21 14:35:10 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
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)
|
|
|
|
{
|
2022-01-10 13:57:08 +01:00
|
|
|
struct bt_audio_unicast_group *unicast_group;
|
2022-05-24 13:59:45 +02:00
|
|
|
const struct bt_codec_qos *group_qos = NULL;
|
|
|
|
int err;
|
2022-01-10 13:57:08 +01:00
|
|
|
|
|
|
|
CHECKIF(out_unicast_group == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("out_unicast_group is NULL");
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
/* Set out_unicast_group to NULL until the source has actually been created */
|
|
|
|
*out_unicast_group = NULL;
|
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
CHECKIF(params == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("streams is NULL");
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
CHECKIF(num_param > UNICAST_GROUP_STREAM_CNT) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Too many streams provided: %u/%u", num_param, UNICAST_GROUP_STREAM_CNT);
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
for (size_t i = 0U; i < num_param; i++) {
|
|
|
|
CHECKIF(params[i].stream == NULL ||
|
|
|
|
params[i].qos == NULL ||
|
|
|
|
(params[i].dir != BT_AUDIO_DIR_SINK &&
|
|
|
|
params[i].dir != BT_AUDIO_DIR_SOURCE)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid params[%zu] values", i);
|
2022-03-11 14:58:51 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
if (params[i].stream->group != NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("params[%zu] stream (%p) already part of group %p", i,
|
|
|
|
params[i].stream, params[i].stream->group);
|
2022-03-11 14:58:51 +01:00
|
|
|
return -EALREADY;
|
|
|
|
}
|
2022-05-24 13:59:45 +02:00
|
|
|
|
|
|
|
if (group_qos == NULL) {
|
|
|
|
group_qos = params[i].qos;
|
|
|
|
} else if (!unicast_group_valid_qos(group_qos, params[i].qos)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Stream[%zu] QoS incompatible with group QoS", i);
|
2022-05-24 13:59:45 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECKIF(!bt_audio_valid_qos(params[i].qos)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid QoS");
|
2022-05-24 13:59:45 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2022-03-11 14:58:51 +01:00
|
|
|
}
|
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
unicast_group = unicast_group_alloc();
|
2022-01-10 13:57:08 +01:00
|
|
|
if (unicast_group == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Could not allocate any more unicast groups");
|
2022-01-10 13:57:08 +01:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
for (size_t i = 0U; i < num_param; i++) {
|
2022-10-21 14:35:10 +02:00
|
|
|
err = unicast_group_add_stream(unicast_group, params[i].stream,
|
|
|
|
params[i].qos, params[i].dir);
|
|
|
|
if (err < 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("unicast_group_add_stream failed: %d", err);
|
2022-10-21 14:35:10 +02:00
|
|
|
unicast_group_free(unicast_group);
|
2022-05-24 13:59:45 +02:00
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
return err;
|
2022-05-24 13:59:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = bt_audio_cig_create(unicast_group, group_qos);
|
|
|
|
if (err != 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("bt_audio_cig_create failed: %d", err);
|
2022-10-21 14:35:10 +02:00
|
|
|
unicast_group_free(unicast_group);
|
2022-05-24 13:59:45 +02:00
|
|
|
|
|
|
|
return err;
|
2022-01-10 13:57:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
*out_unicast_group = unicast_group;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-02-04 12:40:29 +01:00
|
|
|
int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_group,
|
2022-05-24 13:59:45 +02:00
|
|
|
struct bt_audio_unicast_group_param params[],
|
|
|
|
size_t num_param)
|
2022-02-04 12:40:29 +01:00
|
|
|
{
|
2022-05-24 13:59:45 +02:00
|
|
|
const struct bt_codec_qos *group_qos = unicast_group->qos;
|
2022-02-04 12:40:29 +01:00
|
|
|
struct bt_audio_stream *tmp_stream;
|
2022-02-22 14:47:00 +01:00
|
|
|
size_t total_stream_cnt;
|
2022-02-04 12:40:29 +01:00
|
|
|
struct bt_iso_cig *cig;
|
2022-10-21 14:35:10 +02:00
|
|
|
size_t num_added;
|
2022-05-24 13:59:45 +02:00
|
|
|
int err;
|
2022-02-04 12:40:29 +01:00
|
|
|
|
|
|
|
CHECKIF(unicast_group == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("unicast_group is NULL");
|
2022-02-04 12:40:29 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
CHECKIF(params == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("params is NULL");
|
2022-02-04 12:40:29 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
CHECKIF(num_param == 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("num_param is 0");
|
2022-02-04 12:40:29 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
for (size_t i = 0U; i < num_param; i++) {
|
|
|
|
CHECKIF(params[i].stream == NULL ||
|
|
|
|
params[i].qos == NULL ||
|
|
|
|
(params[i].dir != BT_AUDIO_DIR_SINK &&
|
|
|
|
params[i].dir != BT_AUDIO_DIR_SOURCE)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid params[%zu] values", i);
|
2022-05-24 13:59:45 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params[i].stream->group != NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("params[%zu] stream (%p) already part of group %p", i,
|
|
|
|
params[i].stream, params[i].stream->group);
|
2022-05-24 13:59:45 +02:00
|
|
|
return -EALREADY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (group_qos == NULL) {
|
|
|
|
group_qos = params[i].qos;
|
|
|
|
} else if (!unicast_group_valid_qos(group_qos, params[i].qos)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Stream[%zu] QoS incompatible with group QoS", i);
|
2022-03-11 14:58:51 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
total_stream_cnt = num_param;
|
2022-05-24 12:17:11 +02:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&unicast_group->streams, tmp_stream, _node) {
|
2022-02-04 12:40:29 +01:00
|
|
|
total_stream_cnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (total_stream_cnt > UNICAST_GROUP_STREAM_CNT) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Too many streams provided: %u/%u", total_stream_cnt,
|
|
|
|
UNICAST_GROUP_STREAM_CNT);
|
2022-02-04 12:40:29 +01:00
|
|
|
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 && cig->state != BT_ISO_CIG_STATE_CONFIGURED) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("At least one unicast group stream is started");
|
2022-02-04 12:40:29 +01:00
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
for (num_added = 0U; num_added < num_param; num_added++) {
|
|
|
|
err = unicast_group_add_stream(unicast_group,
|
|
|
|
params[num_added].stream,
|
|
|
|
params[num_added].qos,
|
|
|
|
params[num_added].dir);
|
|
|
|
if (err < 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("unicast_group_add_stream failed: %d", err);
|
2022-10-21 14:35:10 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
2022-02-04 12:40:29 +01:00
|
|
|
}
|
|
|
|
|
2022-05-24 13:59:45 +02:00
|
|
|
err = bt_audio_cig_reconfigure(unicast_group, group_qos);
|
|
|
|
if (err != 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("bt_audio_cig_reconfigure failed: %d", err);
|
2022-10-21 14:35:10 +02:00
|
|
|
goto fail;
|
2022-02-04 12:40:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2022-10-21 14:35:10 +02:00
|
|
|
|
|
|
|
fail:
|
|
|
|
/* Restore group by removing the newly added streams */
|
|
|
|
while (num_added--) {
|
|
|
|
unicast_group_del_stream(unicast_group, params[num_added].stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
2022-02-04 12:40:29 +01:00
|
|
|
}
|
|
|
|
|
2022-01-10 13:57:08 +01:00
|
|
|
int bt_audio_unicast_group_delete(struct bt_audio_unicast_group *unicast_group)
|
|
|
|
{
|
|
|
|
CHECKIF(unicast_group == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("unicast_group is NULL");
|
2022-01-10 13:57:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-03-31 14:36:29 +02:00
|
|
|
if (unicast_group->cig != NULL) {
|
|
|
|
const int err = bt_audio_cig_terminate(unicast_group);
|
|
|
|
|
|
|
|
if (err != 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("bt_audio_cig_terminate failed with err %d", err);
|
2022-03-31 14:36:29 +02:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
2022-02-04 13:11:00 +01:00
|
|
|
}
|
2022-01-10 13:57:08 +01:00
|
|
|
|
2022-10-21 14:35:10 +02:00
|
|
|
unicast_group_free(unicast_group);
|
2022-01-10 13:57:08 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_BT_AUDIO_UNICAST_CLIENT */
|
2022-04-15 13:16:08 +02:00
|
|
|
|
2022-04-19 11:48:03 +02:00
|
|
|
int bt_audio_stream_reconfig(struct bt_audio_stream *stream,
|
2022-07-18 17:09:59 +02:00
|
|
|
struct bt_codec *codec)
|
2022-04-19 11:48:03 +02:00
|
|
|
{
|
|
|
|
uint8_t state;
|
|
|
|
uint8_t role;
|
|
|
|
int err;
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("stream %p codec %p", stream, codec);
|
2022-04-19 11:48:03 +02:00
|
|
|
|
|
|
|
CHECKIF(stream == NULL || stream->ep == NULL || stream->conn == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid stream");
|
2022-04-19 11:48:03 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECKIF(codec == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("codec is NULL");
|
2022-04-19 11:48:03 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = stream->ep->status.state;
|
|
|
|
switch (state) {
|
|
|
|
/* Valid only if ASE_State field = 0x00 (Idle) */
|
|
|
|
case BT_AUDIO_EP_STATE_IDLE:
|
|
|
|
/* or 0x01 (Codec Configured) */
|
|
|
|
case BT_AUDIO_EP_STATE_CODEC_CONFIGURED:
|
|
|
|
/* or 0x02 (QoS Configured) */
|
|
|
|
case BT_AUDIO_EP_STATE_QOS_CONFIGURED:
|
|
|
|
break;
|
|
|
|
default:
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_ERR("Invalid state: %s", bt_audio_ep_state_str(state));
|
2022-04-19 11:48:03 +02:00
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
role = stream->conn->role;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_AUDIO_UNICAST_CLIENT) &&
|
|
|
|
role == BT_HCI_ROLE_CENTRAL) {
|
|
|
|
err = bt_unicast_client_config(stream, codec);
|
|
|
|
} else if (IS_ENABLED(CONFIG_BT_AUDIO_UNICAST_SERVER) &&
|
|
|
|
role == BT_HCI_ROLE_PERIPHERAL) {
|
|
|
|
err = bt_unicast_server_reconfig(stream, codec);
|
|
|
|
} else {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err != 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("reconfiguring stream failed: %d", err);
|
2022-04-19 11:48:03 +02:00
|
|
|
} else {
|
|
|
|
stream->codec = codec;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-04-15 15:48:08 +02:00
|
|
|
int bt_audio_stream_start(struct bt_audio_stream *stream)
|
|
|
|
{
|
|
|
|
uint8_t state;
|
|
|
|
uint8_t role;
|
|
|
|
int err;
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("stream %p ep %p", stream, stream == NULL ? NULL : stream->ep);
|
2022-04-15 15:48:08 +02:00
|
|
|
|
|
|
|
CHECKIF(stream == NULL || stream->ep == NULL || stream->conn == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid stream");
|
2022-04-15 15:48:08 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = stream->ep->status.state;
|
|
|
|
switch (state) {
|
|
|
|
/* Valid only if ASE_State field = 0x03 (Enabling) */
|
|
|
|
case BT_AUDIO_EP_STATE_ENABLING:
|
|
|
|
break;
|
|
|
|
default:
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_ERR("Invalid state: %s", bt_audio_ep_state_str(state));
|
2022-04-15 15:48:08 +02:00
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
role = stream->conn->role;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_AUDIO_UNICAST_CLIENT) &&
|
|
|
|
role == BT_HCI_ROLE_CENTRAL) {
|
|
|
|
err = bt_unicast_client_start(stream);
|
|
|
|
} else if (IS_ENABLED(CONFIG_BT_AUDIO_UNICAST_SERVER) &&
|
|
|
|
role == BT_HCI_ROLE_PERIPHERAL) {
|
|
|
|
err = bt_unicast_server_start(stream);
|
|
|
|
} else {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err != 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Starting stream failed: %d", err);
|
2022-04-15 15:48:08 +02:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-04-15 14:32:20 +02:00
|
|
|
int bt_audio_stream_metadata(struct bt_audio_stream *stream,
|
|
|
|
struct bt_codec_data *meta,
|
|
|
|
size_t meta_count)
|
|
|
|
{
|
|
|
|
uint8_t state;
|
|
|
|
uint8_t role;
|
|
|
|
int err;
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("stream %p metadata count %u", stream, meta_count);
|
2022-04-15 14:32:20 +02:00
|
|
|
|
|
|
|
CHECKIF(stream == NULL || stream->ep == NULL || stream->conn == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid stream");
|
2022-04-15 14:32:20 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECKIF((meta == NULL && meta_count != 0U) ||
|
|
|
|
(meta != NULL && meta_count == 0U)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid meta (%p) or count (%zu)", meta, meta_count);
|
2022-04-15 14:32:20 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = stream->ep->status.state;
|
|
|
|
switch (state) {
|
|
|
|
/* Valid for an ASE only if ASE_State field = 0x03 (Enabling) */
|
|
|
|
case BT_AUDIO_EP_STATE_ENABLING:
|
|
|
|
/* or 0x04 (Streaming) */
|
|
|
|
case BT_AUDIO_EP_STATE_STREAMING:
|
|
|
|
break;
|
|
|
|
default:
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_ERR("Invalid state: %s", bt_audio_ep_state_str(state));
|
2022-04-15 14:32:20 +02:00
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
role = stream->conn->role;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_AUDIO_UNICAST_CLIENT) &&
|
|
|
|
role == BT_HCI_ROLE_CENTRAL) {
|
|
|
|
err = bt_unicast_client_metadata(stream, meta, meta_count);
|
|
|
|
} else if (IS_ENABLED(CONFIG_BT_AUDIO_UNICAST_SERVER) &&
|
|
|
|
role == BT_HCI_ROLE_PERIPHERAL) {
|
|
|
|
err = bt_unicast_server_metadata(stream, meta, meta_count);
|
|
|
|
} else {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err != 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Updating metadata failed: %d", err);
|
2022-04-15 14:32:20 +02:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-04-15 13:48:16 +02:00
|
|
|
int bt_audio_stream_disable(struct bt_audio_stream *stream)
|
|
|
|
{
|
|
|
|
uint8_t state;
|
|
|
|
uint8_t role;
|
|
|
|
int err;
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("stream %p", stream);
|
2022-04-15 13:48:16 +02:00
|
|
|
|
|
|
|
CHECKIF(stream == NULL || stream->ep == NULL || stream->conn == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid stream");
|
2022-04-15 13:48:16 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = stream->ep->status.state;
|
|
|
|
switch (state) {
|
|
|
|
/* Valid only if ASE_State field = 0x03 (Enabling) */
|
|
|
|
case BT_AUDIO_EP_STATE_ENABLING:
|
|
|
|
/* or 0x04 (Streaming) */
|
|
|
|
case BT_AUDIO_EP_STATE_STREAMING:
|
|
|
|
break;
|
|
|
|
default:
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_ERR("Invalid state: %s", bt_audio_ep_state_str(state));
|
2022-04-15 13:48:16 +02:00
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
role = stream->conn->role;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_AUDIO_UNICAST_CLIENT) &&
|
|
|
|
role == BT_HCI_ROLE_CENTRAL) {
|
|
|
|
err = bt_unicast_client_disable(stream);
|
|
|
|
} else if (IS_ENABLED(CONFIG_BT_AUDIO_UNICAST_SERVER) &&
|
|
|
|
role == BT_HCI_ROLE_PERIPHERAL) {
|
|
|
|
err = bt_unicast_server_disable(stream);
|
|
|
|
} else {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err != 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Disabling stream failed: %d", err);
|
2022-04-15 13:48:16 +02:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Bluetooth: Audio: Remove caching ASE on release
Remove the support for caching ASEs on release.
This is mainly due to the 2-stage design in Zephyr: We
have the bt_audio_stream and the bt_audio_ep, where the
the latter represents an ASE in most cases.
However, once the endpoint goes into the codec configured
state, then a stream must be bound to it. This would be fine
(although a stream is technically not established at this point),
except that a stream is also, for unicast, specified for a
specific ACL. Once the ACL disconnects, the stream technically
becomes invalid, and if the stream is invalid, so is the endpoint.
Based on that, there's no reason (or even option) to keep the
endpoint in a codec configured state, as it would not
have a valid stream associated with it.
If we want to support an ASE going into the codec
configured state on ASE release (or ACL disconnect),
the relationsship between ASEs and streams needs to
be redefined.
Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
2022-09-01 16:14:15 +02:00
|
|
|
int bt_audio_stream_release(struct bt_audio_stream *stream)
|
2022-04-15 13:16:08 +02:00
|
|
|
{
|
|
|
|
uint8_t state;
|
|
|
|
uint8_t role;
|
|
|
|
int err;
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("stream %p", stream);
|
2022-04-15 13:16:08 +02:00
|
|
|
|
|
|
|
CHECKIF(stream == NULL || stream->ep == NULL || stream->conn == NULL) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Invalid stream");
|
2022-04-15 13:16:08 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = stream->ep->status.state;
|
|
|
|
switch (state) {
|
|
|
|
/* Valid only if ASE_State field = 0x01 (Codec Configured) */
|
|
|
|
case BT_AUDIO_EP_STATE_CODEC_CONFIGURED:
|
|
|
|
/* or 0x02 (QoS Configured) */
|
|
|
|
case BT_AUDIO_EP_STATE_QOS_CONFIGURED:
|
|
|
|
/* or 0x03 (Enabling) */
|
|
|
|
case BT_AUDIO_EP_STATE_ENABLING:
|
|
|
|
/* or 0x04 (Streaming) */
|
|
|
|
case BT_AUDIO_EP_STATE_STREAMING:
|
|
|
|
/* or 0x04 (Disabling) */
|
|
|
|
case BT_AUDIO_EP_STATE_DISABLING:
|
|
|
|
break;
|
|
|
|
default:
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_ERR("Invalid state: %s", bt_audio_ep_state_str(state));
|
2022-04-15 13:16:08 +02:00
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
role = stream->conn->role;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_AUDIO_UNICAST_CLIENT) &&
|
|
|
|
role == BT_HCI_ROLE_CENTRAL) {
|
|
|
|
err = bt_unicast_client_release(stream);
|
|
|
|
} else if (IS_ENABLED(CONFIG_BT_AUDIO_UNICAST_SERVER) &&
|
|
|
|
role == BT_HCI_ROLE_PERIPHERAL) {
|
Bluetooth: Audio: Remove caching ASE on release
Remove the support for caching ASEs on release.
This is mainly due to the 2-stage design in Zephyr: We
have the bt_audio_stream and the bt_audio_ep, where the
the latter represents an ASE in most cases.
However, once the endpoint goes into the codec configured
state, then a stream must be bound to it. This would be fine
(although a stream is technically not established at this point),
except that a stream is also, for unicast, specified for a
specific ACL. Once the ACL disconnects, the stream technically
becomes invalid, and if the stream is invalid, so is the endpoint.
Based on that, there's no reason (or even option) to keep the
endpoint in a codec configured state, as it would not
have a valid stream associated with it.
If we want to support an ASE going into the codec
configured state on ASE release (or ACL disconnect),
the relationsship between ASEs and streams needs to
be redefined.
Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
2022-09-01 16:14:15 +02:00
|
|
|
err = bt_unicast_server_release(stream);
|
2022-04-15 13:16:08 +02:00
|
|
|
} else {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err != 0) {
|
2022-11-02 14:31:13 +01:00
|
|
|
LOG_DBG("Releasing stream failed: %d", err);
|
2022-04-15 13:16:08 +02:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2022-01-07 17:23:45 +01:00
|
|
|
#endif /* CONFIG_BT_AUDIO_UNICAST */
|