bluez5: backend-native: use quirks + usb adapter caps for checking msbc

Use the quirks database to check whether to enable MSBC codec for each
device.

If quirks don't allow ALT1 mode for an USB adapter, check whether the
adapter has an usable ALT6 mode and disable MSBC if not.
This commit is contained in:
Pauli Virtanen 2021-06-13 21:52:41 +03:00
parent b57ae8c2a6
commit 5e0b63b149
4 changed files with 157 additions and 22 deletions

View file

@ -323,6 +323,11 @@ sndfile_dep = dependency('sndfile', version : '>= 1.0.20', required : get_option
pulseaudio_dep = dependency('libpulse', required : get_option('libpulse')) pulseaudio_dep = dependency('libpulse', required : get_option('libpulse'))
avahi_dep = dependency('avahi-client', required : get_option('avahi')) avahi_dep = dependency('avahi-client', required : get_option('avahi'))
libusb_dep = dependency('libusb-1.0', required : get_option('libusb'))
if libusb_dep.found()
cdata.set('HAVE_LIBUSB', 1)
endif
cap_lib = dependency('libcap', required : false) cap_lib = dependency('libcap', required : false)
if cap_lib.found() if cap_lib.found()
cdata.set('HAVE_LIBCAP', 1) cdata.set('HAVE_LIBCAP', 1)

View file

@ -191,6 +191,10 @@ option('echo-cancel-webrtc',
description : 'Enable WebRTC-based echo canceller', description : 'Enable WebRTC-based echo canceller',
type : 'feature', type : 'feature',
value : 'auto') value : 'auto')
option('libusb',
description: 'Enable code that depends on libusb',
type: 'feature',
value: 'auto')
option('media-session', option('media-session',
description: 'Build and install pipewire-media-session', description: 'Build and install pipewire-media-session',
type: 'feature', type: 'feature',

View file

@ -25,6 +25,9 @@
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <bluetooth/bluetooth.h> #include <bluetooth/bluetooth.h>
#include <bluetooth/sco.h> #include <bluetooth/sco.h>
@ -44,6 +47,10 @@
#include "defs.h" #include "defs.h"
#ifdef HAVE_LIBUSB
#include <libusb.h>
#endif
#define NAME "native" #define NAME "native"
#define PROP_KEY_HEADSET_ROLES "bluez5.headset-roles" #define PROP_KEY_HEADSET_ROLES "bluez5.headset-roles"
@ -125,7 +132,6 @@ 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;
unsigned int hfp_ag_switching_codec:1; unsigned int hfp_ag_switching_codec:1;
unsigned int hfp_ag_initial_codec_setup:2; unsigned int hfp_ag_initial_codec_setup:2;
@ -413,24 +419,135 @@ static bool rfcomm_hsp_hs(struct spa_source *source, char* buf)
} }
#endif #endif
#ifdef HAVE_LIBUSB
static bool check_usb_altsetting_6(struct impl *backend, uint16_t vendor_id, uint16_t product_id)
{
libusb_context *ctx = NULL;
struct libusb_config_descriptor *cfg = NULL;
libusb_device **devices = NULL;
ssize_t ndev, idev;
int res;
bool ok = false;
if ((res = libusb_init(&ctx)) < 0) {
ctx = NULL;
goto fail;
}
if ((ndev = libusb_get_device_list(ctx, &devices)) < 0) {
res = ndev;
devices = NULL;
goto fail;
}
for (idev = 0; idev < ndev; ++idev) {
libusb_device *dev = devices[idev];
struct libusb_device_descriptor desc;
int icfg;
libusb_get_device_descriptor(dev, &desc);
if (vendor_id != desc.idVendor || product_id != desc.idProduct)
continue;
/* Check the device has Bluetooth isoch. altsetting 6 interface */
for (icfg = 0; icfg < desc.bNumConfigurations; ++icfg) {
int iiface;
if ((res = libusb_get_config_descriptor(dev, icfg, &cfg)) != 0) {
cfg = NULL;
goto fail;
}
for (iiface = 0; iiface < cfg->bNumInterfaces; ++iiface) {
const struct libusb_interface *iface = &cfg->interface[iiface];
int ialt;
for (ialt = 0; ialt < iface->num_altsetting; ++ialt) {
const struct libusb_interface_descriptor *idesc = &iface->altsetting[ialt];
int iep;
if (idesc->bInterfaceClass != LIBUSB_CLASS_WIRELESS ||
idesc->bInterfaceSubClass != 1 /* RF */ ||
idesc->bInterfaceProtocol != 1 /* Bluetooth */ ||
idesc->bAlternateSetting != 6)
continue;
for (iep = 0; iep < idesc->bNumEndpoints; ++iep) {
const struct libusb_endpoint_descriptor *ep = &idesc->endpoint[iep];
if ((ep->bmAttributes & 0x3) == LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS) {
ok = true;
goto done;
}
}
}
}
libusb_free_config_descriptor(cfg);
cfg = NULL;
}
}
done:
if (cfg)
libusb_free_config_descriptor(cfg);
if (devices)
libusb_free_device_list(devices, 0);
if (ctx)
libusb_exit(ctx);
return ok;
fail:
spa_log_info(backend->log, NAME": failed to acquire USB device info: %d (%s)",
res, libusb_strerror(res));
ok = false;
goto done;
}
#endif
#ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE #ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE
static bool device_supports_required_mSBC_transport_modes( static bool device_supports_required_mSBC_transport_modes(
struct impl *backend, struct spa_bt_device *device) { struct impl *backend, struct spa_bt_device *device) {
bdaddr_t src; bdaddr_t src;
uint8_t features[8], max_page = 0; uint8_t features[8], max_page = 0;
struct hci_dev_info di;
int device_id; int device_id;
int sock; int sock;
bool msbc_ok, msbc_alt1_ok;
uint32_t bt_features;
if (device->adapter == NULL) if (device->adapter == NULL)
return false; return false;
spa_log_debug(backend->log, NAME": Entering function"); spa_log_debug(backend->log, NAME": Entering function");
if (backend->quirks && spa_bt_quirks_get_features(backend->quirks, device->adapter, device, &bt_features) == 0) {
msbc_ok = bt_features & SPA_BT_FEATURE_MSBC;
msbc_alt1_ok = bt_features & (SPA_BT_FEATURE_MSBC_ALT1 | SPA_BT_FEATURE_MSBC_ALT1_RTL);
} else {
msbc_ok = true;
msbc_alt1_ok = true;
}
spa_log_info(backend->log,
NAME": bluez-monitor/hardware.conf: msbc:%d msbc-alt1:%d", (int)msbc_ok, (int)msbc_alt1_ok);
if (!msbc_ok && !msbc_alt1_ok)
return false;
str2ba(device->adapter->address, &src); str2ba(device->adapter->address, &src);
device_id = hci_get_route(&src); device_id = hci_get_route(&src);
if (hci_devinfo(device_id, &di) < 0) {
spa_log_error(backend->log, NAME": Error getting device info for hci%d: %s (%d)\n",
device_id, strerror(errno), errno);
return false;
}
sock = hci_open_dev(device_id); sock = hci_open_dev(device_id);
if (sock < 0) { if (sock < 0) {
spa_log_error(backend->log, NAME": Error opening device hci%d: %s (%d)\n", spa_log_error(backend->log, NAME": Error opening device hci%d: %s (%d)\n",
device_id, strerror(errno), errno); device_id, strerror(errno), errno);
return false; return false;
@ -459,11 +576,29 @@ static bool device_supports_required_mSBC_transport_modes(
} else { } else {
spa_log_info(backend->log, spa_log_info(backend->log,
NAME": bluetooth host adapter supports eSCO link and Transparent Data mode" ); NAME": bluetooth host adapter supports eSCO link and Transparent Data mode" );
return true;
} }
spa_log_debug(backend->log, NAME": Fallthrough - we should not be here"); if ((di.type & 0x0f) == HCI_USB && !msbc_alt1_ok && msbc_ok) {
return false; /* Check if USB ALT6 is really available on the device */
#if HAVE_LIBUSB
if (device->adapter->source_id == SOURCE_ID_USB) {
msbc_ok = check_usb_altsetting_6(backend, device->adapter->vendor_id,
device->adapter->product_id);
} else {
msbc_ok = false;
}
if (!msbc_ok)
spa_log_info(backend->log, NAME": bluetooth host adapter does not support USB ALT6");
#else
spa_log_info(backend->log,
NAME": compiled without libusb; can't check if bluetooth adapter has USB ALT6");
msbc_ok = false;
#endif
}
if ((di.type & 0x0f) != HCI_USB)
msbc_alt1_ok = false;
return msbc_ok || msbc_alt1_ok;
} }
static int codec_switch_start_timer(struct rfcomm *rfcomm, int timeout_msec); static int codec_switch_start_timer(struct rfcomm *rfcomm, int timeout_msec);
@ -489,11 +624,8 @@ static bool rfcomm_hfp_ag(struct spa_source *source, char* buf)
rfcomm->has_volume = (features & SPA_BT_HFP_HF_FEATURE_REMOTE_VOLUME_CONTROL); rfcomm->has_volume = (features & SPA_BT_HFP_HF_FEATURE_REMOTE_VOLUME_CONTROL);
/* Decide if we want to signal that the computer supports mSBC negotiation /* Decide if we want to signal that the computer supports mSBC negotiation
This should be done when This should be done when the computers bluetooth adapter supports the necessary transport mode */
a) mSBC support is enabled in config file and if (device_supports_required_mSBC_transport_modes(backend, rfcomm->device)) {
b) the computers bluetooth adapter supports the necessary transport mode */
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 */ /* set the feature bit that indicates AG (=computer) supports codec negotiation */
ag_features |= SPA_BT_HFP_AG_FEATURE_CODEC_NEGOTIATION; ag_features |= SPA_BT_HFP_AG_FEATURE_CODEC_NEGOTIATION;
@ -1265,7 +1397,6 @@ static int backend_native_supports_codec(void *data, struct spa_bt_device *devic
return (codec == HFP_AUDIO_CODEC_MSBC && return (codec == HFP_AUDIO_CODEC_MSBC &&
(rfcomm->profile == SPA_BT_PROFILE_HFP_AG || (rfcomm->profile == SPA_BT_PROFILE_HFP_AG ||
rfcomm->profile == SPA_BT_PROFILE_HFP_HF) && rfcomm->profile == SPA_BT_PROFILE_HFP_HF) &&
rfcomm->msbc_support_enabled_in_config &&
rfcomm->msbc_supported_by_hfp && rfcomm->msbc_supported_by_hfp &&
rfcomm->codec_negotiation_supported) ? 1 : 0; rfcomm->codec_negotiation_supported) ? 1 : 0;
#else #else
@ -1413,7 +1544,7 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
struct impl *backend = userdata; struct impl *backend = userdata;
DBusMessage *r; DBusMessage *r;
DBusMessageIter it[5]; DBusMessageIter it[5];
const char *handler, *path, *str; const char *handler, *path;
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;
@ -1483,11 +1614,6 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
spa_loop_add_source(backend->main_loop, &rfcomm->source); spa_loop_add_source(backend->main_loop, &rfcomm->source);
spa_list_append(&backend->rfcomm_list, &rfcomm->link); spa_list_append(&backend->rfcomm_list, &rfcomm->link);
if (d->settings && (str = spa_dict_lookup(d->settings, "bluez5.msbc-support")))
rfcomm->msbc_support_enabled_in_config = spa_atob(str);
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) {
@ -1510,11 +1636,8 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
char *cmd; char *cmd;
/* Decide if we want to signal that the HF supports mSBC negotiation /* Decide if we want to signal that the HF supports mSBC negotiation
This should be done when This should be done when the bluetooth adapter supports the necessary transport mode */
a) mSBC support is enabled in config file and if (device_supports_required_mSBC_transport_modes(backend, rfcomm->device)) {
b) the bluetooth adapter supports the necessary transport mode */
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 */ /* 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;
rfcomm->msbc_supported_by_hfp = true; rfcomm->msbc_supported_by_hfp = true;

View file

@ -55,6 +55,9 @@ if fdk_aac_dep.found()
endif endif
if not get_option('bluez5-backend-hsp-native').disabled() or not get_option('bluez5-backend-hfp-native').disabled() if not get_option('bluez5-backend-hsp-native').disabled() or not get_option('bluez5-backend-hfp-native').disabled()
if libusb_dep.found()
bluez5_deps += libusb_dep
endif
bluez5_sources += ['backend-native.c'] bluez5_sources += ['backend-native.c']
endif endif