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

Arun Raghavan arun at kemper.freedesktop.org
Wed Sep 26 21:24:51 PDT 2012


 src/modules/bluetooth/bluetooth-util.c          |    1 
 src/modules/bluetooth/module-bluetooth-device.c |  427 ++++++++++++++++++------
 src/modules/bluetooth/module-bluetooth-policy.c |   94 +++++
 src/pulsecore/sink.c                            |    7 
 src/pulsecore/sink.h                            |    2 
 src/pulsecore/source.c                          |    7 
 src/pulsecore/source.h                          |    2 
 7 files changed, 437 insertions(+), 103 deletions(-)

New commits:
commit bf0a640cfd8807cfe856b02c175497e76243e7cd
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:51:13 2012 +0200

    bluetooth: Add port availability transition policies
    
    Handle availability changes in Bluetooth ports inside
    module-bluetooth-policy. The implemented behavior is similar to how
    module-switch-on-port-available behaves, but the conditions are more
    relaxed and thus more profile changes are triggered.

diff --git a/src/modules/bluetooth/module-bluetooth-policy.c b/src/modules/bluetooth/module-bluetooth-policy.c
index cc12014..87a5716 100644
--- a/src/modules/bluetooth/module-bluetooth-policy.c
+++ b/src/modules/bluetooth/module-bluetooth-policy.c
@@ -54,6 +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;
 };
 
 /* When a source is created, loopback it to default sink */
@@ -132,6 +133,89 @@ 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_PORT_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) {
+    void *state;
+    pa_card_profile *profile;
+
+    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);
+
+        if (pa_card_set_profile(card, profile->name, FALSE) != 0)
+            pa_log_warn("Could not set profile '%s'", profile->name);
+
+        return;
+    }
+}
+
+static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port *port, void *userdata) {
+    pa_card *card;
+    const char *s;
+    uint32_t state;
+    pa_bool_t is_active_profile;
+    pa_device_port *port2;
+
+    PA_IDXSET_FOREACH(card, c->cards, state)
+        if (port == pa_hashmap_get(card->ports, port->name))
+            break;
+
+    if (!card) {
+        pa_log_warn("Did not find port %s in array of cards", port->name);
+        return PA_HOOK_OK;
+    }
+
+    /* Only consider bluetooth cards */
+    s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS);
+    if (!s || !pa_streq(s, "bluetooth"))
+        return PA_HOOK_OK;
+
+    is_active_profile = card->active_profile == pa_hashmap_get(port->profiles, card->active_profile->name);
+
+    if (is_active_profile && port->available == PA_PORT_AVAILABLE_YES)
+        return PA_HOOK_OK;
+
+    if (!is_active_profile && port->available != PA_PORT_AVAILABLE_YES)
+        return PA_HOOK_OK;
+
+    if ((port2 = find_best_port(card->ports)) == NULL)
+        return PA_HOOK_OK;
+
+    set_port_profile(card, port2);
+
+    return PA_HOOK_OK;
+}
+
+static void handle_all_ports(pa_core *core) {
+    pa_card *card;
+    uint32_t state;
+
+    PA_IDXSET_FOREACH(card, core->cards, state) {
+        pa_device_port *port;
+        void *state2;
+
+        PA_HASHMAP_FOREACH(port, card->ports, state2)
+            port_available_hook_callback(core, port, NULL);
+    }
+}
+
 int pa__init(pa_module *m) {
     pa_modargs *ma;
     struct userdata *u;
@@ -161,6 +245,11 @@ 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);
+
+    handle_all_ports(m->core);
+
     pa_modargs_free(ma);
     return 0;
 
@@ -183,5 +272,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);
+
     pa_xfree(u);
 }

commit d84e36563cf98d99474b7f952ecf0e8252c61643
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:51:12 2012 +0200

    bluetooth: Let suspend-on-idle request audio in headset
    
    When PA is doing gateway role, let module-suspend-on-idle resume the
    audio stream automatically. This will work until the user (or the remote
    side, which we also consider user-initiated) suspend the stream
    manually.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index a3d5c7c..80ce52c 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1736,7 +1736,18 @@ static int add_sink(struct userdata *u) {
         connect_ports(u, &data, PA_DIRECTION_OUTPUT);
 
         if (!bt_transport_is_acquired(u))
-            data.suspend_cause = PA_SUSPEND_USER;
+            switch (u->profile) {
+                case PROFILE_A2DP:
+                case PROFILE_HSP:
+                    data.suspend_cause = PA_SUSPEND_IDLE;
+                    break;
+                case PROFILE_HFGW:
+                    data.suspend_cause = PA_SUSPEND_USER;
+                    break;
+                case PROFILE_A2DP_SOURCE:
+                case PROFILE_OFF:
+                    pa_assert_not_reached();
+            }
 
         u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
         pa_sink_new_data_done(&data);
@@ -1799,7 +1810,18 @@ static int add_source(struct userdata *u) {
         connect_ports(u, &data, PA_DIRECTION_INPUT);
 
         if (!bt_transport_is_acquired(u))
-            data.suspend_cause = PA_SUSPEND_USER;
+            switch (u->profile) {
+                case PROFILE_HSP:
+                    data.suspend_cause = PA_SUSPEND_IDLE;
+                    break;
+                case PROFILE_A2DP_SOURCE:
+                case PROFILE_HFGW:
+                    data.suspend_cause = PA_SUSPEND_USER;
+                    break;
+                case PROFILE_A2DP:
+                case PROFILE_OFF:
+                    pa_assert_not_reached();
+            }
 
         u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
         pa_source_new_data_done(&data);

commit e492f2912e72ac01d56c2f3fe479fa1ba70915bc
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:51:08 2012 +0200

    bluetooth: Do not switch to HFGW automatically
    
    Card profile hfgw should be no different from the rest, and thus no
    internal policy inside module-bluetooth-device should decide to switch
    to its profile automatically.
    
    This should be handled by policy modules.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index d2c0d2b..a3d5c7c 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1360,22 +1360,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
     } else if (dbus_message_is_signal(m, "org.bluez.HandsfreeGateway", "PropertyChanged")) {
         pa_bt_audio_state_t state = parse_state_property_change(m);
 
-        switch(state) {
-            case PA_BT_AUDIO_STATE_INVALID:
-            case PA_BT_AUDIO_STATE_DISCONNECTED:
-            case PA_BT_AUDIO_STATE_CONNECTED:
-            case PA_BT_AUDIO_STATE_CONNECTING:
-                goto fail;
-
-            case PA_BT_AUDIO_STATE_PLAYING:
-                if (u->card) {
-                    pa_log_debug("Changing profile to hfgw");
-                    if (pa_card_set_profile(u->card, "hfgw", FALSE) < 0)
-                        pa_log("Failed to change profile to hfgw");
-                }
-                break;
-        }
-
         if (state != PA_BT_AUDIO_STATE_INVALID) {
             pa_device_port *port;
             pa_port_available_t available = audio_state_to_availability(state);

commit cb2a6d34f606308b5fbb31b8d938027213e17378
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:51:07 2012 +0200

    bluetooth: Release transport when not available
    
    Handle the Playing->Connected transition gracefully by releasing the
    transport and setting the sink and sources as suspended. This is
    necessary since the IO thread might not encounter a HUP always.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 3696221..d2c0d2b 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1308,6 +1308,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
     DBusError err;
     struct userdata *u;
     bool acquire = FALSE;
+    bool release = FALSE;
 
     pa_assert(bus);
     pa_assert(m);
@@ -1386,6 +1387,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
             pa_device_port_set_available(port, available);
 
             acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_HFGW);
+            release = (available != PA_PORT_AVAILABLE_YES && u->profile == PROFILE_HFGW);
         }
     } else if (dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged")) {
         pa_bt_audio_state_t state = parse_state_property_change(m);
@@ -1401,6 +1403,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
             pa_device_port_set_available(port, available);
 
             acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_HSP);
