bluez5: pass per-device settings to codec handler, make 'bluez5.sbc-xq-support' a per-device setting

This commit is contained in:
Huang-Huang Bao 2021-03-14 17:53:31 +08:00 committed by Wim Taymans
parent af8272fe08
commit 374180e211
7 changed files with 71 additions and 27 deletions

View file

@ -707,7 +707,8 @@ static int do_start(struct impl *this)
this->codec_data = this->codec->init(this->codec, 0, this->codec_data = this->codec->init(this->codec, 0,
this->transport->configuration, this->transport->configuration,
this->transport->configuration_len, this->transport->configuration_len,
&port->current_format, NULL, &port->current_format,
this->transport->device->settings,
this->transport->write_mtu); this->transport->write_mtu);
if (this->codec_data == NULL) if (this->codec_data == NULL)
return -EIO; return -EIO;

View file

@ -569,7 +569,8 @@ static int transport_start(struct impl *this)
this->codec_data = this->codec->init(this->codec, 0, this->codec_data = this->codec->init(this->codec, 0,
this->transport->configuration, this->transport->configuration,
this->transport->configuration_len, this->transport->configuration_len,
&port->current_format, NULL, &port->current_format,
this->transport->device->settings,
this->transport->read_mtu); this->transport->read_mtu);
if (this->codec_data == NULL) if (this->codec_data == NULL)
return -EIO; return -EIO;

View file

