bluez5: add support for hardware quirk/feature database

Implement hardware quirk/feature database with pattern matching.
This commit is contained in:
Pauli Virtanen 2021-01-24 15:15:27 +02:00
parent 88077a29db
commit b57ae8c2a6
9 changed files with 396 additions and 15 deletions

View file

@ -51,6 +51,8 @@ struct impl {
struct spa_dbus *dbus; struct spa_dbus *dbus;
DBusConnection *conn; DBusConnection *conn;
const struct spa_bt_quirks *quirks;
struct spa_list endpoint_list; struct spa_list endpoint_list;
bool endpoints_listed; bool endpoints_listed;
@ -1485,6 +1487,7 @@ static const struct spa_bt_backend_implementation backend_impl = {
struct spa_bt_backend *backend_hsphfpd_new(struct spa_bt_monitor *monitor, struct spa_bt_backend *backend_hsphfpd_new(struct spa_bt_monitor *monitor,
void *dbus_connection, void *dbus_connection,
const struct spa_dict *info, const struct spa_dict *info,
const struct spa_bt_quirks *quirks,
const struct spa_support *support, const struct spa_support *support,
uint32_t n_support) uint32_t n_support)
{ {
@ -1504,11 +1507,12 @@ struct spa_bt_backend *backend_hsphfpd_new(struct spa_bt_monitor *monitor,
spa_bt_backend_set_implementation(&backend->this, &backend_impl, backend); spa_bt_backend_set_implementation(&backend->this, &backend_impl, backend);
backend->monitor = monitor; backend->monitor = monitor;
backend->quirks = quirks;
backend->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); backend->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
backend->dbus = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DBus); backend->dbus = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DBus);
backend->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop); backend->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
backend->conn = dbus_connection; backend->conn = dbus_connection;
if (info && (str = spa_dict_lookup(info, "bluez5.msbc-support"))) if (info && (str = spa_dict_lookup(info, "bluez5.enable-msbc")))
backend->msbc_supported = spa_atob(str); backend->msbc_supported = spa_atob(str);
else else
backend->msbc_supported = false; backend->msbc_supported = false;

View file

@ -73,6 +73,8 @@ struct impl {
struct spa_source sco; struct spa_source sco;
const struct spa_bt_quirks *quirks;
struct spa_list rfcomm_list; struct spa_list rfcomm_list;
unsigned int defer_setup_enabled:1; unsigned int defer_setup_enabled:1;
}; };
@ -1925,6 +1927,7 @@ static const struct spa_bt_backend_implementation backend_impl = {
struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor, struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor,
void *dbus_connection, void *dbus_connection,
const struct spa_dict *info, const struct spa_dict *info,
const struct spa_bt_quirks *quirks,
const struct spa_support *support, const struct spa_support *support,
uint32_t n_support) uint32_t n_support)
{ {
@ -1941,6 +1944,7 @@ struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor,
spa_bt_backend_set_implementation(&backend->this, &backend_impl, backend); spa_bt_backend_set_implementation(&backend->this, &backend_impl, backend);
backend->monitor = monitor; backend->monitor = monitor;
backend->quirks = quirks;
backend->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); backend->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
backend->dbus = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DBus); backend->dbus = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DBus);
backend->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop); backend->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);

View file

@ -54,6 +54,8 @@ struct impl {
struct spa_dbus *dbus; struct spa_dbus *dbus;
DBusConnection *conn; DBusConnection *conn;
const struct spa_bt_quirks *quirks;
unsigned int filters_added:1; unsigned int filters_added:1;
unsigned int msbc_supported:1; unsigned int msbc_supported:1;
}; };
@ -769,6 +771,7 @@ static const struct spa_bt_backend_implementation backend_impl = {
struct spa_bt_backend *backend_ofono_new(struct spa_bt_monitor *monitor, struct spa_bt_backend *backend_ofono_new(struct spa_bt_monitor *monitor,
void *dbus_connection, void *dbus_connection,
const struct spa_dict *info, const struct spa_dict *info,
const struct spa_bt_quirks *quirks,
const struct spa_support *support, const struct spa_support *support,
uint32_t n_support) uint32_t n_support)
{ {
@ -785,11 +788,12 @@ struct spa_bt_backend *backend_ofono_new(struct spa_bt_monitor *monitor,
spa_bt_backend_set_implementation(&backend->this, &backend_impl, backend); spa_bt_backend_set_implementation(&backend->this, &backend_impl, backend);
backend->monitor = monitor; backend->monitor = monitor;
backend->quirks = quirks;
backend->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); backend->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
backend->dbus = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DBus); backend->dbus = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DBus);
backend->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop); backend->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
backend->conn = dbus_connection; backend->conn = dbus_connection;
if (info && (str = spa_dict_lookup(info, "bluez5.msbc-support"))) if (info && (str = spa_dict_lookup(info, "bluez5.enable-msbc")))
backend->msbc_supported = spa_atob(str); backend->msbc_supported = spa_atob(str);
else else
backend->msbc_supported = false; backend->msbc_supported = false;

