bluez5: temporarily remove BAP nodes when another device is switching

Unicast BAP codec switch requires CIG reconfiguration, which cannot be
done if there is an acquired transport.

When doing BAP codec switch, disable nodes of other devices sharing the
same CIG.

To avoid problems with node start/stop, just remove and re-add them.
This commit is contained in:
Pauli Virtanen 2025-06-12 23:58:16 +03:00 committed by Wim Taymans
parent 3ed969144a
commit 26b09b0ee3
3 changed files with 81 additions and 11 deletions

View file

@ -158,6 +158,7 @@ struct impl {
uint32_t profile;
unsigned int switching_codec:1;
unsigned int switching_codec_other:1;
unsigned int save_profile:1;
uint32_t prev_bt_connected_profiles;
@ -1161,6 +1162,15 @@ static int emit_nodes(struct impl *this)
{
struct spa_bt_transport *t;
switch (this->profile) {
case DEVICE_PROFILE_BAP:
case DEVICE_PROFILE_BAP_SINK:
case DEVICE_PROFILE_BAP_SOURCE:
if (this->switching_codec_other)
return -EBUSY;
break;
}
this->props.codec = 0;
device_set_update(this, &this->device_set, this->profile);
@ -1459,6 +1469,7 @@ static int set_profile(struct impl *this, uint32_t profile, enum spa_bluetooth_a
}
this->switching_codec = false;
emit_nodes(this);
this->info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS;
@ -1502,6 +1513,43 @@ static void codec_switched(void *userdata, int status)
emit_info(this, false);
}
static void codec_switch_other(void *userdata, bool switching)
{
struct impl *this = userdata;
this->switching_codec_other = switching;
switch (this->profile) {
case DEVICE_PROFILE_BAP:
case DEVICE_PROFILE_BAP_SINK:
case DEVICE_PROFILE_BAP_SOURCE:
break;
default:
return;
}
spa_log_debug(this->log, "%p: BAP codec switching by another device, switching:%d",
this, (int)switching);
/*
* In unicast BAP, output/input must be halted when another device is
* switching codec, because CIG must be torn down before it can be
* reconfigured. Easiest way to do this and to suspend output/input is to
* remove the nodes.
*/
if (!find_device_transport(this->bt_dev, SPA_BT_PROFILE_BAP_SINK) &&
!find_device_transport(this->bt_dev, SPA_BT_PROFILE_BAP_SOURCE))
return;
if (switching) {
emit_remove_nodes(this);
spa_bt_device_release_transports(this->bt_dev);
} else {
emit_remove_nodes(this);
emit_nodes(this);
}
}
static bool device_set_needs_update(struct impl *this)
{
struct device_set dset = { .impl = this };
@ -1655,6 +1703,7 @@ static const struct spa_bt_device_events bt_dev_events = {
SPA_VERSION_BT_DEVICE_EVENTS,
.connected = device_connected,
.codec_switched = codec_switched,
.codec_switch_other = codec_switch_other,
.profiles_changed = profiles_changed,
.device_set_changed = device_set_changed,
.switch_profile = device_switch_profile,