mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-17 07:00:03 -05:00
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:
parent
b57ae8c2a6
commit
5e0b63b149
4 changed files with 157 additions and 22 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue