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>
This commit is contained in:
James Bottomley 2016-08-18 08:48:48 -07:00 committed by Igor Kovalenko
parent 709909a1fc
commit 66ed99a13d
6 changed files with 112 additions and 19 deletions

View file

@ -262,6 +262,7 @@ static int sco_process_render(struct userdata *u) {
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);
@ -328,6 +329,7 @@ static int sco_process_push(struct userdata *u) {
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_change(struct userdata *u) {
/* 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;
@ -1009,7 +1013,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_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);
@ -1021,6 +1026,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)
@ -1045,7 +1051,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;
}
@ -1195,7 +1203,8 @@ 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_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);
@ -1206,6 +1215,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)
@ -1232,7 +1242,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;
}
@ -1241,7 +1253,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;
@ -1311,6 +1325,7 @@ static pa_direction_t get_profile_direction(pa_bluetooth_profile_t p) {
[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
};
@ -1930,7 +1945,20 @@ static pa_card_profile *create_card_profile(struct userdata *u, pa_bluetooth_pro
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;
@ -2016,8 +2044,10 @@ 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))
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
@ -2036,6 +2066,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);
@ -2066,9 +2097,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;