From: Mikel Astiz <mikel.astiz@xxxxxxxxxxxx> Use the card profile availability flag instead of port availability in order to automatically switch profiles, for example when a paired phone starts streaming A2DP audio. --- src/modules/bluetooth/module-bluetooth-policy.c | 105 +++++++++++------------- 1 file changed, 50 insertions(+), 55 deletions(-) diff --git a/src/modules/bluetooth/module-bluetooth-policy.c b/src/modules/bluetooth/module-bluetooth-policy.c index f53a422..84f47b6 100644 --- a/src/modules/bluetooth/module-bluetooth-policy.c +++ b/src/modules/bluetooth/module-bluetooth-policy.c @@ -54,7 +54,7 @@ struct userdata { bool enable_hfgw; pa_hook_slot *source_put_slot; pa_hook_slot *sink_put_slot; - pa_hook_slot *port_available_changed_slot; + pa_hook_slot *profile_available_changed_slot; }; /* When a source is created, loopback it to default sink */ @@ -133,54 +133,36 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void * return PA_HOOK_OK; } -static pa_device_port* find_best_port(pa_hashmap *ports) { - void *state; - pa_device_port *port; - pa_device_port *result = NULL; - - PA_HASHMAP_FOREACH(port, ports, state) { - if (port->available != PA_AVAILABLE_YES) - continue; - - if (result == NULL || port->priority > result->priority) - result = port; - } - - return result; -} - -static void set_port_profile(pa_card *card, pa_device_port *port) { +static pa_card_profile *find_best_profile(pa_card *card) { void *state; pa_card_profile *profile; + pa_card_profile *result = card->active_profile; + pa_card_profile *off; - PA_HASHMAP_FOREACH(profile, port->profiles, state) { - if (card->active_profile == profile) - return; - - pa_log_debug("Setting card '%s' to profile '%s'", card->name, profile->name); + pa_assert_se(off = pa_hashmap_get(card->profiles, "off")); - if (pa_card_set_profile(card, profile->name, false) != 0) - pa_log_warn("Could not set profile '%s'", profile->name); + PA_HASHMAP_FOREACH(profile, card->profiles, state) { + if (profile->available == PA_AVAILABLE_NO || profile == off) + continue; - return; + if (result == NULL || + (profile->available == PA_AVAILABLE_YES && result->available == PA_AVAILABLE_UNKNOWN) || + (profile->available == result->available && profile->priority > profile->priority)) + result = profile; } + + return result ? result : off; } -static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port *port, void *userdata) { +static pa_hook_result_t profile_available_hook_callback(pa_core *c, pa_card_profile *profile, void *userdata) { pa_card *card; const char *s; - uint32_t state; bool is_active_profile; - pa_device_port *port2; - - PA_IDXSET_FOREACH(card, c->cards, state) - if (port == pa_hashmap_get(card->ports, port->name)) - break; + pa_card_profile *selected_profile; - if (!card) { - pa_log_warn("Did not find port %s in array of cards", port->name); - return PA_HOOK_OK; - } + pa_assert(c); + pa_assert(profile); + pa_assert_se((card = profile->card)); /* Only consider bluetooth cards */ s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS); @@ -188,35 +170,48 @@ static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port return PA_HOOK_OK; /* Do not automatically switch profiles for headsets, just in case */ - if (pa_hashmap_get(port->profiles, "hsp") || pa_hashmap_get(port->profiles, "a2dp")) + if (pa_streq(profile->name, "hsp") || pa_streq(profile->name, "a2dp")) return PA_HOOK_OK; - is_active_profile = card->active_profile == pa_hashmap_get(port->profiles, card->active_profile->name); + is_active_profile = card->active_profile == profile; - if (is_active_profile && port->available == PA_AVAILABLE_YES) - return PA_HOOK_OK; + if (profile->available == PA_AVAILABLE_YES) { + if (is_active_profile) + return PA_HOOK_OK; - if (!is_active_profile && port->available != PA_AVAILABLE_YES) - return PA_HOOK_OK; + if (card->active_profile && card->active_profile->available == PA_AVAILABLE_YES && + card->active_profile->priority >= profile->priority) + return PA_HOOK_OK; - if ((port2 = find_best_port(card->ports)) == NULL) - return PA_HOOK_OK; + selected_profile = profile; + } else { + if (!is_active_profile) + return PA_HOOK_OK; + + pa_assert_se((selected_profile = find_best_profile(card))); + + if (selected_profile == card->active_profile) + return PA_HOOK_OK; + } + + pa_log_debug("Setting card '%s' to profile '%s'", card->name, selected_profile->name); - set_port_profile(card, port2); + if (pa_card_set_profile(card, selected_profile->name, false) != 0) + pa_log_warn("Could not set profile '%s'", selected_profile->name); return PA_HOOK_OK; } -static void handle_all_ports(pa_core *core) { +static void handle_all_profiles(pa_core *core) { pa_card *card; uint32_t state; PA_IDXSET_FOREACH(card, core->cards, state) { - pa_device_port *port; + pa_card_profile *profile; void *state2; - PA_HASHMAP_FOREACH(port, card->ports, state2) - port_available_hook_callback(core, port, NULL); + PA_HASHMAP_FOREACH(profile, card->profiles, state2) + profile_available_hook_callback(core, profile, NULL); } } @@ -249,10 +244,10 @@ int pa__init(pa_module *m) { u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_put_hook_callback, u); - u->port_available_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED], - PA_HOOK_NORMAL, (pa_hook_cb_t) port_available_hook_callback, u); + u->profile_available_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED], + PA_HOOK_NORMAL, (pa_hook_cb_t) profile_available_hook_callback, u); - handle_all_ports(m->core); + handle_all_profiles(m->core); pa_modargs_free(ma); return 0; @@ -276,8 +271,8 @@ void pa__done(pa_module *m) { if (u->sink_put_slot) pa_hook_slot_free(u->sink_put_slot); - if (u->port_available_changed_slot) - pa_hook_slot_free(u->port_available_changed_slot); + if (u->profile_available_changed_slot) + pa_hook_slot_free(u->profile_available_changed_slot); pa_xfree(u); } -- 1.8.1