View file

@ -88,6 +88,9 @@ struct spa_bt_monitor {
struct spa_dict enabled_codecs; struct spa_dict enabled_codecs;
unsigned int connection_info_supported:1; unsigned int connection_info_supported:1;
struct spa_bt_quirks *quirks;
unsigned int enable_sbc_xq:1; unsigned int enable_sbc_xq:1;
unsigned int backend_native_registered:1; unsigned int backend_native_registered:1;
unsigned int backend_ofono_registered:1; unsigned int backend_ofono_registered:1;
@ -3842,6 +3845,8 @@ static int impl_clear(struct spa_handle *handle)
monitor->backend_ofono_registered = false; monitor->backend_ofono_registered = false;
monitor->backend_hsphfpd_registered = false; monitor->backend_hsphfpd_registered = false;
spa_bt_quirks_destroy(monitor->quirks);
return 0; return 0;
} }
@ -3986,6 +3991,12 @@ impl_init(const struct spa_handle_factory *factory,
return -EINVAL; return -EINVAL;
} }
this->quirks = spa_bt_quirks_create(info, this->log);
if (this->quirks == NULL) {
spa_log_error(this->log, NAME ": failed to parse quirk table");
return -EINVAL;
}
this->dbus_connection = spa_dbus_get_connection(this->dbus, SPA_DBUS_TYPE_SYSTEM); this->dbus_connection = spa_dbus_get_connection(this->dbus, SPA_DBUS_TYPE_SYSTEM);
if (this->dbus_connection == NULL) { if (this->dbus_connection == NULL) {
spa_log_error(this->log, "no dbus connection"); spa_log_error(this->log, "no dbus connection");
@ -4027,16 +4038,16 @@ impl_init(const struct spa_handle_factory *factory,
((tmp = atoi(str)) > 0)) ((tmp = atoi(str)) > 0))
this->default_audio_info.channels = tmp; this->default_audio_info.channels = tmp;
if ((str = spa_dict_lookup(info, "bluez5.sbc-xq-support")) != NULL && if ((str = spa_dict_lookup(info, "bluez5.enable-sbc-xq")) != NULL &&
spa_atob(str)) spa_atob(str))
this->enable_sbc_xq = true; this->enable_sbc_xq = true;
} }
register_media_application(this); register_media_application(this);
this->backend_native = backend_native_new(this, this->conn, info, support, n_support); this->backend_native = backend_native_new(this, this->conn, info, this->quirks, support, n_support);
this->backend_ofono = backend_ofono_new(this, this->conn, info, support, n_support); this->backend_ofono = backend_ofono_new(this, this->conn, info, this->quirks, support, n_support);
this->backend_hsphfpd = backend_hsphfpd_new(this, this->conn, info, support, n_support); this->backend_hsphfpd = backend_hsphfpd_new(this, this->conn, info, this->quirks, support, n_support);
if (this->backend_ofono && spa_bt_backend_register_profiles(this->backend_ofono) == 0) if (this->backend_ofono && spa_bt_backend_register_profiles(this->backend_ofono) == 0)
this->backend_ofono_registered = true; this->backend_ofono_registered = true;

View file

