@@ -852,6 +852,36 @@ static void unicast_client_ep_qos_update(struct bt_bap_ep *ep,
852
852
iso_io_qos -> rtn = qos -> rtn ;
853
853
}
854
854
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
+
855
885
static void unicast_client_ep_config_state (struct bt_bap_ep * ep , struct net_buf_simple * buf )
856
886
{
857
887
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_
932
962
unicast_client_ep_set_codec_cfg (ep , cfg -> codec .id , sys_le16_to_cpu (cfg -> codec .cid ),
933
963
sys_le16_to_cpu (cfg -> codec .vid ), cc , cfg -> cc_len , NULL );
934
964
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
+
935
973
/* Notify upper layer */
936
974
if (stream -> ops != NULL && stream -> ops -> configured != NULL ) {
937
975
stream -> ops -> configured (stream , pref );
@@ -943,8 +981,10 @@ static void unicast_client_ep_config_state(struct bt_bap_ep *ep, struct net_buf_
943
981
static void unicast_client_ep_qos_state (struct bt_bap_ep * ep , struct net_buf_simple * buf ,
944
982
uint8_t old_state )
945
983
{
984
+ const enum bt_audio_dir dir = ep -> dir ;
946
985
const struct bt_bap_stream_ops * ops ;
947
986
struct bt_ascs_ase_status_qos * qos ;
987
+ struct bt_bap_unicast_group * group ;
948
988
struct bt_bap_stream * stream ;
949
989
950
990
ep -> receiver_ready = false;
@@ -1009,10 +1049,36 @@ static void unicast_client_ep_qos_state(struct bt_bap_ep *ep, struct net_buf_sim
1009
1049
1010
1050
LOG_DBG ("dir %s cig 0x%02x cis 0x%02x codec 0x%02x interval %u "
1011
1051
"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 ,
1013
1053
stream -> qos -> interval , stream -> qos -> framing , stream -> qos -> phy , stream -> qos -> rtn ,
1014
1054
stream -> qos -> latency , stream -> qos -> pd );
1015
1055
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
+
1016
1082
/* Disconnect ISO if connected */
1017
1083
if (bt_bap_stream_can_disconnect (stream )) {
1018
1084
const int err = bt_bap_stream_disconnect (stream );
@@ -2632,6 +2698,8 @@ static struct bt_bap_unicast_group *unicast_group_alloc(void)
2632
2698
2633
2699
group -> allocated = true;
2634
2700
group -> index = i ;
2701
+ group -> sink_pd = BT_BAP_PD_UNSET ;
2702
+ group -> source_pd = BT_BAP_PD_UNSET ;
2635
2703
2636
2704
break ;
2637
2705
}
@@ -3224,11 +3292,15 @@ int bt_bap_unicast_client_config(struct bt_bap_stream *stream,
3224
3292
3225
3293
int bt_bap_unicast_client_qos (struct bt_conn * conn , struct bt_bap_unicast_group * group )
3226
3294
{
3295
+ bool source_qos_configured_on_other_conn ;
3296
+ bool sink_qos_configured_on_other_conn ;
3227
3297
struct bt_bap_stream * stream ;
3228
3298
struct bt_ascs_config_op * op ;
3229
3299
struct net_buf_simple * buf ;
3230
3300
struct bt_bap_ep * ep ;
3231
3301
bool conn_stream_found ;
3302
+ uint32_t source_pd ;
3303
+ uint32_t sink_pd ;
3232
3304
int err ;
3233
3305
3234
3306
if (conn == NULL ) {
@@ -3237,18 +3309,55 @@ int bt_bap_unicast_client_qos(struct bt_conn *conn, struct bt_bap_unicast_group
3237
3309
return - ENOTCONN ;
3238
3310
}
3239
3311
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
+
3240
3322
/* Used to determine if a stream for the supplied connection pointer
3241
3323
* was actually found
3242
3324
*/
3243
3325
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 ;
3244
3355
3245
- /* Validate streams before starting the QoS execution */
3246
3356
SYS_SLIST_FOR_EACH_CONTAINER (& group -> streams , stream , _node ) {
3247
3357
if (stream -> conn != conn ) {
3248
3358
/* Channel not part of this ACL, skip */
3249
3359
continue ;
3250
3360
}
3251
- conn_stream_found = true;
3252
3361
3253
3362
ep = stream -> ep ;
3254
3363
if (ep == NULL ) {
@@ -3272,10 +3381,43 @@ int bt_bap_unicast_client_qos(struct bt_conn *conn, struct bt_bap_unicast_group
3272
3381
return - EINVAL ;
3273
3382
}
3274
3383
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
+ */
3276
3395
switch (ep -> dir ) {
3277
3396
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 ;
3278
3409
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
+ }
3279
3421
break ;
3280
3422
default :
3281
3423
__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
3291
3433
}
3292
3434
}
3293
3435
3294
- if (!conn_stream_found ) {
3295
- LOG_DBG ("No streams in the group %p for conn %p" , group , conn );
3296
- return - EINVAL ;
3297
- }
3298
-
3299
3436
/* Generate the control point write */
3300
3437
buf = bt_bap_unicast_client_ep_create_pdu (conn , BT_ASCS_QOS_OP );
3301
3438
if (buf == NULL ) {
0 commit comments