+            release = (available != PA_PORT_AVAILABLE_YES && u->profile == PROFILE_HSP);
         }
     } else if (dbus_message_is_signal(m, "org.bluez.AudioSource", "PropertyChanged")) {
         pa_bt_audio_state_t state = parse_state_property_change(m);
@@ -1413,6 +1416,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
             pa_device_port_set_available(port, available);
 
             acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_A2DP_SOURCE);
+            release = (available != PA_PORT_AVAILABLE_YES && u->profile == PROFILE_A2DP_SOURCE);
         }
     } else if (dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged")) {
         pa_bt_audio_state_t state = parse_state_property_change(m);
@@ -1425,6 +1429,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
             pa_device_port_set_available(port, available);
 
             acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_A2DP);
+            release = (available != PA_PORT_AVAILABLE_YES && u->profile == PROFILE_A2DP);
         }
     }
 
@@ -1437,6 +1442,20 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
                 pa_sink_suspend(u->sink, FALSE, PA_SUSPEND_IDLE|PA_SUSPEND_USER);
         }
 
+    if (release && bt_transport_is_acquired(u)) {
+        /* FIXME: this release is racy, since the audio stream might have
+           been set up again in the meantime (but not processed yet by PA).
+           BlueZ should probably release the transport automatically, and
+           in that case we would just mark the transport as released */
+
+        /* Remote side closed the stream so we consider it PA_SUSPEND_USER */
+        if (u->source)
+            pa_source_suspend(u->source, TRUE, PA_SUSPEND_USER);
+
+        if (u->sink)
+            pa_sink_suspend(u->sink, TRUE, PA_SUSPEND_USER);
+    }
+
 fail:
     dbus_error_free(&err);
 

commit ebadda816d2f6faaf0130c6d4482be34b0c9b57d
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:51:06 2012 +0200

    bluetooth: Acquire transport when becomes available
    
    Try to acquire the transport as soon as the audio stream is started,
    along with the availability flag update.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 6757e87..3696221 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1307,6 +1307,7 @@ static pa_port_available_t audio_state_to_availability(pa_bt_audio_state_t state
 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
     DBusError err;
     struct userdata *u;
+    bool acquire = FALSE;
 
     pa_assert(bus);
     pa_assert(m);
@@ -1383,6 +1384,8 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
 
             pa_assert_se(port = pa_hashmap_get(u->card->ports, "hfgw-input"));
             pa_device_port_set_available(port, available);
+
+            acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_HFGW);
         }
     } else if (dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged")) {
         pa_bt_audio_state_t state = parse_state_property_change(m);
@@ -1396,6 +1399,8 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
 
             pa_assert_se(port = pa_hashmap_get(u->card->ports, "hsp-input"));
             pa_device_port_set_available(port, available);
+
+            acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_HSP);
         }
     } else if (dbus_message_is_signal(m, "org.bluez.AudioSource", "PropertyChanged")) {
         pa_bt_audio_state_t state = parse_state_property_change(m);
@@ -1406,6 +1411,8 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
 
             pa_assert_se(port = pa_hashmap_get(u->card->ports, "a2dp-input"));
             pa_device_port_set_available(port, available);
+
+            acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_A2DP_SOURCE);
         }
     } else if (dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged")) {
         pa_bt_audio_state_t state = parse_state_property_change(m);
@@ -1416,9 +1423,20 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
 
             pa_assert_se(port = pa_hashmap_get(u->card->ports, "a2dp-output"));
             pa_device_port_set_available(port, available);
+
+            acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_A2DP);
         }
     }
 
+    if (acquire)
+        if (bt_transport_acquire(u, FALSE) >= 0) {
+            if (u->source)
+                pa_source_suspend(u->source, FALSE, PA_SUSPEND_IDLE|PA_SUSPEND_USER);
+
+            if (u->sink)
+                pa_sink_suspend(u->sink, FALSE, PA_SUSPEND_IDLE|PA_SUSPEND_USER);
+        }
+
 fail:
     dbus_error_free(&err);
 