@ -666,6 +666,23 @@ static inline double spa_bt_volume_hw_to_linear(uint32_t v, uint32_t hw_volume_m
0.0, 1.0); 0.0, 1.0);
} }
enum spa_bt_feature {
SPA_BT_FEATURE_MSBC = (1 << 0),
SPA_BT_FEATURE_MSBC_ALT1 = (1 << 1),
SPA_BT_FEATURE_MSBC_ALT1_RTL = (1 << 2),
SPA_BT_FEATURE_HW_VOLUME = (1 << 3),
SPA_BT_FEATURE_SBC_XQ = (1 << 4),
};
struct spa_bt_quirks;
struct spa_bt_quirks *spa_bt_quirks_create(const struct spa_dict *info, struct spa_log *log);
int spa_bt_quirks_get_features(const struct spa_bt_quirks *quirks,
const struct spa_bt_adapter *adapter,
const struct spa_bt_device *device,
uint32_t *features);
void spa_bt_quirks_destroy(struct spa_bt_quirks *quirks);
struct spa_bt_backend_implementation { struct spa_bt_backend_implementation {
#define SPA_VERSION_BT_BACKEND_IMPLEMENTATION 0 #define SPA_VERSION_BT_BACKEND_IMPLEMENTATION 0
uint32_t version; uint32_t version;
@ -707,6 +724,7 @@ struct spa_bt_backend {
static inline struct spa_bt_backend *dummy_backend_new(struct spa_bt_monitor *monitor, static inline struct spa_bt_backend *dummy_backend_new(struct spa_bt_monitor *monitor,
void *dbus_connection, void *dbus_connection,
const struct spa_dict *info, const struct spa_dict *info,
const struct spa_bt_quirks *quirks,
const struct spa_support *support, const struct spa_support *support,
uint32_t n_support) uint32_t n_support)
{ {
@ -717,6 +735,7 @@ static inline struct spa_bt_backend *dummy_backend_new(struct spa_bt_monitor *mo
struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor, struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor,
void *dbus_connection, void *dbus_connection,
const struct spa_dict *info, const struct spa_dict *info,
const struct spa_bt_quirks *quirks,
const struct spa_support *support, const struct spa_support *support,
uint32_t n_support); uint32_t n_support);
#else #else
@ -728,6 +747,7 @@ struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor,
struct spa_bt_backend *backend_ofono_new(struct spa_bt_monitor *monitor, struct spa_bt_backend *backend_ofono_new(struct spa_bt_monitor *monitor,
void *dbus_connection, void *dbus_connection,
const struct spa_dict *info, const struct spa_dict *info,
const struct spa_bt_quirks *quirks,
const struct spa_support *support, const struct spa_support *support,
uint32_t n_support); uint32_t n_support);
#else #else
@ -739,6 +759,7 @@ struct spa_bt_backend *backend_ofono_new(struct spa_bt_monitor *monitor,
struct spa_bt_backend *backend_hsphfpd_new(struct spa_bt_monitor *monitor, struct spa_bt_backend *backend_hsphfpd_new(struct spa_bt_monitor *monitor,
void *dbus_connection, void *dbus_connection,
const struct spa_dict *info, const struct spa_dict *info,
const struct spa_bt_quirks *quirks,
const struct spa_support *support, const struct spa_support *support,
uint32_t n_support); uint32_t n_support);
#else #else

View file

@ -28,6 +28,7 @@ bluez5_sources = ['plugin.c',
'sco-sink.c', 'sco-sink.c',
'sco-source.c', 'sco-source.c',
'sco-io.c', 'sco-io.c',
'quirks.c',
'bluez5-device.c', 'bluez5-device.c',
'bluez5-dbus.c'] 'bluez5-dbus.c']

319
spa/plugins/bluez5/quirks.c Normal file
View file

@ -0,0 +1,319 @@
/* Device/adapter/kernel quirk table
*
* Copyright © 2021 Pauli Virtanen
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <errno.h>
#include <stddef.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <regex.h>
#include <sys/utsname.h>
#include <bluetooth/bluetooth.h>
#include <dbus/dbus.h>
#include <spa/support/log.h>
#include <spa/support/loop.h>
#include <spa/support/dbus.h>
#include <spa/support/plugin.h>
#include <spa/monitor/device.h>
#include <spa/monitor/utils.h>
#include <spa/utils/hook.h>
#include <spa/utils/type.h>
#include <spa/utils/keys.h>
#include <spa/utils/names.h>
#include <spa/utils/result.h>
#include <spa/utils/json.h>
#include <spa/utils/string.h>
#include "a2dp-codecs.h"
#include "defs.h"
#define NAME "bluez5-quirks"
struct spa_bt_quirks {
struct spa_log *log;
int force_msbc;
int force_hw_volume;
int force_sbc_xq;
char *device_rules;
char *adapter_rules;
char *kernel_rules;
};
static enum spa_bt_feature parse_feature(const char *str)
{
static const struct { const char *key; enum spa_bt_feature value; } feature_keys[] = {
{ "msbc", SPA_BT_FEATURE_MSBC },
{ "msbc-alt1", SPA_BT_FEATURE_MSBC_ALT1 },
{ "msbc-alt1-rtl", SPA_BT_FEATURE_MSBC_ALT1_RTL },
{ "hw-volume", SPA_BT_FEATURE_HW_VOLUME },
{ "sbc-xq", SPA_BT_FEATURE_SBC_XQ },
};
size_t i;
for (i = 0; i < SPA_N_ELEMENTS(feature_keys); ++i) {
if (spa_streq(str, feature_keys[i].key))
return feature_keys[i].value;
}
return 0;
}
static int do_match(const char *rules, struct spa_dict *dict, uint32_t *no_features)
{
struct spa_json rules_json = SPA_JSON_INIT(rules, strlen(rules));
struct spa_json rules_arr, it[2];
if (spa_json_enter_array(&rules_json, &rules_arr) <= 0)
return 1;
while (spa_json_enter_object(&rules_arr, &it[0]) > 0) {
char key[256];
int match = true;
uint32_t no_features_cur = 0;
while (spa_json_get_string(&it[0], key, sizeof(key)-1) > 0) {
char val[4096];
const char *str, *value;
int len;
bool success = false;
if (spa_streq(key, "no-features")) {
if (spa_json_enter_array(&it[0], &it[1]) > 0) {
while (spa_json_get_string(&it[1], val, sizeof(val)-1) > 0)
no_features_cur |= parse_feature(val);
}
continue;
}
if ((len = spa_json_next(&it[0], &value)) <= 0)
break;
if (spa_json_is_null(value, len)) {
value = NULL;
} else {
spa_json_parse_string(value, SPA_MIN(len, (int)sizeof(val)-1), val);
value = val;
}
str = spa_dict_lookup(dict, key);
if (value == NULL) {
success = str == NULL;
} else if (str != NULL) {
if (value[0] == '~') {
regex_t r;
if (regcomp(&r, value+1, REG_EXTENDED | REG_NOSUB) == 0) {
if (regexec(&r, str, 0, NULL, 0) == 0)
success = true;
regfree(&r);
}
} else if (spa_streq(str, value)) {
success = true;
}
}
if (!success) {
match = false;
break;
}
}
if (match) {
*no_features = no_features_cur;
return 0;
}
}
return 0;
}
static int parse_force_flag(const struct spa_dict *info, const char *key)
{
const char *str;
str = spa_dict_lookup(info, key);
if (str == NULL)
return -1;
else
return (strcmp(str, "true") == 0 || atoi(str)) ? 1 : 0;
}
struct spa_bt_quirks *spa_bt_quirks_create(const struct spa_dict *info, struct spa_log *log)
{
struct spa_bt_quirks *this;
const char *str;
if (!info) {
errno = -EINVAL;
return NULL;
}
this = calloc(1, sizeof(struct spa_bt_quirks));
if (this == NULL)
return NULL;
this->log = log;
this->force_sbc_xq = parse_force_flag(info, "bluez5.enable-sbc-xq");
this->force_msbc = parse_force_flag(info, "bluez5.enable-msbc");
this->force_hw_volume = parse_force_flag(info, "bluez5.enable-hw-volume");
str = spa_dict_lookup(info, "bluez5.features.kernel");
this->kernel_rules = str ? strdup(str) : NULL;
str = spa_dict_lookup(info, "bluez5.features.adapter");
this->adapter_rules = str ? strdup(str) : NULL;
str = spa_dict_lookup(info, "bluez5.features.device");
this->device_rules = str ? strdup(str) : NULL;
if (!(this->kernel_rules && this->adapter_rules && this->device_rules))
spa_log_info(this->log, NAME " failed to find data from bluez-hardware.conf");
return this;
}
void spa_bt_quirks_destroy(struct spa_bt_quirks *this)
{
free(this->kernel_rules);
free(this->adapter_rules);
free(this->device_rules);
free(this);
}
static void log_props(struct spa_log *log, const struct spa_dict *dict)
{
const struct spa_dict_item *item;
spa_dict_for_each(item, dict)
spa_log_debug(log, "quirk property %s=%s", item->key, item->value);
}
static void strtolower(char *src, char *dst, int maxsize)
{
while (maxsize > 1 && *src != '\0') {
*dst = (*src >= 'A' && *src <= 'Z') ? ('a' + (*src - 'A')) : *src;
++src;
++dst;
--maxsize;
}
if (maxsize > 0)
*dst = '\0';
}
int spa_bt_quirks_get_features(const struct spa_bt_quirks *this,
const struct spa_bt_adapter *adapter,
const struct spa_bt_device *device,
uint32_t *features)
{
struct spa_dict props;
struct spa_dict_item items[5];
int res;
*features = ~(uint32_t)0;
/* Kernel */
if (this->kernel_rules) {
uint32_t no_features = 0;
int nitems = 0;
struct utsname name;
if ((res = uname(&name)) < 0)
return res;
items[nitems++] = SPA_DICT_ITEM_INIT("sysname", name.sysname);
items[nitems++] = SPA_DICT_ITEM_INIT("release", name.release);
items[nitems++] = SPA_DICT_ITEM_INIT("version", name.version);
props = SPA_DICT_INIT(items, nitems);
log_props(this->log, &props);
do_match(this->kernel_rules, &props, &no_features);
spa_log_debug(this->log, NAME ": kernel quirks:%08x", no_features);
*features &= ~no_features;
}
/* Adapter */
if (this->adapter_rules) {
uint32_t no_features = 0;
int nitems = 0;
char vendor_id[64], product_id[64], address[64];
if (spa_bt_format_vendor_product_id(
adapter->source_id, adapter->vendor_id, adapter->product_id,
vendor_id, sizeof(vendor_id), product_id, sizeof(product_id)) == 0) {
items[nitems++] = SPA_DICT_ITEM_INIT("vendor-id", vendor_id);
items[nitems++] = SPA_DICT_ITEM_INIT("product-id", product_id);
}
items[nitems++] = SPA_DICT_ITEM_INIT("bus-type",
(adapter->bus_type == BUS_TYPE_USB) ? "usb" : "other");
if (adapter->address) {
strtolower(adapter->address, address, sizeof(address));
items[nitems++] = SPA_DICT_ITEM_INIT("address", address);
}
props = SPA_DICT_INIT(items, nitems);
log_props(this->log, &props);
do_match(this->adapter_rules, &props, &no_features);
spa_log_debug(this->log, NAME ": adapter quirks:%08x", no_features);
*features &= ~no_features;
}
/* Device */
if (this->device_rules) {
uint32_t no_features = 0;
int nitems = 0;
char vendor_id[64], product_id[64], version_id[64], address[64];
if (spa_bt_format_vendor_product_id(
device->source_id, device->vendor_id, device->product_id,
vendor_id, sizeof(vendor_id), product_id, sizeof(product_id)) == 0) {
snprintf(version_id, sizeof(version_id), "%04x",
(unsigned int)device->version_id);
items[nitems++] = SPA_DICT_ITEM_INIT("vendor-id", vendor_id);
items[nitems++] = SPA_DICT_ITEM_INIT("product-id", product_id);
items[nitems++] = SPA_DICT_ITEM_INIT("version-id", version_id);
}
if (device->name)
items[nitems++] = SPA_DICT_ITEM_INIT("name", device->name);
if (device->address) {
strtolower(device->address, address, sizeof(address));
items[nitems++] = SPA_DICT_ITEM_INIT("address", address);
}
props = SPA_DICT_INIT(items, nitems);
log_props(this->log, &props);
do_match(this->device_rules, &props, &no_features);
spa_log_debug(this->log, NAME ": device quirks:%08x", no_features);
*features &= ~no_features;
}
/* Force flags */
if (this->force_msbc != -1) {
SPA_FLAG_UPDATE(*features, SPA_BT_FEATURE_MSBC, this->force_msbc);
SPA_FLAG_UPDATE(*features, SPA_BT_FEATURE_MSBC_ALT1, this->force_msbc);
SPA_FLAG_UPDATE(*features, SPA_BT_FEATURE_MSBC_ALT1_RTL, this->force_msbc);
}
if (this->force_hw_volume != -1)
SPA_FLAG_UPDATE(*features, SPA_BT_FEATURE_HW_VOLUME, this->force_hw_volume);
if (this->force_sbc_xq != -1)
SPA_FLAG_UPDATE(*features, SPA_BT_FEATURE_SBC_XQ, this->force_sbc_xq);
return 0;
}

View file

@ -7,10 +7,11 @@
properties = { properties = {
# These features do not work on all headsets, so they are enabled # These features do not work on all headsets, so they are enabled
# by default based on the hardware database. They can also be # by default based on the hardware database. They can also be
# forced on/off by the following options: # forced on/off for all devices by the following options:
#bluez5.force-sbc-xq = true #bluez5.enable-sbc-xq = true
#bluez5.force-msbc = true #bluez5.enable-msbc = true
#bluez5.enable-hw-volume = true
# See bluez-hardware.conf for the hardware database. # See bluez-hardware.conf for the hardware database.
@ -62,10 +63,7 @@ rules = [
#] #]
bluez5.auto-connect = [ hfp_hf hsp_hs a2dp_sink ] bluez5.auto-connect = [ hfp_hf hsp_hs a2dp_sink ]
# Overload mSBC support for native backend and a specific device. # Hardware volume control (default: all)
#bluez5.msbc-support = false
# Hardware volume control (default: [ hfp_ag hsp_ag a2dp_source ])
#bluez5.hw-volume = [ #bluez5.hw-volume = [
# hfp_hf # hfp_hf
# hsp_hs # hsp_hs

View file

@ -49,6 +49,7 @@
#define NAME "bluez5-monitor" #define NAME "bluez5-monitor"
#define SESSION_CONF "bluez-monitor.conf" #define SESSION_CONF "bluez-monitor.conf"
#define FEATURES_CONF "bluez-hardware.conf"
struct device; struct device;
@ -715,7 +716,9 @@ int sm_bluez5_monitor_start(struct sm_media_session *session)
{ {
int res; int res;
struct impl *impl; struct impl *impl;
const char *str; const char *key, *str;
struct pw_properties *hw_features = NULL;
void *state = NULL;
impl = calloc(1, sizeof(struct impl)); impl = calloc(1, sizeof(struct impl));
if (impl == NULL) { if (impl == NULL) {
@ -731,9 +734,16 @@ int sm_bluez5_monitor_start(struct sm_media_session *session)
res = -errno; res = -errno;
goto out_free; goto out_free;
} }
if ((hw_features = pw_properties_new(NULL, NULL)) == NULL) {
res = -errno;
goto out_free;
}
if ((res = sm_media_session_load_conf(impl->session, if ((res = sm_media_session_load_conf(impl->session,
SESSION_CONF, impl->conf)) < 0) SESSION_CONF, impl->conf)) < 0)
pw_log_info("can't load "SESSION_CONF" config: %s", spa_strerror(res)); pw_log_info("can't load "SESSION_CONF" config: %s", spa_strerror(res));
if ((res = sm_media_session_load_conf(impl->session,
FEATURES_CONF, hw_features)) < 0)
pw_log_info("can't load "FEATURES_CONF" config: %s", spa_strerror(res));
if ((impl->props = pw_properties_new(NULL, NULL)) == NULL) { if ((impl->props = pw_properties_new(NULL, NULL)) == NULL) {
res = -errno; res = -errno;
@ -744,14 +754,23 @@ int sm_bluez5_monitor_start(struct sm_media_session *session)
pw_properties_set(impl->props, "api.bluez5.connection-info", "true"); pw_properties_set(impl->props, "api.bluez5.connection-info", "true");
while ((key = pw_properties_iterate(hw_features, &state)) != NULL) {
if (strncmp(key, "bluez5.features.", strlen("bluez5.features.")) != 0)
continue;
if ((str = pw_properties_get(hw_features, key)) != NULL)
pw_properties_set(impl->props, key, str);
}
pw_properties_free(hw_features);
sm_media_session_add_listener(session, &impl->session_listener, sm_media_session_add_listener(session, &impl->session_listener,
&session_events, impl); &session_events, impl);
return 0; return 0;
out_free: out_free:
pw_properties_free(impl->conf); pw_properties_free(impl->conf);
pw_properties_free(impl->props); pw_properties_free(impl->props);
pw_properties_free(hw_features);
free(impl); free(impl);
out: out:
return res; return res;