Skip to content

Commit

Permalink
Bluetooth: BAP: Support setting different values per dir in CIG
Browse files Browse the repository at this point in the history
The interval and latency for a CIG are set for each direction now,
allowing applications to use e.g. 10ms for sink ASEs and 7.5ms for
source ASEs.

Signed-off-by: Emil Gydesen <[email protected]>
  • Loading branch information
Thalley committed Jun 21, 2024
1 parent d0a2451 commit 92b1266
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 70 deletions.
14 changes: 12 additions & 2 deletions subsys/bluetooth/audio/bap_endpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,21 @@ struct bt_bap_ep {
struct bt_bap_broadcast_sink *broadcast_sink;
};

struct bt_bap_unicast_group_cig_param {
uint32_t c_to_p_interval;
uint32_t p_to_c_interval;
uint16_t c_to_p_latency;
uint16_t p_to_c_latency;
uint8_t framing;
};

struct bt_bap_unicast_group {
/* Group-wide QoS used to create the CIG */
struct bt_bap_unicast_group_cig_param cig_param;

/* Unicast group fields */
uint8_t index;
bool allocated;
/* QoS used to create the CIG */
const struct bt_audio_codec_qos *qos;
struct bt_iso_cig *cig;
/* The ISO API for CIG creation requires an array of pointers to ISO channels */
struct bt_iso_chan *cis[UNICAST_GROUP_STREAM_CNT];
Expand Down
230 changes: 167 additions & 63 deletions subsys/bluetooth/audio/bap_unicast_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -2136,15 +2136,14 @@ static void unicast_client_ep_reset(struct bt_conn *conn, uint8_t reason)
}