commit b8fd6f869ede117bf0a0b937c49c25c60c039b7c
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:51:05 2012 +0200

    bluetooth: Do not acquire transport during profile change
    
    Until today, setting the card to some profile resulted in a transport
    acquisition, leading to audio stream setup. This is generally not very
    interesting and even undesireable for HFGW use-cases, where the
    Gateway role (the remote end) would typically request the SCO link.
    
    Nevertheless, there is no safe way to implement such check without race
    conditions, since the BlueZ's state can change between the state report
    and the call to Acquire(). The chances for this to reproduce are quite
    low though, since interface state changes are relatively slow.
    
    This race condition requires that BlueZ's API is extended in order to
    perform the operation atomically, which has already been discussed and
    ack-ed in the BlueZ mailing list.
    
    Note that this patch does not introduce a new race condition, since it
    already existed before (the PropertyChanged->Acquire race condition,
    affecting HFGW use-cases). It is just more explicit now.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index c900979..6757e87 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -365,8 +365,26 @@ static void bt_transport_release(struct userdata *u) {
     teardown_stream(u);
 }
 
+static pa_bt_audio_state_t get_profile_audio_state(const struct userdata *u, const pa_bluetooth_device *d) {
+    switch(u->profile) {
+        case PROFILE_HSP:
+            return d->headset_state;
+        case PROFILE_A2DP:
+            return d->audio_sink_state;
+        case PROFILE_A2DP_SOURCE:
+            return d->audio_source_state;
+        case PROFILE_HFGW:
+            return d->hfgw_state;
+        case PROFILE_OFF:
+            break;
+    }
+
+    pa_assert_not_reached();
+}
+
 static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
     const char *accesstype = "rw";
+    const pa_bluetooth_device *d;
     const pa_bluetooth_transport *t;
 
     if (u->transport == NULL) {
@@ -382,6 +400,12 @@ static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
 
     pa_log_debug("Acquiring transport %s", u->transport);
 
+    d = pa_bluetooth_discovery_get_by_path(u->discovery, u->path);
+    if (!d) {
+        pa_log_error("Failed to get device object.");
+        return -1;
+    }
+
     t = pa_bluetooth_discovery_get_transport(u->discovery, u->transport);
     if (!t) {
         pa_log("Transport %s no longer available", u->transport);
@@ -390,6 +414,20 @@ static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
         return -1;
     }
 
+    if (!start) {
+        /* FIXME: we are trying to acquire the transport only if the stream is
+           playing, without actually initiating the stream request from our side
+           (which is typically undesireable specially for hfgw use-cases.
+           However this approach is racy, since the stream could have been
+           suspended in the meantime, so we can't really guarantee that the
+           stream will not be requested until BlueZ's API supports this
+           atomically. */
+        if (get_profile_audio_state(u, d) < PA_BT_AUDIO_STATE_PLAYING) {
+            pa_log_info("Failed optional acquire of transport %s", u->transport);
+            return -1;
+        }
+    }
+
     u->stream_fd = pa_bluetooth_transport_acquire(t, accesstype, &u->read_link_mtu, &u->write_link_mtu);
     if (u->stream_fd < 0) {
         if (start)

commit 884d1d46ca5b563cae319003a2b3d0196dff05d1
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:51:04 2012 +0200

    bluetooth: Set profile even if transport not acquired
    
    If the acquisition of the transport fails, the profile should still be
    set. In this case the audio is not actually streaming, so the sink and
    source will be created but left suspended.
    
    If the transport needs to be acquired later, for example because the
    user wants to route the audio the remote device, the suspend flag should
    have to be changed.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index bfe00dc..cb7b69d 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1014,7 +1014,6 @@ int pa_bluetooth_transport_acquire(const pa_bluetooth_transport *t, const char *
     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->y->connection), m, -1, &err);
 
     if (dbus_error_is_set(&err) || !r) {
-        pa_log("Failed to acquire transport fd: %s", err.message);
         dbus_error_free(&err);
         return -1;
     }
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index d56cfc1..c900979 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -321,11 +321,30 @@ static bool bt_transport_is_acquired(struct userdata *u) {
         pa_assert(u->stream_fd < 0);
         return FALSE;
     } else {
-        pa_assert(u->stream_fd >= 0);
+        /* During IO thread HUP stream_fd can be -1 */
         return TRUE;
     }
 }
 
+static void teardown_stream(struct userdata *u) {
+    if (u->rtpoll_item) {
+        pa_rtpoll_item_free(u->rtpoll_item);
+        u->rtpoll_item = NULL;
+    }
+
+    if (u->stream_fd >= 0) {
+        pa_close(u->stream_fd);
+        u->stream_fd = -1;
+    }
+
+    if (u->read_smoother) {
+        pa_smoother_free(u->read_smoother);
+        u->read_smoother = NULL;
+    }
+
+    pa_log_debug("Audio stream torn down");
+}
+
 static void bt_transport_release(struct userdata *u) {
     const char *accesstype = "rw";
     const pa_bluetooth_transport *t;
@@ -343,20 +362,7 @@ static void bt_transport_release(struct userdata *u) {
     pa_xfree(u->accesstype);
     u->accesstype = NULL;
 
-    if (u->rtpoll_item) {
-        pa_rtpoll_item_free(u->rtpoll_item);
-        u->rtpoll_item = NULL;
-    }
-
-    if (u->stream_fd >= 0) {
-        pa_close(u->stream_fd);
-        u->stream_fd = -1;
-    }
-
-    if (u->read_smoother) {
-        pa_smoother_free(u->read_smoother);
-        u->read_smoother = NULL;
-    }
+    teardown_stream(u);
 }
 
 static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
@@ -385,8 +391,14 @@ static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
     }
 
     u->stream_fd = pa_bluetooth_transport_acquire(t, accesstype, &u->read_link_mtu, &u->write_link_mtu);