@ -61,7 +61,7 @@ struct spa_bt_backend {
struct spa_source sco; struct spa_source sco;
struct spa_list rfcomm_list; struct spa_list rfcomm_list;
unsigned int msbc_support_enabled_in_config:1; unsigned int defer_setup_enabled:1;
}; };
struct transport_data { struct transport_data {
@ -90,6 +90,7 @@ struct rfcomm {
#ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE #ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE
unsigned int slc_configured:1; unsigned int slc_configured:1;
unsigned int codec_negotiation_supported:1; unsigned int codec_negotiation_supported:1;
unsigned int msbc_support_enabled_in_config:1;
unsigned int msbc_supported_by_hfp:1; unsigned int msbc_supported_by_hfp:1;
enum hfp_hf_state hf_state; enum hfp_hf_state hf_state;
unsigned int codec; unsigned int codec;
@ -336,7 +337,7 @@ static bool rfcomm_hfp_ag(struct spa_source *source, char* buf)
This should be done when This should be done when
a) mSBC support is enabled in config file and a) mSBC support is enabled in config file and
b) the computers bluetooth adapter supports the necessary transport mode */ b) the computers bluetooth adapter supports the necessary transport mode */
if ((backend->msbc_support_enabled_in_config == true) && if ((rfcomm->msbc_support_enabled_in_config == true) &&
(device_supports_required_mSBC_transport_modes(backend, rfcomm->device))) { (device_supports_required_mSBC_transport_modes(backend, rfcomm->device))) {
/* set the feature bit that indicates AG (=computer) supports codec negotiation */ /* set the feature bit that indicates AG (=computer) supports codec negotiation */
@ -877,7 +878,7 @@ static void sco_listen_event(struct spa_source *source)
} }
spa_log_debug(backend->log, NAME": transport %p: codec=%u", t, t->codec); spa_log_debug(backend->log, NAME": transport %p: codec=%u", t, t->codec);
if (backend->msbc_support_enabled_in_config) { if (backend->defer_setup_enabled) {
/* In BT_DEFER_SETUP mode, when a connection is accepted, the listening socket is unblocked but /* In BT_DEFER_SETUP mode, when a connection is accepted, the listening socket is unblocked but
* the effective connection setup happens only on first receive, allowing to configure the * the effective connection setup happens only on first receive, allowing to configure the
* accepted socket. */ * accepted socket. */
@ -950,10 +951,11 @@ static int sco_listen(struct spa_bt_backend *backend)
goto fail_close; goto fail_close;
} }
if (backend->msbc_support_enabled_in_config && if (setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &defer, sizeof(defer)) < 0) {
setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &defer, sizeof(defer)) < 0) {
spa_log_warn(backend->log, NAME": Can't enable deferred setup: %s", strerror(errno)); spa_log_warn(backend->log, NAME": Can't enable deferred setup: %s", strerror(errno));
goto fail_close; backend->defer_setup_enabled = 0;
} else {
backend->defer_setup_enabled = 1;
} }
spa_log_debug(backend->log, NAME": doing listen"); spa_log_debug(backend->log, NAME": doing listen");
@ -987,7 +989,7 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
struct spa_bt_backend *backend = userdata; struct spa_bt_backend *backend = userdata;
DBusMessage *r; DBusMessage *r;
DBusMessageIter it[5]; DBusMessageIter it[5];
const char *handler, *path; const char *handler, *path, *str;
enum spa_bt_profile profile = SPA_BT_PROFILE_NULL; enum spa_bt_profile profile = SPA_BT_PROFILE_NULL;
struct rfcomm *rfcomm; struct rfcomm *rfcomm;
struct spa_bt_device *d; struct spa_bt_device *d;
@ -1046,6 +1048,11 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
rfcomm->source.mask = SPA_IO_IN; rfcomm->source.mask = SPA_IO_IN;
rfcomm->source.rmask = 0; rfcomm->source.rmask = 0;
if (d->settings && (str = spa_dict_lookup(d->settings, "bluez5.msbc-support")))
rfcomm->msbc_support_enabled_in_config = strcmp(str, "true") == 0 || atoi(str) == 1;
else
rfcomm->msbc_support_enabled_in_config = false;
if (profile == SPA_BT_PROFILE_HSP_HS || profile == SPA_BT_PROFILE_HSP_AG) { if (profile == SPA_BT_PROFILE_HSP_HS || profile == SPA_BT_PROFILE_HSP_AG) {
t = _transport_create(rfcomm); t = _transport_create(rfcomm);
if (t == NULL) { if (t == NULL) {
@ -1066,7 +1073,7 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
This should be done when This should be done when
a) mSBC support is enabled in config file and a) mSBC support is enabled in config file and
b) the bluetooth adapter supports the necessary transport mode */ b) the bluetooth adapter supports the necessary transport mode */
if ((backend->msbc_support_enabled_in_config == true) && if ((rfcomm->msbc_support_enabled_in_config == true) &&
(device_supports_required_mSBC_transport_modes(backend, rfcomm->device))) { (device_supports_required_mSBC_transport_modes(backend, rfcomm->device))) {
/* set the feature bit that indicates HF supports codec negotiation */ /* set the feature bit that indicates HF supports codec negotiation */
hf_features |= SPA_BT_HFP_HF_FEATURE_CODEC_NEGOTIATION; hf_features |= SPA_BT_HFP_HF_FEATURE_CODEC_NEGOTIATION;
@ -1288,9 +1295,9 @@ static int register_profile(struct spa_bt_backend *backend, const char *profile,
dbus_message_iter_close_container(&it[1], &it[2]); dbus_message_iter_close_container(&it[1], &it[2]);
} else if (strcmp(uuid, SPA_BT_UUID_HFP_AG) == 0) { } else if (strcmp(uuid, SPA_BT_UUID_HFP_AG) == 0) {
str = "Features"; str = "Features";
features = SPA_BT_HFP_SDP_AG_FEATURE_NONE;
if (backend->msbc_support_enabled_in_config == true) /* We announce wideband speech support anyway */
features |= SPA_BT_HFP_SDP_AG_FEATURE_WIDEBAND_SPEECH; features = SPA_BT_HFP_SDP_AG_FEATURE_WIDEBAND_SPEECH;
dbus_message_iter_open_container(&it[1], DBUS_TYPE_DICT_ENTRY, NULL, &it[2]); dbus_message_iter_open_container(&it[1], DBUS_TYPE_DICT_ENTRY, NULL, &it[2]);
dbus_message_iter_append_basic(&it[2], DBUS_TYPE_STRING, &str); dbus_message_iter_append_basic(&it[2], DBUS_TYPE_STRING, &str);
dbus_message_iter_open_container(&it[2], DBUS_TYPE_VARIANT, "q", &it[3]); dbus_message_iter_open_container(&it[2], DBUS_TYPE_VARIANT, "q", &it[3]);
@ -1309,9 +1316,9 @@ static int register_profile(struct spa_bt_backend *backend, const char *profile,
dbus_message_iter_close_container(&it[1], &it[2]); dbus_message_iter_close_container(&it[1], &it[2]);
} else if (strcmp(uuid, SPA_BT_UUID_HFP_HF) == 0) { } else if (strcmp(uuid, SPA_BT_UUID_HFP_HF) == 0) {
str = "Features"; str = "Features";
features = SPA_BT_HFP_SDP_HF_FEATURE_NONE;
if (backend->msbc_support_enabled_in_config == true) /* We announce wideband speech support anyway */
features |= SPA_BT_HFP_SDP_HF_FEATURE_WIDEBAND_SPEECH; features = SPA_BT_HFP_SDP_HF_FEATURE_WIDEBAND_SPEECH;
dbus_message_iter_open_container(&it[1], DBUS_TYPE_DICT_ENTRY, NULL, &it[2]); dbus_message_iter_open_container(&it[1], DBUS_TYPE_DICT_ENTRY, NULL, &it[2]);
dbus_message_iter_append_basic(&it[2], DBUS_TYPE_STRING, &str); dbus_message_iter_append_basic(&it[2], DBUS_TYPE_STRING, &str);
dbus_message_iter_open_container(&it[2], DBUS_TYPE_VARIANT, "q", &it[3]); dbus_message_iter_open_container(&it[2], DBUS_TYPE_VARIANT, "q", &it[3]);
@ -1486,7 +1493,6 @@ struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor,
uint32_t n_support) uint32_t n_support)
{ {
struct spa_bt_backend *backend; struct spa_bt_backend *backend;
const char *str;
static const DBusObjectPathVTable vtable_profile = { static const DBusObjectPathVTable vtable_profile = {
.message_function = profile_handler, .message_function = profile_handler,
@ -1505,11 +1511,6 @@ struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor,
spa_list_init(&backend->rfcomm_list); spa_list_init(&backend->rfcomm_list);
if (info && (str = spa_dict_lookup(info, "bluez5.msbc-support")))
backend->msbc_support_enabled_in_config = strcmp(str, "true") == 0 || atoi(str) == 1;
else
backend->msbc_support_enabled_in_config = false;
if (parse_headset_roles(backend, info) < 0) if (parse_headset_roles(backend, info) < 0)
goto fail; goto fail;

View file

@ -432,6 +432,11 @@ static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBu
codec = a2dp_endpoint_to_codec(path); codec = a2dp_endpoint_to_codec(path);
if (codec != NULL) if (codec != NULL)
/* FIXME: We can't determine which device the SelectConfiguration()
* call is associated with, therefore device settings are not passed.
* This causes inconsistency with SelectConfiguration() triggered
* by codec switching.
*/
res = codec->select_config(codec, 0, cap, size, NULL, config); res = codec->select_config(codec, 0, cap, size, NULL, config);
else else
res = -ENOTSUP; res = -ENOTSUP;
@ -1856,7 +1861,7 @@ static bool a2dp_codec_switch_process_current(struct spa_bt_a2dp_codec_switch *s
goto next; goto next;
} }
res = codec->select_config(codec, 0, ep->capabilities, ep->capabilities_len, NULL, config); res = codec->select_config(codec, 0, ep->capabilities, ep->capabilities_len, sw->device->settings, config);
if (res < 0) { if (res < 0) {
spa_log_debug(sw->device->monitor->log, NAME": a2dp codec switch %p: incompatible capabilities (%d), try next", spa_log_debug(sw->device->monitor->log, NAME": a2dp codec switch %p: incompatible capabilities (%d), try next",
sw, res); sw, res);

View file

@ -106,6 +106,10 @@ struct impl {
const struct a2dp_codec **supported_codecs; const struct a2dp_codec **supported_codecs;
size_t supported_codec_count; size_t supported_codec_count;
#define MAX_SETTINGS 32
struct spa_dict_item setting_items[MAX_SETTINGS];
struct spa_dict setting_dict;
struct node nodes[2]; struct node nodes[2];
}; };
@ -1200,10 +1204,20 @@ static int impl_get_interface(struct spa_handle *handle, const char *type, void
static int impl_clear(struct spa_handle *handle) static int impl_clear(struct spa_handle *handle)
{ {
struct impl *this = (struct impl *) handle; struct impl *this = (struct impl *) handle;
const struct spa_dict_item *it;
free(this->supported_codecs); free(this->supported_codecs);
if (this->bt_dev) if (this->bt_dev) {
this->bt_dev->settings = NULL;
spa_hook_remove(&this->bt_dev_listener); spa_hook_remove(&this->bt_dev_listener);
}
spa_dict_for_each(it, &this->setting_dict) {
if(it->key)
free((void *)it->key);
if(it->value)
free((void *)it->value);
}
return 0; return 0;
} }
@ -1215,6 +1229,24 @@ impl_get_size(const struct spa_handle_factory *factory,
return sizeof(struct impl); return sizeof(struct impl);
} }
static const struct spa_dict*
filter_bluez_device_setting(struct impl *this, const struct spa_dict *dict)
{
uint32_t n_items = 0;
for (uint32_t i = 0
; i < dict->n_items && n_items < SPA_N_ELEMENTS(this->setting_items)
; i++)
{
const struct spa_dict_item *it = &dict->items[i];
if (it->key != NULL && strncmp(it->key, "bluez", 5) == 0 && it->value != NULL) {
this->setting_items[n_items++] =
SPA_DICT_ITEM_INIT(strdup(it->key), strdup(it->value));
}
}
this->setting_dict = SPA_DICT_INIT(this->setting_items, n_items);
return &this->setting_dict;
}
static int static int
impl_init(const struct spa_handle_factory *factory, impl_init(const struct spa_handle_factory *factory,
struct spa_handle *handle, struct spa_handle *handle,
@ -1242,6 +1274,9 @@ impl_init(const struct spa_handle_factory *factory,
spa_log_error(this->log, "a device is needed"); spa_log_error(this->log, "a device is needed");
return -EINVAL; return -EINVAL;
} }
this->bt_dev->settings = filter_bluez_device_setting(this, info);
this->device.iface = SPA_INTERFACE_INIT( this->device.iface = SPA_INTERFACE_INIT(
SPA_TYPE_INTERFACE_Device, SPA_TYPE_INTERFACE_Device,
SPA_VERSION_DEVICE, SPA_VERSION_DEVICE,

View file

@ -415,6 +415,8 @@ struct spa_bt_device {
struct spa_hook_list listener_list; struct spa_hook_list listener_list;
bool added; bool added;
const struct spa_dict *settings;
DBusPendingCall *battery_pending_call; DBusPendingCall *battery_pending_call;
}; };

View file

@ -1,8 +1,6 @@
# Bluez monitor config file # # Bluez monitor config file #
properties = { properties = {
# MSBC is not expected to work on all headset + adapter combinations.
#bluez5.msbc-support = true
#bluez5.sbc-xq-support = true #bluez5.sbc-xq-support = true
# Enabled headset roles (default: [ hsp_hs hfp_ag ]), this # Enabled headset roles (default: [ hsp_hs hfp_ag ]), this
@ -35,7 +33,8 @@ rules = [
actions = { actions = {
# Actions can update properties on the matched object. # Actions can update properties on the matched object.
update-props = { update-props = {
#device.nick = "My Device" # MSBC is not expected to work on all headset + adapter combinations.
#bluez5.msbc-support = true
} }
} }
} }