Takashi Iwai
37ac29471d
- 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
491 lines
21 KiB
Diff
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 =
|