-    if (u->stream_fd < 0)
+    if (u->stream_fd < 0) {
+        if (start)
+            pa_log("Failed to acquire transport %s", u->transport);
+        else
+            pa_log_info("Failed optional acquire of transport %s", u->transport);
+
         return -1;
+    }
 
     u->accesstype = pa_xstrdup(accesstype);
     pa_log_info("Transport %s acquired: fd %d", u->transport, u->stream_fd);
@@ -1015,8 +1027,9 @@ static void thread_func(void *userdata) {
 
     pa_thread_mq_install(&u->thread_mq);
 
-    if (bt_transport_acquire(u, TRUE) < 0)
-        goto fail;
+    /* Setup the stream only if the transport was already acquired */
+    if (bt_transport_is_acquired(u))
+        bt_transport_acquire(u, TRUE);
 
     for (;;) {
         struct pollfd *pollfd;
@@ -1042,7 +1055,7 @@ static void thread_func(void *userdata) {
                     n_read = a2dp_process_push(u);
 
                 if (n_read < 0)
-                    goto fail;
+                    goto io_fail;
 
                 /* We just read something, so we are supposed to write something, too */
                 pending_read_bytes += n_read;
@@ -1110,10 +1123,10 @@ static void thread_func(void *userdata) {
 
                     if (u->profile == PROFILE_A2DP) {
                         if ((n_written = a2dp_process_render(u)) < 0)
-                            goto fail;
+                            goto io_fail;
                     } else {
                         if ((n_written = hsp_process_render(u)) < 0)
-                            goto fail;
+                            goto io_fail;
                     }
 
                     if (n_written == 0)
@@ -1170,8 +1183,21 @@ static void thread_func(void *userdata) {
                         pollfd->revents & POLLHUP ? "POLLHUP " :"",
                         pollfd->revents & POLLPRI ? "POLLPRI " :"",
                         pollfd->revents & POLLNVAL ? "POLLNVAL " :"");
-            goto fail;
+            goto io_fail;
         }
+
+        continue;
+
+io_fail:
+        /* In case of HUP, just tear down the streams */
+        if (!pollfd || (pollfd->revents & POLLHUP) == 0)
+            goto fail;
+
+        do_write = 0;
+        pending_read_bytes = 0;
+        writable = FALSE;
+
+        teardown_stream(u);
     }
 
 fail:
@@ -1650,6 +1676,9 @@ static int add_sink(struct userdata *u) {
         }
         connect_ports(u, &data, PA_DIRECTION_OUTPUT);
 
+        if (!bt_transport_is_acquired(u))
+            data.suspend_cause = PA_SUSPEND_USER;
+
         u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
         pa_sink_new_data_done(&data);
 
@@ -1709,6 +1738,10 @@ static int add_source(struct userdata *u) {
         }
 
         connect_ports(u, &data, PA_DIRECTION_INPUT);
+
+        if (!bt_transport_is_acquired(u))
+            data.suspend_cause = PA_SUSPEND_USER;
+
         u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
         pa_source_new_data_done(&data);
 
@@ -1892,8 +1925,7 @@ static int setup_bt(struct userdata *u) {
 
     u->transport = pa_xstrdup(t->path);
 
-    if (bt_transport_acquire(u, FALSE) < 0)
-        return -1;
+    bt_transport_acquire(u, FALSE);
 
     bt_transport_config(u);
 

commit dcc11dcbe912aae5da101b28e0153dde05ea71e6
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:51:02 2012 +0200

    bluetooth: Support port availability flag
    
    Use the port availability flag to expose whether a certain profile is
    connected and whether it's doing actual audio streaming.
    
    The proposed mapping is the following:
    - Profile disconnected: port is unavailable
    - Profile is connected (but not streaming/playing): availability unknown
    - Profile is streaming/playing: port is available
    
    The availability-unknown is specially interesting: it involves that if
    the sink/source exists (corresponding card profile set), it is currently
    in suspended state.
    
    For example, for SCO cases (HFGW or HSP), this means the SCO is down. A
    policy module would typically not change this, unless someone is really
    trying to use the sink/source. This situation would be nicely handled by
    module-suspend-on-idle, which would automatically connect SCO.
    
    On the other hand, if the user wants to control the status of the SCO,
    it will still be possible by resuming the sink or source (suspend=0).
    This works out-of-the-box since most UIs would show to the user ports
    whose availability is unknown.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 206dc2e..d56cfc1 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1230,6 +1230,15 @@ static pa_bt_audio_state_t parse_state_property_change(DBusMessage *m) {
     return state;
 }
 
+static pa_port_available_t audio_state_to_availability(pa_bt_audio_state_t state) {
+    if (state < PA_BT_AUDIO_STATE_CONNECTED)
+        return PA_PORT_AVAILABLE_NO;
+    else if (state >= PA_BT_AUDIO_STATE_PLAYING)
+        return PA_PORT_AVAILABLE_YES;
+    else
+        return PA_PORT_AVAILABLE_UNKNOWN;
+}
+
 /* Run from main thread */
 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
     DBusError err;
@@ -1300,6 +1309,50 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
                 }
                 break;
         }
+
+        if (state != PA_BT_AUDIO_STATE_INVALID) {
+            pa_device_port *port;
+            pa_port_available_t available = audio_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);
+        }
+    } else if (dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged")) {
+        pa_bt_audio_state_t state = parse_state_property_change(m);
+
+        if (state != PA_BT_AUDIO_STATE_INVALID) {
+            pa_device_port *port;
+            pa_port_available_t available = audio_state_to_availability(state);
+
+            pa_assert_se(port = pa_hashmap_get(u->card->ports, "hsp-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);
+        }
+    } else if (dbus_message_is_signal(m, "org.bluez.AudioSource", "PropertyChanged")) {
+        pa_bt_audio_state_t state = parse_state_property_change(m);
+
+        if (state != PA_BT_AUDIO_STATE_INVALID) {
+            pa_device_port *port;
+            pa_port_available_t available = audio_state_to_availability(state);
+
+            pa_assert_se(port = pa_hashmap_get(u->card->ports, "a2dp-input"));
+            pa_device_port_set_available(port, available);
+        }
+    } else if (dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged")) {
+        pa_bt_audio_state_t state = parse_state_property_change(m);
+
+        if (state != PA_BT_AUDIO_STATE_INVALID) {
+            pa_device_port *port;
+            pa_port_available_t available = audio_state_to_availability(state);
+
+            pa_assert_se(port = pa_hashmap_get(u->card->ports, "a2dp-output"));
+            pa_device_port_set_available(port, available);
+        }
     }
 
 fail:
@@ -2107,7 +2160,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
     return 0;
 }
 
-static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_new_data, pa_card_profile *profile) {
+static void create_ports_for_profile(struct userdata *u, const pa_bluetooth_device *device, pa_card_new_data *card_new_data, pa_card_profile *profile) {
     pa_device_port *port;
     enum profile *d;
 
@@ -2120,6 +2173,7 @@ static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_
             port->is_output = 1;
             port->is_input = 0;
             port->priority = profile->priority * 100;
+            port->available = audio_state_to_availability(device->audio_sink_state);
             pa_hashmap_put(port->profiles, profile->name, profile);
             break;
 
@@ -2129,6 +2183,7 @@ static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_
             port->is_output = 0;
             port->is_input = 1;
             port->priority = profile->priority * 100;
+            port->available = audio_state_to_availability(device->audio_source_state);
             pa_hashmap_put(port->profiles, profile->name, profile);
             break;
 
@@ -2138,6 +2193,7 @@ static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_
             port->is_output = 1;
             port->is_input = 0;
             port->priority = profile->priority * 100;
+            port->available = audio_state_to_availability(device->headset_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));
@@ -2145,6 +2201,7 @@ static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_
             port->is_output = 0;
             port->is_input = 1;
             port->priority = profile->priority * 100;
+            port->available = audio_state_to_availability(device->headset_state);
             pa_hashmap_put(port->profiles, profile->name, profile);
             break;
 
@@ -2154,6 +2211,7 @@ static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_
             port->is_output = 1;
             port->is_input = 0;
             port->priority = profile->priority * 100;
+            port->available = audio_state_to_availability(device->hfgw_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));
@@ -2161,6 +2219,7 @@ static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_
             port->is_output = 0;
             port->is_input = 1;
             port->priority = profile->priority * 100;
