diff --git a/spa/plugins/bluez5/a2dp-sink.c b/spa/plugins/bluez5/a2dp-sink.c index ba05e36d6..45cfe51b2 100644 --- a/spa/plugins/bluez5/a2dp-sink.c +++ b/spa/plugins/bluez5/a2dp-sink.c @@ -707,7 +707,8 @@ static int do_start(struct impl *this) this->codec_data = this->codec->init(this->codec, 0, this->transport->configuration, this->transport->configuration_len, - &port->current_format, NULL, + &port->current_format, + this->transport->device->settings, this->transport->write_mtu); if (this->codec_data == NULL) return -EIO; diff --git a/spa/plugins/bluez5/a2dp-source.c b/spa/plugins/bluez5/a2dp-source.c index 49a589ee8..845ea9618 100644 --- a/spa/plugins/bluez5/a2dp-source.c +++ b/spa/plugins/bluez5/a2dp-source.c @@ -569,7 +569,8 @@ static int transport_start(struct impl *this) this->codec_data = this->codec->init(this->codec, 0, this->transport->configuration, this->transport->configuration_len, - &port->current_format, NULL, + &port->current_format, + this->transport->device->settings, this->transport->read_mtu); if (this->codec_data == NULL) return -EIO; diff --git a/spa/plugins/bluez5/backend-native.c b/spa/plugins/bluez5/backend-native.c index 86f183dbc..06a565338 100644 --- a/spa/plugins/bluez5/backend-native.c +++ b/spa/plugins/bluez5/backend-native.c @@ -61,7 +61,7 @@ struct spa_bt_backend { struct spa_source sco; struct spa_list rfcomm_list; - unsigned int msbc_support_enabled_in_config:1; + unsigned int defer_setup_enabled:1; }; struct transport_data { @@ -90,6 +90,7 @@ struct rfcomm { #ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE unsigned int slc_configured:1; unsigned int codec_negotiation_supported:1; + unsigned int msbc_support_enabled_in_config:1; unsigned int msbc_supported_by_hfp:1; enum hfp_hf_state hf_state; unsigned int codec; @@ -336,7 +337,7 @@ static bool rfcomm_hfp_ag(struct spa_source *source, char* buf) This should be done when a) mSBC support is enabled in config file and 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))) { /* 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); - 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 * the effective connection setup happens only on first receive, allowing to configure the * accepted socket. */ @@ -950,10 +951,11 @@ static int sco_listen(struct spa_bt_backend *backend) goto fail_close; } - if (backend->msbc_support_enabled_in_config && - setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &defer, sizeof(defer)) < 0) { + if (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)); - goto fail_close; + backend->defer_setup_enabled = 0; + } else { + backend->defer_setup_enabled = 1; } 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; DBusMessage *r; DBusMessageIter it[5]; - const char *handler, *path; + const char *handler, *path, *str; enum spa_bt_profile profile = SPA_BT_PROFILE_NULL; struct rfcomm *rfcomm; 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.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) { t = _transport_create(rfcomm); if (t == NULL) { @@ -1066,7 +1073,7 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag This should be done when a) mSBC support is enabled in config file and 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))) { /* set the feature bit that indicates HF supports 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]); } else if (strcmp(uuid, SPA_BT_UUID_HFP_AG) == 0) { str = "Features"; - features = SPA_BT_HFP_SDP_AG_FEATURE_NONE; - if (backend->msbc_support_enabled_in_config == true) - features |= SPA_BT_HFP_SDP_AG_FEATURE_WIDEBAND_SPEECH; + + /* We announce wideband speech support anyway */ + 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_append_basic(&it[2], DBUS_TYPE_STRING, &str); 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]); } else if (strcmp(uuid, SPA_BT_UUID_HFP_HF) == 0) { str = "Features"; - features = SPA_BT_HFP_SDP_HF_FEATURE_NONE; - if (backend->msbc_support_enabled_in_config == true) - features |= SPA_BT_HFP_SDP_HF_FEATURE_WIDEBAND_SPEECH; + + /* We announce wideband speech support anyway */ + 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_append_basic(&it[2], DBUS_TYPE_STRING, &str); 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) { struct spa_bt_backend *backend; - const char *str; static const DBusObjectPathVTable vtable_profile = { .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); - 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) goto fail; diff --git a/spa/plugins/bluez5/bluez5-dbus.c b/spa/plugins/bluez5/bluez5-dbus.c index 0717284be..85aa5b285 100644 --- a/spa/plugins/bluez5/bluez5-dbus.c +++ b/spa/plugins/bluez5/bluez5-dbus.c @@ -432,6 +432,11 @@ static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBu codec = a2dp_endpoint_to_codec(path); 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); else res = -ENOTSUP; @@ -1856,7 +1861,7 @@ static bool a2dp_codec_switch_process_current(struct spa_bt_a2dp_codec_switch *s 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) { spa_log_debug(sw->device->monitor->log, NAME": a2dp codec switch %p: incompatible capabilities (%d), try next", sw, res); diff --git a/spa/plugins/bluez5/bluez5-device.c b/spa/plugins/bluez5/bluez5-device.c index 82c1be2c0..7a747f415 100644 --- a/spa/plugins/bluez5/bluez5-device.c +++ b/spa/plugins/bluez5/bluez5-device.c @@ -106,6 +106,10 @@ struct impl { const struct a2dp_codec **supported_codecs; 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]; }; @@ -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) { struct impl *this = (struct impl *) handle; + const struct spa_dict_item *it; 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_dict_for_each(it, &this->setting_dict) { + if(it->key) + free((void *)it->key); + if(it->value) + free((void *)it->value); + } return 0; } @@ -1215,6 +1229,24 @@ impl_get_size(const struct spa_handle_factory *factory, 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 impl_init(const struct spa_handle_factory *factory, 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"); return -EINVAL; } + + this->bt_dev->settings = filter_bluez_device_setting(this, info); + this->device.iface = SPA_INTERFACE_INIT( SPA_TYPE_INTERFACE_Device, SPA_VERSION_DEVICE, diff --git a/spa/plugins/bluez5/defs.h b/spa/plugins/bluez5/defs.h index f49bb09e5..e65b29865 100644 --- a/spa/plugins/bluez5/defs.h +++ b/spa/plugins/bluez5/defs.h @@ -415,6 +415,8 @@ struct spa_bt_device { struct spa_hook_list listener_list; bool added; + const struct spa_dict *settings; + DBusPendingCall *battery_pending_call; }; diff --git a/src/daemon/media-session.d/bluez-monitor.conf b/src/daemon/media-session.d/bluez-monitor.conf index cbb28fd1c..19de897c3 100644 --- a/src/daemon/media-session.d/bluez-monitor.conf +++ b/src/daemon/media-session.d/bluez-monitor.conf @@ -1,8 +1,6 @@ # Bluez monitor config file # properties = { - # MSBC is not expected to work on all headset + adapter combinations. - #bluez5.msbc-support = true #bluez5.sbc-xq-support = true # Enabled headset roles (default: [ hsp_hs hfp_ag ]), this @@ -35,7 +33,8 @@ rules = [ actions = { # Actions can update properties on the matched object. update-props = { - #device.nick = "My Device" + # MSBC is not expected to work on all headset + adapter combinations. + #bluez5.msbc-support = true } } }