Skip to content

Commit e42e875

Browse files
Thalleyjhedberg
authored andcommitted
Bluetooth: BAP: Add validation of presentation delay
BAP section 7.1 states that all streams in a direction shall have the same presentation delay. To achieve this, we need to store the configured presentation delay for a unicast group, so that we can compare with the new presentation delay. Signed-off-by: Emil Gydesen <[email protected]>
1 parent aedd23f commit e42e875

File tree

2 files changed

+153
-9
lines changed

2 files changed

+153
-9
lines changed

subsys/bluetooth/audio/bap_endpoint.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
#define BROADCAST_STREAM_CNT 0
3838
#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
3939

40+
#define BT_BAP_PD_UNSET 0xFFFFFFFFU /* Valid value range uses 24 bits, but is stored in 32 */
41+
4042
/* Temp struct declarations to handle circular dependencies */
4143
struct bt_bap_unicast_group;
4244
struct bt_bap_broadcast_source;
@@ -94,6 +96,11 @@ struct bt_bap_unicast_group {
9496
/* The ISO API for CIG creation requires an array of pointers to ISO channels */
9597
struct bt_iso_chan *cis[UNICAST_GROUP_STREAM_CNT];
9698
sys_slist_t streams;
99+
100+
/* Configured sink presentation delay */
101+
uint32_t sink_pd;
102+
/* Configured source presentation delay */
103+
uint32_t source_pd;
97104
};
98105

99106
#if CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0

subsys/bluetooth/audio/bap_unicast_client.c

Lines changed: 146 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,36 @@ static void unicast_client_ep_qos_update(struct bt_bap_ep *ep,
852852
iso_io_qos->rtn = qos->rtn;
853853
}
854854

