[pulseaudio-commits] [Git][pulseaudio/pulseaudio][master] 11 commits: bluetooth: use consistent profile names
PulseAudio Marge Bot
gitlab at gitlab.freedesktop.org
Tue Feb 23 13:20:15 UTC 2021
PulseAudio Marge Bot pushed to branch master at PulseAudio / pulseaudio
Commits:
709909a1 by James Bottomley at 2021-02-23T06:54:39+00:00
bluetooth: use consistent profile names
The PA_BLUETOOTH_PROFILE names should mirror the PA_BLUETOOTH_UUID
names using profile_function instead of randomly made up names. Fix
this with the transformation:
PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT -> PA_BLUETOOTH_PROFILE_HSP_HS
PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY -> PA_BLUETOOTH_PROFILE_HFP_AG
Signed-off-by: James Bottomley <James.Bottomley at HansenPartnership.com>
---
v4: update for PA 11.0
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/491>
- - - - -
66ed99a1 by James Bottomley at 2021-02-23T06:54:39+00:00
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 at 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>
- - - - -
485a6464 by James Bottomley at 2021-02-23T06:54:39+00:00
bluetooth: add correct HFP rfcomm negotiation
HFP 1.6 requires a stateful negotiation of AT commands. The prior
version got away with initialising HFP simply by replying 'OK' to
every negotiation attempt. This one actually tries to parse the state
and make sure the negotiation occurs correctly
Signed-off-by: James Bottomley <James.Bottomley at HansenPartnership.com>
---
v4:
- Update for PA 11.0
- Finally sort out CIND negotiaton for complex headsets
v3:
- remove internal debugging
- added comment for t->config being not null for hfp
- removed unused returns from hfp_rfcomm_handle()
- remove rfcomm comment
- use pa_startswith
- simplify negotiation
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/491>
- - - - -
cb193e19 by James Bottomley at 2021-02-23T06:54:39+00:00
bluetooth: make native the default backend
Change default backend from 'auto' to 'native' so that in the usual
install pulseaudio uses the native backend with HFP_HF handling.
set default to false unless the backend is the native one, in which
case the default becomes true.
Additionally set default value of enable_native_hfp_hf to false unless
the backend is the native one, in which case the default becomes
true. so that we only bind the HFP_HF end point in the native case
(leaving it free for ofono in the ofono backend or auto case)
Signed-off-by: James Bottomley <James.Bottomley at HansenPartnership.com>
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/491>
- - - - -
8491477b by Igor V. Kovalenko at 2021-02-23T06:54:39+00:00
bluetooth: enable module-bluez5-discover argument enable_native_hfp_hf
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/491>
- - - - -
70171158 by Igor V. Kovalenko at 2021-02-23T06:54:39+00:00
bluetooth: fix headset=auto ofono handover
Native backend implements HFP AG but not HFP HF yet, therefore headset=auto
functionality is still needed if HFP HF is required.
To make headset=auto work again, drop both HFP AG and HSP AG roles while
performing handover from native backend when oFono is detected running.
While at it, restore profile description to Headset Head Unit (HSP/HFP)
to note that HFP may be still provided via oFono backend.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/491>
- - - - -
815dd2d6 by Igor V. Kovalenko at 2021-02-23T06:54:39+00:00
bluetooth: prefer headset HFP HF connection with native backend
When HFP HF support is enabled in native backend, peer HFP HF profile connection
is preferred over same peer HSP HS profile connection if peer supports both
profiles.
Enforce the preference by rejecting HSP HS profile connections from such peer.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/491>
- - - - -
698fb3bc by Igor V. Kovalenko at 2021-02-23T06:54:39+00:00
bluetooth: complete bluetooth profile separation
This is a follow-up change to review of these series on pulseaudio-discuss
https://lists.freedesktop.org/archives/pulseaudio-discuss/2017-September/028801.html
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/491>
- - - - -
c884ae8c by Igor V. Kovalenko at 2021-02-23T06:54:39+00:00
bluetooth: use device flag to prevent assertion failure during shutdown
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/491>
- - - - -
0a36c154 by Igor V. Kovalenko at 2021-02-23T06:54:39+00:00
bluetooth: rename enable_hs_role to enable_shared_profiles
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/491>
- - - - -
45d896f8 by Igor V. Kovalenko at 2021-02-23T06:54:39+00:00
bluetooth: clean up rfcomm_write usage
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/491>
- - - - -
7 changed files:
- src/modules/bluetooth/backend-native.c
- src/modules/bluetooth/backend-ofono.c
- src/modules/bluetooth/bluez5-util.c
- src/modules/bluetooth/bluez5-util.h
- src/modules/bluetooth/module-bluetooth-policy.c
- src/modules/bluetooth/module-bluez5-device.c
- src/modules/bluetooth/module-bluez5-discover.c
Changes:
=====================================
src/modules/bluetooth/backend-native.c
=====================================
@@ -40,7 +40,8 @@ struct pa_bluetooth_backend {
pa_core *core;
pa_dbus_connection *connection;
pa_bluetooth_discovery *discovery;
- bool enable_hs_role;
+ bool enable_shared_profiles;
+ bool enable_hfp_hf;
PA_LLIST_HEAD(pa_dbus_pending, pending);
};
@@ -53,7 +54,45 @@ struct transport_data {
pa_mainloop_api *mainloop;
};
+struct hfp_config {
+ uint32_t capabilities;
+ int state;
+};
+
+/*
+ * the separate hansfree headset (HF) and Audio Gateway (AG) features
+ */
+enum hfp_hf_features {
+ HFP_HF_EC_NR = 0,
+ HFP_HF_CALL_WAITING = 1,
+ HFP_HF_CLI = 2,
+ HFP_HF_VR = 3,
+ HFP_HF_RVOL = 4,
+ HFP_HF_ESTATUS = 5,
+ HFP_HF_ECALL = 6,
+ HFP_HF_CODECS = 7,
+};
+
+enum hfp_ag_features {
+ HFP_AG_THREE_WAY = 0,
+ HFP_AG_EC_NR = 1,
+ HFP_AG_VR = 2,
+ HFP_AG_RING = 3,
+ HFP_AG_NUM_TAG = 4,
+ HFP_AG_REJECT = 5,
+ HFP_AG_ESTATUS = 6,
+ HFP_AG_ECALL = 7,
+ HFP_AG_EERR = 8,
+ HFP_AG_CODECS = 9,
+};
+
+/* gateway features we support, which is as little as we can get away with */
+static uint32_t hfp_features =
+ /* HFP 1.6 requires this */
+ (1 << HFP_AG_ESTATUS );
+
#define HSP_AG_PROFILE "/Profile/HSPAGProfile"
+#define HFP_AG_PROFILE "/Profile/HFPAGProfile"
#define HSP_HS_PROFILE "/Profile/HSPHSProfile"
/* RFCOMM channel for HSP headset role
@@ -100,6 +139,28 @@ static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_backend *backend, D
return p;
}
+static void rfcomm_write(int fd, const char *fmt, ...)
+{
+ va_list ap;
+ size_t len;
+ char buf[512];
+ char command[512];
+
+ va_start(ap, fmt);
+ pa_vsnprintf(command, sizeof(command), fmt, ap);
+ va_end(ap);
+
+ pa_log_debug("RFCOMM >> %s", command);
+
+ len = pa_snprintf(buf, sizeof(buf), "\r\n%s\r\n", command);
+
+ /* we ignore any errors, it's not critical and real errors should
+ * be caught with the HANGUP and ERROR events handled above */
+
+ if ((size_t)write(fd, buf, len) != len)
+ pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno));
+}
+
static int sco_do_connect(pa_bluetooth_transport *t) {
pa_bluetooth_device *d = t->device;
struct sockaddr_sco addr;
@@ -347,6 +408,61 @@ static void register_profile(pa_bluetooth_backend *b, const char *profile, const
send_and_add_to_pending(b, m, register_profile_reply, pa_xstrdup(profile));
}
+static void transport_put(pa_bluetooth_transport *t)
+{
+ pa_bluetooth_transport_put(t);
+
+ pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
+}
+
+static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf)
+{
+ struct hfp_config *c = t->config;
+ int val;
+
+ /* stateful negotiation */
+ if (c->state == 0 && sscanf(buf, "AT+BRSF=%d", &val) == 1) {
+ c->capabilities = val;
+ pa_log_info("HFP capabilities returns 0x%x", val);
+ rfcomm_write(fd, "+BRSF: %d", hfp_features);
+ c->state = 1;
+ return true;
+ } else if (c->state == 1 && pa_startswith(buf, "AT+CIND=?")) {
+ /* we declare minimal no indicators */
+ rfcomm_write(fd, "+CIND: "
+ /* many indicators can be supported, only call and
+ * callheld are mandatory, so that's all we repy */
+ "(\"call\",(0-1)),"
+ "(\"callheld\",(0-2))");
+ c->state = 2;
+ return true;
+ } else if (c->state == 2 && pa_startswith(buf, "AT+CIND?")) {
+ rfcomm_write(fd, "+CIND: 0,0");
+ c->state = 3;
+ return true;
+ } else if ((c->state == 2 || c->state == 3) && pa_startswith(buf, "AT+CMER=")) {
+ rfcomm_write(fd, "OK");
+ c->state = 4;
+ transport_put(t);
+ return false;
+ }
+
+ /* if we get here, negotiation should be complete */
+ if (c->state != 4) {
+ pa_log_error("HFP negotiation failed in state %d with inbound %s\n",
+ c->state, buf);
+ rfcomm_write(fd, "ERROR");
+ return false;
+ }
+
+ /*
+ * once we're fully connected, just reply OK to everything
+ * it will just be the headset sending the occasional status
+ * update, but we process only the ones we care about
+ */
+ return true;
+}
+
static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
pa_bluetooth_transport *t = userdata;
@@ -393,20 +509,14 @@ static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_i
do_reply = true;
} else if (sscanf(buf, "AT+CKPD=%d", &dummy) == 1) {
do_reply = true;
+ } else if (t->config) { /* t->config is only non-null for hfp profile */
+ do_reply = hfp_rfcomm_handle(fd, t, buf);
} else {
do_reply = false;
}
- if (do_reply) {
- pa_log_debug("RFCOMM >> OK");
-
- len = write(fd, "\r\nOK\r\n", 6);
-
- /* we ignore any errors, it's not critical and real errors should
- * be caught with the HANGUP and ERROR events handled above */
- if (len < 0)
- pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno));
- }
+ if (do_reply)
+ rfcomm_write(fd, "OK");
}
return;
@@ -434,8 +544,6 @@ static void transport_destroy(pa_bluetooth_transport *t) {
static void set_speaker_gain(pa_bluetooth_transport *t, uint16_t gain) {
struct transport_data *trd = t->userdata;
- char buf[512];
- ssize_t len, written;
if (t->speaker_gain == gain)
return;
@@ -445,24 +553,15 @@ static void set_speaker_gain(pa_bluetooth_transport *t, uint16_t gain) {
/* If we are in the AG role, we send a command to the head set to change
* the speaker gain. In the HS role, source and sink are swapped, so
* in this case we notify the AG that the microphone gain has changed */
- if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) {
- len = sprintf(buf, "\r\n+VGS=%d\r\n", gain);
- pa_log_debug("RFCOMM >> +VGS=%d", gain);
+ if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS || t->profile == PA_BLUETOOTH_PROFILE_HFP_HF) {
+ rfcomm_write(trd->rfcomm_fd, "+VGS=%d", gain);
} else {
- len = sprintf(buf, "\r\nAT+VGM=%d\r\n", gain);
- pa_log_debug("RFCOMM >> AT+VGM=%d", gain);
+ rfcomm_write(trd->rfcomm_fd, "AT+VGM=%d", gain);
}
-
- written = write(trd->rfcomm_fd, buf, len);
-
- if (written != len)
- pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno));
}
static void set_microphone_gain(pa_bluetooth_transport *t, uint16_t gain) {
struct transport_data *trd = t->userdata;
- char buf[512];
- ssize_t len, written;
if (t->microphone_gain == gain)
return;
@@ -472,18 +571,11 @@ static void set_microphone_gain(pa_bluetooth_transport *t, uint16_t gain) {
/* If we are in the AG role, we send a command to the head set to change
* the microphone gain. In the HS role, source and sink are swapped, so
* in this case we notify the AG that the speaker gain has changed */
- if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) {
- len = sprintf(buf, "\r\n+VGM=%d\r\n", gain);
- pa_log_debug("RFCOMM >> +VGM=%d", gain);
+ if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS || t->profile == PA_BLUETOOTH_PROFILE_HFP_HF) {
+ rfcomm_write(trd->rfcomm_fd, "+VGM=%d", gain);
} else {
- len = sprintf(buf, "\r\nAT+VGS=%d\r\n", gain);
- pa_log_debug("RFCOMM >> AT+VGS=%d", gain);
+ rfcomm_write(trd->rfcomm_fd, "AT+VGS=%d", gain);
}
-
- written = write (trd->rfcomm_fd, buf, len);
-
- if (written != len)
- pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno));
}
static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m, void *userdata) {
@@ -505,9 +597,11 @@ static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m,
handler = dbus_message_get_path(m);
if (pa_streq(handler, HSP_AG_PROFILE)) {
- p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT;
+ p = PA_BLUETOOTH_PROFILE_HSP_HS;
} else if (pa_streq(handler, HSP_HS_PROFILE)) {
- p = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY;
+ p = PA_BLUETOOTH_PROFILE_HSP_AG;
+ } else if (pa_streq(handler, HFP_AG_PROFILE)) {
+ p = PA_BLUETOOTH_PROFILE_HFP_HF;
} else {
pa_log_error("Invalid handler");
goto fail;
@@ -522,6 +616,16 @@ static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m,
goto fail;
}
+ if (d->enable_hfp_hf) {
+ if (p == PA_BLUETOOTH_PROFILE_HSP_HS && pa_hashmap_get(d->uuids, PA_BLUETOOTH_UUID_HFP_HF)) {
+ /* If peer connecting to HSP Audio Gateway supports HFP HF profile
+ * reject this connection to force it to connect to HSP Audio Gateway instead.
+ */
+ pa_log_info("HFP HF enabled in native backend and is supported by peer, rejecting HSP HS peer connection");
+ goto fail;
+ }
+ }
+
pa_assert_se(dbus_message_iter_next(&arg_i));
pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_UNIX_FD);
@@ -533,7 +637,9 @@ static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m,
sender = dbus_message_get_sender(m);
pathfd = pa_sprintf_malloc ("%s/fd%d", path, fd);
- t = pa_bluetooth_transport_new(d, sender, pathfd, p, NULL, 0);
+ t = pa_bluetooth_transport_new(d, sender, pathfd, p, NULL,
+ p == PA_BLUETOOTH_PROFILE_HFP_HF ?
+ sizeof(struct hfp_config) : 0);
pa_xfree(pathfd);
t->acquire = sco_acquire_cb;
@@ -551,9 +657,8 @@ static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m,
sco_listen(t);
- pa_bluetooth_transport_put(t);
-
- pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
+ if (p != PA_BLUETOOTH_PROFILE_HFP_HF)
+ transport_put(t);
pa_assert_se(r = dbus_message_new_method_return(m));
@@ -585,7 +690,8 @@ static DBusHandlerResult profile_handler(DBusConnection *c, DBusMessage *m, void
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, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) {
@@ -622,14 +728,18 @@ static void profile_init(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile
pa_assert(b);
switch (profile) {
- case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
+ case PA_BLUETOOTH_PROFILE_HSP_HS:
object_name = HSP_AG_PROFILE;
uuid = PA_BLUETOOTH_UUID_HSP_AG;
break;
- case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
+ case PA_BLUETOOTH_PROFILE_HSP_AG:
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;
@@ -643,32 +753,44 @@ static void profile_done(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile
pa_assert(b);
switch (profile) {
- case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
+ case PA_BLUETOOTH_PROFILE_HSP_HS:
dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HSP_AG_PROFILE);
break;
- case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
+ case PA_BLUETOOTH_PROFILE_HSP_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;
}
}
-void pa_bluetooth_native_backend_enable_hs_role(pa_bluetooth_backend *native_backend, bool enable_hs_role) {
+static void native_backend_apply_profile_registration_change(pa_bluetooth_backend *native_backend, bool enable_shared_profiles) {
+ if (enable_shared_profiles) {
+ profile_init(native_backend, PA_BLUETOOTH_PROFILE_HSP_AG);
+ if (native_backend->enable_hfp_hf)
+ profile_init(native_backend, PA_BLUETOOTH_PROFILE_HFP_HF);
+ } else {
+ profile_done(native_backend, PA_BLUETOOTH_PROFILE_HSP_AG);
+ if (native_backend->enable_hfp_hf)
+ profile_done(native_backend, PA_BLUETOOTH_PROFILE_HFP_HF);
+ }
+}
- if (enable_hs_role == native_backend->enable_hs_role)
+void pa_bluetooth_native_backend_enable_shared_profiles(pa_bluetooth_backend *native_backend, bool enable) {
+
+ if (enable == native_backend->enable_shared_profiles)
return;
- if (enable_hs_role)
- profile_init(native_backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
- else
- profile_done(native_backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
+ native_backend_apply_profile_registration_change(native_backend, enable);
- native_backend->enable_hs_role = enable_hs_role;
+ native_backend->enable_shared_profiles = enable;
}
-pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_hs_role) {
+pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_shared_profiles) {
pa_bluetooth_backend *backend;
DBusError err;
@@ -686,11 +808,13 @@ pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_d
}
backend->discovery = y;
- backend->enable_hs_role = enable_hs_role;
+ backend->enable_shared_profiles = enable_shared_profiles;
+ backend->enable_hfp_hf = pa_bluetooth_discovery_get_enable_native_hfp_hf(y);
+
+ if (backend->enable_shared_profiles)
+ native_backend_apply_profile_registration_change(backend, true);
- if (enable_hs_role)
- profile_init(backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
- profile_init(backend, PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT);
+ profile_init(backend, PA_BLUETOOTH_PROFILE_HSP_HS);
return backend;
}
@@ -700,9 +824,10 @@ void pa_bluetooth_native_backend_free(pa_bluetooth_backend *backend) {
pa_dbus_free_pending_list(&backend->pending);
- if (backend->enable_hs_role)
- profile_done(backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
- profile_done(backend, PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT);
+ if (backend->enable_shared_profiles)
+ native_backend_apply_profile_registration_change(backend, false);
+
+ profile_done(backend, PA_BLUETOOTH_PROFILE_HSP_HS);
pa_dbus_connection_unref(backend->connection);
=====================================
src/modules/bluetooth/backend-ofono.c
=====================================
@@ -303,7 +303,7 @@ static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char
const char *key, *value;
struct hf_audio_card *card;
pa_bluetooth_device *d;
- pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY;
+ pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_HFP_AG;
pa_assert(backend);
pa_assert(path);
@@ -337,7 +337,7 @@ static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char
card->local_address = pa_xstrdup(value);
} else if (pa_streq(key, "Type")) {
if (pa_streq(value, "gateway"))
- p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT;
+ p = PA_BLUETOOTH_PROFILE_HFP_HF;
}
pa_log_debug("%s: %s", key, value);
=====================================
src/modules/bluetooth/bluez5-util.c
=====================================
@@ -114,6 +114,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,7 +172,10 @@ pa_bluetooth_transport *pa_bluetooth_transport_new(pa_bluetooth_device *d, const
if (size > 0) {
t->config = pa_xnew(uint8_t, size);
- memcpy(t->config, config, size);
+ if (config)
+ memcpy(t->config, config, size);
+ else
+ memset(t->config, 0, size);
}
return t;
@@ -191,18 +195,31 @@ static const char *transport_state_to_string(pa_bluetooth_transport_state_t stat
}
static bool device_supports_profile(pa_bluetooth_device *device, pa_bluetooth_profile_t profile) {
+ bool show_hfp, show_hsp;
+
+ if (device->enable_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_HEADSET_HEAD_UNIT:
- 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);
- case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
- return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_AG)
- || !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_AG);
+ case PA_BLUETOOTH_PROFILE_HSP_HS:
+ 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_HSP_AG:
+ return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_AG);
+ 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_HFP_AG);
case PA_BLUETOOTH_PROFILE_OFF:
pa_assert_not_reached();
}
@@ -708,6 +725,7 @@ static pa_bluetooth_device* device_create(pa_bluetooth_discovery *y, const char
d = pa_xnew0(pa_bluetooth_device, 1);
d->discovery = y;
+ d->enable_hfp_hf = pa_bluetooth_discovery_get_enable_native_hfp_hf(y);
d->path = pa_xstrdup(path);
d->uuids = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
d->a2dp_sink_endpoints = pa_hashmap_new_full(pa_a2dp_codec_id_hash_func, pa_a2dp_codec_id_compare_func, pa_xfree, (pa_free_cb_t)pa_hashmap_free);
@@ -731,6 +749,14 @@ pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_disc
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;
@@ -1440,14 +1466,16 @@ void pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery *y, bool is
if (y->headset_backend != HEADSET_BACKEND_AUTO)
return;
- /* If ofono starts running, all devices that might be connected to the HS role
+ pa_bluetooth_native_backend_enable_shared_profiles(y->native_backend, !is_running);
+
+ /* If ofono starts running, all devices that might be connected to the HS roles or HFP AG role
* need to be disconnected, so that the devices can be handled by ofono */
if (is_running) {
void *state;
pa_bluetooth_device *d;
PA_HASHMAP_FOREACH(d, y->devices, state) {
- if (device_supports_profile(d, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)) {
+ if (device_supports_profile(d, PA_BLUETOOTH_PROFILE_HFP_AG) || device_supports_profile(d, PA_BLUETOOTH_PROFILE_HFP_HF)) {
DBusMessage *m;
pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, d->path, BLUEZ_DEVICE_INTERFACE, "Disconnect"));
@@ -1457,8 +1485,6 @@ void pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery *y, bool is
}
}
}
-
- pa_bluetooth_native_backend_enable_hs_role(y->native_backend, !is_running);
}
static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata) {
@@ -1697,10 +1723,14 @@ const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
return "a2dp_sink";
case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
return "a2dp_source";
- case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
+ case PA_BLUETOOTH_PROFILE_HSP_HS:
return "headset_head_unit";
- case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
+ case PA_BLUETOOTH_PROFILE_HSP_AG:
return "headset_audio_gateway";
+ case PA_BLUETOOTH_PROFILE_HFP_HF:
+ return "handsfree_head_unit";
+ case PA_BLUETOOTH_PROFILE_HFP_AG:
+ return "handsfree_audio_gateway";
case PA_BLUETOOTH_PROFILE_OFF:
return "off";
}
@@ -2152,7 +2182,7 @@ static void object_manager_done(pa_bluetooth_discovery *y) {
A2DP_OBJECT_MANAGER_PATH);
}
-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;
@@ -2165,6 +2195,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backe
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,
=====================================
src/modules/bluetooth/bluez5-util.h
=====================================
@@ -70,8 +70,10 @@ typedef enum pa_bluetooth_hook {
typedef enum profile {
PA_BLUETOOTH_PROFILE_A2DP_SINK,
PA_BLUETOOTH_PROFILE_A2DP_SOURCE,
- PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT,
- PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY,
+ PA_BLUETOOTH_PROFILE_HSP_HS,
+ PA_BLUETOOTH_PROFILE_HSP_AG,
+ PA_BLUETOOTH_PROFILE_HFP_HF,
+ PA_BLUETOOTH_PROFILE_HFP_AG,
PA_BLUETOOTH_PROFILE_OFF
} pa_bluetooth_profile_t;
#define PA_BLUETOOTH_PROFILE_COUNT PA_BLUETOOTH_PROFILE_OFF
@@ -96,7 +98,7 @@ struct pa_bluetooth_transport {
pa_bluetooth_profile_t profile;
uint8_t codec;
- uint8_t *config;
+ void *config;
size_t config_size;
const pa_a2dp_codec *a2dp_codec;
@@ -118,6 +120,7 @@ struct pa_bluetooth_device {
pa_bluetooth_discovery *discovery;
pa_bluetooth_adapter *adapter;
+ bool enable_hfp_hf;
bool properties_received;
bool tried_to_link_with_adapter;
bool valid;
@@ -161,15 +164,15 @@ static inline void pa_bluetooth_ofono_backend_free(pa_bluetooth_backend *b) {}
#endif
#ifdef HAVE_BLUEZ_5_NATIVE_HEADSET
-pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_hs_role);
+pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_shared_profiles);
void pa_bluetooth_native_backend_free(pa_bluetooth_backend *b);
-void pa_bluetooth_native_backend_enable_hs_role(pa_bluetooth_backend *b, bool enable_hs_role);
+void pa_bluetooth_native_backend_enable_shared_profiles(pa_bluetooth_backend *b, bool enable);
#else
-static inline pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_hs_role) {
+static inline pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_shared_profiles) {
return NULL;
}
static inline void pa_bluetooth_native_backend_free(pa_bluetooth_backend *b) {}
-static inline void pa_bluetooth_native_backend_enable_hs_role(pa_bluetooth_backend *b, bool enable_hs_role) {}
+static inline void pa_bluetooth_native_backend_enable_shared_profiles(pa_bluetooth_backend *b, bool enable) {}
#endif
pa_bluetooth_transport *pa_bluetooth_transport_new(pa_bluetooth_device *d, const char *owner, const char *path,
@@ -198,8 +201,9 @@ static inline bool pa_bluetooth_uuid_is_hsp_hs(const char *uuid) {
#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
=====================================
src/modules/bluetooth/module-bluetooth-policy.c
=====================================
@@ -38,7 +38,7 @@ PA_MODULE_LOAD_ONCE(true);
PA_MODULE_USAGE(
"auto_switch=<Switch between hsp and a2dp profile? (0 - never, 1 - media.role=phone, 2 - heuristic> "
"a2dp_source=<Handle a2dp_source card profile (sink role)?> "
- "ag=<Handle headset_audio_gateway card profile (headset role)?> ");
+ "ag=<Handle headset_audio_gateway or handsfree_audio_gateway card profile (headset role)?> ");
static const char* const valid_modargs[] = {
"auto_switch",
@@ -86,7 +86,7 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source,
if (u->enable_a2dp_source && pa_streq(s, "a2dp_source"))
role = "music";
- else if (u->enable_ag && pa_streq(s, "headset_audio_gateway"))
+ else if (u->enable_ag && (pa_streq(s, "headset_audio_gateway") || pa_streq(s, "handsfree_audio_gateway")))
role = "phone";
else {
pa_log_debug("Profile %s cannot be selected for loopback", s);
@@ -125,7 +125,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void *
if (!s)
return PA_HOOK_OK;
- if (u->enable_ag && pa_streq(s, "headset_audio_gateway"))
+ if (u->enable_ag && (pa_streq(s, "headset_audio_gateway") || pa_streq(s, "handsfree_audio_gateway")))
role = "phone";
else {
pa_log_debug("Profile %s cannot be selected for loopback", s);
@@ -156,7 +156,7 @@ static void card_set_profile(struct userdata *u, pa_card *card, bool revert_to_a
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, "handsfree_head_unit"))
continue;
}
@@ -190,8 +190,8 @@ static void switch_profile(pa_card *card, bool revert_to_a2dp, void *userdata) {
if (!pa_hashmap_remove(u->will_need_revert_card_map, card))
return;
- /* Skip card if does not have active hsp profile */
- if (!pa_streq(card->active_profile->name, "headset_head_unit"))
+ /* Skip card if does not have active headset profile */
+ if (!pa_streq(card->active_profile->name, "headset_head_unit") && !pa_streq(card->active_profile->name, "handsfree_head_unit"))
return;
/* Skip card if already has active a2dp profile */
@@ -202,8 +202,8 @@ static void switch_profile(pa_card *card, bool revert_to_a2dp, void *userdata) {
if (!pa_streq(card->active_profile->name, "a2dp_sink"))
return;
- /* Skip card if already has active hsp profile */
- if (pa_streq(card->active_profile->name, "headset_head_unit"))
+ /* Skip card if already has active headset profile */
+ if (pa_streq(card->active_profile->name, "headset_head_unit") || pa_streq(card->active_profile->name, "handsfree_head_unit"))
return;
}
@@ -358,7 +358,9 @@ static pa_hook_result_t profile_available_hook_callback(pa_core *c, pa_card_prof
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, "handsfree_head_unit"))
return PA_HOOK_OK;
is_active_profile = card->active_profile == profile;
=====================================
src/modules/bluetooth/module-bluez5-device.c
=====================================
@@ -261,8 +261,10 @@ static int sco_process_render(struct userdata *u) {
int saved_errno;
pa_assert(u);
- pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||
- u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
+ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HSP_HS ||
+ u->profile == PA_BLUETOOTH_PROFILE_HSP_AG ||
+ u->profile == PA_BLUETOOTH_PROFILE_HFP_HF ||
+ u->profile == PA_BLUETOOTH_PROFILE_HFP_AG);
pa_assert(u->sink);
pa_sink_render_full(u->sink, u->write_block_size, &memchunk);
@@ -327,8 +329,10 @@ static int sco_process_push(struct userdata *u) {
pa_usec_t tstamp = 0;
pa_assert(u);
- pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||
- u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
+ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HSP_HS ||
+ u->profile == PA_BLUETOOTH_PROFILE_HSP_AG ||
+ 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 +771,10 @@ static void handle_sink_block_size_change(struct userdata *u) {
/* Run from I/O thread */
static void transport_config_mtu(struct userdata *u) {
- if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
+ if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS
+ || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG
+ || 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;
@@ -984,7 +991,7 @@ static void source_set_volume_cb(pa_source *s) {
pa_cvolume_set(&s->real_volume, u->decoder_sample_spec.channels, volume);
/* Set soft volume when in headset role */
- if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
+ if (u->profile == PA_BLUETOOTH_PROFILE_HFP_AG || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG)
pa_cvolume_set(&s->soft_volume, u->decoder_sample_spec.channels, volume);
/* If we are in the AG role, we send a command to the head set to change
@@ -1009,7 +1016,8 @@ static int add_source(struct userdata *u) {
if (u->a2dp_codec)
pa_proplist_sets(data.proplist, PA_PROP_BLUETOOTH_CODEC, u->a2dp_codec->name);
pa_source_new_data_set_sample_spec(&data, &u->decoder_sample_spec);
- if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
+ 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);
@@ -1017,10 +1025,12 @@ static int add_source(struct userdata *u) {
if (!u->transport_acquired)
switch (u->profile) {
case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
- case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
+ case PA_BLUETOOTH_PROFILE_HFP_AG:
+ case PA_BLUETOOTH_PROFILE_HSP_AG:
data.suspend_cause = PA_SUSPEND_USER;
break;
- case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
+ 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)
@@ -1045,7 +1055,10 @@ 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_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
+ if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS
+ || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG
+ || 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;
}
@@ -1170,7 +1183,7 @@ static void sink_set_volume_cb(pa_sink *s) {
pa_cvolume_set(&s->real_volume, u->encoder_sample_spec.channels, volume);
/* Set soft volume when in headset role */
- if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
+ if (u->profile == PA_BLUETOOTH_PROFILE_HFP_AG || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG)
pa_cvolume_set(&s->soft_volume, u->encoder_sample_spec.channels, volume);
/* If we are in the AG role, we send a command to the head set to change
@@ -1195,17 +1208,20 @@ static int add_sink(struct userdata *u) {
if (u->a2dp_codec)
pa_proplist_sets(data.proplist, PA_PROP_BLUETOOTH_CODEC, u->a2dp_codec->name);
pa_sink_new_data_set_sample_spec(&data, &u->encoder_sample_spec);
- if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
+ 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);
if (!u->transport_acquired)
switch (u->profile) {
- case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
+ case PA_BLUETOOTH_PROFILE_HFP_AG:
+ case PA_BLUETOOTH_PROFILE_HSP_AG:
data.suspend_cause = PA_SUSPEND_USER;
break;
- case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
+ 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)
@@ -1232,7 +1248,10 @@ 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_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
+ if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS
+ || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG
+ || 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;
}
@@ -1241,7 +1260,10 @@ static int add_sink(struct userdata *u) {
/* Run from main thread */
static int transport_config(struct userdata *u) {
- if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
+ if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS
+ || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG
+ || 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;
@@ -1292,7 +1314,7 @@ static int setup_transport(struct userdata *u) {
u->transport = t;
- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG)
transport_acquire(u, true); /* In case of error, the sink/sources will be created suspended */
else {
int transport_error;
@@ -1310,8 +1332,10 @@ static pa_direction_t get_profile_direction(pa_bluetooth_profile_t p) {
static const pa_direction_t profile_direction[] = {
[PA_BLUETOOTH_PROFILE_A2DP_SINK] = PA_DIRECTION_OUTPUT,
[PA_BLUETOOTH_PROFILE_A2DP_SOURCE] = PA_DIRECTION_INPUT,
- [PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT,
- [PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT,
+ [PA_BLUETOOTH_PROFILE_HSP_HS] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT,
+ [PA_BLUETOOTH_PROFILE_HSP_AG] = 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
};
@@ -1645,7 +1669,7 @@ static int start_thread(struct userdata *u) {
/* If we are in the headset role, the sink should not become default
* unless there is no other sound device available. */
- if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
+ if (u->profile == PA_BLUETOOTH_PROFILE_HFP_AG || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG)
u->sink->priority = 1500;
pa_sink_put(u->sink);
@@ -1661,7 +1685,7 @@ static int start_thread(struct userdata *u) {
/* If we are in the headset role or the device is an a2dp source,
* the source should not become default unless there is no other
* sound device available. */
- if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY || u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
+ if (u->profile == PA_BLUETOOTH_PROFILE_HFP_AG || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG || u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
u->source->priority = 1500;
pa_source_put(u->source);
@@ -1929,8 +1953,8 @@ static pa_card_profile *create_card_profile(struct userdata *u, pa_bluetooth_pro
p = PA_CARD_PROFILE_DATA(cp);
break;
- case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
- cp = pa_card_profile_new(name, _("Headset Head Unit (HSP/HFP)"), sizeof(pa_bluetooth_profile_t));
+ case PA_BLUETOOTH_PROFILE_HSP_HS:
+ 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;
@@ -1942,8 +1966,34 @@ static pa_card_profile *create_card_profile(struct userdata *u, pa_bluetooth_pro
p = PA_CARD_PROFILE_DATA(cp);
break;
- case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
- cp = pa_card_profile_new(name, _("Headset Audio Gateway (HSP/HFP)"), sizeof(pa_bluetooth_profile_t));
+ case PA_BLUETOOTH_PROFILE_HSP_AG:
+ cp = pa_card_profile_new(name, _("Headset Audio Gateway (HSP)"), sizeof(pa_bluetooth_profile_t));
+ cp->priority = 10;
+ 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, _("Handsfree Head Unit (HFP)"), 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_AG:
+ cp = pa_card_profile_new(name, _("Handsfree Audio Gateway (HFP)"), sizeof(pa_bluetooth_profile_t));
cp->priority = 10;
cp->n_sinks = 1;
cp->n_sources = 1;
@@ -2016,10 +2066,14 @@ static int uuid_to_profile(const char *uuid, pa_bluetooth_profile_t *_r) {
*_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))
- *_r = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT;
- else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_AG) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_AG))
- *_r = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY;
+ 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))
+ *_r = PA_BLUETOOTH_PROFILE_HSP_AG;
+ else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_AG))
+ *_r = PA_BLUETOOTH_PROFILE_HFP_AG;
else
return -PA_ERR_INVALID;
@@ -2235,7 +2289,7 @@ static pa_hook_result_t transport_speaker_gain_changed_cb(pa_bluetooth_discovery
volume++;
pa_cvolume_set(&v, u->encoder_sample_spec.channels, volume);
- if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
+ if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS || t->profile == PA_BLUETOOTH_PROFILE_HFP_HF)
pa_sink_volume_changed(u->sink, &v);
else
pa_sink_set_volume(u->sink, &v, true, true);
@@ -2263,7 +2317,7 @@ static pa_hook_result_t transport_microphone_gain_changed_cb(pa_bluetooth_discov
pa_cvolume_set(&v, u->decoder_sample_spec.channels, volume);
- if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
+ if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS || t->profile == PA_BLUETOOTH_PROFILE_HFP_HF)
pa_source_volume_changed(u->source, &v);
else
pa_source_set_volume(u->source, &v, true, true);
=====================================
src/modules/bluetooth/module-bluez5-discover.c
=====================================
@@ -38,12 +38,14 @@ PA_MODULE_USAGE(
"headset=ofono|native|auto"
"autodetect_mtu=<boolean>"
"output_rate_refresh_interval_ms=<interval between attempts to improve output rate in milliseconds>"
+ "enable_native_hfp_hf=<boolean, enable HFP support in native backend>"
);
static const char* const valid_modargs[] = {
"headset",
"autodetect_mtu",
"output_rate_refresh_interval_ms",
+ "enable_native_hfp_hf",
NULL
};
@@ -98,7 +100,7 @@ static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y,
}
#ifdef HAVE_BLUEZ_5_NATIVE_HEADSET
-const char *default_headset_backend = "auto";
+const char *default_headset_backend = "native";
#else
const char *default_headset_backend = "ofono";
#endif
@@ -110,6 +112,7 @@ int pa__init(pa_module *m) {
int headset_backend;
bool autodetect_mtu;
uint32_t output_rate_refresh_interval_ms;
+ bool enable_native_hfp_hf = true;
pa_assert(m);
@@ -130,9 +133,15 @@ int pa__init(pa_module *m) {
goto fail;
}
+ /* default value if no module parameter */
+ enable_native_hfp_hf = (headset_backend == HEADSET_BACKEND_NATIVE);
+
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;
}
@@ -149,7 +158,7 @@ int pa__init(pa_module *m) {
u->output_rate_refresh_interval_ms = output_rate_refresh_interval_ms;
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 =
View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/compare/bca4c22832d3acbe67d84fcf8b67150ec75ee913...45d896f8eb385a9719fdea3eacc2e42bc2281834
--
View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/compare/bca4c22832d3acbe67d84fcf8b67150ec75ee913...45d896f8eb385a9719fdea3eacc2e42bc2281834
You're receiving this email because of your account on gitlab.freedesktop.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/pulseaudio-commits/attachments/20210223/6f729f73/attachment-0001.htm>
More information about the pulseaudio-commits
mailing list