+            port->available = audio_state_to_availability(device->hfgw_state);
             pa_hashmap_put(port->profiles, profile->name, profile);
             break;
 
@@ -2222,7 +2281,7 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) {
 
         d = PA_CARD_PROFILE_DATA(p);
         *d = PROFILE_A2DP;
-        create_ports_for_profile(u, &data, p);
+        create_ports_for_profile(u, device, &data, p);
 
         pa_hashmap_put(data.profiles, p->name, p);
     }
@@ -2237,7 +2296,7 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) {
 
         d = PA_CARD_PROFILE_DATA(p);
         *d = PROFILE_A2DP_SOURCE;
-        create_ports_for_profile(u, &data, p);
+        create_ports_for_profile(u, device, &data, p);
 
         pa_hashmap_put(data.profiles, p->name, p);
     }
@@ -2253,7 +2312,7 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) {
 
         d = PA_CARD_PROFILE_DATA(p);
         *d = PROFILE_HSP;
-        create_ports_for_profile(u, &data, p);
+        create_ports_for_profile(u, device, &data, p);
 
         pa_hashmap_put(data.profiles, p->name, p);
     }
@@ -2268,7 +2327,7 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) {
 
         d = PA_CARD_PROFILE_DATA(p);
         *d = PROFILE_HFGW;
-        create_ports_for_profile(u, &data, p);
+        create_ports_for_profile(u, device, &data, p);
 
         pa_hashmap_put(data.profiles, p->name, p);
     }
@@ -2467,6 +2526,9 @@ int pa__init(pa_module* m) {
                 mike,
                 "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
                 "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'",
+                "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
+                "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'",
+                "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'",
                 NULL) < 0) {
 
         pa_xfree(speaker);

commit 4cc4c301879f24d6e629b7212563587fc6e0e46a
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:51:03 2012 +0200

    bluetooth: Config MTU transport after acquire
    
    The configuration of the transport that depends on the MTU should be
    performed every time the transport has been acquired, since the
    parameters depend on what the Media API provides. This requires to
    update the parameters of the sinks and sources as well.
    
    This patch moves this code into a new function that will be called
    when the stream is starting (setup_stream), from the IO thread.
    
    This makes the code more robust, since the existing multiple calls to
    bt_transport_acquire() do not rely on setup_bt() being able to acquire
    the transport.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 97bbda1..206dc2e 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -244,11 +244,47 @@ static void a2dp_set_bitpool(struct userdata *u, uint8_t bitpool)
 }
 
 /* from IO thread, except in SCO over PCM */