855+
static void check_and_reset_group_pd(struct bt_bap_unicast_group *group, enum bt_audio_dir dir)
856+
{
857+
bool dir_in_idle_or_config_state = true;
858+
struct bt_bap_stream *stream;
859+
860+
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
861+
if (stream->ep == NULL || stream->ep->dir != dir) {
862+
continue;
863+
}
864+
865+
if (stream->ep->status.state == BT_BAP_EP_STATE_IDLE ||
866+
stream->ep->status.state == BT_BAP_EP_STATE_CODEC_CONFIGURED) {
867+
continue;
868+
} else {
869+
dir_in_idle_or_config_state = false;
870+
break;
871+
}
872+
}
873+
874+
if (dir_in_idle_or_config_state) {
875+
if (dir == BT_AUDIO_DIR_SINK) {
876+
group->sink_pd = BT_BAP_PD_UNSET;
877+
} else if (dir == BT_AUDIO_DIR_SOURCE) {
878+
group->source_pd = BT_BAP_PD_UNSET;
879+
} else {
880+
__ASSERT(false, "Invalid dir %d", dir);
881+
}
882+
}
883+
}
884+
855885
static void unicast_client_ep_config_state(struct bt_bap_ep *ep, struct net_buf_simple *buf)
856886
{
857887
struct bt_bap_unicast_client_ep *client_ep =
@@ -932,6 +962,14 @@ static void unicast_client_ep_config_state(struct bt_bap_ep *ep, struct net_buf_
932962
unicast_client_ep_set_codec_cfg(ep, cfg->codec.id, sys_le16_to_cpu(cfg->codec.cid),
933963
sys_le16_to_cpu(cfg->codec.vid), cc, cfg->cc_len, NULL);
934964

965+
/* Every time a stream enters the codec configured state, there is a chance that all streams
966+
* in that direction has exited the QoS configured state, and we need to update the stored
967+
* presentation delay
968+
*/
969+
if (stream->group != NULL) {
970+
check_and_reset_group_pd((struct bt_bap_unicast_group *)stream->group, ep->dir);
971+
}
972+
935973
/* Notify upper layer */
936974
if (stream->ops != NULL && stream->ops->configured != NULL) {
937975
stream->ops->configured(stream, pref);
@@ -943,8 +981,10 @@ static void unicast_client_ep_config_state(struct bt_bap_ep *ep, struct net_buf_
943981
static void unicast_client_ep_qos_state(struct bt_bap_ep *ep, struct net_buf_simple *buf,
944982
uint8_t old_state)
945983
{
984+
const enum bt_audio_dir dir = ep->dir;
946985
const struct bt_bap_stream_ops *ops;
947986
struct bt_ascs_ase_status_qos *qos;
987+
struct bt_bap_unicast_group *group;
948988
struct bt_bap_stream *stream;
949989

950990
ep->receiver_ready = false;
@@ -1009,10 +1049,36 @@ static void unicast_client_ep_qos_state(struct bt_bap_ep *ep, struct net_buf_sim
10091049

10101050
LOG_DBG("dir %s cig 0x%02x cis 0x%02x codec 0x%02x interval %u "
10111051
"framing 0x%02x phy 0x%02x rtn %u latency %u pd %u",
1012-
bt_audio_dir_str(ep->dir), ep->cig_id, ep->cis_id, stream->codec_cfg->id,
1052+
bt_audio_dir_str(dir), ep->cig_id, ep->cis_id, stream->codec_cfg->id,
10131053
stream->qos->interval, stream->qos->framing, stream->qos->phy, stream->qos->rtn,
10141054
stream->qos->latency, stream->qos->pd);
10151055

1056+
__ASSERT_NO_MSG(stream->group != NULL);
1057+
group = (struct bt_bap_unicast_group *)stream->group;
1058+
if (dir == BT_AUDIO_DIR_SINK) {
1059+
if (group->sink_pd == BT_BAP_PD_UNSET) {
1060+
group->sink_pd = stream->qos->pd;
1061+
} else {
1062+
if (group->sink_pd != stream->qos->pd) {
1063+
LOG_WRN("Sink stream %p PD %u does not match the sink PD %u of the "
1064+
"group %p",
1065+
stream, stream->qos->pd, group->sink_pd, group);
1066+
}
1067+
}
1068+
} else if (dir == BT_AUDIO_DIR_SOURCE) {
1069+
if (group->source_pd == BT_BAP_PD_UNSET) {
1070+
group->source_pd = stream->qos->pd;
1071+
} else {
1072+
if (group->source_pd != stream->qos->pd) {
1073+
LOG_WRN("Source stream %p PD %u does not match the source PD %u of "
1074+
"the group %p",
1075+
stream, stream->qos->pd, group->source_pd, group);
1076+
}
1077+
}
1078+
} else {
1079+
__ASSERT(false, "Invalid dir %d", dir);
1080+
}
1081+
10161082
/* Disconnect ISO if connected */
10171083
if (bt_bap_stream_can_disconnect(stream)) {
10181084
const int err = bt_bap_stream_disconnect(stream);
@@ -2632,6 +2698,8 @@ static struct bt_bap_unicast_group *unicast_group_alloc(void)
26322698

26332699
group->allocated = true;
26342700
group->index = i;
2701+
group->sink_pd = BT_BAP_PD_UNSET;
2702+
group->source_pd = BT_BAP_PD_UNSET;
26352703

26362704
break;
26372705
}
@@ -3224,11 +3292,15 @@ int bt_bap_unicast_client_config(struct bt_bap_stream *stream,
32243292

32253293
int bt_bap_unicast_client_qos(struct bt_conn *conn, struct bt_bap_unicast_group *group)
32263294
{
3295+
bool source_qos_configured_on_other_conn;
3296+
bool sink_qos_configured_on_other_conn;
32273297
struct bt_bap_stream *stream;
32283298
struct bt_ascs_config_op *op;
32293299
struct net_buf_simple *buf;
32303300
struct bt_bap_ep *ep;
32313301
bool conn_stream_found;
3302+
uint32_t source_pd;
3303+
uint32_t sink_pd;
32323304
int err;
32333305

32343306
if (conn == NULL) {
@@ -3237,18 +3309,55 @@ int bt_bap_unicast_client_qos(struct bt_conn *conn, struct bt_bap_unicast_group
32373309
return -ENOTCONN;
32383310
}
32393311

3312+
if (group == NULL) {
3313+
LOG_DBG("group is NULL");
3314+
3315+
return -EINVAL;
3316+
}
3317+
3318+
/* Validate streams before starting the QoS execution */
3319+
source_qos_configured_on_other_conn = false;
3320+
sink_qos_configured_on_other_conn = false;
3321+
32403322
/* Used to determine if a stream for the supplied connection pointer
32413323
* was actually found
32423324
*/
32433325
conn_stream_found = false;
3326+
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
3327+
enum bt_audio_dir dir;
3328+
3329+
if (stream->conn == conn) {
3330+
conn_stream_found = true;
3331+
continue;
3332+
}
3333+
3334+
ep = stream->ep;
3335+
dir = ep->dir;
3336+
3337+
if (ep->status.state >= BT_BAP_EP_STATE_QOS_CONFIGURED) {
3338+
if (dir == BT_AUDIO_DIR_SINK) {
3339+
sink_qos_configured_on_other_conn = true;
3340+
} else if (dir == BT_AUDIO_DIR_SOURCE) {
3341+
source_qos_configured_on_other_conn = true;
3342+
} else {
3343+
__ASSERT(false, "Invalid dir %d", dir);
3344+
}
3345+
}
3346+
}
3347+
3348+
if (!conn_stream_found) {
3349+
LOG_DBG("No streams in the group %p for conn %p", group, conn);
3350+
return -EINVAL;
3351+
}
3352+
3353+
source_pd = group->source_pd;
3354+
sink_pd = group->sink_pd;
32443355

3245-
/* Validate streams before starting the QoS execution */
32463356
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
32473357
if (stream->conn != conn) {
32483358
/* Channel not part of this ACL, skip */
32493359
continue;
32503360
}
3251-
conn_stream_found = true;
32523361

32533362
ep = stream->ep;
32543363
if (ep == NULL) {
@@ -3272,10 +3381,43 @@ int bt_bap_unicast_client_qos(struct bt_conn *conn, struct bt_bap_unicast_group
32723381
return -EINVAL;
32733382
}
32743383

3275-
/* Verify ep->dir */
3384+
/* Verify ep->dir and presentation delay. If the group already has a configured
3385+
* presentation delay in a direction, we compare the stream's presentation delay
3386+
* with the group's presentation delay, and if they differ we reject the request.
3387+
* As per the BAP spec section 7.1, all streams in a direction shall have the same
3388+
* presentation delay. The group presentation delay is set once any endpoint in a
3389+
* direction has changed state to "QoS configured" or "above", and cleared again if
3390+
* all endpoints for that direction enters the codec configured or idle state.
3391+
* The check for presentation delay is also conditional on whether other devices are
3392+
* involved - If there is only a single connection that has ASEs in a QoS Configured
3393+
* state or "above", then we can freely modify the presentation delay.
3394+
*/
32763395
switch (ep->dir) {
32773396
case BT_AUDIO_DIR_SINK:
3397+
if (sink_pd == BT_BAP_PD_UNSET) {
3398+
sink_pd = stream->qos->pd;
3399+
} else {
3400+
if (sink_qos_configured_on_other_conn &&
3401+
sink_pd != stream->qos->pd) {
3402+
LOG_DBG("Sink stream %p did not have the same PD %u as "
3403+
"other sink streams %u",
3404+
stream, stream->qos->pd, sink_pd);
3405+
return -EINVAL;
3406+
}
3407+
}
3408+
break;
32783409
case BT_AUDIO_DIR_SOURCE:
3410+
if (source_pd == BT_BAP_PD_UNSET) {
3411+
source_pd = stream->qos->pd;
3412+
} else {
3413+
if (source_qos_configured_on_other_conn &&
3414+
source_pd != stream->qos->pd) {
3415+
LOG_DBG("Source stream %p did not have the same PD %u as "
3416+
"other source streams %u",
3417+
stream, stream->qos->pd, source_pd);
3418+
return -EINVAL;
3419+
}
3420+
}
32793421
break;
32803422
default:
32813423
__ASSERT(false, "invalid endpoint dir: %u", ep->dir);
@@ -3291,11 +3433,6 @@ int bt_bap_unicast_client_qos(struct bt_conn *conn, struct bt_bap_unicast_group
32913433
}
32923434
}
32933435

3294-
if (!conn_stream_found) {
3295-
LOG_DBG("No streams in the group %p for conn %p", group, conn);
3296-
return -EINVAL;
3297-
}
3298-
32993436
/* Generate the control point write */
33003437
buf = bt_bap_unicast_client_ep_create_pdu(conn, BT_ASCS_QOS_OP);
33013438
if (buf == NULL) {

0 commit comments

Comments
 (0)