diff --git a/0001-bluetooth-use-consistent-profile-names.patch b/0001-bluetooth-use-consistent-profile-names.patch new file mode 100644 index 0000000..836b0ca --- /dev/null +++ b/0001-bluetooth-use-consistent-profile-names.patch @@ -0,0 +1,397 @@ +From 709909a1fca31e9a00883e724c533832a4d4349d Mon Sep 17 00:00:00 2001 +From: James Bottomley +Date: Sat, 20 Aug 2016 10:39:30 -0700 +Subject: [PATCH 01/11] 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 + +--- + +v4: update for PA 11.0 + +Part-of: +--- + src/modules/bluetooth/backend-native.c | 28 +++++++------- + src/modules/bluetooth/backend-ofono.c | 4 +- + src/modules/bluetooth/bluez5-util.c | 10 ++--- + src/modules/bluetooth/bluez5-util.h | 4 +- + src/modules/bluetooth/module-bluez5-device.c | 54 +++++++++++++-------------- + 5 files changed, 50 insertions(+), 50 deletions(-) + +--- a/src/modules/bluetooth/backend-native.c ++++ b/src/modules/bluetooth/backend-native.c +@@ -449,7 +449,7 @@ static void set_speaker_gain(pa_bluetoot + /* 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) { ++ if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS) { + len = sprintf(buf, "\r\n+VGS=%d\r\n", gain); + pa_log_debug("RFCOMM >> +VGS=%d", gain); + } else { +@@ -476,7 +476,7 @@ static void set_microphone_gain(pa_bluet + /* 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) { ++ if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS) { + len = sprintf(buf, "\r\n+VGM=%d\r\n", gain); + pa_log_debug("RFCOMM >> +VGM=%d", gain); + } else { +@@ -509,9 +509,9 @@ static DBusMessage *profile_new_connecti + + 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_HFP_AG; + } else { + pa_log_error("Invalid handler"); + goto fail; +@@ -626,11 +626,11 @@ static void profile_init(pa_bluetooth_ba + 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_HFP_AG: + object_name = HSP_HS_PROFILE; + uuid = PA_BLUETOOTH_UUID_HSP_HS; + break; +@@ -647,10 +647,10 @@ static void profile_done(pa_bluetooth_ba + 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_HFP_AG: + dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HSP_HS_PROFILE); + break; + default: +@@ -665,9 +665,9 @@ void pa_bluetooth_native_backend_enable_ + return; + + if (enable_hs_role) +- profile_init(native_backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY); ++ profile_init(native_backend, PA_BLUETOOTH_PROFILE_HFP_AG); + else +- profile_done(native_backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY); ++ profile_done(native_backend, PA_BLUETOOTH_PROFILE_HFP_AG); + + native_backend->enable_hs_role = enable_hs_role; + } +@@ -693,8 +693,8 @@ pa_bluetooth_backend *pa_bluetooth_nativ + backend->enable_hs_role = enable_hs_role; + + 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_HFP_AG); ++ profile_init(backend, PA_BLUETOOTH_PROFILE_HSP_HS); + + return backend; + } +@@ -705,8 +705,8 @@ void pa_bluetooth_native_backend_free(pa + 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); ++ profile_done(backend, PA_BLUETOOTH_PROFILE_HFP_AG); ++ profile_done(backend, PA_BLUETOOTH_PROFILE_HSP_HS); + + pa_dbus_connection_unref(backend->connection); + +--- a/src/modules/bluetooth/backend-ofono.c ++++ b/src/modules/bluetooth/backend-ofono.c +@@ -303,7 +303,7 @@ static void hf_audio_agent_card_found(pa + 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 + 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_HSP_HS; + } + + pa_log_debug("%s: %s", key, value); +--- a/src/modules/bluetooth/bluez5-util.c ++++ b/src/modules/bluetooth/bluez5-util.c +@@ -176,11 +176,11 @@ static bool device_supports_profile(pa_b + 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: ++ 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); +- case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: ++ 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); + case PA_BLUETOOTH_PROFILE_OFF: +@@ -1021,7 +1021,7 @@ void pa_bluetooth_discovery_set_ofono_ru + 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)) { + DBusMessage *m; + + pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, d->path, "org.bluez.Device1", "Disconnect")); +@@ -1265,9 +1265,9 @@ const char *pa_bluetooth_profile_to_stri + 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_HFP_AG: + return "headset_audio_gateway"; + case PA_BLUETOOTH_PROFILE_OFF: + return "off"; +--- a/src/modules/bluetooth/bluez5-util.h ++++ b/src/modules/bluetooth/bluez5-util.h +@@ -57,8 +57,8 @@ 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_HFP_AG, + PA_BLUETOOTH_PROFILE_OFF + } pa_bluetooth_profile_t; + #define PA_BLUETOOTH_PROFILE_COUNT PA_BLUETOOTH_PROFILE_OFF +--- a/src/modules/bluetooth/module-bluez5-device.c ++++ b/src/modules/bluetooth/module-bluez5-device.c +@@ -253,8 +253,8 @@ static int sco_process_render(struct use + 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_HFP_AG); + pa_assert(u->sink); + + pa_sink_render_full(u->sink, u->write_block_size, &memchunk); +@@ -323,8 +323,8 @@ static int sco_process_push(struct userd + 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_HFP_AG); + pa_assert(u->source); + pa_assert(u->read_smoother); + +@@ -767,7 +767,7 @@ 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_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) { ++ if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS || 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 +984,7 @@ static void source_set_volume_cb(pa_sour + 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) + 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 +@@ -1007,7 +1007,7 @@ 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_HEADSET_HEAD_UNIT) ++ if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS) + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); + + connect_ports(u, &data, PA_DIRECTION_INPUT); +@@ -1015,10 +1015,10 @@ 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: + data.suspend_cause = PA_SUSPEND_USER; + break; +- case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: ++ case PA_BLUETOOTH_PROFILE_HSP_HS: + /* 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 +1043,7 @@ 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_HFP_AG) { + pa_source_set_set_volume_callback(u->source, source_set_volume_cb); + u->source->n_volume_steps = 16; + } +@@ -1168,7 +1168,7 @@ static void sink_set_volume_cb(pa_sink * + 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) + 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 +@@ -1191,17 +1191,17 @@ 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_HEADSET_HEAD_UNIT) ++ if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS) + 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: + data.suspend_cause = PA_SUSPEND_USER; + break; +- case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: ++ case PA_BLUETOOTH_PROFILE_HSP_HS: + /* 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 +1228,7 @@ 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_HFP_AG) { + pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); + u->sink->n_volume_steps = 16; + } +@@ -1237,7 +1237,7 @@ 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_HFP_AG) { + u->encoder_sample_spec.format = PA_SAMPLE_S16LE; + u->encoder_sample_spec.channels = 1; + u->encoder_sample_spec.rate = 8000; +@@ -1288,7 +1288,7 @@ static int setup_transport(struct userda + + 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) + transport_acquire(u, true); /* In case of error, the sink/sources will be created suspended */ + else { + int transport_error; +@@ -1306,8 +1306,8 @@ static pa_direction_t get_profile_direct + 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_HFP_AG] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT, + [PA_BLUETOOTH_PROFILE_OFF] = 0 + }; + +@@ -1621,7 +1621,7 @@ static int start_thread(struct userdata + + /* 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->sink->priority = 1500; + + pa_sink_put(u->sink); +@@ -1637,7 +1637,7 @@ static int start_thread(struct userdata + /* 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_A2DP_SOURCE) + u->source->priority = 1500; + + pa_source_put(u->source); +@@ -1898,7 +1898,7 @@ static pa_card_profile *create_card_prof + p = PA_CARD_PROFILE_DATA(cp); + break; + +- case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: ++ case PA_BLUETOOTH_PROFILE_HSP_HS: + cp = pa_card_profile_new(name, _("Headset Head Unit (HSP/HFP)"), sizeof(pa_bluetooth_profile_t)); + cp->priority = 30; + cp->n_sinks = 1; +@@ -1911,7 +1911,7 @@ static pa_card_profile *create_card_prof + p = PA_CARD_PROFILE_DATA(cp); + break; + +- case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: ++ case PA_BLUETOOTH_PROFILE_HFP_AG: + cp = pa_card_profile_new(name, _("Headset Audio Gateway (HSP/HFP)"), sizeof(pa_bluetooth_profile_t)); + cp->priority = 10; + cp->n_sinks = 1; +@@ -1986,9 +1986,9 @@ static int uuid_to_profile(const char *u + 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; ++ *_r = PA_BLUETOOTH_PROFILE_HSP_HS; + 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; ++ *_r = PA_BLUETOOTH_PROFILE_HFP_AG; + else + return -PA_ERR_INVALID; + +@@ -2199,7 +2199,7 @@ static pa_hook_result_t transport_speake + 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) + pa_sink_volume_changed(u->sink, &v); + else + pa_sink_set_volume(u->sink, &v, true, true); +@@ -2227,7 +2227,7 @@ static pa_hook_result_t transport_microp + + 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) + pa_source_volume_changed(u->source, &v); + else + pa_source_set_volume(u->source, &v, true, true); diff --git a/0002-bluetooth-separate-HSP-and-HFP.patch b/0002-bluetooth-separate-HSP-and-HFP.patch new file mode 100644 index 0000000..8913bcc --- /dev/null +++ b/0002-bluetooth-separate-HSP-and-HFP.patch @@ -0,0 +1,490 @@ +From 66ed99a13da289f25bdb8381d30cfb5f82f9cab6 Mon Sep 17 00:00:00 2001 +From: James Bottomley +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: + +--- +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: +--- + 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 = diff --git a/0003-bluetooth-add-correct-HFP-rfcomm-negotiation.patch b/0003-bluetooth-add-correct-HFP-rfcomm-negotiation.patch new file mode 100644 index 0000000..bb2fe54 --- /dev/null +++ b/0003-bluetooth-add-correct-HFP-rfcomm-negotiation.patch @@ -0,0 +1,228 @@ +From 485a64642e8da895f1b71ccc3d8aee78f5645639 Mon Sep 17 00:00:00 2001 +From: James Bottomley +Date: Sat, 20 Aug 2016 10:56:14 -0700 +Subject: [PATCH 03/11] 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 + +--- + +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: +--- + src/modules/bluetooth/backend-native.c | 124 +++++++++++++++++++++++++++++++-- + src/modules/bluetooth/bluez5-util.c | 5 + + src/modules/bluetooth/bluez5-util.h | 2 + 3 files changed, 125 insertions(+), 6 deletions(-) + +--- a/src/modules/bluetooth/backend-native.c ++++ b/src/modules/bluetooth/backend-native.c +@@ -61,6 +61,43 @@ struct transport_data { + #define BLUEZ_PROFILE_MANAGER_INTERFACE BLUEZ_SERVICE ".ProfileManager1" + #define BLUEZ_PROFILE_INTERFACE BLUEZ_SERVICE ".Profile1" + ++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" +@@ -109,6 +146,27 @@ static pa_dbus_pending* send_and_add_to_ + return p; + } + ++static void rfcomm_write(int fd, const char *str) ++{ ++ size_t len; ++ char buf[512]; ++ ++ pa_log_debug("RFCOMM >> %s", str); ++ sprintf(buf, "\r\n%s\r\n", str); ++ len = write(fd, buf, strlen(buf)); ++ ++ if (len != strlen(buf)) ++ pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno)); ++} ++ ++static void hfp_send_features(int fd) ++{ ++ char buf[512]; ++ ++ sprintf(buf, "+BRSF: %d", hfp_features); ++ rfcomm_write(fd, buf); ++} ++ + static int sco_do_connect(pa_bluetooth_transport *t) { + pa_bluetooth_device *d = t->device; + struct sockaddr_sco addr; +@@ -352,6 +410,61 @@ static void register_profile(pa_bluetoot + 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); ++ hfp_send_features(fd); ++ 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, "\r\nOK\r\n"); ++ 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; + +@@ -398,6 +511,8 @@ static void rfcomm_io_callback(pa_mainlo + 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; + } +@@ -540,7 +655,9 @@ static DBusMessage *profile_new_connecti + 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; +@@ -558,9 +675,8 @@ static DBusMessage *profile_new_connecti + + 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)); + +--- a/src/modules/bluetooth/bluez5-util.c ++++ b/src/modules/bluetooth/bluez5-util.c +@@ -152,7 +152,10 @@ pa_bluetooth_transport *pa_bluetooth_tra + + 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; +--- a/src/modules/bluetooth/bluez5-util.h ++++ b/src/modules/bluetooth/bluez5-util.h +@@ -84,7 +84,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; diff --git a/0004-bluetooth-make-native-the-default-backend.patch b/0004-bluetooth-make-native-the-default-backend.patch new file mode 100644 index 0000000..88b4a99 --- /dev/null +++ b/0004-bluetooth-make-native-the-default-backend.patch @@ -0,0 +1,43 @@ +From cb193e19ee9dc04b54ee62dfba2b194ec275ad5c Mon Sep 17 00:00:00 2001 +From: James Bottomley +Date: Thu, 21 Sep 2017 11:49:45 -0700 +Subject: [PATCH 04/11] 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 +Part-of: +--- + src/modules/bluetooth/module-bluez5-discover.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/src/modules/bluetooth/module-bluez5-discover.c ++++ b/src/modules/bluetooth/module-bluez5-discover.c +@@ -92,7 +92,7 @@ static pa_hook_result_t device_connectio + } + + #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 +@@ -124,6 +124,9 @@ 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"); diff --git a/0005-bluetooth-enable-module-bluez5-discover-argument-ena.patch b/0005-bluetooth-enable-module-bluez5-discover-argument-ena.patch new file mode 100644 index 0000000..cdd3f89 --- /dev/null +++ b/0005-bluetooth-enable-module-bluez5-discover-argument-ena.patch @@ -0,0 +1,27 @@ +From 8491477b3e02c17881abd65818784e268a8e75d6 Mon Sep 17 00:00:00 2001 +From: "Igor V. Kovalenko" +Date: Thu, 28 Jan 2021 15:37:36 +0300 +Subject: [PATCH 05/11] bluetooth: enable module-bluez5-discover argument + enable_native_hfp_hf + +Part-of: +--- + src/modules/bluetooth/module-bluez5-discover.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/src/modules/bluetooth/module-bluez5-discover.c ++++ b/src/modules/bluetooth/module-bluez5-discover.c +@@ -37,11 +37,13 @@ PA_MODULE_LOAD_ONCE(true); + PA_MODULE_USAGE( + "headset=ofono|native|auto" + "autodetect_mtu=" ++ "enable_native_hfp_hf=" + ); + + static const char* const valid_modargs[] = { + "headset", + "autodetect_mtu", ++ "enable_native_hfp_hf", + NULL + }; + diff --git a/0006-bluetooth-fix-headset-auto-ofono-handover.patch b/0006-bluetooth-fix-headset-auto-ofono-handover.patch new file mode 100644 index 0000000..fa8067d --- /dev/null +++ b/0006-bluetooth-fix-headset-auto-ofono-handover.patch @@ -0,0 +1,131 @@ +From 70171158eeda4e3ae44a6a2dd22b25cc6932dd04 Mon Sep 17 00:00:00 2001 +From: "Igor V. Kovalenko" +Date: Thu, 28 Jan 2021 09:08:53 +0300 +Subject: [PATCH 06/11] 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: +--- + src/modules/bluetooth/backend-native.c | 31 +++++++++++++++++---------- + src/modules/bluetooth/bluez5-util.c | 8 +++--- + src/modules/bluetooth/module-bluez5-device.c | 2 - + 3 files changed, 25 insertions(+), 16 deletions(-) + +--- a/src/modules/bluetooth/backend-native.c ++++ b/src/modules/bluetooth/backend-native.c +@@ -41,6 +41,7 @@ struct pa_bluetooth_backend { + pa_dbus_connection *connection; + pa_bluetooth_discovery *discovery; + bool enable_hs_role; ++ bool enable_hfp_hf; + + PA_LLIST_HEAD(pa_dbus_pending, pending); + }; +@@ -786,15 +787,24 @@ static void profile_done(pa_bluetooth_ba + } + } + ++static void native_backend_apply_profile_registration_change(pa_bluetooth_backend *native_backend, bool enable_hs_role) { ++ if (enable_hs_role) { ++ profile_init(native_backend, PA_BLUETOOTH_PROFILE_HFP_AG); ++ if (native_backend->enable_hfp_hf) ++ profile_init(native_backend, PA_BLUETOOTH_PROFILE_HFP_HF); ++ } else { ++ profile_done(native_backend, PA_BLUETOOTH_PROFILE_HFP_AG); ++ if (native_backend->enable_hfp_hf) ++ profile_done(native_backend, PA_BLUETOOTH_PROFILE_HFP_HF); ++ } ++} ++ + void pa_bluetooth_native_backend_enable_hs_role(pa_bluetooth_backend *native_backend, bool enable_hs_role) { + + if (enable_hs_role == native_backend->enable_hs_role) + return; + +- if (enable_hs_role) +- profile_init(native_backend, PA_BLUETOOTH_PROFILE_HFP_AG); +- else +- profile_done(native_backend, PA_BLUETOOTH_PROFILE_HFP_AG); ++ native_backend_apply_profile_registration_change(native_backend, enable_hs_role); + + native_backend->enable_hs_role = enable_hs_role; + } +@@ -818,12 +828,12 @@ pa_bluetooth_backend *pa_bluetooth_nativ + + backend->discovery = y; + backend->enable_hs_role = enable_hs_role; ++ backend->enable_hfp_hf = pa_bluetooth_discovery_get_enable_native_hfp_hf(y); ++ ++ if (backend->enable_hs_role) ++ native_backend_apply_profile_registration_change(backend, true); + +- 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; + } +@@ -834,10 +844,9 @@ void pa_bluetooth_native_backend_free(pa + pa_dbus_free_pending_list(&backend->pending); + + if (backend->enable_hs_role) +- profile_done(backend, PA_BLUETOOTH_PROFILE_HFP_AG); ++ native_backend_apply_profile_registration_change(backend, false); ++ + 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 +@@ -1040,14 +1040,16 @@ void pa_bluetooth_discovery_set_ofono_ru + 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_hs_role(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_HFP_AG)) { ++ 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, "org.bluez.Device1", "Disconnect")); +@@ -1057,8 +1059,6 @@ void pa_bluetooth_discovery_set_ofono_ru + } + } + } +- +- pa_bluetooth_native_backend_enable_hs_role(y->native_backend, !is_running); + } + + static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata) { +--- a/src/modules/bluetooth/module-bluez5-device.c ++++ b/src/modules/bluetooth/module-bluez5-device.c +@@ -1914,7 +1914,7 @@ static pa_card_profile *create_card_prof + break; + + case PA_BLUETOOTH_PROFILE_HSP_HS: +- cp = pa_card_profile_new(name, _("Headset Head Unit (HSP)"), sizeof(pa_bluetooth_profile_t)); ++ cp = pa_card_profile_new(name, _("Headset Head Unit (HSP/HFP)"), sizeof(pa_bluetooth_profile_t)); + cp->priority = 30; + cp->n_sinks = 1; + cp->n_sources = 1; diff --git a/0007-bluetooth-prefer-headset-HFP-HF-connection-with-nati.patch b/0007-bluetooth-prefer-headset-HFP-HF-connection-with-nati.patch new file mode 100644 index 0000000..0943022 --- /dev/null +++ b/0007-bluetooth-prefer-headset-HFP-HF-connection-with-nati.patch @@ -0,0 +1,56 @@ +From 815dd2d6278f17591fa04966b0e4c5a8b102cf0b Mon Sep 17 00:00:00 2001 +From: "Igor V. Kovalenko" +Date: Fri, 29 Jan 2021 09:28:36 +0300 +Subject: [PATCH 07/11] 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: +--- + src/modules/bluetooth/backend-native.c | 10 ++++++++++ + src/modules/bluetooth/module-bluez5-device.c | 10 ---------- + 2 files changed, 10 insertions(+), 10 deletions(-) + +--- a/src/modules/bluetooth/backend-native.c ++++ b/src/modules/bluetooth/backend-native.c +@@ -645,6 +645,16 @@ static DBusMessage *profile_new_connecti + goto fail; + } + ++ if (pa_bluetooth_discovery_get_enable_native_hfp_hf(b->discovery)) { ++ 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); +--- a/src/modules/bluetooth/module-bluez5-device.c ++++ b/src/modules/bluetooth/module-bluez5-device.c +@@ -2072,16 +2072,6 @@ static int add_card(struct userdata *u) + 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; + diff --git a/0008-bluetooth-complete-bluetooth-profile-separation.patch b/0008-bluetooth-complete-bluetooth-profile-separation.patch new file mode 100644 index 0000000..ac876ef --- /dev/null +++ b/0008-bluetooth-complete-bluetooth-profile-separation.patch @@ -0,0 +1,398 @@ +From 698fb3bc26a679063c081cc3089cd9d515d34a4a Mon Sep 17 00:00:00 2001 +From: "Igor V. Kovalenko" +Date: Fri, 29 Jan 2021 21:32:09 +0300 +Subject: [PATCH 08/11] 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: +--- + src/modules/bluetooth/backend-native.c | 14 +++--- + src/modules/bluetooth/backend-ofono.c | 2 + src/modules/bluetooth/bluez5-util.c | 11 +++- + src/modules/bluetooth/bluez5-util.h | 1 + src/modules/bluetooth/module-bluetooth-policy.c | 18 ++++---- + src/modules/bluetooth/module-bluez5-device.c | 54 +++++++++++++++++------- + 6 files changed, 64 insertions(+), 36 deletions(-) + +--- a/src/modules/bluetooth/backend-native.c ++++ b/src/modules/bluetooth/backend-native.c +@@ -566,7 +566,7 @@ static void set_speaker_gain(pa_bluetoot + /* 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_HSP_HS) { ++ if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS || t->profile == PA_BLUETOOTH_PROFILE_HFP_HF) { + len = sprintf(buf, "\r\n+VGS=%d\r\n", gain); + pa_log_debug("RFCOMM >> +VGS=%d", gain); + } else { +@@ -593,7 +593,7 @@ static void set_microphone_gain(pa_bluet + /* 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_HSP_HS) { ++ if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS || t->profile == PA_BLUETOOTH_PROFILE_HFP_HF) { + len = sprintf(buf, "\r\n+VGM=%d\r\n", gain); + pa_log_debug("RFCOMM >> +VGM=%d", gain); + } else { +@@ -628,7 +628,7 @@ static DBusMessage *profile_new_connecti + if (pa_streq(handler, HSP_AG_PROFILE)) { + p = PA_BLUETOOTH_PROFILE_HSP_HS; + } else if (pa_streq(handler, HSP_HS_PROFILE)) { +- p = PA_BLUETOOTH_PROFILE_HFP_AG; ++ p = PA_BLUETOOTH_PROFILE_HSP_AG; + } else if (pa_streq(handler, HFP_AG_PROFILE)) { + p = PA_BLUETOOTH_PROFILE_HFP_HF; + } else { +@@ -761,7 +761,7 @@ static void profile_init(pa_bluetooth_ba + object_name = HSP_AG_PROFILE; + uuid = PA_BLUETOOTH_UUID_HSP_AG; + break; +- case PA_BLUETOOTH_PROFILE_HFP_AG: ++ case PA_BLUETOOTH_PROFILE_HSP_AG: + object_name = HSP_HS_PROFILE; + uuid = PA_BLUETOOTH_UUID_HSP_HS; + break; +@@ -785,7 +785,7 @@ static void profile_done(pa_bluetooth_ba + 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_HFP_AG: ++ 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: +@@ -799,11 +799,11 @@ static void profile_done(pa_bluetooth_ba + + static void native_backend_apply_profile_registration_change(pa_bluetooth_backend *native_backend, bool enable_hs_role) { + if (enable_hs_role) { +- profile_init(native_backend, PA_BLUETOOTH_PROFILE_HFP_AG); ++ 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_HFP_AG); ++ profile_done(native_backend, PA_BLUETOOTH_PROFILE_HSP_AG); + if (native_backend->enable_hfp_hf) + profile_done(native_backend, PA_BLUETOOTH_PROFILE_HFP_HF); + } +--- a/src/modules/bluetooth/backend-ofono.c ++++ b/src/modules/bluetooth/backend-ofono.c +@@ -337,7 +337,7 @@ static void hf_audio_agent_card_found(pa + card->local_address = pa_xstrdup(value); + } else if (pa_streq(key, "Type")) { + if (pa_streq(value, "gateway")) +- p = PA_BLUETOOTH_PROFILE_HSP_HS; ++ p = PA_BLUETOOTH_PROFILE_HFP_HF; + } + + pa_log_debug("%s: %s", key, value); +--- a/src/modules/bluetooth/bluez5-util.c ++++ b/src/modules/bluetooth/bluez5-util.c +@@ -196,11 +196,12 @@ static bool device_supports_profile(pa_b + 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_HSP_AG) +- || !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_AG); ++ return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_AG); + case PA_BLUETOOTH_PROFILE_OFF: + pa_assert_not_reached(); + } +@@ -1293,10 +1294,12 @@ const char *pa_bluetooth_profile_to_stri + return "a2dp_source"; + case PA_BLUETOOTH_PROFILE_HSP_HS: + return "headset_head_unit"; ++ case PA_BLUETOOTH_PROFILE_HSP_AG: ++ return "headset_audio_gateway"; + case PA_BLUETOOTH_PROFILE_HFP_HF: +- return "headset_handsfree"; ++ return "handsfree_head_unit"; + case PA_BLUETOOTH_PROFILE_HFP_AG: +- return "headset_audio_gateway"; ++ return "handsfree_audio_gateway"; + case PA_BLUETOOTH_PROFILE_OFF: + return "off"; + } +--- 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_HSP_AG, + PA_BLUETOOTH_PROFILE_HFP_HF, + PA_BLUETOOTH_PROFILE_HFP_AG, + PA_BLUETOOTH_PROFILE_OFF +--- a/src/modules/bluetooth/module-bluetooth-policy.c ++++ b/src/modules/bluetooth/module-bluetooth-policy.c +@@ -38,7 +38,7 @@ PA_MODULE_LOAD_ONCE(true); + PA_MODULE_USAGE( + "auto_switch= " + "a2dp_source= " +- "ag= "); ++ "ag= "); + + static const char* const valid_modargs[] = { + "auto_switch", +@@ -86,7 +86,7 @@ static pa_hook_result_t source_put_hook_ + + 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_ca + 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 user + if (!pa_streq(profile->name, "a2dp_sink")) + continue; + } else { +- if (!pa_streq(profile->name, "headset_head_unit") && !pa_streq(profile->name, "headset_handsfree")) ++ 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 + 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") && !pa_streq(card->active_profile->name, "headset_handsfree")) ++ /* 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 + 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") || pa_streq(card->active_profile->name, "headset_handsfree")) ++ /* 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; + } + +@@ -360,7 +360,7 @@ static pa_hook_result_t profile_availabl + /* Do not automatically switch profiles for headsets, just in case */ + if (pa_streq(profile->name, "a2dp_sink") || + pa_streq(profile->name, "headset_head_unit") || +- pa_streq(profile->name, "headset_handsfree")) ++ pa_streq(profile->name, "handsfree_head_unit")) + 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,8 +254,9 @@ 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); ++ 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); +@@ -325,8 +326,9 @@ 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); ++ 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); + +@@ -770,6 +772,7 @@ 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_HSP_AG + || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF + || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG) { + u->read_block_size = u->read_link_mtu; +@@ -988,7 +991,7 @@ static void source_set_volume_cb(pa_sour + 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_HFP_AG) ++ 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 +@@ -1021,6 +1024,7 @@ static int add_source(struct userdata *u + switch (u->profile) { + case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: + case PA_BLUETOOTH_PROFILE_HFP_AG: ++ case PA_BLUETOOTH_PROFILE_HSP_AG: + data.suspend_cause = PA_SUSPEND_USER; + break; + case PA_BLUETOOTH_PROFILE_HSP_HS: +@@ -1050,6 +1054,7 @@ static int add_source(struct userdata *u + 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_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); +@@ -1176,7 +1181,7 @@ static void sink_set_volume_cb(pa_sink * + 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_HFP_AG) ++ 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 +@@ -1208,6 +1213,7 @@ static int add_sink(struct userdata *u) + if (!u->transport_acquired) + switch (u->profile) { + case PA_BLUETOOTH_PROFILE_HFP_AG: ++ case PA_BLUETOOTH_PROFILE_HSP_AG: + data.suspend_cause = PA_SUSPEND_USER; + break; + case PA_BLUETOOTH_PROFILE_HSP_HS: +@@ -1239,6 +1245,7 @@ static int add_sink(struct userdata *u) + 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_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); +@@ -1250,6 +1257,7 @@ 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_HSP_AG + || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF + || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG) { + u->encoder_sample_spec.format = PA_SAMPLE_S16LE; +@@ -1302,7 +1310,7 @@ static int setup_transport(struct userda + + u->transport = t; + +- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG) ++ 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; +@@ -1321,6 +1329,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_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 +@@ -1636,7 +1645,7 @@ static int start_thread(struct userdata + + /* 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_HFP_AG) ++ if (u->profile == PA_BLUETOOTH_PROFILE_HFP_AG || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG) + u->sink->priority = 1500; + + pa_sink_put(u->sink); +@@ -1652,7 +1661,7 @@ static int start_thread(struct userdata + /* 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_HFP_AG || 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); +@@ -1914,7 +1923,7 @@ 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; +@@ -1926,8 +1935,21 @@ static pa_card_profile *create_card_prof + p = PA_CARD_PROFILE_DATA(cp); + break; + ++ 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, _("Headset Handsfree (HFP)"), sizeof(pa_bluetooth_profile_t)); ++ 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; +@@ -1940,7 +1962,7 @@ static pa_card_profile *create_card_prof + break; + + case PA_BLUETOOTH_PROFILE_HFP_AG: +- cp = pa_card_profile_new(name, _("Headset Audio Gateway (HSP/HFP)"), sizeof(pa_bluetooth_profile_t)); ++ 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; +@@ -2017,7 +2039,9 @@ static int uuid_to_profile(const char *u + *_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)) ++ 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; +@@ -2233,7 +2257,7 @@ static pa_hook_result_t transport_speake + volume++; + + pa_cvolume_set(&v, u->encoder_sample_spec.channels, volume); +- if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS) ++ 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); +@@ -2261,7 +2285,7 @@ static pa_hook_result_t transport_microp + + pa_cvolume_set(&v, u->decoder_sample_spec.channels, volume); + +- if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS) ++ 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); diff --git a/0009-bluetooth-use-device-flag-to-prevent-assertion-failu.patch b/0009-bluetooth-use-device-flag-to-prevent-assertion-failu.patch new file mode 100644 index 0000000..2189207 --- /dev/null +++ b/0009-bluetooth-use-device-flag-to-prevent-assertion-failu.patch @@ -0,0 +1,79 @@ +From c884ae8c7453c038d7359758e81dbaf3499a2368 Mon Sep 17 00:00:00 2001 +From: "Igor V. Kovalenko" +Date: Mon, 1 Feb 2021 20:51:18 +0300 +Subject: [PATCH 09/11] bluetooth: use device flag to prevent assertion failure + during shutdown + +Part-of: +--- + src/modules/bluetooth/backend-native.c | 2 +- + src/modules/bluetooth/bluez5-util.c | 7 +++---- + src/modules/bluetooth/bluez5-util.h | 1 + + src/modules/bluetooth/module-bluez5-device.c | 4 ---- + 4 files changed, 5 insertions(+), 9 deletions(-) + +--- a/src/modules/bluetooth/backend-native.c ++++ b/src/modules/bluetooth/backend-native.c +@@ -645,7 +645,7 @@ static DBusMessage *profile_new_connecti + goto fail; + } + +- if (pa_bluetooth_discovery_get_enable_native_hfp_hf(b->discovery)) { ++ 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. +--- a/src/modules/bluetooth/bluez5-util.c ++++ b/src/modules/bluetooth/bluez5-util.c +@@ -175,11 +175,9 @@ 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; ++ bool show_hfp, show_hsp; + +- enable_native_hfp_hf = pa_bluetooth_discovery_get_enable_native_hfp_hf(device->discovery); +- +- if (enable_native_hfp_hf) { ++ if (device->enable_hfp_hf) { + show_hfp = pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_HF); + show_hsp = !show_hfp; + } else { +@@ -537,6 +535,7 @@ static pa_bluetooth_device* device_creat + + 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); + +--- a/src/modules/bluetooth/bluez5-util.h ++++ b/src/modules/bluetooth/bluez5-util.h +@@ -107,6 +107,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; +--- a/src/modules/bluetooth/module-bluez5-device.c ++++ b/src/modules/bluetooth/module-bluez5-device.c +@@ -2059,7 +2059,6 @@ 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); +@@ -2090,9 +2089,6 @@ 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; + diff --git a/0010-bluetooth-rename-enable_hs_role-to-enable_shared_pro.patch b/0010-bluetooth-rename-enable_hs_role-to-enable_shared_pro.patch new file mode 100644 index 0000000..2da9eae --- /dev/null +++ b/0010-bluetooth-rename-enable_hs_role-to-enable_shared_pro.patch @@ -0,0 +1,113 @@ +From 0a36c1544d7e995be4528c4dfda43d7de1bbacc5 Mon Sep 17 00:00:00 2001 +From: "Igor V. Kovalenko" +Date: Tue, 16 Feb 2021 08:46:19 +0300 +Subject: [PATCH 10/11] bluetooth: rename enable_hs_role to + enable_shared_profiles + +Part-of: +--- + src/modules/bluetooth/backend-native.c | 22 +++++++++++----------- + src/modules/bluetooth/bluez5-util.c | 2 +- + src/modules/bluetooth/bluez5-util.h | 8 ++++---- + 3 files changed, 16 insertions(+), 16 deletions(-) + +--- a/src/modules/bluetooth/backend-native.c ++++ b/src/modules/bluetooth/backend-native.c +@@ -40,7 +40,7 @@ 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); +@@ -797,8 +797,8 @@ static void profile_done(pa_bluetooth_ba + } + } + +-static void native_backend_apply_profile_registration_change(pa_bluetooth_backend *native_backend, bool enable_hs_role) { +- if (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); +@@ -809,17 +809,17 @@ static void native_backend_apply_profile + } + } + +-void pa_bluetooth_native_backend_enable_hs_role(pa_bluetooth_backend *native_backend, bool enable_hs_role) { ++void pa_bluetooth_native_backend_enable_shared_profiles(pa_bluetooth_backend *native_backend, bool enable) { + +- if (enable_hs_role == native_backend->enable_hs_role) ++ if (enable == native_backend->enable_shared_profiles) + return; + +- native_backend_apply_profile_registration_change(native_backend, enable_hs_role); ++ 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; + +@@ -837,10 +837,10 @@ pa_bluetooth_backend *pa_bluetooth_nativ + } + + 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_hs_role) ++ if (backend->enable_shared_profiles) + native_backend_apply_profile_registration_change(backend, true); + + profile_init(backend, PA_BLUETOOTH_PROFILE_HSP_HS); +@@ -853,7 +853,7 @@ void pa_bluetooth_native_backend_free(pa + + pa_dbus_free_pending_list(&backend->pending); + +- if (backend->enable_hs_role) ++ if (backend->enable_shared_profiles) + native_backend_apply_profile_registration_change(backend, false); + + profile_done(backend, PA_BLUETOOTH_PROFILE_HSP_HS); +--- a/src/modules/bluetooth/bluez5-util.c ++++ b/src/modules/bluetooth/bluez5-util.c +@@ -1040,7 +1040,7 @@ void pa_bluetooth_discovery_set_ofono_ru + if (y->headset_backend != HEADSET_BACKEND_AUTO) + return; + +- pa_bluetooth_native_backend_enable_hs_role(y->native_backend, !is_running); ++ 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 */ +--- a/src/modules/bluetooth/bluez5-util.h ++++ b/src/modules/bluetooth/bluez5-util.h +@@ -145,15 +145,15 @@ static inline void pa_bluetooth_ofono_ba + #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, diff --git a/0011-bluetooth-clean-up-rfcomm_write-usage.patch b/0011-bluetooth-clean-up-rfcomm_write-usage.patch new file mode 100644 index 0000000..933b7f8 --- /dev/null +++ b/0011-bluetooth-clean-up-rfcomm_write-usage.patch @@ -0,0 +1,143 @@ +From 45d896f8eb385a9719fdea3eacc2e42bc2281834 Mon Sep 17 00:00:00 2001 +From: "Igor V. Kovalenko" +Date: Thu, 18 Feb 2021 21:08:21 +0300 +Subject: [PATCH 11/11] bluetooth: clean up rfcomm_write usage + +Part-of: +--- + src/modules/bluetooth/backend-native.c | 67 ++++++++++----------------------- + 1 file changed, 21 insertions(+), 46 deletions(-) + +--- a/src/modules/bluetooth/backend-native.c ++++ b/src/modules/bluetooth/backend-native.c +@@ -147,25 +147,26 @@ static pa_dbus_pending* send_and_add_to_ + return p; + } + +-static void rfcomm_write(int fd, const char *str) ++static void rfcomm_write(int fd, const char *fmt, ...) + { ++ va_list ap; + size_t len; + char buf[512]; ++ char command[512]; + +- pa_log_debug("RFCOMM >> %s", str); +- sprintf(buf, "\r\n%s\r\n", str); +- len = write(fd, buf, strlen(buf)); ++ va_start(ap, fmt); ++ pa_vsnprintf(command, sizeof(command), fmt, ap); ++ va_end(ap); + +- if (len != strlen(buf)) +- pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno)); +-} ++ pa_log_debug("RFCOMM >> %s", command); + +-static void hfp_send_features(int fd) +-{ +- char buf[512]; ++ 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 */ + +- sprintf(buf, "+BRSF: %d", hfp_features); +- rfcomm_write(fd, buf); ++ 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) { +@@ -427,7 +428,7 @@ static bool hfp_rfcomm_handle(int fd, pa + if (c->state == 0 && sscanf(buf, "AT+BRSF=%d", &val) == 1) { + c->capabilities = val; + pa_log_info("HFP capabilities returns 0x%x", val); +- hfp_send_features(fd); ++ rfcomm_write(fd, "+BRSF: %d", hfp_features); + c->state = 1; + return true; + } else if (c->state == 1 && pa_startswith(buf, "AT+CIND=?")) { +@@ -444,7 +445,7 @@ static bool hfp_rfcomm_handle(int fd, pa + c->state = 3; + return true; + } else if ((c->state == 2 || c->state == 3) && pa_startswith(buf, "AT+CMER=")) { +- rfcomm_write(fd, "\r\nOK\r\n"); ++ rfcomm_write(fd, "OK"); + c->state = 4; + transport_put(t); + return false; +@@ -518,16 +519,8 @@ static void rfcomm_io_callback(pa_mainlo + 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; +@@ -555,8 +548,6 @@ static void transport_destroy(pa_bluetoo + + 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; +@@ -567,23 +558,14 @@ static void set_speaker_gain(pa_bluetoot + * 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_HSP_HS || t->profile == PA_BLUETOOTH_PROFILE_HFP_HF) { +- len = sprintf(buf, "\r\n+VGS=%d\r\n", gain); +- pa_log_debug("RFCOMM >> +VGS=%d", gain); ++ 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; +@@ -594,17 +576,10 @@ static void set_microphone_gain(pa_bluet + * 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_HSP_HS || t->profile == PA_BLUETOOTH_PROFILE_HFP_HF) { +- len = sprintf(buf, "\r\n+VGM=%d\r\n", gain); +- pa_log_debug("RFCOMM >> +VGM=%d", gain); ++ 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) { diff --git a/pulseaudio.changes b/pulseaudio.changes index 873e8a4..e2f173c 100644 --- a/pulseaudio.changes +++ b/pulseaudio.changes @@ -1,3 +1,19 @@ +------------------------------------------------------------------- +Mon Mar 1 16:22:36 CET 2021 - tiwai@suse.de + +- 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 + ------------------------------------------------------------------- Thu Feb 25 15:32:55 UTC 2021 - Antonio Larrosa diff --git a/pulseaudio.spec b/pulseaudio.spec index dd70957..5b15607 100644 --- a/pulseaudio.spec +++ b/pulseaudio.spec @@ -52,6 +52,18 @@ Patch1: suppress-socket-error-msg.diff Patch5: qpaeq-shebang.patch # PATCH-FIX-OPENSUSE Workaround for old systemd on Leap 15.x Patch6: pulseaudio-old-systemd-workaround.patch +# HFP support patches (bsc#1167940) +Patch101: 0001-bluetooth-use-consistent-profile-names.patch +Patch102: 0002-bluetooth-separate-HSP-and-HFP.patch +Patch103: 0003-bluetooth-add-correct-HFP-rfcomm-negotiation.patch +Patch104: 0004-bluetooth-make-native-the-default-backend.patch +Patch105: 0005-bluetooth-enable-module-bluez5-discover-argument-ena.patch +Patch106: 0006-bluetooth-fix-headset-auto-ofono-handover.patch +Patch107: 0007-bluetooth-prefer-headset-HFP-HF-connection-with-nati.patch +Patch108: 0008-bluetooth-complete-bluetooth-profile-separation.patch +Patch109: 0009-bluetooth-use-device-flag-to-prevent-assertion-failu.patch +Patch110: 0010-bluetooth-rename-enable_hs_role-to-enable_shared_pro.patch +Patch111: 0011-bluetooth-clean-up-rfcomm_write-usage.patch BuildRequires: alsa-devel >= 1.0.19 BuildRequires: bluez-devel >= 5 BuildRequires: doxygen @@ -349,6 +361,17 @@ Optional dependency offering zsh completion for various PulseAudio utilities %if 0%{?suse_version} < 1550 %patch6 -p1 %endif +%patch101 -p1 +%patch102 -p1 +%patch103 -p1 +%patch104 -p1 +%patch105 -p1 +%patch106 -p1 +%patch107 -p1 +%patch108 -p1 +%patch109 -p1 +%patch110 -p1 +%patch111 -p1 %build NOCONFIGURE=1 ./bootstrap.sh