+static void bt_transport_config_mtu(struct userdata *u) {
+    /* Calculate block sizes */
+    if (u->profile == PROFILE_HSP || u->profile == PROFILE_HFGW) {
+        u->read_block_size = u->read_link_mtu;
+        u->write_block_size = u->write_link_mtu;
+    } else {
+        u->read_block_size =
+            (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
+            / u->a2dp.frame_length * u->a2dp.codesize;
+
+        u->write_block_size =
+            (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
+            / u->a2dp.frame_length * u->a2dp.codesize;
+    }
+
+    if (USE_SCO_OVER_PCM(u))
+        return;
+
+    if (u->sink) {
+        pa_sink_set_max_request_within_thread(u->sink, u->write_block_size);
+        pa_sink_set_fixed_latency_within_thread(u->sink,
+                                                (u->profile == PROFILE_A2DP ?
+                                                 FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_HSP) +
+                                                pa_bytes_to_usec(u->write_block_size, &u->sample_spec));
+    }
+
+    if (u->source)
+        pa_source_set_fixed_latency_within_thread(u->source,
+                                                  (u->profile == PROFILE_A2DP_SOURCE ?
+                                                   FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_HSP) +
+                                                  pa_bytes_to_usec(u->read_block_size, &u->sample_spec));
+}
+
+/* from IO thread, except in SCO over PCM */
 
 static void setup_stream(struct userdata *u) {
     struct pollfd *pollfd;
     int one;
 
+    bt_transport_config_mtu(u);
+
     pa_make_fd_nonblock(u->stream_fd);
     pa_make_socket_low_delay(u->stream_fd);
 
@@ -1572,11 +1608,6 @@ static int add_sink(struct userdata *u) {
         u->sink->userdata = u;
         u->sink->parent.process_msg = sink_process_msg;
         u->sink->set_port = sink_set_port_cb;
-
-        pa_sink_set_max_request(u->sink, u->write_block_size);
-        pa_sink_set_fixed_latency(u->sink,
-                                  (u->profile == PROFILE_A2DP ? FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_HSP) +
-                                  pa_bytes_to_usec(u->write_block_size, &u->sample_spec));
     }
 
     if (u->profile == PROFILE_HSP) {
@@ -1636,10 +1667,6 @@ static int add_source(struct userdata *u) {
         u->source->userdata = u;
         u->source->parent.process_msg = source_process_msg;
         u->source->set_port = source_set_port_cb;
-
-        pa_source_set_fixed_latency(u->source,
-                                    (u->profile == PROFILE_A2DP_SOURCE ? FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_HSP) +
-                                    pa_bytes_to_usec(u->read_block_size, &u->sample_spec));
     }
 
     if ((u->profile == PROFILE_HSP) || (u->profile == PROFILE_HFGW)) {
@@ -1771,22 +1798,12 @@ static void bt_transport_config_a2dp(struct userdata *u) {
     a2dp->codesize = sbc_get_codesize(&a2dp->sbc);
     a2dp->frame_length = sbc_get_frame_length(&a2dp->sbc);
 
-    u->read_block_size =
-        (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
-        / a2dp->frame_length * a2dp->codesize;
-
-    u->write_block_size =
-        (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
-        / a2dp->frame_length * a2dp->codesize;
-
     pa_log_info("SBC parameters:\n\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
                 a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks, a2dp->sbc.bitpool);
 }
 
 static void bt_transport_config(struct userdata *u) {
     if (u->profile == PROFILE_HSP || u->profile == PROFILE_HFGW) {
-        u->read_block_size = u->read_link_mtu;
-        u->write_block_size = u->write_link_mtu;
         u->sample_spec.format = PA_SAMPLE_S16LE;
         u->sample_spec.channels = 1;
         u->sample_spec.rate = 8000;

commit 2a7d001c97da7189d44f27d92e7e3a8ccaa71284
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:51:01 2012 +0200

    bluetooth: Provide dummy set_port callbacks
    
    There should be one port per sink/source so a dummy set_port callback
    will be enough.
    
    Adding this callback avoid the "operation not implemented" error
    message and additionally makes the module work nicely with
    module-switch-on-port-available.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 1379ab5..97bbda1 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1515,6 +1515,14 @@ static const char *profile_to_string(enum profile profile) {
     }
 }
 
+static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
+    return 0;
+}
+
+static int source_set_port_cb(pa_source *s, pa_device_port *p) {
+    return 0;
+}
+
 /* Run from main thread */
 static int add_sink(struct userdata *u) {
     char *k;
@@ -1563,6 +1571,7 @@ static int add_sink(struct userdata *u) {
 
         u->sink->userdata = u;
         u->sink->parent.process_msg = sink_process_msg;
+        u->sink->set_port = sink_set_port_cb;
 
         pa_sink_set_max_request(u->sink, u->write_block_size);
         pa_sink_set_fixed_latency(u->sink,
@@ -1626,6 +1635,7 @@ static int add_source(struct userdata *u) {
 
         u->source->userdata = u;
         u->source->parent.process_msg = source_process_msg;
+        u->source->set_port = source_set_port_cb;
 
         pa_source_set_fixed_latency(u->source,
                                     (u->profile == PROFILE_A2DP_SOURCE ? FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_HSP) +

commit f280e8b50d09c58365dbad6676246fde43220bee
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:51:00 2012 +0200

    sink, source: Support creating suspended sinks and sources
    
    The initial state of a sink or source might not necessarily be IDLE,
    because sometimes it might be suspended from the very beginning.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 7a7ccff..1379ab5 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -381,7 +381,9 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
 
                 case PA_SINK_SUSPENDED:
-                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+                    /* Ignore if transition is PA_SINK_INIT->PA_SINK_SUSPENDED */
+                    if (!PA_SINK_IS_OPENED(u->sink->thread_info.state))
+                        break;
 
                     /* Stop the device if the source is suspended as well */
                     if (!u->source || u->source->state == PA_SOURCE_SUSPENDED)
@@ -455,7 +457,9 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
             switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
 
                 case PA_SOURCE_SUSPENDED:
-                    pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
+                    /* Ignore if transition is PA_SOURCE_INIT->PA_SOURCE_SUSPENDED */
+                    if (!PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+                        break;
 
                     /* Stop the device if the sink is suspended as well */
                     if (!u->sink || u->sink->state == PA_SINK_SUSPENDED)
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index a41d2e7..9958be6 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -248,7 +248,7 @@ pa_sink* pa_sink_new(
     s->state = PA_SINK_INIT;
     s->flags = flags;
     s->priority = 0;
-    s->suspend_cause = 0;
+    s->suspend_cause = data->suspend_cause;
     pa_sink_set_mixer_dirty(s, FALSE);
     s->name = pa_xstrdup(name);
     s->proplist = pa_proplist_copy(data->proplist);
@@ -650,7 +650,10 @@ void pa_sink_put(pa_sink* s) {
     pa_assert(s->monitor_source->thread_info.min_latency == s->thread_info.min_latency);
     pa_assert(s->monitor_source->thread_info.max_latency == s->thread_info.max_latency);
 
-    pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
+    if (s->suspend_cause)
+        pa_assert_se(sink_set_state(s, PA_SINK_SUSPENDED) == 0);
+    else
+        pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
 
     pa_source_put(s->monitor_source);
 
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 2c52348..fcda5ef 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -328,6 +328,8 @@ typedef enum pa_sink_message {
 } pa_sink_message_t;
 
 typedef struct pa_sink_new_data {
+    pa_suspend_cause_t suspend_cause;
+
     char *name;
     pa_proplist *proplist;
 
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index cefddae..7af86f5 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -235,7 +235,7 @@ pa_source* pa_source_new(
     s->state = PA_SOURCE_INIT;
     s->flags = flags;
     s->priority = 0;
-    s->suspend_cause = 0;
+    s->suspend_cause = data->suspend_cause;
     pa_source_set_mixer_dirty(s, FALSE);
     s->name = pa_xstrdup(name);
     s->proplist = pa_proplist_copy(data->proplist);
@@ -595,7 +595,10 @@ void pa_source_put(pa_source *s) {
     pa_assert(!(s->flags & PA_SOURCE_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1);
     pa_assert(!(s->flags & PA_SOURCE_DYNAMIC_LATENCY) == (s->thread_info.fixed_latency != 0));
 
-    pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
+    if (s->suspend_cause)
+        pa_assert_se(source_set_state(s, PA_SOURCE_SUSPENDED) == 0);
+    else
+        pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
 
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
     pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s);
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index dd1e09b..224155b 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -265,6 +265,8 @@ typedef enum pa_source_message {
 } pa_source_message_t;
 
 typedef struct pa_source_new_data {
+    pa_suspend_cause_t suspend_cause;
+
     char *name;
     pa_proplist *proplist;
 

commit 0ee5fa6377dcb0d6120f9a11bf68a10ef5eed320
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:50:59 2012 +0200

    bluetooth: Fix check if transport exists before acquire
    
    The transport might have disapeared exactly before acquiring, so we
    should avoid an assertion failure, in this case inside the function
    pa_bluetooth_discovery_get_by_path().

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 51ee92f..7a7ccff 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -327,6 +327,11 @@ static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
     const char *accesstype = "rw";
     const pa_bluetooth_transport *t;
 
+    if (u->transport == NULL) {
+        pa_log("Transport no longer available.");
+        return -1;
+    }
+
     if (bt_transport_is_acquired(u)) {
         if (start)
             goto done;

commit 76248fd8f2d48f7eb754cad608663759c9be4005
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:50:58 2012 +0200

    bluetooth: Fix using garbage memory
    
    module-bluetooth-policy should set the allocated memory to zero, in
    order to handle failure cases properly.

diff --git a/src/modules/bluetooth/module-bluetooth-policy.c b/src/modules/bluetooth/module-bluetooth-policy.c
index 03beeb2..cc12014 100644
--- a/src/modules/bluetooth/module-bluetooth-policy.c
+++ b/src/modules/bluetooth/module-bluetooth-policy.c
@@ -143,7 +143,7 @@ int pa__init(pa_module *m) {
         return -1;
     }
 
-    m->userdata = u = pa_xnew(struct userdata, 1);
+    m->userdata = u = pa_xnew0(struct userdata, 1);
 
     u->enable_a2dp_source = TRUE;
     if (pa_modargs_get_value_boolean(ma, "a2dp_source", &u->enable_a2dp_source) < 0) {

commit 9a5330e601acc1f0310d0b3d8235a7d01baa3f43
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:50:57 2012 +0200

    bluetooth: Fix wrongly set "phone" role for HFGW
    
    The HFGW source should be consistent with the sink by not setting the
    "phone" intended role.
    
    Even though setting this role seems to make sense strictly speaking, the
    rest of the codebase doesn't handle this well. Therefore, the audio
    coming from a Bluetooth phone can be routed back to the same device.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 70f48a7..51ee92f 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1593,7 +1593,7 @@ static int add_source(struct userdata *u) {
         data.module = u->module;
         pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
         pa_proplist_sets(data.proplist, "bluetooth.protocol", profile_to_string(u->profile));
-        if ((u->profile == PROFILE_HSP) || (u->profile == PROFILE_HFGW))
+        if (u->profile == PROFILE_HSP)
             pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
 
         data.card = u->card;

commit 38de0af64ef1e0bb8c64a96ed10e994841c813b3
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:50:55 2012 +0200

    bluetooth: Refactor code to helper function
    
    Make code more readable by introducing the helper function
    bt_transport_is_acquired(). This also adds assertions to check whether
    the internal state is consistent.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 82f10c2..70f48a7 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -280,12 +280,22 @@ static void setup_stream(struct userdata *u) {
                 TRUE);
 }
 
+static bool bt_transport_is_acquired(struct userdata *u) {
+    if (u->accesstype == NULL) {
+        pa_assert(u->stream_fd < 0);
+        return FALSE;
+    } else {
+        pa_assert(u->stream_fd >= 0);
+        return TRUE;
+    }
+}
+
 static void bt_transport_release(struct userdata *u) {
     const char *accesstype = "rw";
     const pa_bluetooth_transport *t;
 
     /* Ignore if already released */
-    if (!u->accesstype)
+    if (!bt_transport_is_acquired(u))
         return;
 
     pa_log_debug("Releasing transport %s", u->transport);
@@ -317,7 +327,7 @@ static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
     const char *accesstype = "rw";
     const pa_bluetooth_transport *t;
 
-    if (u->accesstype) {
+    if (bt_transport_is_acquired(u)) {
         if (start)
             goto done;
         return 0;

commit 431f56fd68548a57fce8a87ca02b34d5c50e7292
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:50:56 2012 +0200

    bluetooth: Refactor parsing of signal PropertyChanged
    
    Wrap the code parsing the PropertyChanged signal into a helper function
    that will return the new state of the interface.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 5d9d66e..82f10c2 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1129,6 +1129,52 @@ finish:
     pa_log_debug("IO thread shutting down");
 }
 
+static pa_bt_audio_state_t parse_state_property_change(DBusMessage *m) {
+    DBusMessageIter iter;
+    DBusMessageIter variant;
+    const char *key;
+    const char *value;
+    pa_bt_audio_state_t state;
+
+    if (!dbus_message_iter_init(m, &iter)) {
+        pa_log("Failed to parse PropertyChanged");
+        return PA_BT_AUDIO_STATE_INVALID;
+    }
+
+    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+        pa_log("Property name not a string");
+        return PA_BT_AUDIO_STATE_INVALID;
+    }
+
+    dbus_message_iter_get_basic(&iter, &key);
+
+    if (!pa_streq(key, "State"))
+        return PA_BT_AUDIO_STATE_INVALID;
+
+    if (!dbus_message_iter_next(&iter)) {
+        pa_log("Property value missing");
+        return PA_BT_AUDIO_STATE_INVALID;
+    }
+
+    dbus_message_iter_recurse(&iter, &variant);
+
+    if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING) {
+        pa_log("Property value not a string");
+        return PA_BT_AUDIO_STATE_INVALID;
+    }
+
+    dbus_message_iter_get_basic(&variant, &value);
+
+    pa_log_debug("dbus: %s property 'State' changed to value '%s'", dbus_message_get_interface(m), value);
+
+    state = pa_bt_audio_state_from_string(value);
+
+    if (state == PA_BT_AUDIO_STATE_INVALID)
+        pa_log("Unexpected value for property 'State': '%s'", value);
+
+    return state;
+}
+
 /* Run from main thread */
 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
     DBusError err;
@@ -1182,39 +1228,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
             }
         }
     } else if (dbus_message_is_signal(m, "org.bluez.HandsfreeGateway", "PropertyChanged")) {
-        const char *key;
-        DBusMessageIter iter;
-        DBusMessageIter variant;
-        pa_bt_audio_state_t state = PA_BT_AUDIO_STATE_INVALID;
-
-        if (!dbus_message_iter_init(m, &iter)) {
-            pa_log("Failed to parse PropertyChanged: %s", err.message);
-            goto fail;
-        }
-
-        if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
-            pa_log("Property name not a string.");
-            goto fail;
-        }
-
-        dbus_message_iter_get_basic(&iter, &key);
-
-        if (!dbus_message_iter_next(&iter)) {
-            pa_log("Property value missing");
-            goto fail;
-        }
-
-        dbus_message_iter_recurse(&iter, &variant);
-
-        if (dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_STRING) {
-            const char *value;
-            dbus_message_iter_get_basic(&variant, &value);
-
-            if (pa_streq(key, "State")) {
-                pa_log_debug("dbus: HSHFAG property 'State' changed to value '%s'", value);
-                state = pa_bt_audio_state_from_string(value);
-            }
-        }
+        pa_bt_audio_state_t state = parse_state_property_change(m);
 
         switch(state) {
             case PA_BT_AUDIO_STATE_INVALID:

commit 4d2748ef5c0c9d9db5f1d8c0ac1c3c99f55e3ebe
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:50:54 2012 +0200

    bluetooth: Remove return value of setup_stream()
    
    The function setup_stream() never fails so the code can be simplified by
    just removing the return value.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 9762fe9..5d9d66e 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -245,7 +245,7 @@ static void a2dp_set_bitpool(struct userdata *u, uint8_t bitpool)
 
 /* from IO thread, except in SCO over PCM */
 
-static int setup_stream(struct userdata *u) {
+static void setup_stream(struct userdata *u) {
     struct pollfd *pollfd;
     int one;
 
@@ -278,8 +278,6 @@ static int setup_stream(struct userdata *u) {
                 10,
                 pa_rtclock_now(),
                 TRUE);
-
-    return 0;
 }
 
 static void bt_transport_release(struct userdata *u) {
@@ -347,7 +345,9 @@ static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
 
 done:
     pa_log_info("Transport %s resuming", u->transport);
-    return setup_stream(u);
+    setup_stream(u);
+
+    return 0;
 }
 
 /* Run from IO thread */

commit 49f5c111bd4ee1780ad65199aa575668eee1391f
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Aug 31 12:50:53 2012 +0200

    bluetooth: Remove return value of bt_transport_config()
    
    The function bt_transport_config() never fails so the code can be
    simplified by just removing the return value.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index a023c19..9762fe9 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1621,7 +1621,7 @@ static int add_source(struct userdata *u) {
     return 0;
 }
 
-static int bt_transport_config_a2dp(struct userdata *u) {
+static void bt_transport_config_a2dp(struct userdata *u) {
     const pa_bluetooth_transport *t;
     struct a2dp_info *a2dp = &u->a2dp;
     a2dp_sbc_t *config;
@@ -1738,21 +1738,17 @@ static int bt_transport_config_a2dp(struct userdata *u) {
 
     pa_log_info("SBC parameters:\n\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
                 a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks, a2dp->sbc.bitpool);
-
-    return 0;
 }
 
-static int bt_transport_config(struct userdata *u) {
+static void bt_transport_config(struct userdata *u) {
     if (u->profile == PROFILE_HSP || u->profile == PROFILE_HFGW) {
         u->read_block_size = u->read_link_mtu;
         u->write_block_size = u->write_link_mtu;
         u->sample_spec.format = PA_SAMPLE_S16LE;
         u->sample_spec.channels = 1;
         u->sample_spec.rate = 8000;
-        return 0;
-    }
-
-    return bt_transport_config_a2dp(u);
+    } else
+        bt_transport_config_a2dp(u);
 }
 
 /* Run from main thread */
@@ -1786,7 +1782,9 @@ static int setup_bt(struct userdata *u) {
     if (bt_transport_acquire(u, FALSE) < 0)
         return -1;
 
-    return bt_transport_config(u);
+    bt_transport_config(u);
+
+    return 0;
 }
 
 /* Run from main thread */



More information about the pulseaudio-commits mailing list