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'))
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)
if cap_lib.found()
cdata.set('HAVE_LIBCAP', 1)

View file

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

View file

@ -25,6 +25,9 @@
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/sco.h>
@ -44,6 +47,10 @@
#include "defs.h"
#ifdef HAVE_LIBUSB
#include <libusb.h>
#endif
#define NAME "native"
#define PROP_KEY_HEADSET_ROLES "bluez5.headset-roles"
@ -125,7 +132,6 @@ 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;
unsigned int hfp_ag_switching_codec:1;
unsigned int hfp_ag_initial_codec_setup:2;
@ -413,24 +419,135 @@ static bool rfcomm_hsp_hs(struct spa_source *source, char* buf)
}
#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
static bool device_supports_required_mSBC_transport_modes(
struct impl *backend, struct spa_bt_device *device) {
bdaddr_t src;
uint8_t features[8], max_page = 0;
struct hci_dev_info di;
int device_id;
int sock;
bool msbc_ok, msbc_alt1_ok;
uint32_t bt_features;
if (device->adapter == NULL)
return false;
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);
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);
if (sock < 0) {
if (sock < 0) {
spa_log_error(backend->log, NAME": Error opening device hci%d: %s (%d)\n",
device_id, strerror(errno), errno);
return false;
@ -459,11 +576,29 @@ static bool device_supports_required_mSBC_transport_modes(
} else {
spa_log_info(backend->log,
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");
return false;
if ((di.type & 0x0f) == HCI_USB && !msbc_alt1_ok && msbc_ok) {
/* 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);
@ -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);
/* Decide if we want to signal that the computer supports mSBC negotiation
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 ((rfcomm->msbc_support_enabled_in_config == true) &&
(device_supports_required_mSBC_transport_modes(backend, rfcomm->device))) {
This should be done when the computers bluetooth adapter supports the necessary transport mode */
if (device_supports_required_mSBC_transport_modes(backend, rfcomm->device)) {
/* set the feature bit that indicates AG (=computer) supports 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 &&
(rfcomm->profile == SPA_BT_PROFILE_HFP_AG ||
rfcomm->profile == SPA_BT_PROFILE_HFP_HF) &&
rfcomm->msbc_support_enabled_in_config &&
rfcomm->msbc_supported_by_hfp &&
rfcomm->codec_negotiation_supported) ? 1 : 0;
#else
@ -1413,7 +1544,7 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
struct impl *backend = userdata;
DBusMessage *r;
DBusMessageIter it[5];
const char *handler, *path, *str;
const char *handler, *path;
enum spa_bt_profile profile = SPA_BT_PROFILE_NULL;
struct rfcomm *rfcomm;
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_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) {
t = _transport_create(rfcomm);
if (t == NULL) {
@ -1510,11 +1636,8 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
char *cmd;
/* Decide if we want to signal that the HF supports mSBC negotiation
This should be done when
a) mSBC support is enabled in config file and
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))) {
This should be done when the bluetooth adapter supports the necessary transport mode */
if (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;
rfcomm->msbc_supported_by_hfp = true;

View file

@ -55,6 +55,9 @@ if fdk_aac_dep.found()
endif
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']
endif