src/modules/bluetooth/module-bluetooth-device.c | 316 ++++++------------------ src/modules/bluetooth/module-bluetooth-policy.c | 104 +++---- src/pulsecore/card.c | 20 + src/pulsecore/card.h | 4 src/pulsecore/cli-text.c | 7 src/pulsecore/core.h | 1 6 files changed, 170 insertions(+), 282 deletions(-)
New commits: commit 17b3cb954b179392e80b0a46d8f2ba4693aec386 Author: Mikel Astiz <mikel.as...@bmw-carit.de> Date: Mon Feb 18 09:10:35 2013 +0100 bluetooth: Merge all ports into "bluetooth-input" and "bluetooth-output" The card profile availability flag already provides all the necessary information and therefore all Bluetooth ports can be merged, leaving the two generic ones only: "bluetooth-input" and "bluetooth-output". The availability of these port now represents whether the device is streaming audio, with the following mapping: - PA_AVAILABLE_UNKNOWN: some profile connected but not streaming - PA_AVAILABLE_NO: no profiles connected - PA_AVAILABLE_YES: some profile streaming (regardless of which) Each port's flag represents the profiles with the corresponding I/O capabilities (pa_direction_t). diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 28b258d..e04780b 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1200,14 +1200,51 @@ static pa_available_t transport_state_to_availability(pa_bluetooth_transport_sta return PA_AVAILABLE_UNKNOWN; } -static pa_available_t transport_state_to_availability_merged(pa_bluetooth_transport_state_t state1, - pa_bluetooth_transport_state_t state2) { - if (state1 == PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED && state2 == PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) - return PA_AVAILABLE_NO; - else if (state1 >= PA_BLUETOOTH_TRANSPORT_STATE_PLAYING || state2 >= PA_BLUETOOTH_TRANSPORT_STATE_PLAYING) - return PA_AVAILABLE_YES; - else - return PA_AVAILABLE_UNKNOWN; +static pa_direction_t get_profile_direction(enum profile p) { + static const pa_direction_t profile_direction[] = { + [PROFILE_A2DP] = PA_DIRECTION_OUTPUT, + [PROFILE_A2DP_SOURCE] = PA_DIRECTION_INPUT, + [PROFILE_HSP] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT, + [PROFILE_HFGW] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT, + [PROFILE_OFF] = 0 + }; + + return profile_direction[p]; +} + +/* Run from main thread */ +static pa_available_t get_port_availability(struct userdata *u, pa_direction_t direction) { + pa_available_t result = PA_AVAILABLE_NO; + unsigned i; + + pa_assert(u); + pa_assert(u->device); + + for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) { + pa_bluetooth_transport *transport; + + if (!(get_profile_direction(i) & direction)) + continue; + + if (!(transport = u->device->transports[i])) + continue; + + switch(transport->state) { + case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED: + continue; + + case PA_BLUETOOTH_TRANSPORT_STATE_IDLE: + if (result == PA_AVAILABLE_NO) + result = PA_AVAILABLE_UNKNOWN; + + break; + + case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING: + return PA_AVAILABLE_YES; + } + } + + return result; } /* Run from main thread */ @@ -1217,6 +1254,7 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot enum profile profile; pa_card_profile *cp; pa_bluetooth_transport_state_t state; + pa_device_port *port; pa_assert(u); pa_assert(transport); @@ -1231,82 +1269,16 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot pa_card_profile_set_available(cp, transport_state_to_availability(state)); /* Update port availability */ - switch (profile) { - case PROFILE_HFGW: { - pa_device_port *port; - pa_available_t available = transport_state_to_availability(state); - - pa_assert_se(port = pa_hashmap_get(u->card->ports, "hfgw-output")); - pa_device_port_set_available(port, available); - - pa_assert_se(port = pa_hashmap_get(u->card->ports, "hfgw-input")); - pa_device_port_set_available(port, available); + pa_assert_se(port = pa_hashmap_get(u->card->ports, "bluetooth-output")); + pa_device_port_set_available(port, get_port_availability(u, PA_DIRECTION_OUTPUT)); - acquire = (available == PA_AVAILABLE_YES && u->profile == PROFILE_HFGW); - release = (available != PA_AVAILABLE_YES && u->profile == PROFILE_HFGW); - - break; - } - - case PROFILE_HSP: { - pa_device_port *port; - pa_available_t available; - pa_bluetooth_transport *other = u->device->transports[PROFILE_A2DP]; - - if (!other) - available = transport_state_to_availability(state); - else - available = transport_state_to_availability_merged(state, other->state); - - pa_assert_se(port = pa_hashmap_get(u->card->ports, "bluetooth-output")); - pa_device_port_set_available(port, available); - - pa_assert_se(port = pa_hashmap_get(u->card->ports, "hsp-input")); - pa_device_port_set_available(port, available); - - acquire = (available == PA_AVAILABLE_YES && u->profile == PROFILE_HSP); - release = (available != PA_AVAILABLE_YES && u->profile == PROFILE_HSP); - - break; - } - - case PROFILE_A2DP_SOURCE: { - pa_device_port *port; - pa_available_t available = transport_state_to_availability(state); - - pa_assert_se(port = pa_hashmap_get(u->card->ports, "a2dp-input")); - pa_device_port_set_available(port, available); - - acquire = (available == PA_AVAILABLE_YES && u->profile == PROFILE_A2DP_SOURCE); - release = (available != PA_AVAILABLE_YES && u->profile == PROFILE_A2DP_SOURCE); - - break; - } - - case PROFILE_A2DP: { - pa_device_port *port; - pa_available_t available; - pa_bluetooth_transport *other = u->device->transports[PROFILE_HSP]; - - if (!other) - available = transport_state_to_availability(state); - else - available = transport_state_to_availability_merged(state, other->state); - - pa_assert_se(port = pa_hashmap_get(u->card->ports, "bluetooth-output")); - pa_device_port_set_available(port, available); - - acquire = (available == PA_AVAILABLE_YES && u->profile == PROFILE_A2DP); - release = (available != PA_AVAILABLE_YES && u->profile == PROFILE_A2DP); - - break; - } - - case PROFILE_OFF: - pa_assert_not_reached(); - } + pa_assert_se(port = pa_hashmap_get(u->card->ports, "bluetooth-input")); + pa_device_port_set_available(port, get_port_availability(u, PA_DIRECTION_INPUT)); /* Acquire or release transport as needed */ + acquire = (state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING && u->profile == profile); + release = (state != PA_BLUETOOTH_TRANSPORT_STATE_PLAYING && u->profile == profile); + if (acquire) if (bt_transport_acquire(u, true) >= 0) { if (u->source) { @@ -1539,54 +1511,20 @@ static pa_hook_result_t transport_speaker_gain_changed_cb(pa_bluetooth_discovery } static void connect_ports(struct userdata *u, void *sink_or_source_new_data, pa_direction_t direction) { - union { - pa_sink_new_data *sink_new_data; - pa_source_new_data *source_new_data; - } data; pa_device_port *port; - if (direction == PA_DIRECTION_OUTPUT) - data.sink_new_data = sink_or_source_new_data; - else - data.source_new_data = sink_or_source_new_data; + if (direction == PA_DIRECTION_OUTPUT) { + pa_sink_new_data *sink_new_data = sink_or_source_new_data; - switch (u->profile) { - case PROFILE_A2DP: - pa_assert_se(port = pa_hashmap_get(u->card->ports, "bluetooth-output")); - pa_assert_se(pa_hashmap_put(data.sink_new_data->ports, port->name, port) >= 0); - pa_device_port_ref(port); - break; - - case PROFILE_A2DP_SOURCE: - pa_assert_se(port = pa_hashmap_get(u->card->ports, "a2dp-input")); - pa_assert_se(pa_hashmap_put(data.source_new_data->ports, port->name, port) >= 0); - pa_device_port_ref(port); - break; - - case PROFILE_HSP: - if (direction == PA_DIRECTION_OUTPUT) { - pa_assert_se(port = pa_hashmap_get(u->card->ports, "bluetooth-output")); - pa_assert_se(pa_hashmap_put(data.sink_new_data->ports, port->name, port) >= 0); - } else { - pa_assert_se(port = pa_hashmap_get(u->card->ports, "hsp-input")); - pa_assert_se(pa_hashmap_put(data.source_new_data->ports, port->name, port) >= 0); - } - pa_device_port_ref(port); - break; - - case PROFILE_HFGW: - if (direction == PA_DIRECTION_OUTPUT) { - pa_assert_se(port = pa_hashmap_get(u->card->ports, "hfgw-output")); - pa_assert_se(pa_hashmap_put(data.sink_new_data->ports, port->name, port) >= 0); - } else { - pa_assert_se(port = pa_hashmap_get(u->card->ports, "hfgw-input")); - pa_assert_se(pa_hashmap_put(data.source_new_data->ports, port->name, port) >= 0); - } - pa_device_port_ref(port); - break; + pa_assert_se(port = pa_hashmap_get(u->card->ports, "bluetooth-output")); + pa_assert_se(pa_hashmap_put(sink_new_data->ports, port->name, port) >= 0); + pa_device_port_ref(port); + } else { + pa_source_new_data *source_new_data = sink_or_source_new_data; - default: - pa_assert_not_reached(); + pa_assert_se(port = pa_hashmap_get(u->card->ports, "bluetooth-input")); + pa_assert_se(pa_hashmap_put(source_new_data->ports, port->name, port) >= 0); + pa_device_port_ref(port); } } @@ -2126,100 +2064,24 @@ off: return -PA_ERR_IO; } -static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_card_profile *profile) { - pa_bluetooth_device *device = u->device; +/* Run from main thread */ +static void create_card_ports(struct userdata *u, pa_hashmap *ports) { pa_device_port *port; - enum profile *d; - pa_bluetooth_transport *transport; - pa_bluetooth_transport_state_t transport_state; - - d = PA_CARD_PROFILE_DATA(profile); - - pa_assert(*d != PROFILE_OFF); - - transport = device->transports[*d]; - transport_state = transport ? transport->state : PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED; - - switch (*d) { - case PROFILE_A2DP: - if ((port = pa_hashmap_get(ports, "bluetooth-output")) != NULL) { - pa_bluetooth_transport *other = device->transports[PROFILE_HSP]; - pa_bluetooth_transport_state_t other_state = other ? other->state : PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED; - - port->priority = PA_MAX(port->priority, profile->priority * 100); - port->available = transport_state_to_availability_merged(transport_state, other_state); - pa_hashmap_put(port->profiles, profile->name, profile); - } else { - pa_assert_se(port = pa_device_port_new(u->core, "bluetooth-output", _("Bluetooth Output"), 0)); - pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0); - port->is_output = 1; - port->is_input = 0; - port->priority = profile->priority * 100; - port->available = transport_state_to_availability(transport_state); - pa_hashmap_put(port->profiles, profile->name, profile); - } - - break; - - case PROFILE_A2DP_SOURCE: - pa_assert_se(port = pa_device_port_new(u->core, "a2dp-input", _("Bluetooth High Quality (A2DP)"), 0)); - pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0); - port->is_output = 0; - port->is_input = 1; - port->priority = profile->priority * 100; - port->available = transport_state_to_availability(transport_state); - pa_hashmap_put(port->profiles, profile->name, profile); - break; - - case PROFILE_HSP: - if ((port = pa_hashmap_get(ports, "bluetooth-output")) != NULL) { - pa_bluetooth_transport *other = device->transports[PROFILE_A2DP]; - pa_bluetooth_transport_state_t other_state = other ? other->state : PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED; - - port->priority = PA_MAX(port->priority, profile->priority * 100); - port->available = transport_state_to_availability_merged(transport_state, other_state); - pa_hashmap_put(port->profiles, profile->name, profile); - } else { - pa_assert_se(port = pa_device_port_new(u->core, "bluetooth-output", _("Bluetooth Output"), 0)); - pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0); - port->is_output = 1; - port->is_input = 0; - port->priority = profile->priority * 100; - port->available = transport_state_to_availability(transport_state); - pa_hashmap_put(port->profiles, profile->name, profile); - } - - pa_assert_se(port = pa_device_port_new(u->core, "hsp-input", _("Bluetooth Telephony (HSP/HFP)"), 0)); - pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0); - port->is_output = 0; - port->is_input = 1; - port->priority = profile->priority * 100; - port->available = transport_state_to_availability(transport_state); - pa_hashmap_put(port->profiles, profile->name, profile); - break; - - case PROFILE_HFGW: - pa_assert_se(port = pa_device_port_new(u->core, "hfgw-output", _("Bluetooth Handsfree Gateway"), 0)); - pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0); - port->is_output = 1; - port->is_input = 0; - port->priority = profile->priority * 100; - port->available = transport_state_to_availability(transport_state); - pa_hashmap_put(port->profiles, profile->name, profile); - - pa_assert_se(port = pa_device_port_new(u->core, "hfgw-input", _("Bluetooth Handsfree Gateway"), 0)); - pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0); - port->is_output = 0; - port->is_input = 1; - port->priority = profile->priority * 100; - port->available = transport_state_to_availability(transport_state); - pa_hashmap_put(port->profiles, profile->name, profile); - break; - - default: - pa_assert_not_reached(); - } + pa_assert(u); + pa_assert(ports); + + pa_assert_se(port = pa_device_port_new(u->core, "bluetooth-output", _("Bluetooth Output"), 0)); + pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0); + port->is_output = 1; + port->is_input = 0; + port->available = get_port_availability(u, PA_DIRECTION_OUTPUT); + + pa_assert_se(port = pa_device_port_new(u->core, "bluetooth-input", _("Bluetooth Input"), 0)); + pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0); + port->is_output = 0; + port->is_input = 1; + port->available = get_port_availability(u, PA_DIRECTION_INPUT); } /* Run from main thread */ @@ -2331,11 +2193,12 @@ static int add_card(struct userdata *u) { } pa_hashmap_put(data.profiles, p->name, p); - create_ports_for_profile(u, data.ports, p); } pa_assert(!pa_hashmap_isempty(data.profiles)); + create_card_ports(u, data.ports); + p = pa_card_profile_new("off", _("Off"), sizeof(enum profile)); p->available = PA_AVAILABLE_YES; d = PA_CARD_PROFILE_DATA(p); @@ -2419,7 +2282,6 @@ static pa_bluetooth_device* find_device(struct userdata *u, const char *address, static pa_hook_result_t uuid_added_cb(pa_bluetooth_discovery *y, const struct pa_bluetooth_hook_uuid_data *data, struct userdata *u) { pa_card_profile *p; - pa_hashmap *new_ports; pa_assert(data); pa_assert(data->device); @@ -2441,14 +2303,6 @@ static pa_hook_result_t uuid_added_cb(pa_bluetooth_discovery *y, const struct pa pa_card_add_profile(u->card, p); - new_ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - - create_ports_for_profile(u, new_ports, p); - - pa_card_add_ports(u->card, new_ports); - - pa_hashmap_free(new_ports, (pa_free_cb_t) pa_device_port_unref); - return PA_HOOK_OK; } commit d7764167090582a6968d104e7ec8f1161967d86b Author: Mikel Astiz <mikel.as...@bmw-carit.de> Date: Mon Feb 18 09:10:34 2013 +0100 bluetooth: Use profile availability to auto-switch profiles 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. diff --git a/src/modules/bluetooth/module-bluetooth-policy.c b/src/modules/bluetooth/module-bluetooth-policy.c index f53a422..4a90db7 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,47 @@ 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->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 +243,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 +270,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); } commit 966a827d779461af4ea94edc77bfbcb3e34501c3 Author: Mikel Astiz <mikel.as...@bmw-carit.de> Date: Mon Feb 18 09:10:33 2013 +0100 bluetooth: Expose card profile availability Use the transport's state to not only update the ports availability, but also to update the card profile availability flag. The interpretation is as follows: - PA_AVAILABLE_UNKNOWN: BT profile is connected but no audio streaming - PA_AVAILABLE_NO: BT profile disconnected - PA_AVAILABLE_YES: BT profile connected and audio streaming diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 9fa768a..28b258d 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1215,6 +1215,7 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot bool acquire = false; bool release = false; enum profile profile; + pa_card_profile *cp; pa_bluetooth_transport_state_t state; pa_assert(u); @@ -1223,9 +1224,13 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot profile = transport->profile; state = transport->state; - if (!pa_hashmap_get(u->card->profiles, pa_bt_profile_to_string(profile))) + /* Update profile availability */ + if (!(cp = pa_hashmap_get(u->card->profiles, pa_bt_profile_to_string(profile)))) return; + pa_card_profile_set_available(cp, transport_state_to_availability(state)); + + /* Update port availability */ switch (profile) { case PROFILE_HFGW: { pa_device_port *port; @@ -1301,6 +1306,7 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot pa_assert_not_reached(); } + /* Acquire or release transport as needed */ if (acquire) if (bt_transport_acquire(u, true) >= 0) { if (u->source) { @@ -2263,6 +2269,13 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid *d = PROFILE_HFGW; } + if (p) { + pa_bluetooth_transport *t; + + if ((t = u->device->transports[*d])) + p->available = transport_state_to_availability(t->state); + } + return p; } @@ -2324,6 +2337,7 @@ static int add_card(struct userdata *u) { pa_assert(!pa_hashmap_isempty(data.profiles)); p = pa_card_profile_new("off", _("Off"), sizeof(enum profile)); + p->available = PA_AVAILABLE_YES; d = PA_CARD_PROFILE_DATA(p); *d = PROFILE_OFF; pa_hashmap_put(data.profiles, p->name, p); commit cfa79a84fcc0d7ace7f8602f62c658341a082106 Author: Mikel Astiz <mikel.as...@bmw-carit.de> Date: Mon Feb 18 09:10:32 2013 +0100 cli: Show card profile availability status Expose the newly added card profile availability in pacmd. diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index c4e0b37..963f130 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -103,7 +103,7 @@ char *pa_client_list_to_string(pa_core *c) { return pa_strbuf_tostring_free(s); } -static const char *port_available_to_string(pa_available_t a) { +static const char *available_to_string(pa_available_t a) { switch (a) { case PA_AVAILABLE_UNKNOWN: return "unknown"; @@ -131,7 +131,7 @@ static void append_port_list(pa_strbuf *s, pa_hashmap *ports) char *t = pa_proplist_to_string_sep(p->proplist, "\n\t\t\t\t"); pa_strbuf_printf(s, "\t\t%s: %s (priority %u, latency offset %" PRId64 " usec, available: %s)\n", p->name, p->description, p->priority, p->latency_offset, - port_available_to_string(p->available)); + available_to_string(p->available)); pa_strbuf_printf(s, "\t\t\tproperties:\n\t\t\t\t%s\n", t); pa_xfree(t); } @@ -173,7 +173,8 @@ char *pa_card_list_to_string(pa_core *c) { pa_strbuf_puts(s, "\tprofiles:\n"); PA_HASHMAP_FOREACH(profile, card->profiles, state) - pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", profile->name, profile->description, profile->priority); + pa_strbuf_printf(s, "\t\t%s: %s (priority %u, available: %s)\n", profile->name, profile->description, + profile->priority, available_to_string(profile->available)); pa_strbuf_printf( s, commit afd33da56a0b174c43ca44bce21b8ef0efaca1fa Author: Mikel Astiz <mikel.as...@bmw-carit.de> Date: Mon Feb 18 09:10:31 2013 +0100 card: Add card profile availability Some cards are capable to announce if a specific profile is available or not, effectively predicting whether a profile switch would fail or would likely succeed. This can for example be useful for a UI that would gray out any unavailable profile. In addition, this information can be useful for internal modules implementing automatic profile-switching policies, such as module-switch-on-port-available or module-bluetooth-policy. In particular, this information is essential when a port is associated to multiple card profiles and therefore the port availability flag does not provide enough information. The port "bluetooth-output" falls into this category, for example, since it doesn't distinguish HSP/HFP from A2DP. diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c index c05fc7a..5ea8a63 100644 --- a/src/pulsecore/card.c +++ b/src/pulsecore/card.c @@ -63,6 +63,26 @@ void pa_card_profile_free(pa_card_profile *c) { pa_xfree(c); } +void pa_card_profile_set_available(pa_card_profile *c, pa_available_t available) { + pa_core *core; + + pa_assert(c); + pa_assert(c->card); /* Modify member variable directly during creation instead of using this function */ + + if (c->available == available) + return; + + c->available = available; + pa_log_debug("Setting card %s profile %s to availability status %s", c->card->name, c->name, + available == PA_AVAILABLE_YES ? "yes" : available == PA_AVAILABLE_NO ? "no" : "unknown"); + + /* Post subscriptions to the card which owns us */ + pa_assert_se(core = c->card->core); + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->card->index); + + pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED], c); +} + pa_card_new_data* pa_card_new_data_init(pa_card_new_data *data) { pa_assert(data); diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h index ec2f9d2..393dbac 100644 --- a/src/pulsecore/card.h +++ b/src/pulsecore/card.h @@ -43,6 +43,7 @@ typedef struct pa_card_profile { char *description; unsigned priority; + pa_available_t available; /* PA_AVAILABLE_UNKNOWN, PA_AVAILABLE_NO or PA_AVAILABLE_YES */ /* We probably want to have different properties later on here */ unsigned n_sinks; @@ -101,6 +102,9 @@ typedef struct pa_card_new_data { pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra); void pa_card_profile_free(pa_card_profile *c); +/* The profile's available status has changed */ +void pa_card_profile_set_available(pa_card_profile *c, pa_available_t available); + pa_card_new_data *pa_card_new_data_init(pa_card_new_data *data); void pa_card_new_data_set_name(pa_card_new_data *data, const char *name); void pa_card_new_data_set_profile(pa_card_new_data *data, const char *profile); diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 2099bb0..381897a 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -114,6 +114,7 @@ typedef enum pa_core_hook { PA_CORE_HOOK_CARD_UNLINK, PA_CORE_HOOK_CARD_PROFILE_CHANGED, PA_CORE_HOOK_CARD_PROFILE_ADDED, + PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED, PA_CORE_HOOK_PORT_AVAILABLE_CHANGED, PA_CORE_HOOK_PORT_ADDED, PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED, _______________________________________________ pulseaudio-commits mailing list pulseaudio-commits@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/pulseaudio-commits