pulseaudio/0002-bluetooth-separate-HSP-and-HFP.patch
Takashi Iwai 37ac29471d Accepting request 877714 from home:tiwai:branches:multimedia:libs
- Upstream fixes for supporting HFP in native backend (bsc#1167940):
  0001-bluetooth-use-consistent-profile-names.patch
  0002-bluetooth-separate-HSP-and-HFP.patch
  0003-bluetooth-add-correct-HFP-rfcomm-negotiation.patch
  0004-bluetooth-make-native-the-default-backend.patch
  0005-bluetooth-enable-module-bluez5-discover-argument-ena.patch
  0006-bluetooth-fix-headset-auto-ofono-handover.patch
  0007-bluetooth-prefer-headset-HFP-HF-connection-with-nati.patch
  0008-bluetooth-complete-bluetooth-profile-separation.patch
  0009-bluetooth-use-device-flag-to-prevent-assertion-failu.patch
  0010-bluetooth-rename-enable_hs_role-to-enable_shared_pro.patch
  0011-bluetooth-clean-up-rfcomm_write-usage.patch

OBS-URL: https://build.opensuse.org/request/show/877714
OBS-URL: https://build.opensuse.org/package/show/multimedia:libs/pulseaudio?expand=0&rev=234
2021-03-08 12:50:38 +00:00

491 lines
21 KiB
Diff

From 66ed99a13da289f25bdb8381d30cfb5f82f9cab6 Mon Sep 17 00:00:00 2001
From: James Bottomley <James.Bottomley@HansenPartnership.com>
Date: Thu, 18 Aug 2016 08:48:48 -0700
Subject: [PATCH 02/11] bluetooth: separate HSP and HFP
When all headsets supported both HSP and HFP, life was good and we
only needed to implement HSP in the native backend. Unfortunately
some headsets have started supporting HFP only. Unfortuantely, we
can't simply switch to HFP only because that might break older HSP
only headsets meaning we need to support both HSP and HFP separately.
This patch separates them from a joint profile to being two separate
ones. The older one retains the headset_head_unit name, meaning any
saved parameters will still select this (keeping us backward
compatible). It also introduces a new headset_handsfree.
For headsets that support both HSP and HFP, the two profiles will
become separately visible and selectable. This will only matter once
we start adding features to HFP that HSP can't support (like wideband
audio).
Signed-off-by: <James.Bottomley@HansenPartnership.com>
---
v6:
- merge profile switching fixes patch from Rodrigo Araujo
v5:
- rename option to enable_native_hfp_hf
- don't call profile_done for HFP_HF unless it was initialised
v3:
- Update for PA 11.0
v2:
- fold in review feedback
- add global disable option for not registering HFP
v3:
- change parameter to enable_profile_hfp
- update device_supports_profile to be aware of hfp/hsp exclusivity
- change parameter to enable_profile_hfp_hf
bluetooth: separate HSP and HFP (to me merged with this patch)
Hi.
First, just to say that your patches are going great. Finally I can use
the microphone of my HFP only headset (a version of a Bluedio T2+).
So far, I've only encontered one problem: the auto_switch option of
module_bluetooth_policy stops working. Dug through the code and I think
you missed a few spots were you have to hangle the new headset_handsfree
profile in module_bluetooth_policy.c
Applying the following after applying your v5 patches fixed the issue
for me, now when I start making a VOIP call the profile switches to
headset_handsfree and the mic works automatically, and when the call
finishes it reverts back to a2dp.
Thanks and best regards.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/491>
---
src/modules/bluetooth/backend-native.c | 17 ++++++
src/modules/bluetooth/bluez5-util.c | 34 ++++++++++++-
src/modules/bluetooth/bluez5-util.h | 4 +
src/modules/bluetooth/module-bluetooth-policy.c | 10 ++--
src/modules/bluetooth/module-bluez5-device.c | 60 ++++++++++++++++++++----
src/modules/bluetooth/module-bluez5-discover.c | 6 ++
6 files changed, 112 insertions(+), 19 deletions(-)
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -62,6 +62,7 @@ struct transport_data {
#define BLUEZ_PROFILE_INTERFACE BLUEZ_SERVICE ".Profile1"
#define HSP_AG_PROFILE "/Profile/HSPAGProfile"
+#define HFP_AG_PROFILE "/Profile/HFPAGProfile"
#define HSP_HS_PROFILE "/Profile/HSPHSProfile"
/* RFCOMM channel for HSP headset role
@@ -512,6 +513,8 @@ static DBusMessage *profile_new_connecti
p = PA_BLUETOOTH_PROFILE_HSP_HS;
} else if (pa_streq(handler, HSP_HS_PROFILE)) {
p = PA_BLUETOOTH_PROFILE_HFP_AG;
+ } else if (pa_streq(handler, HFP_AG_PROFILE)) {
+ p = PA_BLUETOOTH_PROFILE_HFP_HF;
} else {
pa_log_error("Invalid handler");
goto fail;
@@ -589,7 +592,8 @@ static DBusHandlerResult profile_handler
pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
- if (!pa_streq(path, HSP_AG_PROFILE) && !pa_streq(path, HSP_HS_PROFILE))
+ if (!pa_streq(path, HSP_AG_PROFILE) && !pa_streq(path, HSP_HS_PROFILE)
+ && !pa_streq(path, HFP_AG_PROFILE))
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
@@ -634,6 +638,10 @@ static void profile_init(pa_bluetooth_ba
object_name = HSP_HS_PROFILE;
uuid = PA_BLUETOOTH_UUID_HSP_HS;
break;
+ case PA_BLUETOOTH_PROFILE_HFP_HF:
+ object_name = HFP_AG_PROFILE;
+ uuid = PA_BLUETOOTH_UUID_HFP_AG;
+ break;
default:
pa_assert_not_reached();
break;
@@ -653,6 +661,9 @@ static void profile_done(pa_bluetooth_ba
case PA_BLUETOOTH_PROFILE_HFP_AG:
dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HSP_HS_PROFILE);
break;
+ case PA_BLUETOOTH_PROFILE_HFP_HF:
+ dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HFP_AG_PROFILE);
+ break;
default:
pa_assert_not_reached();
break;
@@ -695,6 +706,8 @@ pa_bluetooth_backend *pa_bluetooth_nativ
if (enable_hs_role)
profile_init(backend, PA_BLUETOOTH_PROFILE_HFP_AG);
profile_init(backend, PA_BLUETOOTH_PROFILE_HSP_HS);
+ if (pa_bluetooth_discovery_get_enable_native_hfp_hf(y))
+ profile_init(backend, PA_BLUETOOTH_PROFILE_HFP_HF);
return backend;
}
@@ -707,6 +720,8 @@ void pa_bluetooth_native_backend_free(pa
if (backend->enable_hs_role)
profile_done(backend, PA_BLUETOOTH_PROFILE_HFP_AG);
profile_done(backend, PA_BLUETOOTH_PROFILE_HSP_HS);
+ if (pa_bluetooth_discovery_get_enable_native_hfp_hf(backend->discovery))
+ profile_done(backend, PA_BLUETOOTH_PROFILE_HFP_HF);
pa_dbus_connection_unref(backend->connection);
--- a/src/modules/bluetooth/bluez5-util.c
+++ b/src/modules/bluetooth/bluez5-util.c
@@ -94,6 +94,7 @@ struct pa_bluetooth_discovery {
int headset_backend;
pa_bluetooth_backend *ofono_backend, *native_backend;
PA_LLIST_HEAD(pa_dbus_pending, pending);
+ bool enable_native_hfp_hf;
};
static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, DBusMessage *m,
@@ -171,15 +172,29 @@ static const char *transport_state_to_st
}
static bool device_supports_profile(pa_bluetooth_device *device, pa_bluetooth_profile_t profile) {
+ bool show_hfp, show_hsp, enable_native_hfp_hf;
+
+ enable_native_hfp_hf = pa_bluetooth_discovery_get_enable_native_hfp_hf(device->discovery);
+
+ if (enable_native_hfp_hf) {
+ show_hfp = pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_HF);
+ show_hsp = !show_hfp;
+ } else {
+ show_hfp = false;
+ show_hsp = true;
+ }
+
switch (profile) {
case PA_BLUETOOTH_PROFILE_A2DP_SINK:
return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SINK);
case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SOURCE);
case PA_BLUETOOTH_PROFILE_HSP_HS:
- return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_HS)
- || !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_HS_ALT)
- || !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_HF);
+ return show_hsp
+ && ( !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_HS)
+ || !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_HS_ALT));
+ case PA_BLUETOOTH_PROFILE_HFP_HF:
+ return show_hfp && !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_HF);
case PA_BLUETOOTH_PROFILE_HFP_AG:
return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_AG)
|| !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_AG);
@@ -539,6 +554,14 @@ pa_bluetooth_device* pa_bluetooth_discov
return NULL;
}
+bool pa_bluetooth_discovery_get_enable_native_hfp_hf(pa_bluetooth_discovery *y)
+{
+ pa_assert(y);
+ pa_assert(PA_REFCNT_VALUE(y) > 0);
+
+ return y->enable_native_hfp_hf;
+}
+
pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery *y, const char *remote, const char *local) {
pa_bluetooth_device *d;
void *state = NULL;
@@ -1267,6 +1290,8 @@ const char *pa_bluetooth_profile_to_stri
return "a2dp_source";
case PA_BLUETOOTH_PROFILE_HSP_HS:
return "headset_head_unit";
+ case PA_BLUETOOTH_PROFILE_HFP_HF:
+ return "headset_handsfree";
case PA_BLUETOOTH_PROFILE_HFP_AG:
return "headset_audio_gateway";
case PA_BLUETOOTH_PROFILE_OFF:
@@ -1582,7 +1607,7 @@ static void endpoint_done(pa_bluetooth_d
dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), endpoint);
}
-pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backend) {
+pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backend, bool enable_native_hfp_hf) {
pa_bluetooth_discovery *y;
DBusError err;
DBusConnection *conn;
@@ -1594,6 +1619,7 @@ pa_bluetooth_discovery* pa_bluetooth_dis
PA_REFCNT_INIT(y);
y->core = c;
y->headset_backend = headset_backend;
+ y->enable_native_hfp_hf = enable_native_hfp_hf;
y->adapters = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
(pa_free_cb_t) adapter_free);
y->devices = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
--- a/src/modules/bluetooth/bluez5-util.h
+++ b/src/modules/bluetooth/bluez5-util.h
@@ -58,6 +58,7 @@ typedef enum profile {
PA_BLUETOOTH_PROFILE_A2DP_SINK,
PA_BLUETOOTH_PROFILE_A2DP_SOURCE,
PA_BLUETOOTH_PROFILE_HSP_HS,
+ PA_BLUETOOTH_PROFILE_HFP_HF,
PA_BLUETOOTH_PROFILE_HFP_AG,
PA_BLUETOOTH_PROFILE_OFF
} pa_bluetooth_profile_t;
@@ -178,8 +179,9 @@ static inline bool pa_bluetooth_uuid_is_
#define HEADSET_BACKEND_NATIVE 1
#define HEADSET_BACKEND_AUTO 2
-pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core, int headset_backend);
+pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core, int headset_backend, bool default_profile_hfp);
pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y);
void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y);
void pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery *y, bool is_running);
+bool pa_bluetooth_discovery_get_enable_native_hfp_hf(pa_bluetooth_discovery *y);
#endif
--- a/src/modules/bluetooth/module-bluetooth-policy.c
+++ b/src/modules/bluetooth/module-bluetooth-policy.c
@@ -156,7 +156,7 @@ static void card_set_profile(struct user
if (!pa_streq(profile->name, "a2dp_sink"))
continue;
} else {
- if (!pa_streq(profile->name, "headset_head_unit"))
+ if (!pa_streq(profile->name, "headset_head_unit") && !pa_streq(profile->name, "headset_handsfree"))
continue;
}
@@ -191,7 +191,7 @@ static void switch_profile(pa_card *card
return;
/* Skip card if does not have active hsp profile */
- if (!pa_streq(card->active_profile->name, "headset_head_unit"))
+ if (!pa_streq(card->active_profile->name, "headset_head_unit") && !pa_streq(card->active_profile->name, "headset_handsfree"))
return;
/* Skip card if already has active a2dp profile */
@@ -203,7 +203,7 @@ static void switch_profile(pa_card *card
return;
/* Skip card if already has active hsp profile */
- if (pa_streq(card->active_profile->name, "headset_head_unit"))
+ if (pa_streq(card->active_profile->name, "headset_head_unit") || pa_streq(card->active_profile->name, "headset_handsfree"))
return;
}
@@ -358,7 +358,9 @@ static pa_hook_result_t profile_availabl
return PA_HOOK_OK;
/* Do not automatically switch profiles for headsets, just in case */
- if (pa_streq(profile->name, "a2dp_sink") || pa_streq(profile->name, "headset_head_unit"))
+ if (pa_streq(profile->name, "a2dp_sink") ||
+ pa_streq(profile->name, "headset_head_unit") ||
+ pa_streq(profile->name, "headset_handsfree"))
return PA_HOOK_OK;
is_active_profile = card->active_profile == profile;
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -254,6 +254,7 @@ static int sco_process_render(struct use
pa_assert(u);
pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HSP_HS ||
+ u->profile == PA_BLUETOOTH_PROFILE_HFP_HF ||
u->profile == PA_BLUETOOTH_PROFILE_HFP_AG);
pa_assert(u->sink);
@@ -324,6 +325,7 @@ static int sco_process_push(struct userd
pa_assert(u);
pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HSP_HS ||
+ u->profile == PA_BLUETOOTH_PROFILE_HFP_HF||
u->profile == PA_BLUETOOTH_PROFILE_HFP_AG);
pa_assert(u->source);
pa_assert(u->read_smoother);
@@ -767,7 +769,9 @@ static void handle_sink_block_size_chang
/* Run from I/O thread */
static void transport_config_mtu(struct userdata *u) {
- if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG) {
+ if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS
+ || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF
+ || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG) {
u->read_block_size = u->read_link_mtu;
u->write_block_size = u->write_link_mtu;
@@ -1007,7 +1011,8 @@ static int add_source(struct userdata *u
data.namereg_fail = false;
pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile));
pa_source_new_data_set_sample_spec(&data, &u->decoder_sample_spec);
- if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS)
+ if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS
+ || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF)
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
connect_ports(u, &data, PA_DIRECTION_INPUT);
@@ -1019,6 +1024,7 @@ static int add_source(struct userdata *u
data.suspend_cause = PA_SUSPEND_USER;
break;
case PA_BLUETOOTH_PROFILE_HSP_HS:
+ case PA_BLUETOOTH_PROFILE_HFP_HF:
/* u->stream_fd contains the error returned by the last transport_acquire()
* EAGAIN means we are waiting for a NewConnection signal */
if (u->stream_fd == -EAGAIN)
@@ -1043,7 +1049,9 @@ static int add_source(struct userdata *u
u->source->parent.process_msg = source_process_msg;
u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb;
- if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG) {
+ if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS
+ || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG
+ || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF) {
pa_source_set_set_volume_callback(u->source, source_set_volume_cb);
u->source->n_volume_steps = 16;
}
@@ -1191,7 +1199,8 @@ static int add_sink(struct userdata *u)
data.namereg_fail = false;
pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile));
pa_sink_new_data_set_sample_spec(&data, &u->encoder_sample_spec);
- if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS)
+ if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS
+ || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF)
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
connect_ports(u, &data, PA_DIRECTION_OUTPUT);
@@ -1202,6 +1211,7 @@ static int add_sink(struct userdata *u)
data.suspend_cause = PA_SUSPEND_USER;
break;
case PA_BLUETOOTH_PROFILE_HSP_HS:
+ case PA_BLUETOOTH_PROFILE_HFP_HF:
/* u->stream_fd contains the error returned by the last transport_acquire()
* EAGAIN means we are waiting for a NewConnection signal */
if (u->stream_fd == -EAGAIN)
@@ -1228,7 +1238,9 @@ static int add_sink(struct userdata *u)
u->sink->parent.process_msg = sink_process_msg;
u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
- if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG) {
+ if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS
+ || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG
+ || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF) {
pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
u->sink->n_volume_steps = 16;
}
@@ -1237,7 +1249,9 @@ static int add_sink(struct userdata *u)
/* Run from main thread */
static int transport_config(struct userdata *u) {
- if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG) {
+ if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS
+ || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF
+ || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG) {
u->encoder_sample_spec.format = PA_SAMPLE_S16LE;
u->encoder_sample_spec.channels = 1;
u->encoder_sample_spec.rate = 8000;
@@ -1307,6 +1321,7 @@ static pa_direction_t get_profile_direct
[PA_BLUETOOTH_PROFILE_A2DP_SINK] = PA_DIRECTION_OUTPUT,
[PA_BLUETOOTH_PROFILE_A2DP_SOURCE] = PA_DIRECTION_INPUT,
[PA_BLUETOOTH_PROFILE_HSP_HS] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT,
+ [PA_BLUETOOTH_PROFILE_HFP_HF] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT,
[PA_BLUETOOTH_PROFILE_HFP_AG] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT,
[PA_BLUETOOTH_PROFILE_OFF] = 0
};
@@ -1899,7 +1914,20 @@ static pa_card_profile *create_card_prof
break;
case PA_BLUETOOTH_PROFILE_HSP_HS:
- cp = pa_card_profile_new(name, _("Headset Head Unit (HSP/HFP)"), sizeof(pa_bluetooth_profile_t));
+ cp = pa_card_profile_new(name, _("Headset Head Unit (HSP)"), sizeof(pa_bluetooth_profile_t));
+ cp->priority = 30;
+ cp->n_sinks = 1;
+ cp->n_sources = 1;
+ cp->max_sink_channels = 1;
+ cp->max_source_channels = 1;
+ pa_hashmap_put(input_port->profiles, cp->name, cp);
+ pa_hashmap_put(output_port->profiles, cp->name, cp);
+
+ p = PA_CARD_PROFILE_DATA(cp);
+ break;
+
+ case PA_BLUETOOTH_PROFILE_HFP_HF:
+ cp = pa_card_profile_new(name, _("Headset Handsfree (HFP)"), sizeof(pa_bluetooth_profile_t));
cp->priority = 30;
cp->n_sinks = 1;
cp->n_sources = 1;
@@ -1985,8 +2013,10 @@ static int uuid_to_profile(const char *u
*_r = PA_BLUETOOTH_PROFILE_A2DP_SINK;
else if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE))
*_r = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
- else if (pa_bluetooth_uuid_is_hsp_hs(uuid) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_HF))
+ else if (pa_bluetooth_uuid_is_hsp_hs(uuid))
*_r = PA_BLUETOOTH_PROFILE_HSP_HS;
+ else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_HF))
+ *_r = PA_BLUETOOTH_PROFILE_HFP_HF;
else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_AG) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_AG))
*_r = PA_BLUETOOTH_PROFILE_HFP_AG;
else
@@ -2005,6 +2035,7 @@ static int add_card(struct userdata *u)
pa_bluetooth_profile_t *p;
const char *uuid;
void *state;
+ bool enable_native_hfp_hf, has_both;
pa_assert(u);
pa_assert(u->device);
@@ -2035,9 +2066,22 @@ static int add_card(struct userdata *u)
create_card_ports(u, data.ports);
+ enable_native_hfp_hf = pa_bluetooth_discovery_get_enable_native_hfp_hf(u->discovery);
+
+ has_both = enable_native_hfp_hf && pa_hashmap_get(d->uuids, PA_BLUETOOTH_UUID_HFP_HF) && pa_hashmap_get(d->uuids, PA_BLUETOOTH_UUID_HSP_HS);
PA_HASHMAP_FOREACH(uuid, d->uuids, state) {
pa_bluetooth_profile_t profile;
+ if (!enable_native_hfp_hf && pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_HF)) {
+ pa_log_info("device supports HFP but disabling profile as requested");
+ continue;
+ }
+
+ if (has_both && pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS)) {
+ pa_log_info("device support HSP and HFP, selecting HFP only");
+ continue;
+ }
+
if (uuid_to_profile(uuid, &profile) < 0)
continue;
--- a/src/modules/bluetooth/module-bluez5-discover.c
+++ b/src/modules/bluetooth/module-bluez5-discover.c
@@ -103,6 +103,7 @@ int pa__init(pa_module *m) {
const char *headset_str;
int headset_backend;
bool autodetect_mtu;
+ bool enable_native_hfp_hf = true;
pa_assert(m);
@@ -126,6 +127,9 @@ int pa__init(pa_module *m) {
autodetect_mtu = false;
if (pa_modargs_get_value_boolean(ma, "autodetect_mtu", &autodetect_mtu) < 0) {
pa_log("Invalid boolean value for autodetect_mtu parameter");
+ }
+ if (pa_modargs_get_value_boolean(ma, "enable_native_hfp_hf", &enable_native_hfp_hf) < 0) {
+ pa_log("enable_native_hfp_hf must be true or false");
goto fail;
}
@@ -135,7 +139,7 @@ int pa__init(pa_module *m) {
u->autodetect_mtu = autodetect_mtu;
u->loaded_device_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- if (!(u->discovery = pa_bluetooth_discovery_get(u->core, headset_backend)))
+ if (!(u->discovery = pa_bluetooth_discovery_get(u->core, headset_backend, enable_native_hfp_hf)))
goto fail;
u->device_connection_changed_slot =