static void bt_audio_codec_qos_to_cig_param(struct bt_iso_cig_param *cig_param,
const struct bt_audio_codec_qos *qos,
const struct bt_bap_unicast_group *group,
const struct bt_bap_unicast_group_param *group_param)
{
cig_param->framing = qos->framing;
cig_param->packing = BT_ISO_PACKING_SEQUENTIAL; /* TODO: Add to QoS struct */
cig_param->c_to_p_interval = qos->interval;
cig_param->p_to_c_interval = qos->interval;
cig_param->c_to_p_latency = qos->latency;
cig_param->p_to_c_latency = qos->latency;
cig_param->framing = group->cig_param.framing;
cig_param->c_to_p_interval = group->cig_param.c_to_p_interval;
cig_param->p_to_c_interval = group->cig_param.p_to_c_interval;
cig_param->c_to_p_latency = group->cig_param.c_to_p_latency;
cig_param->p_to_c_latency = group->cig_param.p_to_c_latency;
cig_param->sca = BT_GAP_SCA_UNKNOWN;

if (group_param != NULL) {
Expand All @@ -2155,21 +2154,26 @@ static void bt_audio_codec_qos_to_cig_param(struct bt_iso_cig_param *cig_param,
cig_param->iso_interval = group_param->iso_interval;
#endif /* CONFIG_BT_ISO_TEST_PARAMS */
}

/* In the case that we only setup a single direction, we still need
* (as per section 7.8.97 LE Set CIG Parameters command) to set the interval both sides.
* If it is at this point unset, then we set the opposing direction to the same value.
*/
if (cig_param->c_to_p_interval == 0U) {
cig_param->c_to_p_interval = cig_param->p_to_c_interval;
} else if (cig_param->p_to_c_interval == 0U) {
cig_param->p_to_c_interval = cig_param->c_to_p_interval;
}
}

/* FIXME: Remove `qos` parameter. Some of the QoS related CIG can be different
* between CIS'es. The implementation shall take the CIG parameters from
* unicast_group instead.
*/
static int bt_audio_cig_create(struct bt_bap_unicast_group *group,
const struct bt_audio_codec_qos *qos,
const struct bt_bap_unicast_group_param *group_param)
{
struct bt_iso_cig_param param;
struct bt_iso_cig_param param = {0};
uint8_t cis_count;
int err;

LOG_DBG("group %p qos %p", group, qos);
LOG_DBG("group %p", group);

cis_count = 0U;
for (size_t i = 0U; i < ARRAY_SIZE(group->cis); i++) {
Expand All @@ -2183,27 +2187,24 @@ static int bt_audio_cig_create(struct bt_bap_unicast_group *group,

param.num_cis = cis_count;
param.cis_channels = group->cis;
bt_audio_codec_qos_to_cig_param(&param, qos, group_param);
bt_audio_codec_qos_to_cig_param(&param, group, group_param);

err = bt_iso_cig_create(&param, &group->cig);
if (err != 0) {
LOG_ERR("bt_iso_cig_create failed: %d", err);
return err;
}

group->qos = qos;

return 0;
}

static int bt_audio_cig_reconfigure(struct bt_bap_unicast_group *group,
const struct bt_audio_codec_qos *qos)
static int bt_audio_cig_reconfigure(struct bt_bap_unicast_group *group)
{
struct bt_iso_cig_param param;
uint8_t cis_count;
int err;

LOG_DBG("group %p qos %p", group, qos);
LOG_DBG("group %p ", group);

cis_count = 0U;
for (size_t i = 0U; i < ARRAY_SIZE(group->cis); i++) {
Expand All @@ -2217,16 +2218,14 @@ static int bt_audio_cig_reconfigure(struct bt_bap_unicast_group *group,

param.num_cis = cis_count;
param.cis_channels = group->cis;
bt_audio_codec_qos_to_cig_param(&param, qos, NULL);
bt_audio_codec_qos_to_cig_param(&param, group, NULL);

err = bt_iso_cig_reconfigure(group->cig, &param);
if (err != 0) {
LOG_ERR("bt_iso_cig_create failed: %d", err);
return err;
}

group->qos = qos;

return 0;
}

Expand Down Expand Up @@ -2369,9 +2368,21 @@ static void unicast_group_add_stream(struct bt_bap_unicast_group *group,
bt_bap_iso_bind_ep(iso, stream->ep);
}

/* Store the Codec QoS in the bap_iso */
/* Store the stream Codec QoS in the bap_iso */
unicast_client_codec_qos_to_iso_qos(iso, qos, dir);

/* Store the group Codec QoS in the group - This assume thats the parameters have been
* verified first
*/
group->cig_param.framing = qos->framing;
if (dir == BT_AUDIO_DIR_SOURCE) {
group->cig_param.p_to_c_interval = qos->interval;
group->cig_param.p_to_c_latency = qos->latency;
} else {
group->cig_param.c_to_p_interval = qos->interval;
group->cig_param.c_to_p_latency = qos->latency;
}

sys_slist_append(&group->streams, &stream->_node);
}

Expand Down Expand Up @@ -2558,25 +2569,135 @@ static int stream_pair_param_check(const struct bt_bap_unicast_group_stream_pair
return 0;
}

static int group_qos_common_set(const struct bt_audio_codec_qos **group_qos,
const struct bt_bap_unicast_group_stream_pair_param *param)
/** Validates that the stream parameter does not contain invalid values */
static bool valid_unicast_group_stream_param(const struct bt_bap_unicast_group_stream_param *param,
struct bt_bap_unicast_group_cig_param *cig_param,
enum bt_audio_dir dir)
{
if (param->rx_param != NULL && *group_qos == NULL) {
*group_qos = param->rx_param->qos;
const struct bt_audio_codec_qos *qos;

CHECKIF(param->stream == NULL) {
LOG_DBG("param->stream is NULL");
return -EINVAL;
}

if (param->tx_param != NULL && *group_qos == NULL) {
*group_qos = param->tx_param->qos;
CHECKIF(param->qos == NULL) {
LOG_DBG("param->qos is NULL");
return -EINVAL;
}

return 0;
if (param->stream != NULL && param->stream->group != NULL) {
LOG_DBG("stream %p already part of group %p", param->stream, param->stream->group);
return -EALREADY;
}

CHECKIF(bt_audio_verify_qos(param->qos) != BT_BAP_ASCS_REASON_NONE) {
LOG_DBG("Invalid QoS");
return -EINVAL;
}

qos = param->qos;

/* If unset we set the interval else we verify that all streams use the same interval and
* latency in the same direction, as that is required when creating a CIG
*/
if (dir == BT_AUDIO_DIR_SINK) {
if (cig_param->c_to_p_interval == 0) {
cig_param->c_to_p_interval = qos->interval;
} else if (cig_param->c_to_p_interval != qos->interval) {
return false;
}

if (cig_param->c_to_p_latency == 0) {
cig_param->c_to_p_latency = qos->latency;
} else if (cig_param->c_to_p_latency != qos->latency) {
return false;
}
} else {
if (cig_param->p_to_c_interval == 0) {
cig_param->p_to_c_interval = qos->interval;
} else if (cig_param->p_to_c_interval != qos->interval) {
return false;
}

if (cig_param->p_to_c_latency == 0) {
cig_param->p_to_c_latency = qos->latency;
} else if (cig_param->p_to_c_latency != qos->latency) {
return false;
}
}

if (cig_param->framing == 0) {
if (qos->framing == BT_AUDIO_CODEC_QOS_FRAMING_UNFRAMED) {
cig_param->framing = BT_ISO_FRAMING_UNFRAMED;
} else if (qos->framing == BT_AUDIO_CODEC_QOS_FRAMING_FRAMED) {
cig_param->framing = BT_ISO_FRAMING_FRAMED;
}
} else if ((qos->framing == BT_AUDIO_CODEC_QOS_FRAMING_UNFRAMED &&
cig_param->framing != BT_ISO_FRAMING_UNFRAMED) ||
(qos->framing == BT_AUDIO_CODEC_QOS_FRAMING_FRAMED &&
cig_param->framing != BT_ISO_FRAMING_FRAMED)) {
return false;
}

return true;
}

static bool
valid_group_stream_pair_param(const struct bt_bap_unicast_group *unicast_group,
const struct bt_bap_unicast_group_stream_pair_param *pair_param)
{
struct bt_bap_unicast_group_cig_param cig_param = {0};

CHECKIF(pair_param == NULL) {
LOG_DBG("pair_param is NULL");
return false;
}

if (pair_param->rx_param != NULL) {
if (!valid_unicast_group_stream_param(pair_param->rx_param, &cig_param,
BT_AUDIO_DIR_SOURCE)) {
return false;
}
}

if (pair_param->tx_param != NULL) {
if (!valid_unicast_group_stream_param(pair_param->tx_param, &cig_param,
BT_AUDIO_DIR_SINK)) {
return false;
}
}

return true;
}

static bool valid_unicast_group_param(const struct bt_bap_unicast_group *unicast_group,
const struct bt_bap_unicast_group_param *param)
{
CHECKIF(param == NULL) {
LOG_DBG("streams is NULL");
return false;
}

CHECKIF(param->params_count > UNICAST_GROUP_STREAM_CNT) {
LOG_DBG("Too many streams provided: %u/%u", param->params_count,
UNICAST_GROUP_STREAM_CNT);
return false;
}

for (size_t i = 0U; i < param->params_count; i++) {
if (!valid_group_stream_pair_param(unicast_group, &param->params[i])) {
return false;
}
}

return true;
}

int bt_bap_unicast_group_create(struct bt_bap_unicast_group_param *param,
struct bt_bap_unicast_group **out_unicast_group)
{
struct bt_bap_unicast_group *unicast_group;
const struct bt_audio_codec_qos *group_qos = NULL;
int err;

CHECKIF(out_unicast_group == NULL)
Expand All @@ -2587,40 +2708,23 @@ int bt_bap_unicast_group_create(struct bt_bap_unicast_group_param *param,
/* Set out_unicast_group to NULL until the source has actually been created */
*out_unicast_group = NULL;

CHECKIF(param == NULL)
{
LOG_DBG("streams is NULL");
return -EINVAL;
}

CHECKIF(param->params_count > UNICAST_GROUP_STREAM_CNT)
{
LOG_DBG("Too many streams provided: %u/%u", param->params_count,
UNICAST_GROUP_STREAM_CNT);
return -EINVAL;
}

unicast_group = unicast_group_alloc();
if (unicast_group == NULL) {
LOG_DBG("Could not allocate any more unicast groups");
return -ENOMEM;
}

if (!valid_unicast_group_param(unicast_group, param)) {
unicast_group_free(unicast_group);

return -EINVAL;
}

for (size_t i = 0U; i < param->params_count; i++) {
struct bt_bap_unicast_group_stream_pair_param *stream_param;

stream_param = &param->params[i];

err = stream_pair_param_check(stream_param);
if (err < 0) {
return err;
}

err = group_qos_common_set(&group_qos, stream_param);
if (err < 0) {
return err;
}

err = unicast_group_add_stream_pair(unicast_group, stream_param);
if (err < 0) {
LOG_DBG("unicast_group_add_stream failed: %d", err);
Expand All @@ -2630,7 +2734,7 @@ int bt_bap_unicast_group_create(struct bt_bap_unicast_group_param *param,
}
}

err = bt_audio_cig_create(unicast_group, group_qos, param);
err = bt_audio_cig_create(unicast_group, param);
if (err != 0) {
LOG_DBG("bt_audio_cig_create failed: %d", err);
unicast_group_free(unicast_group);
Expand All @@ -2647,7 +2751,6 @@ int bt_bap_unicast_group_add_streams(struct bt_bap_unicast_group *unicast_group,
struct bt_bap_unicast_group_stream_pair_param params[],
size_t num_param)
{
const struct bt_audio_codec_qos *group_qos = unicast_group->qos;
struct bt_bap_stream *tmp_stream;
size_t total_stream_cnt;
struct bt_iso_cig *cig;
Expand Down Expand Up @@ -2683,6 +2786,12 @@ int bt_bap_unicast_group_add_streams(struct bt_bap_unicast_group *unicast_group,
return -EINVAL;
}

for (size_t i = 0U; i < num_param; i++) {
if (!valid_group_stream_pair_param(unicast_group, &params[i])) {
return -EINVAL;
}
}

/* We can just check the CIG state to see if any streams have started as
* that would start the ISO connection procedure
*/
Expand All @@ -2702,19 +2811,14 @@ int bt_bap_unicast_group_add_streams(struct bt_bap_unicast_group *unicast_group,
return err;
}

err = group_qos_common_set(&group_qos, stream_param);
if (err < 0) {
return err;
}

err = unicast_group_add_stream_pair(unicast_group, stream_param);
if (err < 0) {
LOG_DBG("unicast_group_add_stream failed: %d", err);
goto fail;
}
}

err = bt_audio_cig_reconfigure(unicast_group, group_qos);
err = bt_audio_cig_reconfigure(unicast_group);
if (err != 0) {
LOG_DBG("bt_audio_cig_reconfigure failed: %d", err);
goto fail;
Expand Down
Loading

0 comments on commit 92b1266

Please sign in to comment.