From 698fb3bc26a679063c081cc3089cd9d515d34a4a Mon Sep 17 00:00:00 2001 From: "Igor V. Kovalenko" Date: Fri, 29 Jan 2021 21:32:09 +0300 Subject: [PATCH] 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(-) diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c index 7b70345..a3b8494 100644 --- a/src/modules/bluetooth/backend-native.c +++ b/src/modules/bluetooth/backend-native.c @@ -562,7 +562,7 @@ static void set_speaker_gain(pa_bluetooth_transport *t, uint16_t gain) { /* If we are in the AG role, we send a command to the head set to change * the speaker gain. In the HS role, source and sink are swapped, so * in this case we notify the AG that the microphone gain has changed */ - if (t->profile == PA_BLUETOOTH_PROFILE_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 { @@ -589,7 +589,7 @@ static void set_microphone_gain(pa_bluetooth_transport *t, uint16_t gain) { /* If we are in the AG role, we send a command to the head set to change * the microphone gain. In the HS role, source and sink are swapped, so * in this case we notify the AG that the speaker gain has changed */ - if (t->profile == PA_BLUETOOTH_PROFILE_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 { @@ -624,7 +624,7 @@ static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m, 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 { @@ -757,7 +757,7 @@ static void profile_init(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile 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; @@ -781,7 +781,7 @@ static void profile_done(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile 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: @@ -795,11 +795,11 @@ static void profile_done(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile 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); } diff --git a/src/modules/bluetooth/backend-ofono.c b/src/modules/bluetooth/backend-ofono.c index 1eca8a8..4d33935 100644 --- 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_bluetooth_backend *backend, const char card->local_address = pa_xstrdup(value); } else if (pa_streq(key, "Type")) { if (pa_streq(value, "gateway")) - p = PA_BLUETOOTH_PROFILE_HSP_HS; + p = PA_BLUETOOTH_PROFILE_HFP_HF; } pa_log_debug("%s: %s", key, value); diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c index bae627b..759a47b 100644 --- a/src/modules/bluetooth/bluez5-util.c +++ b/src/modules/bluetooth/bluez5-util.c @@ -216,11 +216,12 @@ static bool device_supports_profile(pa_bluetooth_device *device, pa_bluetooth_pr 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(); } @@ -1725,10 +1726,12 @@ const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) { 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"; } diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h index 6d2ff33..ad05711 100644 --- a/src/modules/bluetooth/bluez5-util.h +++ b/src/modules/bluetooth/bluez5-util.h @@ -71,6 +71,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 diff --git a/src/modules/bluetooth/module-bluetooth-policy.c b/src/modules/bluetooth/module-bluetooth-policy.c index ff66070..40a241b 100644 --- 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_callback(pa_core *c, pa_source *source, if (u->enable_a2dp_source && pa_streq(s, "a2dp_source")) role = "music"; - else if (u->enable_ag && pa_streq(s, "headset_audio_gateway")) + else if (u->enable_ag && (pa_streq(s, "headset_audio_gateway") || pa_streq(s, "handsfree_audio_gateway"))) role = "phone"; else { pa_log_debug("Profile %s cannot be selected for loopback", s); @@ -125,7 +125,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void * if (!s) return PA_HOOK_OK; - if (u->enable_ag && pa_streq(s, "headset_audio_gateway")) + if (u->enable_ag && (pa_streq(s, "headset_audio_gateway") || pa_streq(s, "handsfree_audio_gateway"))) role = "phone"; else { pa_log_debug("Profile %s cannot be selected for loopback", s); @@ -156,7 +156,7 @@ static void card_set_profile(struct userdata *u, pa_card *card, bool revert_to_a if (!pa_streq(profile->name, "a2dp_sink")) continue; } else { - if (!pa_streq(profile->name, "headset_head_unit") && !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, bool revert_to_a2dp, void *userdata) { if (!pa_hashmap_remove(u->will_need_revert_card_map, card)) return; - /* Skip card if does not have active hsp profile */ - if (!pa_streq(card->active_profile->name, "headset_head_unit") && !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, bool revert_to_a2dp, void *userdata) { if (!pa_streq(card->active_profile->name, "a2dp_sink")) return; - /* Skip card if already has active hsp profile */ - if (pa_streq(card->active_profile->name, "headset_head_unit") || 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_available_hook_callback(pa_core *c, pa_card_prof /* 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; diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c index 6a10dad..a9f1709 100644 --- a/src/modules/bluetooth/module-bluez5-device.c +++ b/src/modules/bluetooth/module-bluez5-device.c @@ -262,8 +262,9 @@ 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); + 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); @@ -329,8 +330,9 @@ 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); + 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_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_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_source *s) { pa_cvolume_set(&s->real_volume, u->decoder_sample_spec.channels, volume); /* Set soft volume when in headset role */ - if (u->profile == PA_BLUETOOTH_PROFILE_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 @@ -1023,6 +1026,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: @@ -1052,6 +1056,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); @@ -1178,7 +1183,7 @@ static void sink_set_volume_cb(pa_sink *s) { pa_cvolume_set(&s->real_volume, u->encoder_sample_spec.channels, volume); /* Set soft volume when in headset role */ - if (u->profile == PA_BLUETOOTH_PROFILE_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 @@ -1212,6 +1217,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: @@ -1243,6 +1249,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); @@ -1254,6 +1261,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; @@ -1306,7 +1314,7 @@ static int setup_transport(struct userdata *u) { u->transport = t; - if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_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; @@ -1325,6 +1333,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_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 @@ -1660,7 +1669,7 @@ static int start_thread(struct userdata *u) { /* If we are in the headset role, the sink should not become default * unless there is no other sound device available. */ - if (u->profile == PA_BLUETOOTH_PROFILE_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); @@ -1676,7 +1685,7 @@ static int start_thread(struct userdata *u) { /* If we are in the headset role or the device is an a2dp source, * the source should not become default unless there is no other * sound device available. */ - if (u->profile == PA_BLUETOOTH_PROFILE_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); @@ -1945,7 +1954,7 @@ 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; @@ -1957,8 +1966,21 @@ static pa_card_profile *create_card_profile(struct userdata *u, pa_bluetooth_pro 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; @@ -1971,7 +1993,7 @@ static pa_card_profile *create_card_profile(struct userdata *u, pa_bluetooth_pro 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; @@ -2048,7 +2070,9 @@ static int uuid_to_profile(const char *uuid, pa_bluetooth_profile_t *_r) { *_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; @@ -2269,7 +2293,7 @@ static pa_hook_result_t transport_speaker_gain_changed_cb(pa_bluetooth_discovery volume++; pa_cvolume_set(&v, u->encoder_sample_spec.channels, volume); - if (t->profile == PA_BLUETOOTH_PROFILE_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); @@ -2297,7 +2321,7 @@ static pa_hook_result_t transport_microphone_gain_changed_cb(pa_bluetooth_discov pa_cvolume_set(&v, u->decoder_sample_spec.channels, volume); - if (t->profile == PA_BLUETOOTH_PROFILE_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); -- 2.7.4