[pulseaudio-commits] 5 commits - src/modules src/pulsecore

Tanu Kaskinen tanuk at kemper.freedesktop.org
Mon Feb 18 09:03:59 PST 2013


 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.astiz at 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.astiz at 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.astiz at 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.astiz at 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.astiz at 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,



More information about the pulseaudio-commits mailing list