[pulseaudio-commits] 68 commits - doxygen/doxygen.conf.in man/pulseaudio.1.xml.in man/pulse-cli-syntax.5.xml.in man/pulse-daemon.conf.5.xml.in src/daemon src/Makefile.am src/modules src/pulse src/pulsecore src/tests src/utils

Tanu Kaskinen tanuk at kemper.freedesktop.org
Wed Dec 19 02:36:27 PST 2012


 doxygen/doxygen.conf.in                           |   32 +
 man/pulse-cli-syntax.5.xml.in                     |    6 
 man/pulse-daemon.conf.5.xml.in                    |   15 
 man/pulseaudio.1.xml.in                           |    7 
 src/Makefile.am                                   |   10 
 src/daemon/cmdline.c                              |   37 -
 src/daemon/cpulimit.c                             |    2 
 src/modules/alsa/alsa-sink.c                      |   13 
 src/modules/alsa/alsa-source.c                    |   13 
 src/modules/bluetooth/bluetooth-util.c            |  483 ++++++++++-----
 src/modules/bluetooth/bluetooth-util.h            |   78 +-
 src/modules/bluetooth/module-bluetooth-device.c   |  671 ++++++++--------------
 src/modules/bluetooth/module-bluetooth-discover.c |    3 
 src/modules/dbus/iface-card.c                     |   38 +
 src/modules/dbus/iface-device.c                   |   23 
 src/modules/echo-cancel/module-echo-cancel.c      |    4 
 src/modules/echo-cancel/null.c                    |    2 
 src/modules/module-card-restore.c                 |    2 
 src/modules/module-device-restore.c               |    4 
 src/modules/module-position-event-sounds.c        |    4 
 src/modules/module-role-ducking.c                 |  323 ++++++++++
 src/modules/module-suspend-on-idle.c              |    6 
 src/modules/module-udev-detect.c                  |    4 
 src/modules/module-virtual-sink.c                 |    2 
 src/modules/module-virtual-source.c               |    1 
 src/modules/oss/module-oss.c                      |   26 
 src/pulse/mainloop-signal.c                       |    3 
 src/pulse/mainloop.c                              |   24 
 src/pulsecore/cli-command.c                       |   10 
 src/pulsecore/core-util.c                         |    6 
 src/pulsecore/memblockq.c                         |   10 
 src/pulsecore/modargs.c                           |   22 
 src/pulsecore/modargs.h                           |    4 
 src/pulsecore/protocol-esound.c                   |   13 
 src/pulsecore/protocol-native.c                   |    6 
 src/pulsecore/sink-input.c                        |  168 ++++-
 src/pulsecore/sink-input.h                        |   28 
 src/pulsecore/sink.c                              |    9 
 src/pulsecore/sndfile-util.c                      |   22 
 src/pulsecore/source.c                            |    1 
 src/tests/smoother-test.c                         |    3 
 src/utils/pactl.c                                 |   18 
 src/utils/pasuspender.c                           |    6 
 src/utils/qpaeq                                   |   22 
 44 files changed, 1420 insertions(+), 764 deletions(-)

New commits:
commit af0c45386faa8828c5b682211673265d677f376d
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Tue Dec 18 08:47:58 2012 +0200

    bluetooth: Don't access a transport after it's freed.
    
    In addition to moving the freeing a bit later, unnecessary checks for
    t->device are removed. t->device is initialized to a non-NULL value
    when the transport is created, and it's never changed.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 392f4a5..61c52f5 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1388,17 +1388,18 @@ static DBusMessage *endpoint_clear_configuration(DBusConnection *c, DBusMessage
     }
 
     if ((t = pa_hashmap_get(y->transports, path))) {
-        bool old_any_connected = t->device ? pa_bluetooth_device_any_audio_connected(t->device) : false;
+        bool old_any_connected = pa_bluetooth_device_any_audio_connected(t->device);
 
         pa_log_debug("Clearing transport %s profile %d", t->path, t->profile);
         t->device->transports[t->profile] = NULL;
         pa_hashmap_remove(y->transports, t->path);
         t->state = PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED;
         pa_hook_fire(&y->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
-        transport_free(t);
 
-        if (t->device && old_any_connected != pa_bluetooth_device_any_audio_connected(t->device))
+        if (old_any_connected != pa_bluetooth_device_any_audio_connected(t->device))
             run_callback(t->device, FALSE);
+
+        transport_free(t);
     }
 
     pa_assert_se(r = dbus_message_new_method_return(m));

commit 6f1c3df37b85b47cd1d2d3b046d79ad13d8e61ac
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Dec 17 07:46:04 2012 +0200

    man: Update log-target documentation.

diff --git a/man/pulse-daemon.conf.5.xml.in b/man/pulse-daemon.conf.5.xml.in
index c270d2b..744e94e 100644
--- a/man/pulse-daemon.conf.5.xml.in
+++ b/man/pulse-daemon.conf.5.xml.in
@@ -297,12 +297,15 @@ USA.
 
     <option>
       <p><opt>log-target=</opt> The default log target. Use either
-      <opt>stderr</opt>, <opt>syslog</opt> or <opt>auto</opt>. The
-      latter is equivalent to <opt>sylog</opt> in case
-      <opt>daemonize</opt> is enabled, otherwise to
-      <opt>stderr</opt>. Defaults to <opt>auto</opt>. The
-      <opt>--log-target</opt> command line option takes
-      precedence.</p>
+      <opt>stderr</opt>, <opt>syslog</opt>, <opt>auto</opt>,
+      <opt>file:PATH</opt> or <opt>newfile:PATH</opt>. <opt>auto</opt> is
+      equivalent to <opt>sylog</opt> in case <opt>daemonize</opt> is enabled,
+      otherwise to <opt>stderr</opt>. If set to <opt>file:PATH</opt>, logging
+      is directed to the file indicated by PATH. <opt>newfile:PATH</opt> is
+      otherwise the same as <opt>file:PATH</opt>, but existing files are never
+      overwritten. If the specified file already exists, a suffix is added to
+      the file name to avoid overwriting. Defaults to <opt>auto</opt>. The
+      <opt>--log-target</opt> command line option takes precedence.</p>
     </option>
 
     <option>
diff --git a/man/pulseaudio.1.xml.in b/man/pulseaudio.1.xml.in
index c556bda..12f05e8 100644
--- a/man/pulseaudio.1.xml.in
+++ b/man/pulseaudio.1.xml.in
@@ -217,13 +217,16 @@ USA.
     </option>
 
     <option>
-      <p><opt>--log-target</opt><arg>={auto,syslog,stderr,file:PATH}</arg></p>
+      <p><opt>--log-target</opt><arg>={auto,syslog,stderr,file:PATH,newfile:PATH}</arg></p>
 
       <optdesc><p>Specify the log target. If set to <arg>auto</arg>
       (which is the default), then logging is directed to syslog when
       <opt>--daemonize</opt> is passed, otherwise to
       STDERR. If set to <arg>file:PATH</arg>, logging is directed to
-      the file indicated by PATH.</p></optdesc>
+      the file indicated by PATH. <arg>newfile:PATH</arg> is otherwise
+      the same as file:PATH, but existing files are never overwritten.
+      If the specified file already exists, a suffix is added to the
+      file name to avoid overwriting.</p></optdesc>
     </option>
 
     <option>

commit 8d0e9d4662d2fefc803921a607742588c12e8367
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed May 9 06:04:52 2012 +0300

    modargs: Don't fail needlessly in pa_modargs_get_sample_spec_and_channel_map().
    
    BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=49664

diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index e86ffa5..0abc243 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -453,8 +453,12 @@ int pa_modargs_get_sample_spec_and_channel_map(
     if (pa_modargs_get_channel_map(ma, NULL, &map) < 0)
         return -1;
 
-    if (map.channels != ss.channels)
-        return -1;
+    if (map.channels != ss.channels) {
+        if (!pa_modargs_get_value(ma, "channels", NULL))
+            ss.channels = map.channels;
+        else
+            return -1;
+    }
 
     *rmap = map;
     *rss = ss;

commit 953bedc974df95ac434698b71e76c2066d5e8006
Author: Wang Xingchao <xingchao.wang at intel.com>
Date:   Mon May 7 16:52:18 2012 +0800

    sndfile-util: reduce useless loop
    
    it's useless to get the same SF_FORMAT_INFO three times, just compare the
    name/extention in the same loop.
    
    Signed-off-by: Wang Xingchao <xingchao.wang at intel.com>

diff --git a/src/pulsecore/sndfile-util.c b/src/pulsecore/sndfile-util.c
index fe20486..d8cc244 100644
--- a/src/pulsecore/sndfile-util.c
+++ b/src/pulsecore/sndfile-util.c
@@ -424,7 +424,6 @@ int pa_sndfile_format_from_string(const char *name) {
 
     pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) == 0);
 
-    /* First try to match via full type string */
     for (i = 0; i < count; i++) {
         SF_FORMAT_INFO fi;
         pa_zero(fi);
@@ -432,30 +431,15 @@ int pa_sndfile_format_from_string(const char *name) {
 
         pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
 
+	/* First try to match via full type string */
         if (strcasecmp(name, fi.name) == 0)
             return fi.format;
-    }
-
-    /* Then, try to match via the full extension */
-    for (i = 0; i < count; i++) {
-        SF_FORMAT_INFO fi;
-        pa_zero(fi);
-        fi.format = i;
-
-        pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
 
+	/* Then, try to match via the full extension */
         if (strcasecmp(name, fi.extension) == 0)
             return fi.format;
-    }
-
-    /* Then, try to match via the start of the type string */
-    for (i = 0; i < count; i++) {
-        SF_FORMAT_INFO fi;
-        pa_zero(fi);
-        fi.format = i;
-
-        pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
 
+	/* Then, try to match via the start of the type string */
         if (strncasecmp(name, fi.name, strlen(name)) == 0)
             return fi.format;
     }

commit 0c5e39a9613ea4eff5499b3a41cf85a9bf95c3ce
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat Apr 28 18:54:10 2012 +0300

    memblockq: Use pa_xnew0() to avoid manual zeroing.

diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
index 23bb772..4eeb4d6 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -76,11 +76,8 @@ pa_memblockq* pa_memblockq_new(
     pa_assert(sample_spec);
     pa_assert(name);
 
-    bq = pa_xnew(pa_memblockq, 1);
+    bq = pa_xnew0(pa_memblockq, 1);
     bq->name = pa_xstrdup(name);
-    bq->blocks = bq->blocks_tail = NULL;
-    bq->current_read = bq->current_write = NULL;
-    bq->n_blocks = 0;
 
     bq->sample_spec = *sample_spec;
     bq->base = pa_frame_size(sample_spec);
@@ -89,8 +86,6 @@ pa_memblockq* pa_memblockq_new(
     pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
                  (unsigned long) maxlength, (unsigned long) tlength, (unsigned long) bq->base, (unsigned long) prebuf, (unsigned long) minreq, (unsigned long) maxrewind);
 
-    bq->missing = bq->requested = 0;
-    bq->maxlength = bq->tlength = bq->prebuf = bq->minreq = bq->maxrewind = 0;
     bq->in_prebuf = TRUE;
 
     pa_memblockq_set_maxlength(bq, maxlength);
@@ -105,8 +100,7 @@ pa_memblockq* pa_memblockq_new(
     if (silence) {
         bq->silence = *silence;
         pa_memblock_ref(bq->silence.memblock);
-    } else
-        pa_memchunk_reset(&bq->silence);
+    }
 
     bq->mcalign = pa_mcalign_new(bq->base);
 

commit 7615a4853c5e89aeedc31a7cc2828bf8881617a6
Author: Christoph Gysin <christoph.gysin at gmail.com>
Date:   Wed Apr 11 23:05:54 2012 +0300

    doc: Generate API documentation for ext-device-manager and ext-device-restore.
    
    I noticed that the doxygen API (http://freedesktop.org/software/pulseaudio/doxygen)
    does not include ext-device-manager.h. The following patch adds ext-device-manager.h
    and ext-device-restore.h to the list of files processed by doxygen.

diff --git a/doxygen/doxygen.conf.in b/doxygen/doxygen.conf.in
index 0306114..1369070 100644
--- a/doxygen/doxygen.conf.in
+++ b/doxygen/doxygen.conf.in
@@ -417,7 +417,37 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = ../src/pulse/context.h ../src/pulse/stream.h ../src/pulse/pulseaudio.h ../src/pulse/sample.h ../src/pulse/def.h ../src/pulse/subscribe.h ../src/pulse/introspect.h ../src/pulse/scache.h ../src/pulse/mainloop-api.h ../src/pulse/glib-mainloop.h ../src/pulse/mainloop.h ../src/pulse/mainloop-signal.h ../src/pulse/error.h ../src/pulse/operation.h ../src/pulse/simple.h ../src/pulse/version.h ../src/pulse/volume.h ../src/pulse/channelmap.h ../src/pulse/thread-mainloop.h ../src/pulse/xmalloc.h ../src/pulse/utf8.h ../src/pulse/util.h ../src/pulse/timeval.h ../src/pulse/proplist.h ../src/pulse/gccmacro.h ../src/pulse/ext-stream-restore.h ../src/pulse/rtclock.h ../src/pulse/format.h
+INPUT = \
+    ../src/pulse/channelmap.h \
+    ../src/pulse/context.h \
+    ../src/pulse/def.h \
+    ../src/pulse/error.h \
+    ../src/pulse/ext-stream-restore.h \
+    ../src/pulse/ext-device-manager.h \
+    ../src/pulse/ext-device-restore.h \
+    ../src/pulse/format.h \
+    ../src/pulse/gccmacro.h \
+    ../src/pulse/glib-mainloop.h \
+    ../src/pulse/introspect.h \
+    ../src/pulse/mainloop-api.h \
+    ../src/pulse/mainloop-signal.h \
+    ../src/pulse/mainloop.h \
+    ../src/pulse/operation.h \
+    ../src/pulse/proplist.h \
+    ../src/pulse/pulseaudio.h \
+    ../src/pulse/rtclock.h \
+    ../src/pulse/sample.h \
+    ../src/pulse/scache.h \
+    ../src/pulse/simple.h \
+    ../src/pulse/stream.h \
+    ../src/pulse/subscribe.h \
+    ../src/pulse/thread-mainloop.h \
+    ../src/pulse/timeval.h \
+    ../src/pulse/utf8.h \
+    ../src/pulse/util.h \
+    ../src/pulse/version.h \
+    ../src/pulse/volume.h \
+    ../src/pulse/xmalloc.h \
 
 # If the value of the INPUT tag contains directories, you can use the
 # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp

commit da5a02e97d78d38394a6b25b7e30cb1d6db0afd0
Author: Tanu Kaskinen <tanu.kaskinen at digia.com>
Date:   Tue Apr 10 14:36:04 2012 +0300

    device-restore: Fix empty argument list declaration.

diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index 16acfd8..2a666ac 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -182,7 +182,7 @@ static pa_bool_t perportentry_write(struct userdata *u, const char *basekeyname,
 static void perportentry_free(struct perportentry* e);
 #endif
 
-static struct entry* entry_new() {
+static struct entry* entry_new(void) {
     struct entry *r = pa_xnew0(struct entry, 1);
     r->version = ENTRY_VERSION;
     return r;
@@ -239,7 +239,7 @@ static struct entry* entry_read(struct userdata *u, const char *name) {
         goto fail;
 
     t = pa_tagstruct_new(data.data, data.size);
-    e = entry_new(FALSE);
+    e = entry_new();
 
     if (pa_tagstruct_getu8(t, &e->version) < 0 ||
         e->version > ENTRY_VERSION ||

commit fb293db68df83874c2f93d7b767eb963d839b94d
Author: Ștefan Săftescu <stefan.saftescu at gmail.com>
Date:   Sat Apr 7 00:57:43 2012 +0300

    virtual-sink: Removed the option to specify sample format.
    
    BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=46529

diff --git a/src/modules/module-virtual-sink.c b/src/modules/module-virtual-sink.c
index eabd321..59e9ce6 100644
--- a/src/modules/module-virtual-sink.c
+++ b/src/modules/module-virtual-sink.c
@@ -48,7 +48,6 @@ PA_MODULE_USAGE(
         _("sink_name=<name for the sink> "
           "sink_properties=<properties for the sink> "
           "master=<name of sink to filter> "
-          "format=<sample format> "
           "rate=<sample rate> "
           "channels=<number of channels> "
           "channel_map=<channel map> "
@@ -77,7 +76,6 @@ static const char* const valid_modargs[] = {
     "sink_name",
     "sink_properties",
     "master",
-    "format",
     "rate",
     "channels",
     "channel_map",

commit 562378e62ffc5d217e18d287ce2528d40a16f1a4
Author: Tanu Kaskinen <tanu.kaskinen at digia.com>
Date:   Tue Apr 3 10:18:55 2012 +0300

    cpulimit: Explicitly ignore pa_read() return value.
    
    It doesn't matter if the function fails (I'm not sure if
    it's even possible), because the read data isn't used for
    anything and the daemon will terminate in any case. The
    void cast should get rid of a Coverity warning.
    
    Removing the whole pa_read() call should be ok too, but I
    guess it's nice to clean up the pipe before terminating...

diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c
index 3a97297..0abbac0 100644
--- a/src/daemon/cpulimit.c
+++ b/src/daemon/cpulimit.c
@@ -167,7 +167,7 @@ static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags
 
     pa_log("Received request to terminate due to CPU overload.");
 
-    pa_read(the_pipe[0], &c, sizeof(c), NULL);
+    (void) pa_read(the_pipe[0], &c, sizeof(c), NULL);
     m->quit(m, 1); /* Quit the main loop */
 }
 

commit 46eb6f11e29f8ad8f500a33d1fd3c5683fd9a7f3
Author: Tanu Kaskinen <tanu.kaskinen at digia.com>
Date:   Mon Apr 2 16:13:54 2012 +0300

    oss: Check pa_read() return value.

diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
index c4746ca..fbf0d7c 100644
--- a/src/modules/oss/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -157,13 +157,13 @@ static const char* const valid_modargs[] = {
     NULL
 };
 
-static void trigger(struct userdata *u, pa_bool_t quick) {
+static int trigger(struct userdata *u, pa_bool_t quick) {
     int enable_bits = 0, zero = 0;
 
     pa_assert(u);
 
     if (u->fd < 0)
-        return;
+        return 0;
 
     pa_log_debug("trigger");
 
@@ -210,11 +210,19 @@ static void trigger(struct userdata *u, pa_bool_t quick) {
 
             if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
                 uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size);
-                pa_read(u->fd, buf, u->in_fragment_size, NULL);
+
+                if (pa_read(u->fd, buf, u->in_fragment_size, NULL) < 0) {
+                    pa_log("pa_read() failed: %s", pa_cstrerror(errno));
+                    pa_xfree(buf);
+                    return -1;
+                }
+
                 pa_xfree(buf);
             }
         }
     }
+
+    return 0;
 }
 
 static void mmap_fill_memblocks(struct userdata *u, unsigned n) {
@@ -714,8 +722,10 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
     ret = pa_sink_process_msg(o, code, data, offset, chunk);
 
-    if (do_trigger)
-        trigger(u, quick);
+    if (ret >= 0 && do_trigger) {
+        if (trigger(u, quick) < 0)
+            return -1;
+    }
 
     return ret;
 }
@@ -794,8 +804,10 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 
     ret = pa_source_process_msg(o, code, data, offset, chunk);
 
-    if (do_trigger)
-        trigger(u, quick);
+    if (ret >= 0 && do_trigger) {
+        if (trigger(u, quick) < 0)
+            return -1;
+    }
 
     return ret;
 }

commit eb484d6c3792be18d6ae36daeabcf862fa2a6730
Author: Tanu Kaskinen <tanu.kaskinen at digia.com>
Date:   Fri Mar 30 11:04:33 2012 +0300

    mainloop-signal: Explicitly ignore pa_write() return value.
    
    Coverity warned about an ignored return value. I'm not sure
    if there's something that should be done if writing fails;
    at least I couldn't think of anything. Would logging an
    error be acceptable here?

diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c
index 9482fe3..89aafcb 100644
--- a/src/pulse/mainloop-signal.c
+++ b/src/pulse/mainloop-signal.c
@@ -72,7 +72,8 @@ static void signal_handler(int sig) {
     signal(sig, signal_handler);
 #endif
 
-    pa_write(signal_pipe[1], &sig, sizeof(sig), NULL);
+    /* XXX: If writing fails, there's nothing we can do? */
+    (void) pa_write(signal_pipe[1], &sig, sizeof(sig), NULL);
 
     errno = saved_errno;
 }

commit 2567bc10ec91def57552c69821d31afee30f55dd
Author: Tanu Kaskinen <tanu.kaskinen at digia.com>
Date:   Fri Mar 30 10:31:48 2012 +0300

    smoother-test: Fix array overflow.

diff --git a/src/tests/smoother-test.c b/src/tests/smoother-test.c
index c06f96d..eac824e 100644
--- a/src/tests/smoother-test.c
+++ b/src/tests/smoother-test.c
@@ -75,7 +75,8 @@ START_TEST (smoother_test) {
             pa_log_debug("%i\t\t%i", msec[u],  msec[u+1]);
             u += 2;
 
-            pa_smoother_resume(s, (pa_usec_t) msec[u] * PA_USEC_PER_MSEC, TRUE);
+            if (u < PA_ELEMENTSOF(msec))
+                pa_smoother_resume(s, (pa_usec_t) msec[u] * PA_USEC_PER_MSEC, TRUE);
         }
 
         pa_log_debug("%llu\t%llu", (unsigned long long) (x/PA_USEC_PER_MSEC), (unsigned long long) (pa_smoother_get(s, x)/PA_USEC_PER_MSEC));

commit 19c058dd083d2f0f2e73e82dc9b7d82952199045
Author: Tanu Kaskinen <tanu.kaskinen at digia.com>
Date:   Thu Mar 29 15:24:02 2012 +0300

    Fix pa_parse_boolean() return value checking.
    
    pa_parse_boolean() return value shouldn't be stored in
    pa_bool_t, because 1 and -1 need to be distinguished.

diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
index 8fffda1..a6ceb8b 100644
--- a/src/daemon/cmdline.c
+++ b/src/daemon/cmdline.c
@@ -170,6 +170,7 @@ void pa_cmdline_help(const char *argv0) {
 int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d) {
     pa_strbuf *buf = NULL;
     int c;
+    int b;
 
     pa_assert(conf);
     pa_assert(argc > 0);
@@ -240,17 +241,19 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
 
             case ARG_DAEMONIZE:
             case 'D':
-                if ((conf->daemonize = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
                     pa_log(_("--daemonize expects boolean argument"));
                     goto fail;
                 }
+                conf->daemonize = !!b;
                 break;
 
             case ARG_FAIL:
-                if ((conf->fail = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
                     pa_log(_("--fail expects boolean argument"));
                     goto fail;
                 }
+                conf->fail = !!b;
                 break;
 
             case 'v':
@@ -269,38 +272,43 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
                 break;
 
             case ARG_HIGH_PRIORITY:
-                if ((conf->high_priority = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
                     pa_log(_("--high-priority expects boolean argument"));
                     goto fail;
                 }
+                conf->high_priority = !!b;
                 break;
 
             case ARG_REALTIME:
-                if ((conf->realtime_scheduling = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
                     pa_log(_("--realtime expects boolean argument"));
                     goto fail;
                 }
+                conf->realtime_scheduling = !!b;
                 break;
 
             case ARG_DISALLOW_MODULE_LOADING:
-                if ((conf->disallow_module_loading = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
                     pa_log(_("--disallow-module-loading expects boolean argument"));
                     goto fail;
                 }
+                conf->disallow_module_loading = !!b;
                 break;
 
             case ARG_DISALLOW_EXIT:
-                if ((conf->disallow_exit = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
                     pa_log(_("--disallow-exit expects boolean argument"));
                     goto fail;
                 }
+                conf->disallow_exit = !!b;
                 break;
 
             case ARG_USE_PID_FILE:
-                if ((conf->use_pid_file = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
                     pa_log(_("--use-pid-file expects boolean argument"));
                     goto fail;
                 }
+                conf->use_pid_file = !!b;
                 break;
 
             case 'p':
@@ -321,17 +329,19 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
                 break;
 
             case ARG_LOG_TIME:
-                if ((conf->log_time = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
                     pa_log(_("--log-time expects boolean argument"));
                     goto fail;
                 }
+                conf->log_time = !!b;
                 break;
 
             case ARG_LOG_META:
-                if ((conf->log_meta = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
                     pa_log(_("--log-meta expects boolean argument"));
                     goto fail;
                 }
+                conf->log_meta = !!b;
                 break;
 
             case ARG_LOG_BACKTRACE:
@@ -354,24 +364,27 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
                 break;
 
             case ARG_SYSTEM:
-                if ((conf->system_instance = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
                     pa_log(_("--system expects boolean argument"));
                     goto fail;
                 }
+                conf->system_instance = !!b;
                 break;
 
             case ARG_NO_CPU_LIMIT:
-                if ((conf->no_cpu_limit = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
                     pa_log(_("--no-cpu-limit expects boolean argument"));
                     goto fail;
                 }
+                conf->no_cpu_limit = !!b;
                 break;
 
             case ARG_DISABLE_SHM:
-                if ((conf->disable_shm = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
                     pa_log(_("--disable-shm expects boolean argument"));
                     goto fail;
                 }
+                conf->disable_shm = !!b;
                 break;
 
             default:
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 8ddd3d5..1ec8054 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -1565,7 +1565,7 @@ static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
 
 static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     const char *m;
-    pa_bool_t b;
+    int b;
 
     pa_core_assert_ref(c);
     pa_assert(t);
@@ -1589,7 +1589,7 @@ static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
 
 static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     const char *m;
-    pa_bool_t b;
+    int b;
 
     pa_core_assert_ref(c);
     pa_assert(t);
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index e15520c..8d1d947 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -1622,6 +1622,8 @@ int main(int argc, char *argv[]) {
                 module_name = argv[optind + 1];
 
         } else if (pa_streq(argv[optind], "suspend-sink")) {
+            int b;
+
             action = SUSPEND_SINK;
 
             if (argc > optind+3 || optind+1 >= argc) {
@@ -1629,12 +1631,19 @@ int main(int argc, char *argv[]) {
                 goto quit;
             }
 
-            suspend = pa_parse_boolean(argv[argc-1]);
+            if ((b = pa_parse_boolean(argv[argc-1])) < 0) {
+                pa_log(_("Invalid suspend specification."));
+                goto quit;
+            }
+
+            suspend = !!b;
 
             if (argc > optind+2)
                 sink_name = pa_xstrdup(argv[optind+1]);
 
         } else if (pa_streq(argv[optind], "suspend-source")) {
+            int b;
+
             action = SUSPEND_SOURCE;
 
             if (argc > optind+3 || optind+1 >= argc) {
@@ -1642,7 +1651,12 @@ int main(int argc, char *argv[]) {
                 goto quit;
             }
 
-            suspend = pa_parse_boolean(argv[argc-1]);
+            if ((b = pa_parse_boolean(argv[argc-1])) < 0) {
+                pa_log(_("Invalid suspend specification."));
+                goto quit;
+            }
+
+            suspend = !!b;
 
             if (argc > optind+2)
                 source_name = pa_xstrdup(argv[optind+1]);

commit 188d037150feb29ce31438b1b258c6b2117f688e
Author: Tanu Kaskinen <tanu.kaskinen at digia.com>
Date:   Thu Mar 29 12:18:32 2012 +0300

    pasuspender: Check pa_context_connect() return value.

diff --git a/src/utils/pasuspender.c b/src/utils/pasuspender.c
index 0a60923..5825191 100644
--- a/src/utils/pasuspender.c
+++ b/src/utils/pasuspender.c
@@ -292,7 +292,11 @@ int main(int argc, char *argv[]) {
     }
 
     pa_context_set_state_callback(context, context_state_callback, NULL);
-    pa_context_connect(context, server, PA_CONTEXT_NOAUTOSPAWN, NULL);
+
+    if (pa_context_connect(context, server, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) {
+        fprintf(stderr, "pa_context_connect() failed: %s\n", pa_strerror(pa_context_errno(context)));
+        goto quit;
+    }
 
     if (pa_mainloop_run(m, &ret) < 0) {
         fprintf(stderr, _("pa_mainloop_run() failed.\n"));

commit 9f832ca565f1c73d0bfec389314f1ece9d2b4424
Author: Tanu Kaskinen <tanu.kaskinen at digia.com>
Date:   Thu Mar 22 12:23:13 2012 +0200

    bluetooth: Don't free read_smoother in pa__done().
    
    pa__done() calls stop_thread(), and stop_thread() already
    frees the smoother. The duplicate freeing is not strictly
    a bug, but static analyzers (in this case Coverity) may
    complain about double-freeing, because when pa__done()
    "frees" the smoother (which doesn't actually ever happen),
    the pointer is not nulled. pa__done() then calls
    bt_transport_release(), which will also free the smoother
    if it's not NULL.
    
    The analyzer complaint could be silenced also by nulling
    the pointer in pa__done(), but since this is clearly
    redundant code, I chose to remove it.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 7b95608..6962c7a 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -2626,9 +2626,6 @@ void pa__done(pa_module *m) {
     if (u->card)
         pa_card_free(u->card);
 
-    if (u->read_smoother)
-        pa_smoother_free(u->read_smoother);
-
     if (u->a2dp.buffer)
         pa_xfree(u->a2dp.buffer);
 

commit f36148a82e99b40a0adebfdf10c83e480b484847
Author: Matěj Laitl <matej at laitl.cz>
Date:   Mon Mar 19 22:44:30 2012 +0100

    qpaeq: Try to load equalizer module before failing, better error messages
    
    This fixes bug 38728 [1]. When equalizer features are unavailable in running
    pulseaudio daemon, try to load relevant module. If this fails, following error
    is printed on stderr instead of a confusing traceback:
    
    It seems that running pulseaudio does not support equalizer features and
    loading module-equalizer-sink module failed. Exiting...
    
    [1] https://bugs.freedesktop.org/show_bug.cgi?id=38728
    
    Signed-off-by: Matěj Laitl <matej at laitl.cz>

diff --git a/src/utils/qpaeq b/src/utils/qpaeq
index 5a7901e..4b00e3a 100755
--- a/src/utils/qpaeq
+++ b/src/utils/qpaeq
@@ -49,7 +49,7 @@ def connect():
     except Exception as e:
         sys.stderr.write('There was an error connecting to pulseaudio, '
                          'please make sure you have the pulseaudio dbus '
-                         'and equalizer modules loaded, exiting...\n')
+                         'module loaded, exiting...\n')
         sys.exit(-1)
 
 
@@ -67,6 +67,8 @@ class QPaeq(QtGui.QWidget):
     manager_iface='org.PulseAudio.Ext.Equalizing1.Manager'
     core_iface='org.PulseAudio.Core1'
     core_path='/org/pulseaudio/core1'
+    module_name='module-equalizer-sink'
+
     def __init__(self):
         QtGui.QWidget.__init__(self)
         self.setWindowTitle('qpaeq')
@@ -226,9 +228,25 @@ class QPaeq(QtGui.QWidget):
     #)
     def set_connection(self):
         self.connection=connect()
+
         self.manager_obj=self.connection.get_object(object_path=self.manager_path)
         manager_props=dbus.Interface(self.manager_obj,dbus_interface=prop_iface)
-        self.sinks=manager_props.Get(self.manager_iface,'EqualizedSinks')
+        try:
+            self.sinks=manager_props.Get(self.manager_iface,'EqualizedSinks')
+        except dbus.exceptions.DBusException:
+            # probably module not yet loaded, try to load it:
+            try:
+                core=self.connection.get_object(object_path=self.core_path)
+                core.LoadModule(self.module_name,{},dbus_interface=self.core_iface)
+                # yup, we don't need to re-create manager_obj and manager_props,
+                # these are late-bound
+                self.sinks=manager_props.Get(self.manager_iface,'EqualizedSinks')
+            except dbus.exceptions.DBusException:
+                sys.stderr.write('It seems that running pulseaudio does not support '
+                                 'equalizer features and loading %s module failed.\n'
+                                 'Exiting...\n' % self.module_name)
+                sys.exit(-1)
+
     def set_callbacks(self):
         manager=dbus.Interface(self.manager_obj,dbus_interface=self.manager_iface)
         manager.connect_to_signal('ProfilesChanged',self.update_profiles)

commit 7b8681de07bbeedda8a331ef7a120ea336bef5d7
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Dec 16 07:03:00 2012 +0200

    bluetooth: Make pa_bt_audio_state_from_string() private.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 0e05c98..392f4a5 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -78,7 +78,7 @@ static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, DBusM
 static void found_adapter(pa_bluetooth_discovery *y, const char *path);
 static pa_bluetooth_device *found_device(pa_bluetooth_discovery *y, const char* path);
 
-pa_bt_audio_state_t pa_bt_audio_state_from_string(const char* value) {
+static pa_bt_audio_state_t audio_state_from_string(const char* value) {
     pa_assert(value);
 
     if (pa_streq(value, "disconnected"))
@@ -529,7 +529,7 @@ static int parse_audio_property(pa_bluetooth_device *d, const char *interface, D
             dbus_message_iter_get_basic(&variant_i, &value);
 
             if (pa_streq(key, "State")) {
-                pa_bt_audio_state_t state = pa_bt_audio_state_from_string(value);
+                pa_bt_audio_state_t state = audio_state_from_string(value);
                 pa_bluetooth_transport_state_t old_state;
 
                 pa_log_debug("Device %s interface %s property 'State' changed to value '%s'", d->path, interface, value);
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 755da4c..59db33c 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -161,7 +161,6 @@ const char* pa_bluetooth_get_form_factor(uint32_t class);
 char *pa_bluetooth_cleanup_name(const char *name);
 
 pa_bool_t pa_bluetooth_uuid_has(pa_bluetooth_uuid *uuids, const char *uuid);
-pa_bt_audio_state_t pa_bt_audio_state_from_string(const char* value);
 const char *pa_bt_profile_to_string(enum profile profile);
 
 #endif

commit 51c88fb8b95bfe2c0e97177d097bc1d4681c4fc8
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Dec 16 06:57:22 2012 +0200

    bluetooth: Improve transport state change log message.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 73accc0..0e05c98 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -483,6 +483,19 @@ static int parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i) {
     return 0;
 }
 
+static const char *transport_state_to_string(pa_bluetooth_transport_state_t state) {
+    switch (state) {
+        case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED:
+            return "disconnected";
+        case PA_BLUETOOTH_TRANSPORT_STATE_IDLE:
+            return "idle";
+        case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING:
+            return "playing";
+    }
+
+    pa_assert_not_reached();
+}
+
 static int parse_audio_property(pa_bluetooth_device *d, const char *interface, DBusMessageIter *i) {
     pa_bluetooth_transport *transport;
     const char *key;
@@ -540,8 +553,9 @@ static int parse_audio_property(pa_bluetooth_device *d, const char *interface, D
                 transport->state = audio_state_to_transport_state(state);
 
                 if (transport->state != old_state) {
-                    pa_log_debug("profile=%s, transport=%s, new_state=%d", pa_bt_profile_to_string(transport->profile),
-                                 transport->path, transport->state);
+                    pa_log_debug("Transport %s (profile %s) changed state from %s to %s.", transport->path,
+                                 pa_bt_profile_to_string(transport->profile), transport_state_to_string(old_state),
+                                 transport_state_to_string(transport->state));
 
                     pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], transport);
                 }

commit e6139c8d37d43c1d40f21ffae91a68c103771994
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Dec 14 15:14:40 2012 +0100

    bluetooth: Replace acquire param 'start' with 'optional'
    
    Make the internal function bt_transport_acquire() consistent with the
    API in bluetooth-util by replacing the old 'start' parameter with
    exactly the opposite: 'optional'.
    
    Therefore, all calls to the function need to negate the second
    parameter.
    
    Note also that the name is more accurate now that setup_stream() is not
    called inside bt_transport_acquire().

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 32fbd03..7b95608 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -351,7 +351,7 @@ static void bt_transport_release(struct userdata *u) {
     teardown_stream(u);
 }
 
-static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
+static int bt_transport_acquire(struct userdata *u, pa_bool_t optional) {
     pa_assert(u->transport);
 
     if (u->transport_acquired)
@@ -359,9 +359,9 @@ static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
 
     pa_log_debug("Acquiring transport %s", u->transport->path);
 
-    u->stream_fd = pa_bluetooth_transport_acquire(u->transport, !start, &u->read_link_mtu, &u->write_link_mtu);
+    u->stream_fd = pa_bluetooth_transport_acquire(u->transport, optional, &u->read_link_mtu, &u->write_link_mtu);
     if (u->stream_fd < 0) {
-        if (start)
+        if (!optional)
             pa_log("Failed to acquire transport %s", u->transport->path);
         else
             pa_log_info("Failed optional acquire of transport %s", u->transport->path);
@@ -411,7 +411,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
                     /* Resume the device if the source was suspended as well */
                     if (!u->source || !PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
-                        if (bt_transport_acquire(u, TRUE) < 0)
+                        if (bt_transport_acquire(u, false) < 0)
                             failed = TRUE;
                         else
                             setup_stream(u);
@@ -488,7 +488,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 
                     /* Resume the device if the sink was suspended as well */
                     if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
-                        if (bt_transport_acquire(u, TRUE) < 0)
+                        if (bt_transport_acquire(u, false) < 0)
                             failed = TRUE;
                         else
                             setup_stream(u);
@@ -1286,7 +1286,7 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot
     }
 
     if (acquire)
-        if (bt_transport_acquire(u, FALSE) >= 0) {
+        if (bt_transport_acquire(u, true) >= 0) {
             if (u->source) {
                 pa_log_debug("Resuming source %s, because the bluetooth audio state changed to 'playing'.", u->source->name);
                 pa_source_suspend(u->source, FALSE, PA_SUSPEND_IDLE|PA_SUSPEND_USER);
@@ -1416,7 +1416,7 @@ static int sco_over_pcm_state_update(struct userdata *u, pa_bool_t changed) {
             return -1;
         }
 
-        if (bt_transport_acquire(u, TRUE) < 0)
+        if (bt_transport_acquire(u, false) < 0)
             return -1;
 
         setup_stream(u);
@@ -1880,8 +1880,8 @@ static int setup_transport(struct userdata *u) {
     u->transport = t;
 
     if (u->profile == PROFILE_A2DP_SOURCE || u->profile == PROFILE_HFGW)
-        bt_transport_acquire(u, FALSE); /* In case of error, the sink/sources will be created suspended */
-    else if (bt_transport_acquire(u, TRUE) < 0)
+        bt_transport_acquire(u, true); /* In case of error, the sink/sources will be created suspended */
+    else if (bt_transport_acquire(u, false) < 0)
         return -1; /* We need to fail here until the interactions with module-suspend-on-idle and alike get improved */
 
     bt_transport_config(u);

commit 04d60ae2a01836366b9058b9a39f1ea3b85cf5e3
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Dec 14 15:14:39 2012 +0100

    bluetooth: Call setup_stream() manually
    
    Do not call setup_stream() automatically inside bt_transport_acquire().
    Instead, the caller is responsible to use both functions as necessary.
    
    As a first trivial step, setup_stream() is now called manually after
    all calls to bt_transport_acquire(u, TRUE), with the exception of
    setup_transport() where the thread is still about to start and thus
    setup_stream() will be called later on from thread_func().

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 9039eb5..32fbd03 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -354,11 +354,8 @@ static void bt_transport_release(struct userdata *u) {
 static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
     pa_assert(u->transport);
 
-    if (u->transport_acquired) {
-        if (start)
-            goto done;
+    if (u->transport_acquired)
         return 0;
-    }
 
     pa_log_debug("Acquiring transport %s", u->transport->path);
 
@@ -375,16 +372,6 @@ static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
     u->transport_acquired = true;
     pa_log_info("Transport %s acquired: fd %d", u->transport->path, u->stream_fd);
 
-    if (!start)
-        return 0;
-
-done:
-    /* If thread is still about to start, the stream will be set up in the beginning of thread_func() */
-    if (u->thread == NULL)
-        return 0;
-
-    setup_stream(u);
-
     return 0;
 }
 
@@ -426,6 +413,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
                     if (!u->source || !PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
                         if (bt_transport_acquire(u, TRUE) < 0)
                             failed = TRUE;
+                        else
+                            setup_stream(u);
                     }
                     break;
 
@@ -501,6 +490,8 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
                     if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
                         if (bt_transport_acquire(u, TRUE) < 0)
                             failed = TRUE;
+                        else
+                            setup_stream(u);
                     }
                     /* We don't resume the smoother here. Instead we
                      * wait until the first packet arrives */
@@ -1425,7 +1416,12 @@ static int sco_over_pcm_state_update(struct userdata *u, pa_bool_t changed) {
             return -1;
         }
 
-        return bt_transport_acquire(u, TRUE);
+        if (bt_transport_acquire(u, TRUE) < 0)
+            return -1;
+
+        setup_stream(u);
+
+        return 0;
     }
 
     if (changed) {

commit 0fc6aa5034da26dcf05795a9caac98d043664f68
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Dec 14 15:14:38 2012 +0100

    bluetooth: Remove D-Bus dependency in module-bluetooth-device
    
    All D-Bus infrastructure is now unused after bluetooth-util has covered
    the pieces that were pending. Therefore, all D-Bus related code in
    module-bluetooth-device can be safely removed.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 91e9d15..9039eb5 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -49,7 +49,6 @@
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/time-smoother.h>
 #include <pulsecore/namereg.h>
-#include <pulsecore/dbus-shared.h>
 
 #include <sbc/sbc.h>
 
@@ -153,8 +152,6 @@ struct userdata {
     pa_bluetooth_discovery *discovery;
     pa_bool_t auto_connect;
 
-    pa_dbus_connection *connection;
-
     pa_card *card;
     pa_sink *sink;
     pa_source *source;
@@ -189,8 +186,6 @@ struct userdata {
     pa_modargs *modargs;
 
     int stream_write_type;
-
-    pa_bool_t filter_added;
 };
 
 enum {
@@ -1332,24 +1327,8 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot
 }
 
 /* Run from main thread */
-static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
-    struct userdata *u;
-
-    pa_assert(bus);
-    pa_assert(m);
-    pa_assert_se(u = userdata);
-
-    pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
-                 dbus_message_get_interface(m),
-                 dbus_message_get_path(m),
-                 dbus_message_get_member(m));
-
-    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-/* Run from main thread */
 static void sink_set_volume_cb(pa_sink *s) {
-    dbus_uint16_t gain;
+    uint16_t gain;
     pa_volume_t volume;
     struct userdata *u;
     char *k;
@@ -1376,7 +1355,7 @@ static void sink_set_volume_cb(pa_sink *s) {
 
 /* Run from main thread */
 static void source_set_volume_cb(pa_source *s) {
-    dbus_uint16_t gain;
+    uint16_t gain;
     pa_volume_t volume;
     struct userdata *u;
     char *k;
@@ -2447,23 +2426,6 @@ static pa_hook_result_t uuid_added_cb(pa_bluetooth_discovery *y, const struct pa
 }
 
 /* Run from main thread */
-static int setup_dbus(struct userdata *u) {
-    DBusError err;
-
-    dbus_error_init(&err);
-
-    u->connection = pa_dbus_bus_get(u->core, DBUS_BUS_SYSTEM, &err);
-
-    if (dbus_error_is_set(&err) || !u->connection) {
-        pa_log("Failed to get D-Bus connection: %s", err.message);
-        dbus_error_free(&err);
-        return -1;
-    }
-
-    return 0;
-}
-
-/* Run from main thread */
 static pa_hook_result_t discovery_hook_cb(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct userdata *u) {
     pa_assert(u);
     pa_assert(d);
@@ -2488,14 +2450,10 @@ int pa__init(pa_module* m) {
     uint32_t channels;
     struct userdata *u;
     const char *address, *path;
-    DBusError err;
-    char *mike, *speaker;
     pa_bluetooth_device *device;
 
     pa_assert(m);
 
-    dbus_error_init(&err);
-
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
         pa_log_error("Failed to parse module arguments");
         goto fail;
@@ -2544,9 +2502,6 @@ int pa__init(pa_module* m) {
     address = pa_modargs_get_value(ma, "address", NULL);
     path = pa_modargs_get_value(ma, "path", NULL);
 
-    if (setup_dbus(u) < 0)
-        goto fail;
-
     if (!(u->discovery = pa_bluetooth_discovery_get(m->core)))
         goto fail;
 
@@ -2597,36 +2552,6 @@ int pa__init(pa_module* m) {
     u->msg->parent.process_msg = device_process_msg;
     u->msg->card = u->card;
 
-    if (!dbus_connection_add_filter(pa_dbus_connection_get(u->connection), filter_cb, u, NULL)) {
-        pa_log_error("Failed to add filter function");
-        goto fail;
-    }
-    u->filter_added = TRUE;
-
-    speaker = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='SpeakerGainChanged',path='%s'", u->path);
-    mike = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='MicrophoneGainChanged',path='%s'", u->path);
-
-    if (pa_dbus_add_matches(
-                pa_dbus_connection_get(u->connection), &err,
-                speaker,
-                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);
-        pa_xfree(mike);
-
-        pa_log("Failed to add D-Bus matches: %s", err.message);
-        goto fail;
-    }
-
-    pa_xfree(speaker);
-    pa_xfree(mike);
-
     if (u->profile != PROFILE_OFF)
         if (init_profile(u) < 0)
             goto off;
@@ -2648,8 +2573,6 @@ fail:
 
     pa__done(m);
 
-    dbus_error_free(&err);
-
     return -1;
 }
 
@@ -2701,28 +2624,6 @@ void pa__done(pa_module *m) {
     if (USE_SCO_OVER_PCM(u))
         restore_sco_volume_callbacks(u);
 
-    if (u->connection) {
-
-        if (u->path) {
-            char *speaker, *mike;
-            speaker = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='SpeakerGainChanged',path='%s'", u->path);
-            mike = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='MicrophoneGainChanged',path='%s'", u->path);
-
-            pa_dbus_remove_matches(pa_dbus_connection_get(u->connection), speaker, mike,
-                "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
-                "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'",
-                NULL);
-
-            pa_xfree(speaker);
-            pa_xfree(mike);
-        }
-
-        if (u->filter_added)
-            dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), filter_cb, u);
-
-        pa_dbus_connection_unref(u->connection);
-    }
-
     if (u->msg)
         pa_xfree(u->msg);
 

commit d2bd3aa4445ccc84a8d4a33ae46fc35a3c446f22
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Dec 14 15:14:37 2012 +0100

    bluetooth: Use transport state instead of profile states
    
    The transport state also reflects the state of the audio interface. The
    state redundancy can thus be minimized by always using the first one,
    and avoiding the use of profile-specific states with the exception of
    finding out the initial state of a transport.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 52a335d..73accc0 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1087,7 +1087,7 @@ bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d) {
         return false;
 
     for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++)
-        if (d->profile_state[i] >= PA_BT_AUDIO_STATE_CONNECTED)
+        if (d->transports[i])
             return true;
 
     return false;
@@ -1257,6 +1257,7 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
     enum profile p;
     DBusMessageIter args, props;
     DBusMessage *r;
+    bool old_any_connected;
 
     dbus_message_iter_init(m, &args);
 
@@ -1330,6 +1331,8 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
         goto fail;
     }
 
+    old_any_connected = pa_bluetooth_device_any_audio_connected(d);
+
     sender = dbus_message_get_sender(m);
 
     t = transport_new(d, sender, path, p, config, size);
@@ -1343,6 +1346,9 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
 
     pa_assert_se(r = dbus_message_new_method_return(m));
 
+    if (old_any_connected != pa_bluetooth_device_any_audio_connected(d))
+        run_callback(d, FALSE);
+
     return r;
 
 fail:
@@ -1368,12 +1374,17 @@ static DBusMessage *endpoint_clear_configuration(DBusConnection *c, DBusMessage
     }
 
     if ((t = pa_hashmap_get(y->transports, path))) {
+        bool old_any_connected = t->device ? pa_bluetooth_device_any_audio_connected(t->device) : false;
+
         pa_log_debug("Clearing transport %s profile %d", t->path, t->profile);
         t->device->transports[t->profile] = NULL;
         pa_hashmap_remove(y->transports, t->path);
         t->state = PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED;
         pa_hook_fire(&y->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
         transport_free(t);
+
+        if (t->device && old_any_connected != pa_bluetooth_device_any_audio_connected(t->device))
+            run_callback(t->device, FALSE);
     }
 
     pa_assert_se(r = dbus_message_new_method_return(m));
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index f0fcdac..91e9d15 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -2094,7 +2094,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
     if (*d != PROFILE_OFF) {
         const pa_bluetooth_device *device = u->device;
 
-        if (device->profile_state[*d] < PA_BT_AUDIO_STATE_CONNECTED) {
+        if (!device->transports[*d]) {
             pa_log_warn("Profile not connected, refused to switch profile to %s", new_profile->name);
             return -PA_ERR_IO;
         }
@@ -2357,7 +2357,7 @@ static int add_card(struct userdata *u) {
 
     d = PA_CARD_PROFILE_DATA(u->card->active_profile);
 
-    if (*d != PROFILE_OFF && (device->profile_state[*d] < PA_BT_AUDIO_STATE_CONNECTED)) {
+    if (*d != PROFILE_OFF && !device->transports[*d]) {
         pa_log_warn("Default profile not connected, selecting off profile");
         u->card->active_profile = pa_hashmap_get(u->card->profiles, "off");
         u->card->save_profile = FALSE;

commit 468c67bb62fcc721818ad292fd85dd87aac320e5
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Dec 14 15:14:36 2012 +0100

    bluetooth: Refactor dependency to org.bluez.Audio
    
    The state of this interface is needed for one single reason: we need to
    wait until all profiles have been connected (or more precisely, until
    are connection attempts are finished). This can be made more explicit in
    the code by just checking the CONNECTING state (and not loading
    module-bluetooth-device during that state), but otherwise treating all
    transport types equally.
    
    Ideally, audio_state should be completely removed but it's left there to
    avoid an issue with module-card-restore, as documented in the source
    code's comments.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 8da327f..52a335d 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1063,13 +1063,15 @@ pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *
 }
 
 bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d) {
+    unsigned i;
+
     pa_assert(d);
 
     if (d->dead || !device_is_audio_ready(d))
         return false;
 
-    /* Deliberately ignore audio_sink_state and headset_state since they are
-     * reflected in audio_state. This is actually very important in order to
+    /* Make sure audio_state is *not* in CONNECTING state before we fire the hook
+     * to report the new device state. This is actually very important in order to
      * make module-card-restore work well with headsets: if the headset
      * supports both HSP and A2DP, one of those profiles is connected first and
      * then the other, and lastly the Audio interface becomes connected.
@@ -1081,10 +1083,14 @@ bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d) {
      * connected. Waiting until the Audio interface gets connected means that
      * both headset profiles will be connected when the device module is
      * loaded. */
-    return
-        d->audio_state >= PA_BT_AUDIO_STATE_CONNECTED ||
-        d->profile_state[PROFILE_A2DP_SOURCE] >= PA_BT_AUDIO_STATE_CONNECTED ||
-        d->profile_state[PROFILE_HFGW] >= PA_BT_AUDIO_STATE_CONNECTED;
+    if (d->audio_state == PA_BT_AUDIO_STATE_CONNECTING)
+        return false;
+
+    for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++)
+        if (d->profile_state[i] >= PA_BT_AUDIO_STATE_CONNECTED)
+            return true;
+
+    return false;
 }
 
 int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {

commit 203c6f8ed478dd716d101a9169d8fee403c037a4
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Dec 14 15:14:35 2012 +0100

    bluetooth: Trivially remove bt_transport_is_acquired()
    
    The function body is now trivial and can thus be inlined.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index d3770a8..f0fcdac 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -321,16 +321,6 @@ static void setup_stream(struct userdata *u) {
                 TRUE);
 }
 
-static bool bt_transport_is_acquired(struct userdata *u) {
-    if (!u->transport_acquired) {
-        pa_assert(u->stream_fd < 0);
-        return FALSE;
-    } else {
-        /* 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);
@@ -354,7 +344,7 @@ static void bt_transport_release(struct userdata *u) {
     pa_assert(u->transport);
 
     /* Ignore if already released */
-    if (!bt_transport_is_acquired(u))
+    if (!u->transport_acquired)
         return;
 
     pa_log_debug("Releasing transport %s", u->transport->path);
@@ -369,7 +359,7 @@ static void bt_transport_release(struct userdata *u) {
 static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
     pa_assert(u->transport);
 
-    if (bt_transport_is_acquired(u)) {
+    if (u->transport_acquired) {
         if (start)
             goto done;
         return 0;
@@ -1017,7 +1007,7 @@ static void thread_func(void *userdata) {
     pa_thread_mq_install(&u->thread_mq);
 
     /* Setup the stream only if the transport was already acquired */
-    if (bt_transport_is_acquired(u))
+    if (u->transport_acquired)
         setup_stream(u);
 
     for (;;) {
@@ -1322,7 +1312,7 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot
             }
         }
 
-    if (release && bt_transport_is_acquired(u)) {
+    if (release && u->transport_acquired) {
         /* 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
@@ -1647,7 +1637,7 @@ static int add_sink(struct userdata *u) {
         }
         connect_ports(u, &data, PA_DIRECTION_OUTPUT);
 
-        if (!bt_transport_is_acquired(u))
+        if (!u->transport_acquired)
             switch (u->profile) {
                 case PROFILE_A2DP:
                 case PROFILE_HSP:
@@ -1719,7 +1709,7 @@ static int add_source(struct userdata *u) {
 
         connect_ports(u, &data, PA_DIRECTION_INPUT);
 
-        if (!bt_transport_is_acquired(u))
+        if (!u->transport_acquired)
             switch (u->profile) {
                 case PROFILE_HSP:
                     pa_assert_not_reached(); /* Profile switch should have failed */

commit d2080a59f3c107add4b95977e4ce28e1d42643da
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Dec 14 15:14:34 2012 +0100

    bluetooth: Abstract transport access types inside bluetooth-util
    
    Transports can be acquired with different access rights, but in practice
    "rw" was always used inside module-bluetooth-device. In addition, this
    feature is removed in BlueZ 5.0 and therefore it is convenient to
    abstract all this inside bluetooth-util.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index b1ea2e3..8da327f 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1087,7 +1087,8 @@ bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d) {
         d->profile_state[PROFILE_HFGW] >= PA_BT_AUDIO_STATE_CONNECTED;
 }
 
-int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, const char *accesstype, size_t *imtu, size_t *omtu) {
+int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
+    const char *accesstype = "rw";
     DBusMessage *m, *r;
     DBusError err;
     int ret;
@@ -1097,6 +1098,20 @@ int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, const char *access
     pa_assert(t->device);
     pa_assert(t->device->discovery);
 
+    if (optional) {
+        /* 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 (t->state < PA_BLUETOOTH_TRANSPORT_STATE_PLAYING) {
+            pa_log_info("Failed optional acquire of transport %s", t->path);
+            return -1;
+        }
+    }
+
     dbus_error_init(&err);
 
     pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport", "Acquire"));
@@ -1126,7 +1141,8 @@ fail:
     return ret;
 }
 
-void pa_bluetooth_transport_release(pa_bluetooth_transport *t, const char *accesstype) {
+void pa_bluetooth_transport_release(pa_bluetooth_transport *t) {
+    const char *accesstype = "rw";
     DBusMessage *m;
     DBusError err;
 
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index fb4a211..755da4c 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -148,8 +148,8 @@ pa_bluetooth_device* pa_bluetooth_discovery_get_by_address(pa_bluetooth_discover
 
 bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d);
 
-int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, const char *accesstype, size_t *imtu, size_t *omtu);
-void pa_bluetooth_transport_release(pa_bluetooth_transport *t, const char *accesstype);
+int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu);
+void pa_bluetooth_transport_release(pa_bluetooth_transport *t);
 
 void pa_bluetooth_transport_set_microphone_gain(pa_bluetooth_transport *t, uint16_t value);
 void pa_bluetooth_transport_set_speaker_gain(pa_bluetooth_transport *t, uint16_t value);
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 179a146..d3770a8 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -141,7 +141,7 @@ struct userdata {
     char *address;
     char *path;
     pa_bluetooth_transport *transport;
-    char *accesstype;
+    bool transport_acquired;
     pa_hook_slot *discovery_slot;
     pa_hook_slot *sink_state_changed_slot;
     pa_hook_slot *source_state_changed_slot;
@@ -322,7 +322,7 @@ static void setup_stream(struct userdata *u) {
 }
 
 static bool bt_transport_is_acquired(struct userdata *u) {
-    if (u->accesstype == NULL) {
+    if (!u->transport_acquired) {
         pa_assert(u->stream_fd < 0);
         return FALSE;
     } else {
@@ -359,17 +359,14 @@ static void bt_transport_release(struct userdata *u) {
 
     pa_log_debug("Releasing transport %s", u->transport->path);
 
-    pa_bluetooth_transport_release(u->transport, u->accesstype);
+    pa_bluetooth_transport_release(u->transport);
 
-    pa_xfree(u->accesstype);
-    u->accesstype = NULL;
+    u->transport_acquired = false;
 
     teardown_stream(u);
 }
 
 static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
-    const char *accesstype = "rw";
-
     pa_assert(u->transport);
 
     if (bt_transport_is_acquired(u)) {
@@ -380,21 +377,7 @@ static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
 
     pa_log_debug("Acquiring transport %s", u->transport->path);
 
-    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 (u->device->profile_state[u->profile] < PA_BT_AUDIO_STATE_PLAYING) {
-            pa_log_info("Failed optional acquire of transport %s", u->transport->path);
-            return -1;
-        }
-    }
-
-    u->stream_fd = pa_bluetooth_transport_acquire(u->transport, accesstype, &u->read_link_mtu, &u->write_link_mtu);
+    u->stream_fd = pa_bluetooth_transport_acquire(u->transport, !start, &u->read_link_mtu, &u->write_link_mtu);
     if (u->stream_fd < 0) {
         if (start)
             pa_log("Failed to acquire transport %s", u->transport->path);
@@ -404,7 +387,7 @@ static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
         return -1;
     }
 
-    u->accesstype = pa_xstrdup(accesstype);
+    u->transport_acquired = true;
     pa_log_info("Transport %s acquired: fd %d", u->transport->path, u->stream_fd);
 
     if (!start)

commit 0c126bdf5bd6ce2b4dfe79e2c4f9dea6768b978b
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Dec 14 15:14:33 2012 +0100

    bluetooth: Use transport state to update port availability
    
    Use transport state to calculate the corresponding port availability,
    and while doing so use bluetooth-util to receive profile state updates
    instead of directly parsing D-Bus PropertyChanged signals.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index e3cfcda..b1ea2e3 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -539,8 +539,12 @@ static int parse_audio_property(pa_bluetooth_device *d, const char *interface, D
                 old_state = transport->state;
                 transport->state = audio_state_to_transport_state(state);
 
-                if (transport->state != old_state)
+                if (transport->state != old_state) {
+                    pa_log_debug("profile=%s, transport=%s, new_state=%d", pa_bt_profile_to_string(transport->profile),
+                                 transport->path, transport->state);
+
                     pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], transport);
+                }
             }
 
             break;
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 3c0b60b..179a146 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1216,77 +1216,37 @@ 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;
-}
-
-static pa_port_available_t audio_state_to_availability(pa_bt_audio_state_t state) {
-    if (state < PA_BT_AUDIO_STATE_CONNECTED)
+static pa_port_available_t transport_state_to_availability(pa_bluetooth_transport_state_t state) {
+    if (state == PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
         return PA_PORT_AVAILABLE_NO;
-    else if (state >= PA_BT_AUDIO_STATE_PLAYING)
+    else if (state >= PA_BLUETOOTH_TRANSPORT_STATE_PLAYING)
         return PA_PORT_AVAILABLE_YES;
     else
         return PA_PORT_AVAILABLE_UNKNOWN;
 }
 
-static pa_port_available_t audio_state_to_availability_merged(pa_bt_audio_state_t state1, pa_bt_audio_state_t state2) {
-    if (state1 < PA_BT_AUDIO_STATE_CONNECTED && state2 < PA_BT_AUDIO_STATE_CONNECTED)
+static pa_port_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_PORT_AVAILABLE_NO;
-    else if (state1 >= PA_BT_AUDIO_STATE_PLAYING || state2 >= PA_BT_AUDIO_STATE_PLAYING)
+    else if (state1 >= PA_BLUETOOTH_TRANSPORT_STATE_PLAYING || state2 >= PA_BLUETOOTH_TRANSPORT_STATE_PLAYING)
         return PA_PORT_AVAILABLE_YES;
     else
         return PA_PORT_AVAILABLE_UNKNOWN;
 }
 
 /* Run from main thread */
-static void handle_profile_state_change(struct userdata *u, enum profile profile, pa_bt_audio_state_t state) {
+static void handle_transport_state_change(struct userdata *u, struct pa_bluetooth_transport *transport) {
     bool acquire = FALSE;
     bool release = FALSE;
+    enum profile profile;
+    pa_bluetooth_transport_state_t state;
 
     pa_assert(u);
-    pa_assert(state != PA_BT_AUDIO_STATE_INVALID);
+    pa_assert(transport);
+
+    profile = transport->profile;
+    state = transport->state;
 
     if (!pa_hashmap_get(u->card->profiles, pa_bt_profile_to_string(profile)))
         return;
@@ -1294,7 +1254,7 @@ static void handle_profile_state_change(struct userdata *u, enum profile profile
     switch (profile) {
         case PROFILE_HFGW: {
             pa_device_port *port;
-            pa_port_available_t available = audio_state_to_availability(state);
+            pa_port_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);
@@ -1311,11 +1271,12 @@ static void handle_profile_state_change(struct userdata *u, enum profile profile
         case PROFILE_HSP: {
             pa_device_port *port;
             pa_port_available_t available;
+            pa_bluetooth_transport *other = u->device->transports[PROFILE_A2DP];
 
-            if (pa_hashmap_get(u->card->profiles, "a2dp") == NULL)
-                available = audio_state_to_availability(state);
+            if (!other)
+                available = transport_state_to_availability(state);
             else
-                available = audio_state_to_availability_merged(state, u->device->profile_state[PROFILE_A2DP]);
+                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);
@@ -1331,7 +1292,7 @@ static void handle_profile_state_change(struct userdata *u, enum profile profile
 
         case PROFILE_A2DP_SOURCE: {
             pa_device_port *port;
-            pa_port_available_t available = audio_state_to_availability(state);
+            pa_port_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);
@@ -1345,11 +1306,12 @@ static void handle_profile_state_change(struct userdata *u, enum profile profile
         case PROFILE_A2DP: {
             pa_device_port *port;
             pa_port_available_t available;
+            pa_bluetooth_transport *other = u->device->transports[PROFILE_HSP];
 
-            if (pa_hashmap_get(u->card->profiles, "hsp") == NULL)
-                available = audio_state_to_availability(state);
+            if (!other)
+                available = transport_state_to_availability(state);
             else
-                available = audio_state_to_availability_merged(state, u->device->profile_state[PROFILE_HSP]);
+                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);
@@ -1399,8 +1361,6 @@ static void handle_profile_state_change(struct userdata *u, enum profile profile
 /* Run from main thread */
 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
     struct userdata *u;
-    enum profile profile;
-    pa_bt_audio_state_t state;
 
     pa_assert(bus);
     pa_assert(m);
@@ -1411,25 +1371,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
                  dbus_message_get_path(m),
                  dbus_message_get_member(m));
 
-    if (!dbus_message_has_path(m, u->path) && (!u->transport || !dbus_message_has_path(m, u->transport->path)))
-        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
-    if (dbus_message_is_signal(m, "org.bluez.HandsfreeGateway", "PropertyChanged"))
-        profile = PROFILE_HFGW;
-    else if (dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged"))
-        profile = PROFILE_HSP;
-    else if (dbus_message_is_signal(m, "org.bluez.AudioSource", "PropertyChanged"))
-        profile = PROFILE_A2DP_SOURCE;
-    else if (dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged"))
-        profile = PROFILE_A2DP;
-    else
-        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
-    if ((state = parse_state_property_change(m)) == PA_BT_AUDIO_STATE_INVALID)
-        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
-    handle_profile_state_change(u, profile, state);
-
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
@@ -1967,6 +1908,9 @@ static pa_hook_result_t transport_state_changed_cb(pa_bluetooth_discovery *y, pa
     if (t == u->transport && t->state == PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
         pa_assert_se(pa_card_set_profile(u->card, "off", false) >= 0);
 
+    if (t->device == u->device)
+        handle_transport_state_change(u, t);
+
     return PA_HOOK_OK;
 }
 
@@ -2216,15 +2160,24 @@ static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_c
     pa_bluetooth_device *device = u->device;
     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 = audio_state_to_availability_merged(device->profile_state[PROFILE_HSP],
-                                                                     device->profile_state[PROFILE_A2DP]);
+                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));
@@ -2232,7 +2185,7 @@ static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_c
                 port->is_output = 1;
                 port->is_input = 0;
                 port->priority = profile->priority * 100;
-                port->available = audio_state_to_availability(device->profile_state[*d]);
+                port->available = transport_state_to_availability(transport_state);
                 pa_hashmap_put(port->profiles, profile->name, profile);
             }
 
@@ -2244,15 +2197,17 @@ static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_c
             port->is_output = 0;
             port->is_input = 1;
             port->priority = profile->priority * 100;
-            port->available = audio_state_to_availability(device->profile_state[*d]);
+            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 = audio_state_to_availability_merged(device->profile_state[PROFILE_HSP],
-                                                                     device->profile_state[PROFILE_A2DP]);
+                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));
@@ -2260,7 +2215,7 @@ static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_c
                 port->is_output = 1;
                 port->is_input = 0;
                 port->priority = profile->priority * 100;
-                port->available = audio_state_to_availability(device->profile_state[*d]);
+                port->available = transport_state_to_availability(transport_state);
                 pa_hashmap_put(port->profiles, profile->name, profile);
             }
 
@@ -2269,7 +2224,7 @@ static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_c
             port->is_output = 0;
             port->is_input = 1;
             port->priority = profile->priority * 100;
-            port->available = audio_state_to_availability(device->profile_state[*d]);
+            port->available = transport_state_to_availability(transport_state);
             pa_hashmap_put(port->profiles, profile->name, profile);
             break;
 
@@ -2279,7 +2234,7 @@ static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_c
             port->is_output = 1;
             port->is_input = 0;
             port->priority = profile->priority * 100;
-            port->available = audio_state_to_availability(device->profile_state[*d]);
+            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));
@@ -2287,7 +2242,7 @@ static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_c
             port->is_output = 0;
             port->is_input = 1;
             port->priority = profile->priority * 100;
-            port->available = audio_state_to_availability(device->profile_state[*d]);
+            port->available = transport_state_to_availability(transport_state);
             pa_hashmap_put(port->profiles, profile->name, profile);
             break;
 

commit 8ae8b51937581942f79677af586f95c70ab0cdd8
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Dec 14 15:14:32 2012 +0100

    bluetooth: Refactor parsing of profile state changes
    
    Split filter_cb() to separate the parsing of the D-Bus message from
    the internal logic handling the new state.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index f005b86..3c0b60b 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1281,30 +1281,18 @@ static pa_port_available_t audio_state_to_availability_merged(pa_bt_audio_state_
 }
 
 /* Run from main thread */
-static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
-    DBusError err;
-    struct userdata *u;
+static void handle_profile_state_change(struct userdata *u, enum profile profile, pa_bt_audio_state_t state) {
     bool acquire = FALSE;
     bool release = FALSE;
 
-    pa_assert(bus);
-    pa_assert(m);
-    pa_assert_se(u = userdata);
-
-    dbus_error_init(&err);
-
-    pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
-                 dbus_message_get_interface(m),
-                 dbus_message_get_path(m),
-                 dbus_message_get_member(m));
-
-    if (!dbus_message_has_path(m, u->path) && (!u->transport || !dbus_message_has_path(m, u->transport->path)))
-        goto fail;
+    pa_assert(u);
+    pa_assert(state != PA_BT_AUDIO_STATE_INVALID);
 
-    if (dbus_message_is_signal(m, "org.bluez.HandsfreeGateway", "PropertyChanged")) {
-        pa_bt_audio_state_t state = parse_state_property_change(m);
+    if (!pa_hashmap_get(u->card->profiles, pa_bt_profile_to_string(profile)))
+        return;
 
-        if (state != PA_BT_AUDIO_STATE_INVALID && pa_hashmap_get(u->card->profiles, "hfgw")) {
+    switch (profile) {
+        case PROFILE_HFGW: {
             pa_device_port *port;
             pa_port_available_t available = audio_state_to_availability(state);
 
@@ -1316,11 +1304,11 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
 
             acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_HFGW);
             release = (available != PA_PORT_AVAILABLE_YES && u->profile == PROFILE_HFGW);
+
+            break;
         }
-    } 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_hashmap_get(u->card->profiles, "hsp")) {
+        case PROFILE_HSP: {
             pa_device_port *port;
             pa_port_available_t available;
 
@@ -1337,11 +1325,11 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
 
             acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_HSP);
             release = (available != PA_PORT_AVAILABLE_YES && u->profile == PROFILE_HSP);
+
+            break;
         }
-    } 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_hashmap_get(u->card->profiles, "a2dp_source")) {
+        case PROFILE_A2DP_SOURCE: {
             pa_device_port *port;
             pa_port_available_t available = audio_state_to_availability(state);
 
@@ -1350,11 +1338,11 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
 
             acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_A2DP_SOURCE);
             release = (available != PA_PORT_AVAILABLE_YES && u->profile == PROFILE_A2DP_SOURCE);
+
+            break;
         }
-    } 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_hashmap_get(u->card->profiles, "a2dp")) {
+        case PROFILE_A2DP: {
             pa_device_port *port;
             pa_port_available_t available;
 
@@ -1368,7 +1356,12 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
 
             acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_A2DP);
             release = (available != PA_PORT_AVAILABLE_YES && u->profile == PROFILE_A2DP);
+
+            break;
         }
+
+        case PROFILE_OFF:
+            pa_assert_not_reached();
     }
 
     if (acquire)
@@ -1401,9 +1394,41 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
             pa_sink_suspend(u->sink, TRUE, PA_SUSPEND_USER);
         }
     }
+}
 
-fail:
-    dbus_error_free(&err);
+/* Run from main thread */
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
+    struct userdata *u;
+    enum profile profile;
+    pa_bt_audio_state_t state;
+
+    pa_assert(bus);
+    pa_assert(m);
+    pa_assert_se(u = userdata);
+
+    pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
+                 dbus_message_get_interface(m),
+                 dbus_message_get_path(m),
+                 dbus_message_get_member(m));
+
+    if (!dbus_message_has_path(m, u->path) && (!u->transport || !dbus_message_has_path(m, u->transport->path)))
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if (dbus_message_is_signal(m, "org.bluez.HandsfreeGateway", "PropertyChanged"))
+        profile = PROFILE_HFGW;
+    else if (dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged"))
+        profile = PROFILE_HSP;
+    else if (dbus_message_is_signal(m, "org.bluez.AudioSource", "PropertyChanged"))
+        profile = PROFILE_A2DP_SOURCE;
+    else if (dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged"))
+        profile = PROFILE_A2DP;
+    else
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if ((state = parse_state_property_change(m)) == PA_BT_AUDIO_STATE_INVALID)
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    handle_profile_state_change(u, profile, state);
 
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }

commit f5a4626ffbed1ebbbf7ca8e720b81467cd40f302
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Dec 14 15:14:31 2012 +0100

    bluetooth: Move profile_to_string() to bluetooth-util
    
    Move the function to the utility library where the enum is defined. At
    same time avoid using the default clause in order to make sure the
    compiler will complain if the enum type gets extended.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 28dc6d8..e3cfcda 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -93,6 +93,23 @@ pa_bt_audio_state_t pa_bt_audio_state_from_string(const char* value) {
     return PA_BT_AUDIO_STATE_INVALID;
 }
 
+const char *pa_bt_profile_to_string(enum profile profile) {
+    switch(profile) {
+        case PROFILE_A2DP:
+            return "a2dp";
+        case PROFILE_A2DP_SOURCE:
+            return "a2dp_source";
+        case PROFILE_HSP:
+            return "hsp";
+        case PROFILE_HFGW:
+            return "hfgw";
+        case PROFILE_OFF:
+            pa_assert_not_reached();
+    }
+
+    pa_assert_not_reached();
+}
+
 static int profile_from_interface(const char *interface, enum profile *p) {
     pa_assert(interface);
     pa_assert(p);
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index e471e0d..fb4a211 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -162,5 +162,6 @@ char *pa_bluetooth_cleanup_name(const char *name);
 
 pa_bool_t pa_bluetooth_uuid_has(pa_bluetooth_uuid *uuids, const char *uuid);
 pa_bt_audio_state_t pa_bt_audio_state_from_string(const char* value);
+const char *pa_bt_profile_to_string(enum profile profile);
 
 #endif
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 547aea3..f005b86 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1654,21 +1654,6 @@ static void connect_ports(struct userdata *u, void *sink_or_source_new_data, pa_
     }
 }
 
-static const char *profile_to_string(enum profile profile) {
-    switch(profile) {
-        case PROFILE_A2DP:
-            return "a2dp";
-        case PROFILE_A2DP_SOURCE:
-            return "a2dp_source";
-        case PROFILE_HSP:
-            return "hsp";
-        case PROFILE_HFGW:
-            return "hfgw";
-        default:
-            pa_assert_not_reached();
-    }
-}
-
 static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
     return 0;
 }
@@ -1688,7 +1673,7 @@ static int add_sink(struct userdata *u) {
 
         u->sink = u->hsp.sco_sink;
         p = pa_proplist_new();
-        pa_proplist_sets(p, "bluetooth.protocol", profile_to_string(u->profile));
+        pa_proplist_sets(p, "bluetooth.protocol", pa_bt_profile_to_string(u->profile));
         pa_proplist_update(u->sink->proplist, PA_UPDATE_MERGE, p);
         pa_proplist_free(p);
     } else {
@@ -1699,7 +1684,7 @@ static int add_sink(struct userdata *u) {
         data.driver = __FILE__;
         data.module = u->module;
         pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
-        pa_proplist_sets(data.proplist, "bluetooth.protocol", profile_to_string(u->profile));
+        pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bt_profile_to_string(u->profile));
         if (u->profile == PROFILE_HSP)
             pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
         data.card = u->card;
@@ -1760,7 +1745,7 @@ static int add_source(struct userdata *u) {
 
     if (USE_SCO_OVER_PCM(u)) {
         u->source = u->hsp.sco_source;
-        pa_proplist_sets(u->source->proplist, "bluetooth.protocol", profile_to_string(u->profile));
+        pa_proplist_sets(u->source->proplist, "bluetooth.protocol", pa_bt_profile_to_string(u->profile));
     } else {
         pa_source_new_data data;
         pa_bool_t b;
@@ -1769,7 +1754,7 @@ static int add_source(struct userdata *u) {
         data.driver = __FILE__;
         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));
+        pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bt_profile_to_string(u->profile));
         if (u->profile == PROFILE_HSP)
             pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
 

commit d6d6770a04d424b8ff621abbb62edf4ce9e73bde
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Dec 14 15:14:30 2012 +0100

    bluetooth: Use bluetooth-util for mic/speaker gain control
    
    Use the new abstraction in bluetooth-util to both receive volume updates
    and request them.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 70d3334..547aea3 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -147,6 +147,8 @@ struct userdata {
     pa_hook_slot *source_state_changed_slot;
     pa_hook_slot *transport_state_changed_slot;
     pa_hook_slot *transport_nrec_changed_slot;
+    pa_hook_slot *transport_microphone_changed_slot;
+    pa_hook_slot *transport_speaker_changed_slot;
 
     pa_bluetooth_discovery *discovery;
     pa_bool_t auto_connect;
@@ -1299,32 +1301,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
     if (!dbus_message_has_path(m, u->path) && (!u->transport || !dbus_message_has_path(m, u->transport->path)))
         goto fail;
 
-    if (dbus_message_is_signal(m, "org.bluez.Headset", "SpeakerGainChanged") ||
-        dbus_message_is_signal(m, "org.bluez.Headset", "MicrophoneGainChanged")) {
-
-        dbus_uint16_t gain;
-        pa_cvolume v;
-
-        if (!dbus_message_get_args(m, &err, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID) || gain > HSP_MAX_GAIN) {
-            pa_log("Failed to parse org.bluez.Headset.{Speaker|Microphone}GainChanged: %s", err.message);
-            goto fail;
-        }
-
-        if (u->profile == PROFILE_HSP) {
-            if (u->sink && dbus_message_is_signal(m, "org.bluez.Headset", "SpeakerGainChanged")) {
-                pa_volume_t volume = (pa_volume_t) round((double) gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
-
-                pa_cvolume_set(&v, u->sample_spec.channels, volume);
-                pa_sink_volume_changed(u->sink, &v);
-
-            } else if (u->source && dbus_message_is_signal(m, "org.bluez.Headset", "MicrophoneGainChanged")) {
-                pa_volume_t volume = (pa_volume_t) round((double) gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
-
-                pa_cvolume_set(&v, u->sample_spec.channels, volume);
-                pa_source_volume_changed(u->source, &v);
-            }
-        }
-    } else if (dbus_message_is_signal(m, "org.bluez.HandsfreeGateway", "PropertyChanged")) {
+    if (dbus_message_is_signal(m, "org.bluez.HandsfreeGateway", "PropertyChanged")) {
         pa_bt_audio_state_t state = parse_state_property_change(m);
 
         if (state != PA_BT_AUDIO_STATE_INVALID && pa_hashmap_get(u->card->profiles, "hfgw")) {
@@ -1433,7 +1410,6 @@ fail:
 
 /* Run from main thread */
 static void sink_set_volume_cb(pa_sink *s) {
-    DBusMessage *m;
     dbus_uint16_t gain;
     pa_volume_t volume;
     struct userdata *u;
@@ -1449,21 +1425,18 @@ static void sink_set_volume_cb(pa_sink *s) {
     pa_assert(u);
     pa_assert(u->sink == s);
     pa_assert(u->profile == PROFILE_HSP);
+    pa_assert(u->transport);
 
     gain = (dbus_uint16_t) round((double) pa_cvolume_max(&s->real_volume) * HSP_MAX_GAIN / PA_VOLUME_NORM);
     volume = (pa_volume_t) round((double) gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
 
     pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
 
-    pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetSpeakerGain"));
-    pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID));
-    pa_assert_se(dbus_connection_send(pa_dbus_connection_get(u->connection), m, NULL));
-    dbus_message_unref(m);
+    pa_bluetooth_transport_set_speaker_gain(u->transport, gain);
 }
 
 /* Run from main thread */
 static void source_set_volume_cb(pa_source *s) {
-    DBusMessage *m;
     dbus_uint16_t gain;
     pa_volume_t volume;
     struct userdata *u;
@@ -1479,16 +1452,14 @@ static void source_set_volume_cb(pa_source *s) {
     pa_assert(u);
     pa_assert(u->source == s);
     pa_assert(u->profile == PROFILE_HSP);
+    pa_assert(u->transport);
 
     gain = (dbus_uint16_t) round((double) pa_cvolume_max(&s->real_volume) * HSP_MAX_GAIN / PA_VOLUME_NORM);
     volume = (pa_volume_t) round((double) gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
 
     pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
 
-    pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetMicrophoneGain"));
-    pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID));
-    pa_assert_se(dbus_connection_send(pa_dbus_connection_get(u->connection), m, NULL));
-    dbus_message_unref(m);
+    pa_bluetooth_transport_set_microphone_gain(u->transport, gain);
 }
 
 /* Run from main thread */
@@ -1594,6 +1565,43 @@ static pa_hook_result_t transport_nrec_changed_cb(pa_bluetooth_discovery *y, pa_
     return PA_HOOK_OK;
 }
 
+static pa_hook_result_t transport_microphone_gain_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t,
+                                                             struct userdata *u) {
+    pa_cvolume v;
+
+    pa_assert(t);
+    pa_assert(u);
+
+    if (t != u->transport)
+        return PA_HOOK_OK;
+
+    pa_assert(u->source);
+
+    pa_cvolume_set(&v, u->sample_spec.channels,
+                   (pa_volume_t) round((double) t->microphone_gain * PA_VOLUME_NORM / HSP_MAX_GAIN));
+    pa_source_volume_changed(u->source, &v);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t transport_speaker_gain_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t,
+                                                          struct userdata *u) {
+    pa_cvolume v;
+
+    pa_assert(t);
+    pa_assert(u);
+
+    if (t != u->transport)
+        return PA_HOOK_OK;
+
+    pa_assert(u->sink);
+
+    pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) round((double) t->speaker_gain * PA_VOLUME_NORM / HSP_MAX_GAIN));
+    pa_sink_volume_changed(u->sink, &v);
+
+    return PA_HOOK_OK;
+}
+
 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;
@@ -2633,6 +2641,14 @@ int pa__init(pa_module* m) {
         pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_NREC_CHANGED),
                         PA_HOOK_NORMAL, (pa_hook_cb_t) transport_nrec_changed_cb, u);
 
+    u->transport_microphone_changed_slot =
+        pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) transport_microphone_gain_changed_cb, u);
+
+    u->transport_speaker_changed_slot =
+        pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) transport_speaker_gain_changed_cb, u);
+
     /* Add the card structure. This will also initialize the default profile */
     if (add_card(u) < 0)
         goto fail;
@@ -2738,6 +2754,12 @@ void pa__done(pa_module *m) {
     if (u->transport_nrec_changed_slot)
         pa_hook_slot_free(u->transport_nrec_changed_slot);
 
+    if (u->transport_microphone_changed_slot)
+        pa_hook_slot_free(u->transport_microphone_changed_slot);
+
+    if (u->transport_speaker_changed_slot)
+        pa_hook_slot_free(u->transport_speaker_changed_slot);
+
     if (USE_SCO_OVER_PCM(u))
         restore_sco_volume_callbacks(u);
 

commit 0b524c1078be03ffeb7833ba722d75eed6904028
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Dec 14 15:14:29 2012 +0100

    bluetooth: Abstract speaker gain in transport
    
    Similarly to the microphone gain, the speaker gain can be abstracted
    inside the transport object, even though the actual D-Bus interface in
    BlueZ differs.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 92cb408..28dc6d8 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -549,6 +549,21 @@ static int parse_audio_property(pa_bluetooth_device *d, const char *interface, D
 
                 transport->microphone_gain = gain;
                 pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED], transport);
+            } else if (pa_streq(key, "SpeakerGain")) {
+                uint16_t gain;
+
+                pa_log_debug("dbus: property '%s' changed to value '%u'", key, value);
+
+                if (!transport) {
+                    pa_log("Volume change does not have an associated transport");
+                    return -1;
+                }
+
+                if ((gain = PA_MIN(value, HSP_MAX_GAIN)) == transport->speaker_gain)
+                    break;
+
+                transport->speaker_gain = gain;
+                pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED], transport);
             }
 
             break;
@@ -1141,6 +1156,16 @@ void pa_bluetooth_transport_set_microphone_gain(pa_bluetooth_transport *t, uint1
                  "MicrophoneGain", DBUS_TYPE_UINT16, &gain);
 }
 
+void pa_bluetooth_transport_set_speaker_gain(pa_bluetooth_transport *t, uint16_t value) {
+    dbus_uint16_t gain = PA_MIN(value, HSP_MAX_GAIN);
+
+    pa_assert(t);
+    pa_assert(t->profile == PROFILE_HSP);
+
+    set_property(t->device->discovery, "org.bluez", t->device->path, "org.bluez.Headset",
+                 "SpeakerGain", DBUS_TYPE_UINT16, &gain);
+}
+
 static int setup_dbus(pa_bluetooth_discovery *y) {
     DBusError err;
 
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 4c909ab..e471e0d 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -79,6 +79,7 @@ typedef enum pa_bluetooth_hook {
     PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED, /* Call data: pa_bluetooth_transport */
     PA_BLUETOOTH_HOOK_TRANSPORT_NREC_CHANGED, /* Call data: pa_bluetooth_transport */
     PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED, /* Call data: pa_bluetooth_transport */
+    PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED, /* Call data: pa_bluetooth_transport */
     PA_BLUETOOTH_HOOK_MAX
 } pa_bluetooth_hook_t;
 
@@ -100,6 +101,7 @@ struct pa_bluetooth_transport {
     pa_bluetooth_transport_state_t state;
     pa_bool_t nrec;
     uint16_t microphone_gain; /* Used for HSP/HFP */
+    uint16_t speaker_gain; /* Used for HSP/HFP */
 };
 
 /* This enum is shared among Audio, Headset, AudioSink, and AudioSource, although not all values are acceptable in all profiles */
@@ -150,6 +152,7 @@ int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, const char *access
 void pa_bluetooth_transport_release(pa_bluetooth_transport *t, const char *accesstype);
 
 void pa_bluetooth_transport_set_microphone_gain(pa_bluetooth_transport *t, uint16_t value);
+void pa_bluetooth_transport_set_speaker_gain(pa_bluetooth_transport *t, uint16_t value);
 
 pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook);
 

commit ce24ef460ac128fa923afa6ffd974731bd62cf69
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Dec 14 15:14:28 2012 +0100

    bluetooth: Abstract microphone gain in transport
    
    The microphone gain represents the volume of the incoming audio stream
    from the headset. This can be nicely abstracted inside the transport
    object in bluetooth-util, so the modules don't have to take care about
    the D-Bus details.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index f3e9b53..92cb408 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -528,6 +528,31 @@ static int parse_audio_property(pa_bluetooth_device *d, const char *interface, D
 
             break;
         }
+
+        case DBUS_TYPE_UINT16: {
+            uint16_t value;
+
+            dbus_message_iter_get_basic(&variant_i, &value);
+
+            if (pa_streq(key, "MicrophoneGain")) {
+                uint16_t gain;
+
+                pa_log_debug("dbus: property '%s' changed to value '%u'", key, value);
+
+                if (!transport) {
+                    pa_log("Volume change does not have an associated transport");
+                    return -1;
+                }
+
+                if ((gain = PA_MIN(value, HSP_MAX_GAIN)) == transport->microphone_gain)
+                    break;
+
+                transport->microphone_gain = gain;
+                pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED], transport);
+            }
+
+            break;
+        }
     }
 
     return 0;
@@ -1086,6 +1111,36 @@ void pa_bluetooth_transport_release(pa_bluetooth_transport *t, const char *acces
         pa_log_info("Transport %s released", t->path);
 }
 
+static void set_property(pa_bluetooth_discovery *y, const char *bus, const char *path, const char *interface,
+                         const char *prop_name, int prop_type, void *prop_value) {
+    DBusMessage *m;
+    DBusMessageIter i;
+
+    pa_assert(y);
+    pa_assert(path);
+    pa_assert(interface);
+    pa_assert(prop_name);
+
+    pa_assert_se(m = dbus_message_new_method_call(bus, path, interface, "SetProperty"));
+    dbus_message_iter_init_append(m, &i);
+    dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &prop_name);
+    pa_dbus_append_basic_variant(&i, prop_type, prop_value);
+
+    dbus_message_set_no_reply(m, true);
+    pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), m, NULL));
+    dbus_message_unref(m);
+}
+
+void pa_bluetooth_transport_set_microphone_gain(pa_bluetooth_transport *t, uint16_t value) {
+    dbus_uint16_t gain = PA_MIN(value, HSP_MAX_GAIN);
+
+    pa_assert(t);
+    pa_assert(t->profile == PROFILE_HSP);
+
+    set_property(t->device->discovery, "org.bluez", t->device->path, "org.bluez.Headset",
+                 "MicrophoneGain", DBUS_TYPE_UINT16, &gain);
+}
+
 static int setup_dbus(pa_bluetooth_discovery *y) {
     DBusError err;
 
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 254eded..4c909ab 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -43,6 +43,8 @@
 #define A2DP_SOURCE_UUID        "0000110a-0000-1000-8000-00805f9b34fb"
 #define A2DP_SINK_UUID          "0000110b-0000-1000-8000-00805f9b34fb"
 
+#define HSP_MAX_GAIN 15
+
 typedef struct pa_bluetooth_uuid pa_bluetooth_uuid;
 typedef struct pa_bluetooth_device pa_bluetooth_device;
 typedef struct pa_bluetooth_discovery pa_bluetooth_discovery;
@@ -76,6 +78,7 @@ typedef enum pa_bluetooth_hook {
     PA_BLUETOOTH_HOOK_DEVICE_UUID_ADDED, /* Call data: pa_bluetooth_hook_uuid_data */
     PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED, /* Call data: pa_bluetooth_transport */
     PA_BLUETOOTH_HOOK_TRANSPORT_NREC_CHANGED, /* Call data: pa_bluetooth_transport */
+    PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED, /* Call data: pa_bluetooth_transport */
     PA_BLUETOOTH_HOOK_MAX
 } pa_bluetooth_hook_t;
 
@@ -96,6 +99,7 @@ struct pa_bluetooth_transport {
 
     pa_bluetooth_transport_state_t state;
     pa_bool_t nrec;
+    uint16_t microphone_gain; /* Used for HSP/HFP */
 };
 
 /* This enum is shared among Audio, Headset, AudioSink, and AudioSource, although not all values are acceptable in all profiles */
@@ -145,6 +149,8 @@ bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d);
 int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, const char *accesstype, size_t *imtu, size_t *omtu);
 void pa_bluetooth_transport_release(pa_bluetooth_transport *t, const char *accesstype);
 
+void pa_bluetooth_transport_set_microphone_gain(pa_bluetooth_transport *t, uint16_t value);
+
 pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook);
 
 const char* pa_bluetooth_get_form_factor(uint32_t class);
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index e5f7f6d..70d3334 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -60,7 +60,6 @@
 
 #define BITPOOL_DEC_LIMIT 32
 #define BITPOOL_DEC_STEP 5
-#define HSP_MAX_GAIN 15
 
 PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");
 PA_MODULE_DESCRIPTION("Bluetooth audio sink and source");

commit dc19d3eb299186d074d26c3e209e40f21dc7efec
Author: Stefan Huber <s.huber at bct-electronic.com>
Date:   Tue Dec 4 14:55:01 2012 +0100

    echo-cancel: Fix null implementation to setup one channel
    
    memcpy() of the null implementation's run() copied data for only one
    channel. Set the number of channels to 1 in init() in order to guarantee
    this.
    
    Signed-off-by: Stefan Huber <s.huber at bct-electronic.com>
    Acked-by: Peter Meerwald <p.meerwald at bct-electronic.com>

diff --git a/src/modules/echo-cancel/null.c b/src/modules/echo-cancel/null.c
index bcdd3a6..1820661 100644
--- a/src/modules/echo-cancel/null.c
+++ b/src/modules/echo-cancel/null.c
@@ -32,6 +32,8 @@ pa_bool_t pa_null_ec_init(pa_core *c, pa_echo_canceller *ec,
     unsigned framelen = 256;
 
     source_ss->format = PA_SAMPLE_S16NE;
+    source_ss->channels = 1;
+
     *sink_ss = *source_ss;
     *sink_map = *source_map;
 

commit 53f2964b4015b83a928d3f98253dfc59cc3d7f62
Author: Stefan Huber <s.huber at bct-electronic.com>
Date:   Tue Dec 4 14:54:58 2012 +0100

    echo-cancel: Fix apply_diff_time() to use correct sample spec
    
    apply_diff_time() fails when dropping bytes from the playback stream
    and the sample spec of sink and source differ as source's sample spec is
    used. Fix this by using sink's sample spec.
    
    Signed-off-by: Stefan Huber <s.huber at bct-electronic.com>
    Acked-by: Peter Meerwald <p.meerwald at bct-electronic.com>

diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
index 8a9823b..26ac30b 100644
--- a/src/modules/echo-cancel/module-echo-cancel.c
+++ b/src/modules/echo-cancel/module-echo-cancel.c
@@ -660,12 +660,12 @@ static void apply_diff_time(struct userdata *u, int64_t diff_time) {
     int64_t diff;
 
     if (diff_time < 0) {
-        diff = pa_usec_to_bytes(-diff_time, &u->source_output->sample_spec);
+        diff = pa_usec_to_bytes(-diff_time, &u->sink_input->sample_spec);
 
         if (diff > 0) {
             /* add some extra safety samples to compensate for jitter in the
              * timings */
-            diff += 10 * pa_frame_size (&u->source_output->sample_spec);
+            diff += 10 * pa_frame_size (&u->sink_input->sample_spec);
 
             pa_log("Playback after capture (%lld), drop sink %lld", (long long) diff_time, (long long) diff);
 

commit 4d65c9582d42bea6816063fc6246bcce3bd02146
Author: Flavio Ceolin <flavio.ceolin at profusion.mobi>
Date:   Thu Dec 13 17:00:55 2012 -0200

    role-ducking: Apply a ducking effect based on streams roles
    
    This module works pretty similar to the module-role-cork.
    It should be used as an alternative to that module. Basically
    it decreases the volume of the streams specified in ducking_roles
    in the presence of at least one stream specified in trigger_roles.
    Also, it's possible to choice the volume that will be used in the
    ducking streams and if it should operates in all devices or not.
    
    For basic reference: http://en.wikipedia.org/wiki/Ducking

diff --git a/src/Makefile.am b/src/Makefile.am
index 155f908..8c04428 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1072,7 +1072,8 @@ modlibexec_LTLIBRARIES += \
 		module-switch-on-connect.la \
 		module-switch-on-port-available.la \
 		module-filter-apply.la \
-		module-filter-heuristics.la
+		module-filter-heuristics.la \
+		module-role-ducking.la
 
 if HAVE_ESOUND
 modlibexec_LTLIBRARIES += \
@@ -1380,6 +1381,7 @@ SYMDEF_FILES = \
 		module-raop-discover-symdef.h \
 		module-gconf-symdef.h \
 		module-position-event-sounds-symdef.h \
+		module-role-ducking-symdef.h \
 		module-augment-properties-symdef.h \
 		module-role-cork-symdef.h \
 		module-console-kit-symdef.h \
@@ -1777,6 +1779,12 @@ module_position_event_sounds_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_position_event_sounds_la_LIBADD = $(MODULE_LIBADD)
 module_position_event_sounds_la_CFLAGS = $(AM_CFLAGS)
 
+# Ducking effect based on stream roles
+module_role_ducking_la_SOURCES = modules/module-role-ducking.c
+module_role_ducking_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_role_ducking_la_LIBADD = $(MODULE_LIBADD)
+module_role_ducking_la_CFLAGS = $(AM_CFLAGS)
+
 # Augment properties from XDG .desktop files
 module_augment_properties_la_SOURCES = modules/module-augment-properties.c
 module_augment_properties_la_LDFLAGS = $(MODULE_LDFLAGS)
diff --git a/src/modules/module-role-ducking.c b/src/modules/module-role-ducking.c
new file mode 100644
index 0000000..8ea43ad
--- /dev/null
+++ b/src/modules/module-role-ducking.c
@@ -0,0 +1,323 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2012 Flavio Ceolin <flavio.ceolin at profusion.mobi>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/volume.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/hook-list.h>
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/modargs.h>
+
+#include "module-role-ducking-symdef.h"
+
+PA_MODULE_AUTHOR("Flavio Ceolin <flavio.ceolin at profusion.mobi>");
+PA_MODULE_DESCRIPTION("Apply a ducking effect based on streams roles");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(
+        "trigger_roles=<Comma separated list of roles which will trigger a ducking> "
+        "ducking_roles=<Comma separated list of roles which will be ducked> "
+        "global=<Should we operate globally or only inside the same device?>"
+        "volume=<Volume for the attenuated streams. Default: -20dB"
+);
+
+static const char* const valid_modargs[] = {
+    "trigger_roles",
+    "ducking_roles",
+    "global",
+    "volume",
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    const char *name;
+    pa_idxset *trigger_roles;
+    pa_idxset *ducking_roles;
+    pa_idxset *ducked_inputs;
+    bool global;
+    pa_volume_t volume;
+    pa_hook_slot
+        *sink_input_put_slot,
+        *sink_input_unlink_slot,
+        *sink_input_move_start_slot,
+        *sink_input_move_finish_slot;
+};
+
+static bool sink_has_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_input *ignore) {
+    pa_sink_input *j;
+    uint32_t idx, role_idx;
+    const char *trigger_role;
+
+    pa_assert(u);
+    pa_sink_assert_ref(s);
+
+    PA_IDXSET_FOREACH(j, s->inputs, idx) {
+        const char *role;
+
+        if (j == ignore)
+            continue;
+
+        if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
+            continue;
+
+        PA_IDXSET_FOREACH(trigger_role, u->trigger_roles, role_idx) {
+            if (pa_streq(role, trigger_role)) {
+                pa_log_debug("Found a '%s' stream that will trigger the ducking.", trigger_role);
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+static void apply_ducking_to_sink(struct userdata *u, pa_sink *s, pa_sink_input *ignore, bool duck) {
+    pa_sink_input *j;
+    uint32_t idx, role_idx;
+    const char *ducking_role;
+    bool trigger = false;
+
+    pa_assert(u);
+    pa_sink_assert_ref(s);
+
+    PA_IDXSET_FOREACH(j, s->inputs, idx) {
+        const char *role;
+        pa_sink_input *i;
+
+        if (j == ignore)
+            continue;
+
+        if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
+            continue;
+
+        PA_IDXSET_FOREACH(ducking_role, u->ducking_roles, role_idx) {
+            if ((trigger = pa_streq(role, ducking_role)))
+                break;
+        }
+        if (!trigger)
+            continue;
+
+        i = pa_idxset_get_by_data(u->ducked_inputs, j, NULL);
+        if (duck && !i) {
+            pa_cvolume vol;
+            vol.channels = 1;
+            vol.values[0] = u->volume;
+
+            pa_log_debug("Found a '%s' stream that should be ducked.", ducking_role);
+            pa_sink_input_add_volume_factor(j, u->name, &vol);
+            pa_idxset_put(u->ducked_inputs, j, NULL);
+        } else if (!duck && i) { /* This stream should not longer be ducked */
+            pa_log_debug("Found a '%s' stream that should be unducked", ducking_role);
+            pa_idxset_remove_by_data(u->ducked_inputs, j, NULL);
+            pa_sink_input_remove_volume_factor(j, u->name);
+        }
+    }
+}
+
+static void apply_ducking(struct userdata *u, pa_sink *s, pa_sink_input *ignore, bool duck) {
+    pa_assert(u);
+
+    if (u->global) {
+        uint32_t idx;
+        PA_IDXSET_FOREACH(s, u->core->sinks, idx)
+            apply_ducking_to_sink(u, s, ignore, duck);
+    } else
+        apply_ducking_to_sink(u, s, ignore, duck);
+}
+
+static pa_hook_result_t process(struct userdata *u, pa_sink_input *i, bool duck) {
+    bool should_duck = false;
+    const char *role;
+
+    pa_assert(u);
+    pa_sink_input_assert_ref(i);
+
+    if (!(role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE)))
+        return PA_HOOK_OK;
+
+    if (!i->sink)
+        return PA_HOOK_OK;
+
+    should_duck = sink_has_trigger_streams(u, i->sink, duck ? NULL : i);
+    apply_ducking(u, i->sink, duck ? NULL : i, should_duck);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    return process(u, i, true);
+}
+
+static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_sink_input_assert_ref(i);
+
+    pa_idxset_remove_by_data(u->ducked_inputs, i, NULL);
+    return process(u, i, false);
+}
+
+static pa_hook_result_t sink_input_move_start_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    return process(u, i, false);
+}
+
+static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    return process(u, i, true);
+}
+
+int pa__init(pa_module *m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    const char *roles;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+
+    u->core = m->core;
+    u->name = m->name;
+
+    u->ducked_inputs = pa_idxset_new(NULL, NULL);
+
+    u->trigger_roles = pa_idxset_new(NULL, NULL);
+    roles = pa_modargs_get_value(ma, "trigger_roles", NULL);
+    if (roles) {
+        const char *split_state = NULL;
+        char *n = NULL;
+        while ((n = pa_split(roles, ",", &split_state))) {
+            if (n[0] != '\0')
+                pa_idxset_put(u->trigger_roles, n, NULL);
+            else
+                pa_xfree(n);
+        }
+    }
+    if (pa_idxset_isempty(u->trigger_roles)) {
+        pa_log_debug("Using role 'phone' as trigger role.");
+        pa_idxset_put(u->trigger_roles, pa_xstrdup("phone"), NULL);
+    }
+
+    u->ducking_roles = pa_idxset_new(NULL, NULL);
+    roles = pa_modargs_get_value(ma, "ducking_roles", NULL);
+    if (roles) {
+        const char *split_state = NULL;
+        char *n = NULL;
+        while ((n = pa_split(roles, ",", &split_state))) {
+            if (n[0] != '\0')
+                pa_idxset_put(u->ducking_roles, n, NULL);
+            else
+                pa_xfree(n);
+        }
+    }
+    if (pa_idxset_isempty(u->ducking_roles)) {
+        pa_log_debug("Using roles 'music' and 'video' as ducking roles.");
+        pa_idxset_put(u->ducking_roles, pa_xstrdup("music"), NULL);
+        pa_idxset_put(u->ducking_roles, pa_xstrdup("video"), NULL);
+    }
+
+    u->global = false;
+    if (pa_modargs_get_value_boolean(ma, "global", &u->global) < 0) {
+        pa_log("Failed to parse a boolean parameter: global");
+        goto fail;
+    }
+
+    u->volume = pa_sw_volume_from_dB(-20);
+    if (pa_modargs_get_value_volume(ma, "volume", &u->volume) < 0) {
+        pa_log("Failed to parse a volume parameter: volume");
+        goto fail;
+    }
+
+    u->sink_input_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_put_cb, u);
+    u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_unlink_cb, u);
+    u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_start_cb, u);
+    u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata* u;
+    pa_sink_input *i;
+    char *role;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->trigger_roles) {
+        while ((role = pa_idxset_steal_first(u->trigger_roles, NULL)))
+            pa_xfree(role);
+        pa_idxset_free(u->trigger_roles, NULL, NULL);
+    }
+    if (u->ducking_roles) {
+        while ((role = pa_idxset_steal_first(u->ducking_roles, NULL)))
+            pa_xfree(role);
+        pa_idxset_free(u->ducking_roles, NULL, NULL);
+    }
+    if (u->ducked_inputs) {
+        while ((i = pa_idxset_steal_first(u->ducked_inputs, NULL)))
+            pa_sink_input_remove_volume_factor(i, u->name);
+
+        pa_idxset_free(u->ducked_inputs, NULL, NULL);
+    }
+
+    if (u->sink_input_put_slot)
+        pa_hook_slot_free(u->sink_input_put_slot);
+    if (u->sink_input_unlink_slot)
+        pa_hook_slot_free(u->sink_input_unlink_slot);
+    if (u->sink_input_move_start_slot)
+        pa_hook_slot_free(u->sink_input_move_start_slot);
+    if (u->sink_input_move_finish_slot)
+        pa_hook_slot_free(u->sink_input_move_finish_slot);
+
+    pa_xfree(u);
+}

commit b9f6bfcadc1cf06e8ef7cc5546f2f086bd489e68
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Dec 14 09:11:01 2012 +0200

    man: Document the possiblity of unloading modules by name in pulse-cli-syntax.

diff --git a/man/pulse-cli-syntax.5.xml.in b/man/pulse-cli-syntax.5.xml.in
index 703a424..9d656b6 100644
--- a/man/pulse-cli-syntax.5.xml.in
+++ b/man/pulse-cli-syntax.5.xml.in
@@ -100,9 +100,9 @@ USA.
     </option>
 
     <option>
-      <p><opt>unload-module</opt> <arg>index</arg></p>
-      <optdesc><p>Unload a module specified by its index in the module list as
-      returned by list-modules.</p></optdesc>
+      <p><opt>unload-module</opt> <arg>index|name</arg></p>
+      <optdesc><p>Unload a module, specified either by its index in the module
+      list or its name.</p></optdesc>
     </option>
 
     <option>

commit 02d6aa648099ae701f1e48f44ab2da53e35d1c6a
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Thu Dec 13 08:26:05 2012 +0200

    core-util: Improve get_path() documentation

diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 2685c78..1f4fca1 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -2138,9 +2138,9 @@ char *pa_make_path_absolute(const char *p) {
     return r;
 }
 
-/* if fn is null return the PulseAudio run time path in s (~/.pulse)
- * if fn is non-null and starts with / return fn
- * otherwise append fn to the run time path and return it */
+/* If fn is NULL, return the PulseAudio runtime or state dir (depending on the
+ * rt parameter). If fn is non-NULL and starts with /, return fn. Otherwise,
+ * append fn to the runtime/state dir and return it. */
 static char *get_path(const char *fn, pa_bool_t prependmid, pa_bool_t rt) {
     char *rtp;
 

commit d3290e958b73c2b3fc89ce57a2aad33edca6cb7d
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Tue Dec 11 14:40:24 2012 +0100

    bluetooth: Connect all hooks during module load/unload
    
    Move the connection of sink/source-related hooks to module
    initialization and shutdown, to group all of them together. There is
    no need to connect them every time the card profile is changed.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 608f80a..e5f7f6d 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -122,8 +122,6 @@ struct hsp_info {
     void (*sco_sink_set_volume)(pa_sink *s);
     pa_source *sco_source;
     void (*sco_source_set_volume)(pa_source *s);
-    pa_hook_slot *sink_state_changed_slot;
-    pa_hook_slot *source_state_changed_slot;
 };
 
 struct bluetooth_msg {
@@ -146,6 +144,8 @@ struct userdata {
     pa_bluetooth_transport *transport;
     char *accesstype;
     pa_hook_slot *discovery_slot;
+    pa_hook_slot *sink_state_changed_slot;
+    pa_hook_slot *source_state_changed_slot;
     pa_hook_slot *transport_state_changed_slot;
     pa_hook_slot *transport_nrec_changed_slot;
 
@@ -1557,7 +1557,7 @@ static pa_hook_result_t sink_state_changed_cb(pa_core *c, pa_sink *s, struct use
     pa_sink_assert_ref(s);
     pa_assert(u);
 
-    if (s != u->hsp.sco_sink)
+    if (!USE_SCO_OVER_PCM(u) || s != u->hsp.sco_sink)
         return PA_HOOK_OK;
 
     sco_over_pcm_state_update(u, TRUE);
@@ -1570,7 +1570,7 @@ static pa_hook_result_t source_state_changed_cb(pa_core *c, pa_source *s, struct
     pa_source_assert_ref(s);
     pa_assert(u);
 
-    if (s != u->hsp.sco_source)
+    if (!USE_SCO_OVER_PCM(u) || s != u->hsp.sco_source)
         return PA_HOOK_OK;
 
     sco_over_pcm_state_update(u, TRUE);
@@ -1684,10 +1684,6 @@ static int add_sink(struct userdata *u) {
         pa_proplist_sets(p, "bluetooth.protocol", profile_to_string(u->profile));
         pa_proplist_update(u->sink->proplist, PA_UPDATE_MERGE, p);
         pa_proplist_free(p);
-
-        if (!u->hsp.sink_state_changed_slot)
-            u->hsp.sink_state_changed_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_cb, u);
-
     } else {
         pa_sink_new_data data;
         pa_bool_t b;
@@ -1758,10 +1754,6 @@ static int add_source(struct userdata *u) {
     if (USE_SCO_OVER_PCM(u)) {
         u->source = u->hsp.sco_source;
         pa_proplist_sets(u->source->proplist, "bluetooth.protocol", profile_to_string(u->profile));
-
-        if (!u->hsp.source_state_changed_slot)
-            u->hsp.source_state_changed_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_state_changed_cb, u);
-
     } else {
         pa_source_new_data data;
         pa_bool_t b;
@@ -2037,16 +2029,6 @@ static void stop_thread(struct userdata *u) {
         u->rtpoll_item = NULL;
     }
 
-    if (u->hsp.sink_state_changed_slot) {
-        pa_hook_slot_free(u->hsp.sink_state_changed_slot);
-        u->hsp.sink_state_changed_slot = NULL;
-    }
-
-    if (u->hsp.source_state_changed_slot) {
-        pa_hook_slot_free(u->hsp.source_state_changed_slot);
-        u->hsp.source_state_changed_slot = NULL;
-    }
-
     if (u->transport) {
         bt_transport_release(u);
         u->transport = NULL;
@@ -2636,6 +2618,14 @@ int pa__init(pa_module* m) {
         pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_UUID_ADDED),
                         PA_HOOK_NORMAL, (pa_hook_cb_t) uuid_added_cb, u);
 
+    u->sink_state_changed_slot =
+        pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED],
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_cb, u);
+
+    u->source_state_changed_slot =
+        pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED],
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) source_state_changed_cb, u);
+
     u->transport_state_changed_slot =
         pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED),
                         PA_HOOK_NORMAL, (pa_hook_cb_t) transport_state_changed_cb, u);
@@ -2734,6 +2724,15 @@ void pa__done(pa_module *m) {
     if (u->discovery_slot)
         pa_hook_slot_free(u->discovery_slot);
 
+    if (u->uuid_added_slot)
+        pa_hook_slot_free(u->uuid_added_slot);
+
+    if (u->sink_state_changed_slot)
+        pa_hook_slot_free(u->sink_state_changed_slot);
+
+    if (u->source_state_changed_slot)
+        pa_hook_slot_free(u->source_state_changed_slot);
+
     if (u->transport_state_changed_slot)
         pa_hook_slot_free(u->transport_state_changed_slot);
 
@@ -2765,9 +2764,6 @@ void pa__done(pa_module *m) {
         pa_dbus_connection_unref(u->connection);
     }
 
-    if (u->uuid_added_slot)
-        pa_hook_slot_free(u->uuid_added_slot);
-
     if (u->msg)
         pa_xfree(u->msg);
 

commit 29bcddcfaea607bb4898ff20ff10e9286c9dfda7
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Tue Dec 11 07:18:29 2012 +0200

    bluetooth: Remove prefixing from static functions.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 49101fe..f3e9b53 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -114,7 +114,7 @@ static int profile_from_interface(const char *interface, enum profile *p) {
     return -1;
 }
 
-static pa_bluetooth_transport_state_t pa_bt_audio_state_to_transport_state(pa_bt_audio_state_t state) {
+static pa_bluetooth_transport_state_t audio_state_to_transport_state(pa_bt_audio_state_t state) {
     switch (state) {
         case PA_BT_AUDIO_STATE_INVALID: /* Typically if state hasn't been received yet */
         case PA_BT_AUDIO_STATE_DISCONNECTED:
@@ -520,7 +520,7 @@ static int parse_audio_property(pa_bluetooth_device *d, const char *interface, D
                     break;
 
                 old_state = transport->state;
-                transport->state = pa_bt_audio_state_to_transport_state(state);
+                transport->state = audio_state_to_transport_state(state);
 
                 if (transport->state != old_state)
                     pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], transport);
@@ -796,7 +796,7 @@ static void list_adapters(pa_bluetooth_discovery *y) {
     send_and_add_to_pending(y, m, get_properties_reply, NULL);
 }
 
-static int pa_bluetooth_transport_parse_property(pa_bluetooth_transport *t, DBusMessageIter *i) {
+static int transport_parse_property(pa_bluetooth_transport *t, DBusMessageIter *i) {
     const char *key;
     DBusMessageIter variant_i;
 
@@ -954,7 +954,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
             goto fail;
         }
 
-        if (pa_bluetooth_transport_parse_property(t, &arg_i) < 0)
+        if (transport_parse_property(t, &arg_i) < 0)
             goto fail;
 
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -1118,7 +1118,7 @@ static pa_bluetooth_transport *transport_new(pa_bluetooth_device *d, const char
         memcpy(t->config, config, size);
     }
 
-    t->state = pa_bt_audio_state_to_transport_state(d->profile_state[p]);
+    t->state = audio_state_to_transport_state(d->profile_state[p]);
 
     return t;
 }

commit abcb7412990d7fa5ad36d2f94e2ce4e8a1dcb48a
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Mon Dec 10 08:30:42 2012 +0100

    bluetooth: Remove deprecated transport hooks
    
    All code has migrated to the new centralized hooks listed in
    pa_bluetooth_hook_t so the old transport-specific hooks can be removed.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 132a50e..49101fe 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -178,13 +178,8 @@ static pa_bluetooth_device* device_new(pa_bluetooth_discovery *discovery, const
 }
 
 static void transport_free(pa_bluetooth_transport *t) {
-    unsigned i;
-
     pa_assert(t);
 
-    for (i = 0; i < PA_BLUETOOTH_TRANSPORT_HOOK_MAX; i++)
-        pa_hook_done(&t->hooks[i]);
-
     pa_xfree(t->owner);
     pa_xfree(t->path);
     pa_xfree(t->config);
@@ -206,7 +201,6 @@ static void device_free(pa_bluetooth_device *d) {
         pa_hashmap_remove(d->discovery->transports, t->path);
         t->state = PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED;
         pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
-        pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_REMOVED], NULL);
         transport_free(t);
     }
 
@@ -823,7 +817,6 @@ static int pa_bluetooth_transport_parse_property(pa_bluetooth_transport *t, DBus
                 t->nrec = value;
                 pa_log_debug("Transport %s: Property 'NREC' changed to %s.", t->path, t->nrec ? "True" : "False");
                 pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_NREC_CHANGED], t);
-                pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_NREC_CHANGED], NULL);
             }
 
             break;
@@ -1112,7 +1105,6 @@ static int setup_dbus(pa_bluetooth_discovery *y) {
 static pa_bluetooth_transport *transport_new(pa_bluetooth_device *d, const char *owner, const char *path, enum profile p,
                                              const uint8_t *config, int size) {
     pa_bluetooth_transport *t;
-    unsigned i;
 
     t = pa_xnew0(pa_bluetooth_transport, 1);
     t->device = d;
@@ -1128,9 +1120,6 @@ static pa_bluetooth_transport *transport_new(pa_bluetooth_device *d, const char
 
     t->state = pa_bt_audio_state_to_transport_state(d->profile_state[p]);
 
-    for (i = 0; i < PA_BLUETOOTH_TRANSPORT_HOOK_MAX; i++)
-        pa_hook_init(&t->hooks[i], t);
-
     return t;
 }
 
@@ -1261,7 +1250,6 @@ static DBusMessage *endpoint_clear_configuration(DBusConnection *c, DBusMessage
         pa_hashmap_remove(y->transports, t->path);
         t->state = PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED;
         pa_hook_fire(&y->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
-        pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_REMOVED], NULL);
         transport_free(t);
     }
 
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index b35e533..254eded 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -79,13 +79,6 @@ typedef enum pa_bluetooth_hook {
     PA_BLUETOOTH_HOOK_MAX
 } pa_bluetooth_hook_t;
 
-/* Hook data: pa_bluetooth_transport pointer. */
-typedef enum pa_bluetooth_transport_hook {
-    PA_BLUETOOTH_TRANSPORT_HOOK_NREC_CHANGED, /* Call data: NULL. */
-    PA_BLUETOOTH_TRANSPORT_HOOK_REMOVED, /* Call data: NULL. */
-    PA_BLUETOOTH_TRANSPORT_HOOK_MAX
-} pa_bluetooth_transport_hook_t;
-
 typedef enum pa_bluetooth_transport_state {
     PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED,
     PA_BLUETOOTH_TRANSPORT_STATE_IDLE, /* Connected but not playing */
@@ -103,8 +96,6 @@ struct pa_bluetooth_transport {
 
     pa_bluetooth_transport_state_t state;
     pa_bool_t nrec;
-
-    pa_hook hooks[PA_BLUETOOTH_TRANSPORT_HOOK_MAX];
 };
 
 /* This enum is shared among Audio, Headset, AudioSink, and AudioSource, although not all values are acceptable in all profiles */

commit 7c2465d5f988e455109a046c83de64d53b732733
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Mon Dec 10 08:30:41 2012 +0100

    bluetooth: Avoid PA_BLUETOOTH_TRANSPORT_HOOK_NREC_CHANGED
    
    The hook has been recently moved to pa_bluetooth_hook_t, so make use
    of the new version.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 254e6b4..608f80a 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -124,7 +124,6 @@ struct hsp_info {
     void (*sco_source_set_volume)(pa_source *s);
     pa_hook_slot *sink_state_changed_slot;
     pa_hook_slot *source_state_changed_slot;
-    pa_hook_slot *nrec_changed_slot;
 };
 
 struct bluetooth_msg {
@@ -148,6 +147,7 @@ struct userdata {
     char *accesstype;
     pa_hook_slot *discovery_slot;
     pa_hook_slot *transport_state_changed_slot;
+    pa_hook_slot *transport_nrec_changed_slot;
 
     pa_bluetooth_discovery *discovery;
     pa_bool_t auto_connect;
@@ -1578,12 +1578,15 @@ static pa_hook_result_t source_state_changed_cb(pa_core *c, pa_source *s, struct
     return PA_HOOK_OK;
 }
 
-static pa_hook_result_t nrec_changed_cb(pa_bluetooth_transport *t, void *call_data, struct userdata *u) {
+static pa_hook_result_t transport_nrec_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) {
     pa_proplist *p;
 
     pa_assert(t);
     pa_assert(u);
 
+    if (t != u->transport)
+        return PA_HOOK_OK;
+
     p = pa_proplist_new();
     pa_proplist_sets(p, "bluetooth.nrec", t->nrec ? "1" : "0");
     pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, p);
@@ -1813,9 +1816,6 @@ static int add_source(struct userdata *u) {
     if ((u->profile == PROFILE_HSP) || (u->profile == PROFILE_HFGW)) {
         pa_bluetooth_transport *t = u->transport;
         pa_proplist_sets(u->source->proplist, "bluetooth.nrec", t->nrec ? "1" : "0");
-
-        if (!u->hsp.nrec_changed_slot)
-            u->hsp.nrec_changed_slot = pa_hook_connect(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_NREC_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) nrec_changed_cb, u);
     }
 
     if (u->profile == PROFILE_HSP) {
@@ -2047,11 +2047,6 @@ static void stop_thread(struct userdata *u) {
         u->hsp.source_state_changed_slot = NULL;
     }
 
-    if (u->hsp.nrec_changed_slot) {
-        pa_hook_slot_free(u->hsp.nrec_changed_slot);
-        u->hsp.nrec_changed_slot = NULL;
-    }
-
     if (u->transport) {
         bt_transport_release(u);
         u->transport = NULL;
@@ -2645,6 +2640,10 @@ int pa__init(pa_module* m) {
         pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED),
                         PA_HOOK_NORMAL, (pa_hook_cb_t) transport_state_changed_cb, u);
 
+    u->transport_nrec_changed_slot =
+        pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_NREC_CHANGED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) transport_nrec_changed_cb, u);
+
     /* Add the card structure. This will also initialize the default profile */
     if (add_card(u) < 0)
         goto fail;
@@ -2738,6 +2737,9 @@ void pa__done(pa_module *m) {
     if (u->transport_state_changed_slot)
         pa_hook_slot_free(u->transport_state_changed_slot);
 
+    if (u->transport_nrec_changed_slot)
+        pa_hook_slot_free(u->transport_nrec_changed_slot);
+
     if (USE_SCO_OVER_PCM(u))
         restore_sco_volume_callbacks(u);
 

commit 2a3874dc3620e1a3715cb0ebe7737c9e849c7d7f
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Mon Dec 10 08:30:40 2012 +0100

    bluetooth: Avoid PA_BLUETOOTH_TRANSPORT_HOOK_REMOVED
    
    The hook is now deprecated so avoid using it and instead use the
    recently introduced PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED which also
    reports the disconnection event.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 83b18f3..254e6b4 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -146,8 +146,8 @@ struct userdata {
     char *path;
     pa_bluetooth_transport *transport;
     char *accesstype;
-    pa_hook_slot *transport_removed_slot;
     pa_hook_slot *discovery_slot;
+    pa_hook_slot *transport_state_changed_slot;
 
     pa_bluetooth_discovery *discovery;
     pa_bool_t auto_connect;
@@ -1951,11 +1951,12 @@ static void bt_transport_config(struct userdata *u) {
 }
 
 /* Run from main thread */
-static pa_hook_result_t transport_removed_cb(pa_bluetooth_transport *t, void *call_data, struct userdata *u) {
+static pa_hook_result_t transport_state_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) {
     pa_assert(t);
     pa_assert(u);
 
-    pa_assert_se(pa_card_set_profile(u->card, "off", false) >= 0);
+    if (t == u->transport && t->state == PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
+        pa_assert_se(pa_card_set_profile(u->card, "off", false) >= 0);
 
     return PA_HOOK_OK;
 }
@@ -1977,9 +1978,6 @@ static int setup_transport(struct userdata *u) {
 
     u->transport = t;
 
-    u->transport_removed_slot = pa_hook_connect(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_REMOVED], PA_HOOK_NORMAL,
-                                                (pa_hook_cb_t) transport_removed_cb, u);
-
     if (u->profile == PROFILE_A2DP_SOURCE || u->profile == PROFILE_HFGW)
         bt_transport_acquire(u, FALSE); /* In case of error, the sink/sources will be created suspended */
     else if (bt_transport_acquire(u, TRUE) < 0)
@@ -2054,11 +2052,6 @@ static void stop_thread(struct userdata *u) {
         u->hsp.nrec_changed_slot = NULL;
     }
 
-    if (u->transport_removed_slot) {
-        pa_hook_slot_free(u->transport_removed_slot);
-        u->transport_removed_slot = NULL;
-    }
-
     if (u->transport) {
         bt_transport_release(u);
         u->transport = NULL;
@@ -2648,6 +2641,10 @@ int pa__init(pa_module* m) {
         pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_UUID_ADDED),
                         PA_HOOK_NORMAL, (pa_hook_cb_t) uuid_added_cb, u);
 
+    u->transport_state_changed_slot =
+        pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) transport_state_changed_cb, u);
+
     /* Add the card structure. This will also initialize the default profile */
     if (add_card(u) < 0)
         goto fail;
@@ -2738,6 +2735,9 @@ void pa__done(pa_module *m) {
     if (u->discovery_slot)
         pa_hook_slot_free(u->discovery_slot);
 
+    if (u->transport_state_changed_slot)
+        pa_hook_slot_free(u->transport_state_changed_slot);
+
     if (USE_SCO_OVER_PCM(u))
         restore_sco_volume_callbacks(u);
 

commit f035ffe3c02867cd551b6caf9ded6484412f9f28
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Mon Dec 10 08:30:39 2012 +0100

    bluetooth: Add transport hooks into pa_bluetooth_hook_t
    
    Add the transport-handling hooks to the centralized list of hooks in
    pa_bluetooth_hook_t. These are intended to replace the now deprecated
    transport-specific hook list in pa_bluetooth_transport_hook_t.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 9df3f9d..132a50e 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -204,6 +204,8 @@ static void device_free(pa_bluetooth_device *d) {
 
         d->transports[i] = NULL;
         pa_hashmap_remove(d->discovery->transports, t->path);
+        t->state = PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED;
+        pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
         pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_REMOVED], NULL);
         transport_free(t);
     }
@@ -504,6 +506,7 @@ static int parse_audio_property(pa_bluetooth_device *d, const char *interface, D
 
             if (pa_streq(key, "State")) {
                 pa_bt_audio_state_t state = pa_bt_audio_state_from_string(value);
+                pa_bluetooth_transport_state_t old_state;
 
                 pa_log_debug("Device %s interface %s property 'State' changed to value '%s'", d->path, interface, value);
 
@@ -522,7 +525,11 @@ static int parse_audio_property(pa_bluetooth_device *d, const char *interface, D
                 if (!transport)
                     break;
 
+                old_state = transport->state;
                 transport->state = pa_bt_audio_state_to_transport_state(state);
+
+                if (transport->state != old_state)
+                    pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], transport);
             }
 
             break;
@@ -815,6 +822,7 @@ static int pa_bluetooth_transport_parse_property(pa_bluetooth_transport *t, DBus
             if (pa_streq(key, "NREC") && t->nrec != value) {
                 t->nrec = value;
                 pa_log_debug("Transport %s: Property 'NREC' changed to %s.", t->path, t->nrec ? "True" : "False");
+                pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_NREC_CHANGED], t);
                 pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_NREC_CHANGED], NULL);
             }
 
@@ -1251,6 +1259,8 @@ static DBusMessage *endpoint_clear_configuration(DBusConnection *c, DBusMessage
         pa_log_debug("Clearing transport %s profile %d", t->path, t->profile);
         t->device->transports[t->profile] = NULL;
         pa_hashmap_remove(y->transports, t->path);
+        t->state = PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED;
+        pa_hook_fire(&y->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
         pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_REMOVED], NULL);
         transport_free(t);
     }
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index cc8b08b..b35e533 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -74,6 +74,8 @@ struct pa_bluetooth_hook_uuid_data {
 typedef enum pa_bluetooth_hook {
     PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluetooth_device */
     PA_BLUETOOTH_HOOK_DEVICE_UUID_ADDED, /* Call data: pa_bluetooth_hook_uuid_data */
+    PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED, /* Call data: pa_bluetooth_transport */
+    PA_BLUETOOTH_HOOK_TRANSPORT_NREC_CHANGED, /* Call data: pa_bluetooth_transport */
     PA_BLUETOOTH_HOOK_MAX
 } pa_bluetooth_hook_t;
 

commit 439745505a338afc7936abf77111e030b734b805
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Mon Dec 10 08:30:38 2012 +0100

    bluetooth: Add state to transport objects
    
    Transport objects have an associated state even though it's not
    explicitly exposed in BlueZ's D-Bus API (prior to 5.0). Instead, the
    state is implicitly represented in the profile-specific D-Bus interface
    (i.e. org.bluez.Headset, org.bluez.AudioSink, etc.) but it can be
    convenient that bluetooth-util would abstract this separation.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index bb8e9de..9df3f9d 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -114,6 +114,21 @@ static int profile_from_interface(const char *interface, enum profile *p) {
     return -1;
 }
 
+static pa_bluetooth_transport_state_t pa_bt_audio_state_to_transport_state(pa_bt_audio_state_t state) {
+    switch (state) {
+        case PA_BT_AUDIO_STATE_INVALID: /* Typically if state hasn't been received yet */
+        case PA_BT_AUDIO_STATE_DISCONNECTED:
+        case PA_BT_AUDIO_STATE_CONNECTING:
+            return PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED;
+        case PA_BT_AUDIO_STATE_CONNECTED:
+            return PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
+        case PA_BT_AUDIO_STATE_PLAYING:
+            return PA_BLUETOOTH_TRANSPORT_STATE_PLAYING;
+    }
+
+    pa_assert_not_reached();
+}
+
 static pa_bluetooth_uuid *uuid_new(const char *uuid) {
     pa_bluetooth_uuid *u;
 
@@ -456,6 +471,7 @@ static int parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i) {
 }
 
 static int parse_audio_property(pa_bluetooth_device *d, const char *interface, DBusMessageIter *i) {
+    pa_bluetooth_transport *transport;
     const char *key;
     DBusMessageIter variant_i;
     bool is_audio_interface;
@@ -473,6 +489,8 @@ static int parse_audio_property(pa_bluetooth_device *d, const char *interface, D
     if (key == NULL)
         return -1;
 
+    transport = p == PROFILE_OFF ? NULL : d->transports[p];
+
     dbus_message_iter_recurse(i, &variant_i);
 
 /*     pa_log_debug("Parsing property org.bluez.{Audio|AudioSink|AudioSource|Headset}.%s", key); */
@@ -500,6 +518,11 @@ static int parse_audio_property(pa_bluetooth_device *d, const char *interface, D
                 pa_assert(p != PROFILE_OFF);
 
                 d->profile_state[p] = state;
+
+                if (!transport)
+                    break;
+
+                transport->state = pa_bt_audio_state_to_transport_state(state);
             }
 
             break;
@@ -1095,6 +1118,8 @@ static pa_bluetooth_transport *transport_new(pa_bluetooth_device *d, const char
         memcpy(t->config, config, size);
     }
 
+    t->state = pa_bt_audio_state_to_transport_state(d->profile_state[p]);
+
     for (i = 0; i < PA_BLUETOOTH_TRANSPORT_HOOK_MAX; i++)
         pa_hook_init(&t->hooks[i], t);
 
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 35f91df..cc8b08b 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -84,6 +84,12 @@ typedef enum pa_bluetooth_transport_hook {
     PA_BLUETOOTH_TRANSPORT_HOOK_MAX
 } pa_bluetooth_transport_hook_t;
 
+typedef enum pa_bluetooth_transport_state {
+    PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED,
+    PA_BLUETOOTH_TRANSPORT_STATE_IDLE, /* Connected but not playing */
+    PA_BLUETOOTH_TRANSPORT_STATE_PLAYING
+} pa_bluetooth_transport_state_t;
+
 struct pa_bluetooth_transport {
     pa_bluetooth_device *device;
     char *owner;
@@ -92,6 +98,8 @@ struct pa_bluetooth_transport {
     uint8_t codec;
     uint8_t *config;
     int config_size;
+
+    pa_bluetooth_transport_state_t state;
     pa_bool_t nrec;
 
     pa_hook hooks[PA_BLUETOOTH_TRANSPORT_HOOK_MAX];

commit ab26cafaf4a61f1a6d2ed13324784f5119451f92
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Mon Dec 10 08:30:37 2012 +0100

    bluetooth: Refactor parse_audio_property() to support more properties
    
    The old implementation is limited to parsing the profile state, but
    the D-Bus API actually exposes many more properties that are currently
    not being considered, specially within org.bluez.Headset.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 3f05e46..bb8e9de 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -455,12 +455,19 @@ static int parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i) {
     return 0;
 }
 
-static int parse_audio_property(pa_bluetooth_discovery *u, int *state, DBusMessageIter *i) {
+static int parse_audio_property(pa_bluetooth_device *d, const char *interface, DBusMessageIter *i) {
     const char *key;
     DBusMessageIter variant_i;
+    bool is_audio_interface;
+    enum profile p = PROFILE_OFF;
 
-    pa_assert(u);
-    pa_assert(state);
+    pa_assert(d);
+    pa_assert(interface);
+    pa_assert(i);
+
+    if (!(is_audio_interface = pa_streq(interface, "org.bluez.Audio")))
+        if (profile_from_interface(interface, &p) < 0)
+            return 0; /* Interface not known so silently ignore property */
 
     key = check_variant_property(i);
     if (key == NULL)
@@ -478,8 +485,21 @@ static int parse_audio_property(pa_bluetooth_discovery *u, int *state, DBusMessa
             dbus_message_iter_get_basic(&variant_i, &value);
 
             if (pa_streq(key, "State")) {
-                *state = pa_bt_audio_state_from_string(value);
-                pa_log_debug("dbus: property 'State' changed to value '%s'", value);
+                pa_bt_audio_state_t state = pa_bt_audio_state_from_string(value);
+
+                pa_log_debug("Device %s interface %s property 'State' changed to value '%s'", d->path, interface, value);
+
+                if (state == PA_BT_AUDIO_STATE_INVALID)
+                    return -1;
+
+                if (is_audio_interface) {
+                    d->audio_state = state;
+                    break;
+                }
+
+                pa_assert(p != PROFILE_OFF);
+
+                d->profile_state[p] = state;
             }
 
             break;
@@ -595,7 +615,6 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
 
         if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
             DBusMessageIter dict_i;
-            enum profile profile;
 
             dbus_message_iter_recurse(&element_i, &dict_i);
 
@@ -611,18 +630,9 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
                 if (parse_device_property(d, &dict_i) < 0)
                     goto finish;
 
-            } else if (dbus_message_has_interface(p->message, "org.bluez.Audio")) {
-                if (parse_audio_property(y, &d->audio_state, &dict_i) < 0)
-                    goto finish;
+            } else if (parse_audio_property(d, dbus_message_get_interface(p->message), &dict_i) < 0)
+                goto finish;
 
-            } else if (profile_from_interface(dbus_message_get_interface(p->message), &profile) >= 0) {
-                pa_bt_audio_state_t state;
-
-                if (parse_audio_property(y, &state, &dict_i) < 0)
-                    goto finish;
-
-                d->profile_state[profile] = state;
-            }
         }
 
         dbus_message_iter_next(&element_i);
@@ -863,7 +873,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
 
         if ((d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
             DBusMessageIter arg_i;
-            enum profile profile;
             bool old_any_connected = pa_bluetooth_device_any_audio_connected(d);
 
             if (!dbus_message_iter_init(m, &arg_i)) {
@@ -875,18 +884,8 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
                 if (parse_device_property(d, &arg_i) < 0)
                     goto fail;
 
-            } else if (dbus_message_has_interface(m, "org.bluez.Audio")) {
-                if (parse_audio_property(y, &d->audio_state, &arg_i) < 0)
-                    goto fail;
-
-            } else if (profile_from_interface(dbus_message_get_interface(m), &profile) >= 0) {
-                pa_bt_audio_state_t state;
-
-                if (parse_audio_property(y, &state, &arg_i) < 0)
-                    goto fail;
-
-                d->profile_state[profile] = state;
-            }
+            } else if (parse_audio_property(d, dbus_message_get_interface(m), &arg_i) < 0)
+                goto fail;
 
             if (old_any_connected != pa_bluetooth_device_any_audio_connected(d))
                 run_callback(d, FALSE);

commit 726435045e838ad0bc848265ffbc9b418eb5ffca
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Thu Dec 6 15:55:29 2012 +0100

    bluetooth: Use array to store profile states
    
    Refactor the code to use an array of states instead of independent
    member fields, avoiding duplicated code and improving readability.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 6f784d1..3f05e46 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -93,6 +93,27 @@ pa_bt_audio_state_t pa_bt_audio_state_from_string(const char* value) {
     return PA_BT_AUDIO_STATE_INVALID;
 }
 
+static int profile_from_interface(const char *interface, enum profile *p) {
+    pa_assert(interface);
+    pa_assert(p);
+
+    if (pa_streq(interface, "org.bluez.AudioSink")) {
+        *p = PROFILE_A2DP;
+        return 0;
+    } else if (pa_streq(interface, "org.bluez.AudioSource")) {
+        *p = PROFILE_A2DP_SOURCE;
+        return 0;
+    } else if (pa_streq(interface, "org.bluez.Headset")) {
+        *p = PROFILE_HSP;
+        return 0;
+    } else if (pa_streq(interface, "org.bluez.HandsfreeGateway")) {
+        *p = PROFILE_HFGW;
+        return 0;
+    }
+
+    return -1;
+}
+
 static pa_bluetooth_uuid *uuid_new(const char *uuid) {
     pa_bluetooth_uuid *u;
 
@@ -112,6 +133,7 @@ static void uuid_free(pa_bluetooth_uuid *u) {
 
 static pa_bluetooth_device* device_new(pa_bluetooth_discovery *discovery, const char *path) {
     pa_bluetooth_device *d;
+    unsigned i;
 
     pa_assert(discovery);
     pa_assert(path);
@@ -133,10 +155,9 @@ static pa_bluetooth_device* device_new(pa_bluetooth_discovery *discovery, const
     d->trusted = -1;
 
     d->audio_state = PA_BT_AUDIO_STATE_INVALID;
-    d->audio_sink_state = PA_BT_AUDIO_STATE_INVALID;
-    d->audio_source_state = PA_BT_AUDIO_STATE_INVALID;
-    d->headset_state = PA_BT_AUDIO_STATE_INVALID;
-    d->hfgw_state = PA_BT_AUDIO_STATE_INVALID;
+
+    for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++)
+        d->profile_state[i] = PA_BT_AUDIO_STATE_INVALID;
 
     return d;
 }
@@ -185,14 +206,18 @@ static void device_free(pa_bluetooth_device *d) {
 }
 
 static pa_bool_t device_is_audio_ready(const pa_bluetooth_device *d) {
+    unsigned i;
+
     pa_assert(d);
 
-    return
-        d->device_info_valid && d->audio_state != PA_BT_AUDIO_STATE_INVALID &&
-        (d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID ||
-         d->audio_source_state != PA_BT_AUDIO_STATE_INVALID ||
-         d->headset_state != PA_BT_AUDIO_STATE_INVALID ||
-         d->hfgw_state != PA_BT_AUDIO_STATE_INVALID);
+    if (!d->device_info_valid || d->audio_state == PA_BT_AUDIO_STATE_INVALID)
+        return FALSE;
+
+    for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++)
+        if (d->profile_state[i] != PA_BT_AUDIO_STATE_INVALID)
+            return TRUE;
+
+    return FALSE;
 }
 
 static const char *check_variant_property(DBusMessageIter *i) {
@@ -570,6 +595,7 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
 
         if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
             DBusMessageIter dict_i;
+            enum profile profile;
 
             dbus_message_iter_recurse(&element_i, &dict_i);
 
@@ -589,22 +615,13 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
                 if (parse_audio_property(y, &d->audio_state, &dict_i) < 0)
                     goto finish;
 
-            } else if (dbus_message_has_interface(p->message, "org.bluez.Headset")) {
-                if (parse_audio_property(y, &d->headset_state, &dict_i) < 0)
-                    goto finish;
-
-            }  else if (dbus_message_has_interface(p->message, "org.bluez.AudioSink")) {
-                if (parse_audio_property(y, &d->audio_sink_state, &dict_i) < 0)
-                    goto finish;
+            } else if (profile_from_interface(dbus_message_get_interface(p->message), &profile) >= 0) {
+                pa_bt_audio_state_t state;
 
-            }  else if (dbus_message_has_interface(p->message, "org.bluez.AudioSource")) {
-                if (parse_audio_property(y, &d->audio_source_state, &dict_i) < 0)
-                    goto finish;
-
-            }  else if (dbus_message_has_interface(p->message, "org.bluez.HandsfreeGateway")) {
-                if (parse_audio_property(y, &d->hfgw_state, &dict_i) < 0)
+                if (parse_audio_property(y, &state, &dict_i) < 0)
                     goto finish;
 
+                d->profile_state[profile] = state;
             }
         }
 
@@ -846,6 +863,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
 
         if ((d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
             DBusMessageIter arg_i;
+            enum profile profile;
             bool old_any_connected = pa_bluetooth_device_any_audio_connected(d);
 
             if (!dbus_message_iter_init(m, &arg_i)) {
@@ -861,21 +879,13 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
                 if (parse_audio_property(y, &d->audio_state, &arg_i) < 0)
                     goto fail;
 
-            } else if (dbus_message_has_interface(m, "org.bluez.Headset")) {
-                if (parse_audio_property(y, &d->headset_state, &arg_i) < 0)
-                    goto fail;
+            } else if (profile_from_interface(dbus_message_get_interface(m), &profile) >= 0) {
+                pa_bt_audio_state_t state;
 
-            }  else if (dbus_message_has_interface(m, "org.bluez.AudioSink")) {
-                if (parse_audio_property(y, &d->audio_sink_state, &arg_i) < 0)
+                if (parse_audio_property(y, &state, &arg_i) < 0)
                     goto fail;
 
-            }  else if (dbus_message_has_interface(m, "org.bluez.AudioSource")) {
-                if (parse_audio_property(y, &d->audio_source_state, &arg_i) < 0)
-                    goto fail;
-
-            }  else if (dbus_message_has_interface(m, "org.bluez.HandsfreeGateway")) {
-                if (parse_audio_property(y, &d->hfgw_state, &arg_i) < 0)
-                    goto fail;
+                d->profile_state[profile] = state;
             }
 
             if (old_any_connected != pa_bluetooth_device_any_audio_connected(d))
@@ -989,8 +999,8 @@ bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d) {
      * loaded. */
     return
         d->audio_state >= PA_BT_AUDIO_STATE_CONNECTED ||
-        d->audio_source_state >= PA_BT_AUDIO_STATE_CONNECTED ||
-        d->hfgw_state >= PA_BT_AUDIO_STATE_CONNECTED;
+        d->profile_state[PROFILE_A2DP_SOURCE] >= PA_BT_AUDIO_STATE_CONNECTED ||
+        d->profile_state[PROFILE_HFGW] >= PA_BT_AUDIO_STATE_CONNECTED;
 }
 
 int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, const char *accesstype, size_t *imtu, size_t *omtu) {
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index ecc663c..35f91df 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -126,17 +126,8 @@ struct pa_bluetooth_device {
     /* Audio state */
     pa_bt_audio_state_t audio_state;
 
-    /* AudioSink state */
-    pa_bt_audio_state_t audio_sink_state;
-
-    /* AudioSource state */
-    pa_bt_audio_state_t audio_source_state;
-
-    /* Headset state */
-    pa_bt_audio_state_t headset_state;
-
-    /* HandsfreeGateway state */
-    pa_bt_audio_state_t hfgw_state;
+    /* AudioSink, AudioSource, Headset and HandsfreeGateway states */
+    pa_bt_audio_state_t profile_state[PA_BLUETOOTH_PROFILE_COUNT];
 };
 
 pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core);
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 649e73f..83b18f3 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -366,23 +366,6 @@ 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";
 
@@ -404,7 +387,7 @@ static int bt_transport_acquire(struct userdata *u, pa_bool_t start) {
            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, u->device) < PA_BT_AUDIO_STATE_PLAYING) {
+        if (u->device->profile_state[u->profile] < PA_BT_AUDIO_STATE_PLAYING) {
             pa_log_info("Failed optional acquire of transport %s", u->transport->path);
             return -1;
         }
@@ -1368,7 +1351,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
             if (pa_hashmap_get(u->card->profiles, "a2dp") == NULL)
                 available = audio_state_to_availability(state);
             else
-                available = audio_state_to_availability_merged(state, u->device->audio_sink_state);
+                available = audio_state_to_availability_merged(state, u->device->profile_state[PROFILE_A2DP]);
 
             pa_assert_se(port = pa_hashmap_get(u->card->ports, "bluetooth-output"));
             pa_device_port_set_available(port, available);
@@ -1402,7 +1385,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
             if (pa_hashmap_get(u->card->profiles, "hsp") == NULL)
                 available = audio_state_to_availability(state);
             else
-                available = audio_state_to_availability_merged(state, u->device->headset_state);
+                available = audio_state_to_availability_merged(state, u->device->profile_state[PROFILE_HSP]);
 
             pa_assert_se(port = pa_hashmap_get(u->card->ports, "bluetooth-output"));
             pa_device_port_set_available(port, available);
@@ -2207,17 +2190,8 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
     if (*d != PROFILE_OFF) {
         const pa_bluetooth_device *device = u->device;
 
-        if (device->headset_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HSP) {
-            pa_log_warn("HSP is not connected, refused to switch profile");
-            return -PA_ERR_IO;
-        } else if (device->audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP) {
-            pa_log_warn("A2DP Sink is not connected, refused to switch profile");
-            return -PA_ERR_IO;
-        } else if (device->audio_source_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP_SOURCE) {
-            pa_log_warn("A2DP Source is not connected, refused to switch profile");
-            return -PA_ERR_IO;
-        } else if (device->hfgw_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HFGW) {
-            pa_log_warn("HandsfreeGateway is not connected, refused to switch profile");
+        if (device->profile_state[*d] < PA_BT_AUDIO_STATE_CONNECTED) {
+            pa_log_warn("Profile not connected, refused to switch profile to %s", new_profile->name);
             return -PA_ERR_IO;
         }
     }
@@ -2262,7 +2236,8 @@ static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_c
         case PROFILE_A2DP:
             if ((port = pa_hashmap_get(ports, "bluetooth-output")) != NULL) {
                 port->priority = PA_MAX(port->priority, profile->priority * 100);
-                port->available = audio_state_to_availability_merged(device->headset_state, device->audio_sink_state);
+                port->available = audio_state_to_availability_merged(device->profile_state[PROFILE_HSP],
+                                                                     device->profile_state[PROFILE_A2DP]);
                 pa_hashmap_put(port->profiles, profile->name, profile);
             } else {
                 pa_assert_se(port = pa_device_port_new(u->core, "bluetooth-output", _("Bluetooth Output"), 0));
@@ -2270,7 +2245,7 @@ static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_c
                 port->is_output = 1;
                 port->is_input = 0;
                 port->priority = profile->priority * 100;
-                port->available = audio_state_to_availability(device->audio_sink_state);
+                port->available = audio_state_to_availability(device->profile_state[*d]);
                 pa_hashmap_put(port->profiles, profile->name, profile);
             }
 
@@ -2282,14 +2257,15 @@ static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_c
             port->is_output = 0;
             port->is_input = 1;
             port->priority = profile->priority * 100;
-            port->available = audio_state_to_availability(device->audio_source_state);
+            port->available = audio_state_to_availability(device->profile_state[*d]);
             pa_hashmap_put(port->profiles, profile->name, profile);
             break;
 
         case PROFILE_HSP:
             if ((port = pa_hashmap_get(ports, "bluetooth-output")) != NULL) {
                 port->priority = PA_MAX(port->priority, profile->priority * 100);
-                port->available = audio_state_to_availability_merged(device->headset_state, device->audio_sink_state);
+                port->available = audio_state_to_availability_merged(device->profile_state[PROFILE_HSP],
+                                                                     device->profile_state[PROFILE_A2DP]);
                 pa_hashmap_put(port->profiles, profile->name, profile);
             } else {
                 pa_assert_se(port = pa_device_port_new(u->core, "bluetooth-output", _("Bluetooth Output"), 0));
@@ -2297,7 +2273,7 @@ static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_c
                 port->is_output = 1;
                 port->is_input = 0;
                 port->priority = profile->priority * 100;
-                port->available = audio_state_to_availability(device->headset_state);
+                port->available = audio_state_to_availability(device->profile_state[*d]);
                 pa_hashmap_put(port->profiles, profile->name, profile);
             }
 
@@ -2306,7 +2282,7 @@ static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_c
             port->is_output = 0;
             port->is_input = 1;
             port->priority = profile->priority * 100;
-            port->available = audio_state_to_availability(device->headset_state);
+            port->available = audio_state_to_availability(device->profile_state[*d]);
             pa_hashmap_put(port->profiles, profile->name, profile);
             break;
 
@@ -2316,7 +2292,7 @@ static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_c
             port->is_output = 1;
             port->is_input = 0;
             port->priority = profile->priority * 100;
-            port->available = audio_state_to_availability(device->hfgw_state);
+            port->available = audio_state_to_availability(device->profile_state[*d]);
             pa_hashmap_put(port->profiles, profile->name, profile);
 
             pa_assert_se(port = pa_device_port_new(u->core, "hfgw-input", _("Bluetooth Handsfree Gateway"), 0));
@@ -2324,7 +2300,7 @@ static void create_ports_for_profile(struct userdata *u, pa_hashmap *ports, pa_c
             port->is_output = 0;
             port->is_input = 1;
             port->priority = profile->priority * 100;
-            port->available = audio_state_to_availability(device->hfgw_state);
+            port->available = audio_state_to_availability(device->profile_state[*d]);
             pa_hashmap_put(port->profiles, profile->name, profile);
             break;
 
@@ -2466,10 +2442,7 @@ static int add_card(struct userdata *u) {
 
     d = PA_CARD_PROFILE_DATA(u->card->active_profile);
 
-    if ((device->headset_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HSP) ||
-        (device->audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP) ||
-        (device->audio_source_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP_SOURCE) ||
-        (device->hfgw_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HFGW)) {
+    if (*d != PROFILE_OFF && (device->profile_state[*d] < PA_BT_AUDIO_STATE_CONNECTED)) {
         pa_log_warn("Default profile not connected, selecting off profile");
         u->card->active_profile = pa_hashmap_get(u->card->profiles, "off");
         u->card->save_profile = FALSE;

commit 689f9413ad64822d75943789c45fe89c5a43a730
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Thu Dec 6 15:55:28 2012 +0100

    bluetooth: Move device hooks into pa_bluetooth_hook_t
    
    Centralize the Bluetooth hooks in one single place, starting with
    the device hooks, while removing the duplicated ones (in this case
    PA_BLUETOOTH_DEVICE_HOOK_REMOVED).

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 5f1ff01..6f784d1 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -112,7 +112,6 @@ static void uuid_free(pa_bluetooth_uuid *u) {
 
 static pa_bluetooth_device* device_new(pa_bluetooth_discovery *discovery, const char *path) {
     pa_bluetooth_device *d;
-    unsigned i;
 
     pa_assert(discovery);
     pa_assert(path);
@@ -139,9 +138,6 @@ static pa_bluetooth_device* device_new(pa_bluetooth_discovery *discovery, const
     d->headset_state = PA_BT_AUDIO_STATE_INVALID;
     d->hfgw_state = PA_BT_AUDIO_STATE_INVALID;
 
-    for (i = 0; i < PA_BLUETOOTH_DEVICE_HOOK_MAX; i++)
-        pa_hook_init(&d->hooks[i], d);
-
     return d;
 }
 
@@ -176,9 +172,6 @@ static void device_free(pa_bluetooth_device *d) {
         transport_free(t);
     }
 
-    for (i = 0; i < PA_BLUETOOTH_DEVICE_HOOK_MAX; i++)
-        pa_hook_done(&d->hooks[i]);
-
     while ((u = d->uuids)) {
         PA_LLIST_REMOVE(pa_bluetooth_uuid, d->uuids, u);
         uuid_free(u);
@@ -385,6 +378,7 @@ static int parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i) {
                 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
                     pa_bluetooth_uuid *node;
                     const char *value;
+                    struct pa_bluetooth_hook_uuid_data uuiddata;
 
                     dbus_message_iter_get_basic(&ai, &value);
 
@@ -396,7 +390,9 @@ static int parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i) {
                     node = uuid_new(value);
                     PA_LLIST_PREPEND(pa_bluetooth_uuid, d->uuids, node);
 
-                    pa_hook_fire(&d->hooks[PA_BLUETOOTH_DEVICE_HOOK_UUID_ADDED], (char *) value);
+                    uuiddata.device = d;
+                    uuiddata.uuid = value;
+                    pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_UUID_ADDED], &uuiddata);
 
                     /* Vudentz said the interfaces are here when the UUIDs are announced */
                     if (strcasecmp(HSP_AG_UUID, value) == 0 || strcasecmp(HFP_AG_UUID, value) == 0) {
@@ -484,7 +480,6 @@ static void remove_all_devices(pa_bluetooth_discovery *y) {
     pa_assert(y);
 
     while ((d = pa_hashmap_steal_first(y->devices))) {
-        pa_hook_fire(&d->hooks[PA_BLUETOOTH_DEVICE_HOOK_REMOVED], NULL);
         run_callback(d, TRUE);
         device_free(d);
     }
@@ -808,7 +803,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
         pa_log_debug("Device %s removed", path);
 
         if ((d = pa_hashmap_remove(y->devices, path))) {
-            pa_hook_fire(&d->hooks[PA_BLUETOOTH_DEVICE_HOOK_REMOVED], NULL);
             run_callback(d, TRUE);
             device_free(d);
         }
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 63f07f3..ecc663c 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -65,9 +65,15 @@ enum profile {
 
 #define PA_BLUETOOTH_PROFILE_COUNT PROFILE_OFF
 
+struct pa_bluetooth_hook_uuid_data {
+    pa_bluetooth_device *device;
+    const char *uuid;
+};
+
 /* Hook data: pa_bluetooth_discovery pointer. */
 typedef enum pa_bluetooth_hook {
     PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluetooth_device */
+    PA_BLUETOOTH_HOOK_DEVICE_UUID_ADDED, /* Call data: pa_bluetooth_hook_uuid_data */
     PA_BLUETOOTH_HOOK_MAX
 } pa_bluetooth_hook_t;
 
@@ -100,13 +106,6 @@ typedef enum pa_bt_audio_state {
     PA_BT_AUDIO_STATE_PLAYING
 } pa_bt_audio_state_t;
 
-/* Hook data: pa_bluetooth_device pointer. */
-typedef enum pa_bluetooth_device_hook {
-    PA_BLUETOOTH_DEVICE_HOOK_REMOVED, /* Call data: NULL. */
-    PA_BLUETOOTH_DEVICE_HOOK_UUID_ADDED, /* Call data: const char *uuid. */
-    PA_BLUETOOTH_DEVICE_HOOK_MAX
-} pa_bluetooth_device_hook_t;
-
 struct pa_bluetooth_device {
     pa_bluetooth_discovery *discovery;
     pa_bool_t dead;
@@ -138,8 +137,6 @@ struct pa_bluetooth_device {
 
     /* HandsfreeGateway state */
     pa_bt_audio_state_t hfgw_state;
-
-    pa_hook hooks[PA_BLUETOOTH_DEVICE_HOOK_MAX];
 };
 
 pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core);
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 95a0db1..649e73f 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -2522,15 +2522,20 @@ static pa_bluetooth_device* find_device(struct userdata *u, const char *address,
 }
 
 /* Run from main thread */
-static pa_hook_result_t uuid_added_cb(pa_bluetooth_device *d, const char *uuid, struct userdata *u) {
+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(d);
-    pa_assert(uuid);
+    pa_assert(data);
+    pa_assert(data->device);
+    pa_assert(data->uuid);
     pa_assert(u);
 
-    p = create_card_profile(u, uuid);
+    if (data->device != u->device)
+        return PA_HOOK_OK;
+
+    p = create_card_profile(u, data->uuid);
 
     if (!p)
         return PA_HOOK_OK;
@@ -2666,8 +2671,9 @@ int pa__init(pa_module* m) {
         pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED),
                         PA_HOOK_NORMAL, (pa_hook_cb_t) discovery_hook_cb, u);
 
-    u->uuid_added_slot = pa_hook_connect(&device->hooks[PA_BLUETOOTH_DEVICE_HOOK_UUID_ADDED], PA_HOOK_NORMAL,
-                                         (pa_hook_cb_t) uuid_added_cb, u);
+    u->uuid_added_slot =
+        pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_UUID_ADDED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) uuid_added_cb, u);
 
     /* Add the card structure. This will also initialize the default profile */
     if (add_card(u) < 0)

commit e425fd61b88a34c26af2d272c9fa96eb64c21ce6
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Thu Dec 6 15:55:27 2012 +0100

    bluetooth: Extend discovery to support multiple hooks
    
    Add the infrastructure to support several hooks inside
    pa_bluetooth_discovery, while using hook names that describe more
    accurately their purpose.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 498f5bb..5f1ff01 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -69,7 +69,7 @@ struct pa_bluetooth_discovery {
     PA_LLIST_HEAD(pa_dbus_pending, pending);
     pa_hashmap *devices;
     pa_hashmap *transports;
-    pa_hook hook;
+    pa_hook hooks[PA_BLUETOOTH_HOOK_MAX];
     pa_bool_t filter_added;
 };
 
@@ -475,7 +475,7 @@ static void run_callback(pa_bluetooth_device *d, pa_bool_t dead) {
         return;
 
     d->dead = dead;
-    pa_hook_fire(&d->discovery->hook, d);
+    pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], d);
 }
 
 static void remove_all_devices(pa_bluetooth_discovery *y) {
@@ -947,7 +947,7 @@ pa_bluetooth_device* pa_bluetooth_discovery_get_by_address(pa_bluetooth_discover
     pa_assert(PA_REFCNT_VALUE(y) > 0);
     pa_assert(address);
 
-    if (!pa_hook_is_firing(&y->hook))
+    if (!pa_hook_is_firing(&y->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED]))
         pa_bluetooth_discovery_sync(y);
 
     while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
@@ -964,7 +964,7 @@ pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *
     pa_assert(PA_REFCNT_VALUE(y) > 0);
     pa_assert(path);
 
-    if (!pa_hook_is_firing(&y->hook))
+    if (!pa_hook_is_firing(&y->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED]))
         pa_bluetooth_discovery_sync(y);
 
     if ((d = pa_hashmap_get(y->devices, path)))
@@ -1453,6 +1453,7 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi
 pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
     DBusError err;
     pa_bluetooth_discovery *y;
+    unsigned i;
     static const DBusObjectPathVTable vtable_endpoint = {
         .message_function = endpoint_handler,
     };
@@ -1470,7 +1471,10 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
     y->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
     y->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
     PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
-    pa_hook_init(&y->hook, y);
+
+    for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++)
+        pa_hook_init(&y->hooks[i], y);
+
     pa_shared_set(c, "bluetooth-discovery", y);
 
     if (setup_dbus(y) < 0)
@@ -1530,6 +1534,8 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
 }
 
 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
+    unsigned i;
+
     pa_assert(y);
     pa_assert(PA_REFCNT_VALUE(y) > 0);
 
@@ -1574,7 +1580,8 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
         pa_dbus_connection_unref(y->connection);
     }
 
-    pa_hook_done(&y->hook);
+    for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++)
+        pa_hook_done(&y->hooks[i]);
 
     if (y->core)
         pa_shared_remove(y->core, "bluetooth-discovery");
@@ -1589,11 +1596,11 @@ void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *y) {
     pa_dbus_sync_pending_list(&y->pending);
 }
 
-pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y) {
+pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook) {
     pa_assert(y);
     pa_assert(PA_REFCNT_VALUE(y) > 0);
 
-    return &y->hook;
+    return &y->hooks[hook];
 }
 
 const char*pa_bluetooth_get_form_factor(uint32_t class) {
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 59d0d2e..63f07f3 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -65,6 +65,12 @@ enum profile {
 
 #define PA_BLUETOOTH_PROFILE_COUNT PROFILE_OFF
 
+/* Hook data: pa_bluetooth_discovery pointer. */
+typedef enum pa_bluetooth_hook {
+    PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluetooth_device */
+    PA_BLUETOOTH_HOOK_MAX
+} pa_bluetooth_hook_t;
+
 /* Hook data: pa_bluetooth_transport pointer. */
 typedef enum pa_bluetooth_transport_hook {
     PA_BLUETOOTH_TRANSPORT_HOOK_NREC_CHANGED, /* Call data: NULL. */
@@ -150,7 +156,7 @@ bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d);
 int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, const char *accesstype, size_t *imtu, size_t *omtu);
 void pa_bluetooth_transport_release(pa_bluetooth_transport *t, const char *accesstype);
 
-pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *d);
+pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook);
 
 const char* pa_bluetooth_get_form_factor(uint32_t class);
 
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index da1b61f..95a0db1 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -2660,11 +2660,12 @@ int pa__init(pa_module* m) {
     if (!(device = find_device(u, address, path)))
         goto fail;
 
-    u->discovery_slot = pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery), PA_HOOK_NORMAL,
-                                        (pa_hook_cb_t) discovery_hook_cb, u);
-
     u->device = device;
 
+    u->discovery_slot =
+        pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED),
+                        PA_HOOK_NORMAL, (pa_hook_cb_t) discovery_hook_cb, u);
+
     u->uuid_added_slot = pa_hook_connect(&device->hooks[PA_BLUETOOTH_DEVICE_HOOK_UUID_ADDED], PA_HOOK_NORMAL,
                                          (pa_hook_cb_t) uuid_added_cb, u);
 
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 48d0bee..41981f6 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -152,7 +152,8 @@ int pa__init(pa_module* m) {
     if (!(u->discovery = pa_bluetooth_discovery_get(u->core)))
         goto fail;
 
-    u->slot = pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery), PA_HOOK_NORMAL, (pa_hook_cb_t) load_module_for_device, u);
+    u->slot = pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED),
+                              PA_HOOK_NORMAL, (pa_hook_cb_t) load_module_for_device, u);
 
     if (!async)
         pa_bluetooth_discovery_sync(u->discovery);

commit c8944dbb4bebd62de0dd6065b2141a77d3c8a12a
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Thu Dec 6 15:55:26 2012 +0100

    bluetooth: Avoid PA_BLUETOOTH_DEVICE_HOOK_REMOVED
    
    The hook PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED gets fired also
    when a device is being removed, so there is actually no need to have
    this duplicated hook.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 4743db8..da1b61f 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -147,7 +147,6 @@ struct userdata {
     pa_bluetooth_transport *transport;
     char *accesstype;
     pa_hook_slot *transport_removed_slot;
-    pa_hook_slot *device_removed_slot;
     pa_hook_slot *discovery_slot;
 
     pa_bluetooth_discovery *discovery;
@@ -2572,17 +2571,6 @@ static int setup_dbus(struct userdata *u) {
 }
 
 /* Run from main thread */
-static pa_hook_result_t device_removed_cb(pa_bluetooth_device *d, void *call_data, struct userdata *u) {
-    pa_assert(d);
-    pa_assert(u);
-
-    pa_log_debug("Device %s removed: unloading module", d->path);
-    pa_module_unload(u->core, u->module, TRUE);
-
-    return PA_HOOK_OK;
-}
-
-/* Run from main thread */
 static pa_hook_result_t discovery_hook_cb(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct userdata *u) {
     pa_assert(u);
     pa_assert(d);
@@ -2590,10 +2578,13 @@ static pa_hook_result_t discovery_hook_cb(pa_bluetooth_discovery *y, const pa_bl
     if (d != u->device)
         return PA_HOOK_OK;
 
-    if (pa_bluetooth_device_any_audio_connected(d))
+    if (d->dead)
+        pa_log_debug("Device %s removed: unloading module", d->path);
+    else if (!pa_bluetooth_device_any_audio_connected(d))
+        pa_log_debug("Unloading module, because device %s doesn't have any audio profiles connected anymore.", d->path);
+    else
         return PA_HOOK_OK;
 
-    pa_log_debug("Unloading module, because device %s doesn't have any audio profiles connected anymore.", d->path);
     pa_module_unload(u->core, u->module, true);
 
     return PA_HOOK_OK;
@@ -2669,9 +2660,6 @@ int pa__init(pa_module* m) {
     if (!(device = find_device(u, address, path)))
         goto fail;
 
-    u->device_removed_slot = pa_hook_connect(&device->hooks[PA_BLUETOOTH_DEVICE_HOOK_REMOVED], PA_HOOK_NORMAL,
-                                             (pa_hook_cb_t) device_removed_cb, u);
-
     u->discovery_slot = pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery), PA_HOOK_NORMAL,
                                         (pa_hook_cb_t) discovery_hook_cb, u);
 
@@ -2770,9 +2758,6 @@ void pa__done(pa_module *m) {
     if (u->discovery_slot)
         pa_hook_slot_free(u->discovery_slot);
 
-    if (u->device_removed_slot)
-        pa_hook_slot_free(u->device_removed_slot);
-
     if (USE_SCO_OVER_PCM(u))
         restore_sco_volume_callbacks(u);
 

commit 0a8068d28eacf90459dc062adf5e892069fb6ae6
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Thu Dec 6 06:49:22 2012 +0200

    bluetooth: Check if BlueZ tries to set the configuration twice for the same transport.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 91ad269..498f5bb 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1113,6 +1113,12 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
     dbus_message_iter_init(m, &args);
 
     dbus_message_iter_get_basic(&args, &path);
+
+    if (pa_hashmap_get(y->transports, path)) {
+        pa_log("org.bluez.MediaEndpoint.SetConfiguration: Transport %s is already configured.", path);
+        goto fail;
+    }
+
     if (!dbus_message_iter_next(&args))
         goto fail;
 
@@ -1183,7 +1189,7 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
         t->nrec = nrec;
 
     d->transports[p] = t;
-    pa_hashmap_put(y->transports, t->path, t);
+    pa_assert_se(pa_hashmap_put(y->transports, t->path, t) >= 0);
 
     pa_log_debug("Transport %s profile %d available", t->path, t->profile);
 

commit ebdb18bca43963eda86509ac1edceb0e472c57c0
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Thu Dec 6 10:35:20 2012 +0100

    bluetooth: Remove pa_bluetooth_device_get_transport()
    
    With the use of an array to represent a device's transpors, the function
    becomes trivial and thus can be removed.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 313df2b..91ad269 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -974,15 +974,6 @@ pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *
     return NULL;
 }
 
-pa_bluetooth_transport* pa_bluetooth_device_get_transport(pa_bluetooth_device *d, enum profile profile) {
-    pa_assert(d);
-
-    if (profile == PROFILE_OFF)
-        return NULL;
-
-    return d->transports[profile];
-}
-
 bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d) {
     pa_assert(d);
 
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index f6ffece..59d0d2e 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -145,7 +145,6 @@ void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *d);
 pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *d, const char* path);
 pa_bluetooth_device* pa_bluetooth_discovery_get_by_address(pa_bluetooth_discovery *d, const char* address);
 
-pa_bluetooth_transport* pa_bluetooth_device_get_transport(pa_bluetooth_device *d, enum profile profile);
 bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d);
 
 int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, const char *accesstype, size_t *imtu, size_t *omtu);
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 29c35e1..4743db8 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1984,9 +1984,10 @@ static int setup_transport(struct userdata *u) {
 
     pa_assert(u);
     pa_assert(!u->transport);
+    pa_assert(u->profile != PROFILE_OFF);
 
     /* check if profile has a transport */
-    t = pa_bluetooth_device_get_transport(u->device, u->profile);
+    t = u->device->transports[u->profile];
     if (t == NULL) {
         pa_log_warn("Profile has no transport");
         return -1;

commit faaf8cd39e2e19beff7f6749fd1555057fd50394
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Thu Dec 6 10:35:19 2012 +0100

    bluetooth: Use transport array instead of hashmap for devices
    
    Devices will have zero or one transports per profile, and besides the
    typical lookup is also profile-based. Therefore, replace the old hashmap
    (which used the transport path as key) with a simple array which holds
    a transport pointer per profile.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 79d76d8..313df2b 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -117,7 +117,7 @@ static pa_bluetooth_device* device_new(pa_bluetooth_discovery *discovery, const
     pa_assert(discovery);
     pa_assert(path);
 
-    d = pa_xnew(pa_bluetooth_device, 1);
+    d = pa_xnew0(pa_bluetooth_device, 1);
 
     d->discovery = discovery;
     d->dead = FALSE;
@@ -126,7 +126,6 @@ static pa_bluetooth_device* device_new(pa_bluetooth_discovery *discovery, const
 
     d->name = NULL;
     d->path = pa_xstrdup(path);
-    d->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
     d->paired = -1;
     d->alias = NULL;
     PA_LLIST_HEAD_INIT(pa_bluetooth_uuid, d->uuids);
@@ -167,14 +166,16 @@ static void device_free(pa_bluetooth_device *d) {
 
     pa_assert(d);
 
-    while ((t = pa_hashmap_steal_first(d->transports))) {
+    for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) {
+        if (!(t = d->transports[i]))
+            continue;
+
+        d->transports[i] = NULL;
         pa_hashmap_remove(d->discovery->transports, t->path);
         pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_REMOVED], NULL);
         transport_free(t);
     }
 
-    pa_hashmap_free(d->transports, NULL, NULL);
-
     for (i = 0; i < PA_BLUETOOTH_DEVICE_HOOK_MAX; i++)
         pa_hook_done(&d->hooks[i]);
 
@@ -974,16 +975,12 @@ pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *
 }
 
 pa_bluetooth_transport* pa_bluetooth_device_get_transport(pa_bluetooth_device *d, enum profile profile) {
-    pa_bluetooth_transport *t;
-    void *state = NULL;
-
     pa_assert(d);
 
-    while ((t = pa_hashmap_iterate(d->transports, &state, NULL)))
-        if (t->profile == profile)
-            return t;
+    if (profile == PROFILE_OFF)
+        return NULL;
 
-    return NULL;
+    return d->transports[profile];
 }
 
 bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d) {
@@ -1183,12 +1180,18 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
     else
         p = PROFILE_A2DP_SOURCE;
 
+    if (d->transports[p] != NULL) {
+        pa_log("Cannot configure transport %s because profile %d is already used", path, p);
+        goto fail;
+    }
+
     sender = dbus_message_get_sender(m);
 
     t = transport_new(d, sender, path, p, config, size);
     if (nrec)
         t->nrec = nrec;
-    pa_hashmap_put(d->transports, t->path, t);
+
+    d->transports[p] = t;
     pa_hashmap_put(y->transports, t->path, t);
 
     pa_log_debug("Transport %s profile %d available", t->path, t->profile);
@@ -1221,7 +1224,7 @@ static DBusMessage *endpoint_clear_configuration(DBusConnection *c, DBusMessage
 
     if ((t = pa_hashmap_get(y->transports, path))) {
         pa_log_debug("Clearing transport %s profile %d", t->path, t->profile);
-        pa_hashmap_remove(t->device->transports, t->path);
+        t->device->transports[t->profile] = NULL;
         pa_hashmap_remove(y->transports, t->path);
         pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_REMOVED], NULL);
         transport_free(t);
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 89e8390..f6ffece 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -63,6 +63,8 @@ enum profile {
     PROFILE_OFF
 };
 
+#define PA_BLUETOOTH_PROFILE_COUNT PROFILE_OFF
+
 /* Hook data: pa_bluetooth_transport pointer. */
 typedef enum pa_bluetooth_transport_hook {
     PA_BLUETOOTH_TRANSPORT_HOOK_NREC_CHANGED, /* Call data: NULL. */
@@ -108,7 +110,7 @@ struct pa_bluetooth_device {
     /* Device information */
     char *name;
     char *path;
-    pa_hashmap *transports;
+    pa_bluetooth_transport *transports[PA_BLUETOOTH_PROFILE_COUNT];
     int paired;
     char *alias;
     PA_LLIST_HEAD(pa_bluetooth_uuid, uuids);

commit 51d28e63310f5a02562e3e5bddf59fa78cbad168
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Thu Dec 6 10:35:18 2012 +0100

    bluetooth: Add transport hashmap to discovery
    
    Path-based transport lookups are required in a discovery basis, before
    the associated device is known. Therefore, it makes more sense to
    maintain a hashmap in the discovery structure itself, instead of
    iterating all devices.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 0daf1c9..79d76d8 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -68,6 +68,7 @@ struct pa_bluetooth_discovery {
     pa_dbus_connection *connection;
     PA_LLIST_HEAD(pa_dbus_pending, pending);
     pa_hashmap *devices;
+    pa_hashmap *transports;
     pa_hook hook;
     pa_bool_t filter_added;
 };
@@ -167,6 +168,7 @@ static void device_free(pa_bluetooth_device *d) {
     pa_assert(d);
 
     while ((t = pa_hashmap_steal_first(d->transports))) {
+        pa_hashmap_remove(d->discovery->transports, t->path);
         pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_REMOVED], NULL);
         transport_free(t);
     }
@@ -913,16 +915,10 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
 
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     } else if (dbus_message_is_signal(m, "org.bluez.MediaTransport", "PropertyChanged")) {
-        pa_bluetooth_device *d;
-        pa_bluetooth_transport *t = NULL;
-        void *state = NULL;
+        pa_bluetooth_transport *t;
         DBusMessageIter arg_i;
 
-        while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
-            if ((t = pa_hashmap_get(d->transports, dbus_message_get_path(m))))
-                break;
-
-        if (!t)
+        if (!(t = pa_hashmap_get(y->transports, dbus_message_get_path(m))))
             goto fail;
 
         if (!dbus_message_iter_init(m, &arg_i)) {
@@ -1193,6 +1189,7 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
     if (nrec)
         t->nrec = nrec;
     pa_hashmap_put(d->transports, t->path, t);
+    pa_hashmap_put(y->transports, t->path, t);
 
     pa_log_debug("Transport %s profile %d available", t->path, t->profile);
 
@@ -1209,9 +1206,7 @@ fail:
 
 static DBusMessage *endpoint_clear_configuration(DBusConnection *c, DBusMessage *m, void *userdata) {
     pa_bluetooth_discovery *y = userdata;
-    pa_bluetooth_device *d;
     pa_bluetooth_transport *t;
-    void *state = NULL;
     DBusMessage *r;
     DBusError e;
     const char *path;
@@ -1224,14 +1219,12 @@ static DBusMessage *endpoint_clear_configuration(DBusConnection *c, DBusMessage
         goto fail;
     }
 
-    while ((d = pa_hashmap_iterate(y->devices, &state, NULL))) {
-        if ((t = pa_hashmap_get(d->transports, path))) {
-            pa_log_debug("Clearing transport %s profile %d", t->path, t->profile);
-            pa_hashmap_remove(d->transports, t->path);
-            pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_REMOVED], NULL);
-            transport_free(t);
-            break;
-        }
+    if ((t = pa_hashmap_get(y->transports, path))) {
+        pa_log_debug("Clearing transport %s profile %d", t->path, t->profile);
+        pa_hashmap_remove(t->device->transports, t->path);
+        pa_hashmap_remove(y->transports, t->path);
+        pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_REMOVED], NULL);
+        transport_free(t);
     }
 
     pa_assert_se(r = dbus_message_new_method_return(m));
@@ -1475,6 +1468,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
     PA_REFCNT_INIT(y);
     y->core = c;
     y->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    y->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
     PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
     pa_hook_init(&y->hook, y);
     pa_shared_set(c, "bluetooth-discovery", y);
@@ -1549,6 +1543,11 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
         pa_hashmap_free(y->devices, NULL, NULL);
     }
 
+    if (y->transports) {
+        pa_assert(pa_hashmap_isempty(y->transports));
+        pa_hashmap_free(y->transports, NULL, NULL);
+    }
+
     if (y->connection) {
         dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), HFP_AG_ENDPOINT);
         dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), HFP_HS_ENDPOINT);

commit ccc9a58dd0b63c1e5d8489e592585c5ad4955e1b
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Thu Dec 6 10:35:17 2012 +0100

    bluetooth: Use round() to convert PA<->BT volumes
    
    The code can be simplified since it's just trying to round the result of
    the division. Note that the resulting behavior is slightly different,
    specially when the volume is 0. In this case, it will remain at 0,
    instead of being set to 1.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index fdc621f..29c35e1 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -26,6 +26,7 @@
 
 #include <string.h>
 #include <errno.h>
+#include <math.h>
 #include <linux/sockios.h>
 #include <arpa/inet.h>
 
@@ -1330,21 +1331,13 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
 
         if (u->profile == PROFILE_HSP) {
             if (u->sink && dbus_message_is_signal(m, "org.bluez.Headset", "SpeakerGainChanged")) {
-                pa_volume_t volume = (pa_volume_t) (gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
-
-                /* increment volume by one to correct rounding errors */
-                if (volume < PA_VOLUME_NORM)
-                    volume++;
+                pa_volume_t volume = (pa_volume_t) round((double) gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
 
                 pa_cvolume_set(&v, u->sample_spec.channels, volume);
                 pa_sink_volume_changed(u->sink, &v);
 
             } else if (u->source && dbus_message_is_signal(m, "org.bluez.Headset", "MicrophoneGainChanged")) {
-                pa_volume_t volume = (pa_volume_t) (gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
-
-                /* increment volume by one to correct rounding errors */
-                if (volume < PA_VOLUME_NORM)
-                    volume++;
+                pa_volume_t volume = (pa_volume_t) round((double) gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
 
                 pa_cvolume_set(&v, u->sample_spec.channels, volume);
                 pa_source_volume_changed(u->source, &v);
@@ -1476,16 +1469,8 @@ static void sink_set_volume_cb(pa_sink *s) {
     pa_assert(u->sink == s);
     pa_assert(u->profile == PROFILE_HSP);
 
-    gain = (pa_cvolume_max(&s->real_volume) * HSP_MAX_GAIN) / PA_VOLUME_NORM;
-
-    if (gain > HSP_MAX_GAIN)
-        gain = HSP_MAX_GAIN;
-
-    volume = (pa_volume_t) (gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
-
-    /* increment volume by one to correct rounding errors */
-    if (volume < PA_VOLUME_NORM)
-        volume++;
+    gain = (dbus_uint16_t) round((double) pa_cvolume_max(&s->real_volume) * HSP_MAX_GAIN / PA_VOLUME_NORM);
+    volume = (pa_volume_t) round((double) gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
 
     pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
 
@@ -1514,16 +1499,8 @@ static void source_set_volume_cb(pa_source *s) {
     pa_assert(u->source == s);
     pa_assert(u->profile == PROFILE_HSP);
 
-    gain = (pa_cvolume_max(&s->real_volume) * HSP_MAX_GAIN) / PA_VOLUME_NORM;
-
-    if (gain > HSP_MAX_GAIN)
-        gain = HSP_MAX_GAIN;
-
-    volume = (pa_volume_t) (gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
-
-    /* increment volume by one to correct rounding errors */
-    if (volume < PA_VOLUME_NORM)
-        volume++;
+    gain = (dbus_uint16_t) round((double) pa_cvolume_max(&s->real_volume) * HSP_MAX_GAIN / PA_VOLUME_NORM);
+    volume = (pa_volume_t) round((double) gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
 
     pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
 

commit b36686ea9af70dc1a9a7d8fdcf7fbf9cde597a74
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Wed Dec 5 17:23:15 2012 +0100

    bluetooth: Add device pointer to transport
    
    Transports always have an associated device, so add the pointer as a
    member to the structure, and remove the discovery pointer since it
    already exists in the device object.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index e2d2e62..0daf1c9 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1022,13 +1022,14 @@ int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, const char *access
     uint16_t i, o;
 
     pa_assert(t);
-    pa_assert(t->y);
+    pa_assert(t->device);
+    pa_assert(t->device->discovery);
 
     dbus_error_init(&err);
 
     pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport", "Acquire"));
     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID));
-    r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->y->connection), m, -1, &err);
+    r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
 
     if (dbus_error_is_set(&err) || !r) {
         dbus_error_free(&err);
@@ -1058,13 +1059,14 @@ void pa_bluetooth_transport_release(pa_bluetooth_transport *t, const char *acces
     DBusError err;
 
     pa_assert(t);
-    pa_assert(t->y);
+    pa_assert(t->device);
+    pa_assert(t->device->discovery);
 
     dbus_error_init(&err);
 
     pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport", "Release"));
     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID));
-    dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->y->connection), m, -1, &err);
+    dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
 
     if (dbus_error_is_set(&err)) {
         pa_log("Failed to release transport %s: %s", t->path, err.message);
@@ -1089,12 +1091,13 @@ static int setup_dbus(pa_bluetooth_discovery *y) {
     return 0;
 }
 
-static pa_bluetooth_transport *transport_new(pa_bluetooth_discovery *y, const char *owner, const char *path, enum profile p, const uint8_t *config, int size) {
+static pa_bluetooth_transport *transport_new(pa_bluetooth_device *d, const char *owner, const char *path, enum profile p,
+                                             const uint8_t *config, int size) {
     pa_bluetooth_transport *t;
     unsigned i;
 
     t = pa_xnew0(pa_bluetooth_transport, 1);
-    t->y = y;
+    t->device = d;
     t->owner = pa_xstrdup(owner);
     t->path = pa_xstrdup(path);
     t->profile = p;
@@ -1186,7 +1189,7 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
 
     sender = dbus_message_get_sender(m);
 
-    t = transport_new(y, sender, path, p, config, size);
+    t = transport_new(d, sender, path, p, config, size);
     if (nrec)
         t->nrec = nrec;
     pa_hashmap_put(d->transports, t->path, t);
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 33ef4cf..89e8390 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -71,7 +71,7 @@ typedef enum pa_bluetooth_transport_hook {
 } pa_bluetooth_transport_hook_t;
 
 struct pa_bluetooth_transport {
-    pa_bluetooth_discovery *y;
+    pa_bluetooth_device *device;
     char *owner;
     char *path;
     enum profile profile;

commit 80dd7c1070853cfcdb0ee1b137fd4cca4746c7ae
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Wed Dec 5 17:23:14 2012 +0100

    bluetooth: Remove unused pa_bluetooth_discovery_get_transport()
    
    The function is not used and it also exposes D-Bus-related information
    in the internal API, which is in general undesired.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 00963fa..e2d2e62 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -977,22 +977,6 @@ pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *
     return NULL;
 }
 
-pa_bluetooth_transport* pa_bluetooth_discovery_get_transport(pa_bluetooth_discovery *y, const char *path) {
-    pa_bluetooth_device *d;
-    pa_bluetooth_transport *t;
-    void *state = NULL;
-
-    pa_assert(y);
-    pa_assert(PA_REFCNT_VALUE(y) > 0);
-    pa_assert(path);
-
-    while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
-        if ((t = pa_hashmap_get(d->transports, path)))
-            return t;
-
-    return NULL;
-}
-
 pa_bluetooth_transport* pa_bluetooth_device_get_transport(pa_bluetooth_device *d, enum profile profile) {
     pa_bluetooth_transport *t;
     void *state = NULL;
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index e5fe119..33ef4cf 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -143,7 +143,6 @@ void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *d);
 pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *d, const char* path);
 pa_bluetooth_device* pa_bluetooth_discovery_get_by_address(pa_bluetooth_discovery *d, const char* address);
 
-pa_bluetooth_transport* pa_bluetooth_discovery_get_transport(pa_bluetooth_discovery *y, const char *path);
 pa_bluetooth_transport* pa_bluetooth_device_get_transport(pa_bluetooth_device *d, enum profile profile);
 bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d);
 

commit 28251e7243cc17b19f2cf9abbbfeb5631cd2a9cc
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Wed Dec 5 17:23:12 2012 +0100

    bluetooth: Make pa_bluetooth_transport_parse_property() private
    
    The function is not being used outside bluetooth-util and thus can be
    made private.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 21ce34e..00963fa 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -747,8 +747,7 @@ static void list_adapters(pa_bluetooth_discovery *y) {
     send_and_add_to_pending(y, m, get_properties_reply, NULL);
 }
 
-int pa_bluetooth_transport_parse_property(pa_bluetooth_transport *t, DBusMessageIter *i)
-{
+static int pa_bluetooth_transport_parse_property(pa_bluetooth_transport *t, DBusMessageIter *i) {
     const char *key;
     DBusMessageIter variant_i;
 
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 95dd5c6..e5fe119 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -149,7 +149,6 @@ bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d);
 
 int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, const char *accesstype, size_t *imtu, size_t *omtu);
 void pa_bluetooth_transport_release(pa_bluetooth_transport *t, const char *accesstype);
-int pa_bluetooth_transport_parse_property(pa_bluetooth_transport *t, DBusMessageIter *i);
 
 pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *d);
 

commit 3f41a4a538e863f6caf5a1682c9a09c3d78ab264
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Wed Dec 5 17:23:11 2012 +0100

    bluetooth: Fix condition to load module
    
    d->hfgw_state is just another profile that should be considered exactly
    as the rest inside device_audio_is_ready(), which is being used to
    decide if the discovery hook gets triggered.
    
    Therefore, there seems to be no reason to make an exception for this
    profile and skip checking if the condition d->audio_state !=
    PA_BT_AUDIO_STATE_INVALID holds true.
    
    This change makes no practical difference but delaying the load of the
    module also for HFGW until Audio.State is received. The benefit is
    that the behavior and the code are more consistent across profiles.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index e09e170..21ce34e 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -192,11 +192,11 @@ static pa_bool_t device_is_audio_ready(const pa_bluetooth_device *d) {
     pa_assert(d);
 
     return
-        d->device_info_valid && (d->hfgw_state != PA_BT_AUDIO_STATE_INVALID ||
-        (d->audio_state != PA_BT_AUDIO_STATE_INVALID &&
-         (d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID ||
-          d->audio_source_state != PA_BT_AUDIO_STATE_INVALID ||
-          d->headset_state != PA_BT_AUDIO_STATE_INVALID)));
+        d->device_info_valid && d->audio_state != PA_BT_AUDIO_STATE_INVALID &&
+        (d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID ||
+         d->audio_source_state != PA_BT_AUDIO_STATE_INVALID ||
+         d->headset_state != PA_BT_AUDIO_STATE_INVALID ||
+         d->hfgw_state != PA_BT_AUDIO_STATE_INVALID);
 }
 
 static const char *check_variant_property(DBusMessageIter *i) {

commit f9beb8e867217db2fbffde7d087bacf9a57dd185
Author: Flavio Ceolin <flavio.ceolin at profusion.mobi>
Date:   Thu Nov 29 11:04:12 2012 -0200

    modargs: Adding pa_modargs_get_value_volume()
    
    This function gets a pa_volume_t from a string.

diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index af9fabf..e86ffa5 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -350,6 +350,20 @@ int pa_modargs_get_value_double(pa_modargs *ma, const char *key, double *value)
     return 0;
 }
 
+int pa_modargs_get_value_volume(pa_modargs *ma, const char *key, pa_volume_t *value) {
+    const char *v;
+
+    pa_assert(value);
+
+    if (!(v = pa_modargs_get_value(ma, key, NULL)))
+        return 0;
+
+    if (pa_parse_volume(v, value) < 0)
+        return -1;
+
+    return 0;
+}
+
 int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) {
     const char *format;
     uint32_t channels;
diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h
index 5da9cf1..c1345ea 100644
--- a/src/pulsecore/modargs.h
+++ b/src/pulsecore/modargs.h
@@ -26,6 +26,7 @@
 #include <pulse/sample.h>
 #include <pulse/channelmap.h>
 #include <pulse/proplist.h>
+#include <pulse/volume.h>
 #include <pulsecore/macro.h>
 
 typedef struct pa_modargs pa_modargs;
@@ -48,6 +49,9 @@ int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, pa_bool_t *val
 /* Return a module argument as double value in *value */
 int pa_modargs_get_value_double(pa_modargs *ma, const char *key, double *value);
 
+/* Return a module argument as pa_volume_t value in *value */
+int pa_modargs_get_value_volume(pa_modargs *ma, const char *key, pa_volume_t *value);
+
 /* Return sample spec data from the three arguments "rate", "format" and "channels" */
 int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *ss);
 

commit 9e2b6a0b5cf2d1ee172f8ff302da33fc6489dad1
Author: Flavio Ceolin <flavio.ceolin at profusion.mobi>
Date:   Thu Nov 29 11:04:11 2012 -0200

    sink-input: New volume_factor system
    
    Implement setting of more than one volume factor.  The
    real value of the volume_factor will be the multiplication of these
    values.

diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c
index ce37588..c298e74 100644
--- a/src/modules/module-position-event-sounds.c
+++ b/src/modules/module-position-event-sounds.c
@@ -52,6 +52,7 @@ static const char* const valid_modargs[] = {
 
 struct userdata {
     pa_hook_slot *sink_input_fixate_hook_slot;
+    const char *name;
 };
 
 static int parse_pos(const char *pos, double *f) {
@@ -132,7 +133,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_i
     }
 
     pa_log_debug("Final volume factor %s.", pa_cvolume_snprint(t, sizeof(t), &v));
-    pa_sink_input_new_data_apply_volume_factor_sink(data, &v);
+    pa_sink_input_new_data_add_volume_factor_sink(data, u->name, &v);
 
     return PA_HOOK_OK;
 }
@@ -152,6 +153,7 @@ int pa__init(pa_module*m) {
     u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
 
     pa_modargs_free(ma);
+    u->name = m->name;
 
     return 0;
 
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index aaabf5c..ae89aed 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -48,6 +48,45 @@
 
 PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject);
 
+struct volume_factor_entry {
+    char *key;
+    pa_cvolume volume;
+};
+
+static struct volume_factor_entry *volume_factor_entry_new(const char *key, const pa_cvolume *volume) {
+    struct volume_factor_entry *entry;
+
+    pa_assert(key);
+    pa_assert(volume);
+
+    entry = pa_xnew(struct volume_factor_entry, 1);
+    entry->key = pa_xstrdup(key);
+
+    entry->volume = *volume;
+
+    return entry;
+}
+
+static void volume_factor_entry_free(struct volume_factor_entry *volume_entry) {
+    pa_assert(volume_entry);
+
+    pa_xfree(volume_entry->key);
+    pa_xfree(volume_entry);
+}
+
+static void volume_factor_entry_free2(struct volume_factor_entry *volume_entry, void *userdarta) {
+    volume_factor_entry_free(volume_entry);
+}
+
+static void volume_factor_from_hashmap(pa_cvolume *v, pa_hashmap *items, uint8_t channels) {
+    struct volume_factor_entry *entry;
+    void *state = NULL;
+
+    pa_cvolume_reset(v, channels);
+    PA_HASHMAP_FOREACH(entry, items, state)
+        pa_sw_cvolume_multiply(v, v, &entry->volume);
+}
+
 static void sink_input_free(pa_object *o);
 static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v);
 
@@ -74,6 +113,9 @@ pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data
     data->proplist = pa_proplist_new();
     data->volume_writable = TRUE;
 
+    data->volume_factor_items = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    data->volume_factor_sink_items = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
     return data;
 }
 
@@ -111,28 +153,26 @@ void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cv
         data->volume = *volume;
 }
 
-void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor) {
+void pa_sink_input_new_data_add_volume_factor(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor) {
+    struct volume_factor_entry *v;
+
     pa_assert(data);
+    pa_assert(key);
     pa_assert(volume_factor);
 
-    if (data->volume_factor_is_set)
-        pa_sw_cvolume_multiply(&data->volume_factor, &data->volume_factor, volume_factor);
-    else {
-        data->volume_factor_is_set = TRUE;
-        data->volume_factor = *volume_factor;
-    }
+    v = volume_factor_entry_new(key, volume_factor);
+    pa_assert_se(pa_hashmap_put(data->volume_factor_items, v->key, v) >= 0);
 }
 
-void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor) {
+void pa_sink_input_new_data_add_volume_factor_sink(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor) {
+    struct volume_factor_entry *v;
+
     pa_assert(data);
+    pa_assert(key);
     pa_assert(volume_factor);
 
-    if (data->volume_factor_sink_is_set)
-        pa_sw_cvolume_multiply(&data->volume_factor_sink, &data->volume_factor_sink, volume_factor);
-    else {
-        data->volume_factor_sink_is_set = TRUE;
-        data->volume_factor_sink = *volume_factor;
-    }
+    v = volume_factor_entry_new(key, volume_factor);
+    pa_assert_se(pa_hashmap_put(data->volume_factor_sink_items, v->key, v) >= 0);
 }
 
 void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
@@ -204,6 +244,12 @@ void pa_sink_input_new_data_done(pa_sink_input_new_data *data) {
     if (data->format)
         pa_format_info_free(data->format);
 
+    if (data->volume_factor_items)
+        pa_hashmap_free(data->volume_factor_items, (pa_free2_cb_t) volume_factor_entry_free2, NULL);
+
+    if (data->volume_factor_sink_items)
+        pa_hashmap_free(data->volume_factor_sink_items, (pa_free2_cb_t) volume_factor_entry_free2, NULL);
+
     pa_proplist_free(data->proplist);
 }
 
@@ -335,16 +381,6 @@ int pa_sink_input_new(
 
     pa_return_val_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec), -PA_ERR_INVALID);
 
-    if (!data->volume_factor_is_set)
-        pa_cvolume_reset(&data->volume_factor, data->sample_spec.channels);
-
-    pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor, &data->sample_spec), -PA_ERR_INVALID);
-
-    if (!data->volume_factor_sink_is_set)
-        pa_cvolume_reset(&data->volume_factor_sink, data->sink->sample_spec.channels);
-
-    pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor_sink, &data->sink->sample_spec), -PA_ERR_INVALID);
-
     if (!data->muted_is_set)
         data->muted = FALSE;
 
@@ -455,8 +491,14 @@ int pa_sink_input_new(
     } else
         i->volume = data->volume;
 
-    i->volume_factor = data->volume_factor;
-    i->volume_factor_sink = data->volume_factor_sink;
+    i->volume_factor_items = data->volume_factor_items;
+    data->volume_factor_items = NULL;
+    volume_factor_from_hashmap(&i->volume_factor, i->volume_factor_items, i->sample_spec.channels);
+
+    i->volume_factor_sink_items = data->volume_factor_sink_items;
+    data->volume_factor_sink_items = NULL;
+    volume_factor_from_hashmap(&i->volume_factor_sink, i->volume_factor_sink_items, i->sample_spec.channels);
+
     i->real_ratio = i->reference_ratio = data->volume;
     pa_cvolume_reset(&i->soft_volume, i->sample_spec.channels);
     pa_cvolume_reset(&i->real_ratio, i->sample_spec.channels);
@@ -705,6 +747,11 @@ static void sink_input_free(pa_object *o) {
     if (i->thread_info.direct_outputs)
         pa_hashmap_free(i->thread_info.direct_outputs, NULL, NULL);
 
+    if (i->volume_factor_items)
+        pa_hashmap_free(i->volume_factor_items, (pa_free2_cb_t) volume_factor_entry_free2, NULL);
+    if (i->volume_factor_sink_items)
+        pa_hashmap_free(i->volume_factor_sink_items, (pa_free2_cb_t) volume_factor_entry_free2, NULL);
+
     pa_xfree(i->driver);
     pa_xfree(i);
 }
@@ -1214,6 +1261,62 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
 }
 
+void pa_sink_input_add_volume_factor(pa_sink_input *i, const char *key, const pa_cvolume *volume_factor) {
+    struct volume_factor_entry *v;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(volume_factor);
+    pa_assert(key);
+    pa_assert(pa_cvolume_valid(volume_factor));
+    pa_assert(volume_factor->channels == 1 || pa_cvolume_compatible(volume_factor, &i->sample_spec));
+
+    v = volume_factor_entry_new(key, volume_factor);
+    if (!pa_cvolume_compatible(volume_factor, &i->sample_spec))
+        pa_cvolume_set(&v->volume, i->sample_spec.channels, volume_factor->values[0]);
+
+    pa_assert_se(pa_hashmap_put(i->volume_factor_items, v->key, v) >= 0);
+    if (pa_hashmap_size(i->volume_factor_items) == 1)
+        i->volume_factor = v->volume;
+    else
+        pa_sw_cvolume_multiply(&i->volume_factor, &i->volume_factor, &v->volume);
+
+    pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor);
+
+    /* Copy the new soft_volume to the thread_info struct */
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
+}
+
+void pa_sink_input_remove_volume_factor(pa_sink_input *i, const char *key) {
+    struct volume_factor_entry *v;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(key);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+    pa_assert_se(v = pa_hashmap_remove(i->volume_factor_items, key));
+    volume_factor_entry_free(v);
+
+    switch (pa_hashmap_size(i->volume_factor_items)) {
+        case 0:
+            pa_cvolume_reset(&i->volume_factor, i->sample_spec.channels);
+            break;
+        case 1:
+            v = pa_hashmap_first(i->volume_factor_items);
+            i->volume_factor = v->volume;
+            break;
+        default:
+            volume_factor_from_hashmap(&i->volume_factor, i->volume_factor_items, i->volume_factor.channels);
+    }
+
+    pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor);
+
+    /* Copy the new soft_volume to the thread_info struct */
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
+}
+
 /* Called from main context */
 static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) {
     pa_sink_input_assert_ref(i);
@@ -1442,6 +1545,8 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
 /* Called from main context */
 int pa_sink_input_start_move(pa_sink_input *i) {
     pa_source_output *o, *p = NULL;
+    struct volume_factor_entry *v;
+    void *state = NULL;
     int r;
 
     pa_sink_input_assert_ref(i);
@@ -1479,7 +1584,12 @@ int pa_sink_input_start_move(pa_sink_input *i) {
     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
 
     pa_sink_update_status(i->sink);
+
+    PA_HASHMAP_FOREACH(v, i->volume_factor_sink_items, state)
+        pa_cvolume_remap(&v->volume, &i->sink->channel_map, &i->channel_map);
+
     pa_cvolume_remap(&i->volume_factor_sink, &i->sink->channel_map, &i->channel_map);
+
     i->sink = NULL;
 
     pa_sink_input_unref(i);
@@ -1639,6 +1749,9 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
 
 /* Called from main context */
 int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
+    struct volume_factor_entry *v;
+    void *state = NULL;
+
     pa_sink_input_assert_ref(i);
     pa_assert_ctl_context();
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
@@ -1677,6 +1790,9 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
     i->save_sink = save;
     pa_idxset_put(dest->inputs, pa_sink_input_ref(i), NULL);
 
+    PA_HASHMAP_FOREACH(v, i->volume_factor_sink_items, state)
+        pa_cvolume_remap(&v->volume, &i->channel_map, &i->sink->channel_map);
+
     pa_cvolume_remap(&i->volume_factor_sink, &i->channel_map, &i->sink->channel_map);
 
     if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index af2177a..8bd5912 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -100,10 +100,19 @@ struct pa_sink_input {
     pa_cvolume volume;             /* The volume clients are informed about */
     pa_cvolume reference_ratio;    /* The ratio of the stream's volume to the sink's reference volume */
     pa_cvolume real_ratio;         /* The ratio of the stream's volume to the sink's real volume */
-    pa_cvolume volume_factor;      /* An internally used volume factor that can be used by modules to apply effects and suchlike without having that visible to the outside */
-    pa_cvolume soft_volume;        /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as real_ratio * volume_factor */
-
-    pa_cvolume volume_factor_sink; /* A second volume factor in format of the sink this stream is connected to */
+    /* volume_factor is an internally used "additional volume" that can be used
+     * by modules without having the volume visible to clients. volume_factor
+     * calculated by merging all the individual items in volume_factor_items.
+     * Modules must not modify these variables directly, instead
+     * pa_sink_input_add/remove_volume_factor() have to be used to add and
+     * remove items, or pa_sink_input_new_data_add_volume_factor() during input
+     * creation time. */
+    pa_cvolume volume_factor;
+    pa_hashmap *volume_factor_items;
+    pa_cvolume soft_volume;          /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as real_ratio * volume_factor */
+
+    pa_cvolume volume_factor_sink; /* A second volume factor in format of the sink this stream is connected to. */
+    pa_hashmap *volume_factor_sink_items;
 
     pa_bool_t volume_writable:1;
 
@@ -284,13 +293,14 @@ typedef struct pa_sink_input_new_data {
     pa_idxset *req_formats;
     pa_idxset *nego_formats;
 
-    pa_cvolume volume, volume_factor, volume_factor_sink;
+    pa_cvolume volume;
     pa_bool_t muted:1;
+    pa_hashmap *volume_factor_items, *volume_factor_sink_items;
 
     pa_bool_t sample_spec_is_set:1;
     pa_bool_t channel_map_is_set:1;
 
-    pa_bool_t volume_is_set:1, volume_factor_is_set:1, volume_factor_sink_is_set:1;
+    pa_bool_t volume_is_set:1;
     pa_bool_t muted_is_set:1;
 
     pa_bool_t volume_is_absolute:1;
@@ -305,8 +315,8 @@ void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const
 void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
 pa_bool_t pa_sink_input_new_data_is_passthrough(pa_sink_input_new_data *data);
 void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
-void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
-void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
+void pa_sink_input_new_data_add_volume_factor(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor);
+void pa_sink_input_new_data_add_volume_factor_sink(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor);
 void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
 pa_bool_t pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *s, pa_bool_t save);
 pa_bool_t pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_idxset *formats);
@@ -354,6 +364,8 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
 pa_bool_t pa_sink_input_is_passthrough(pa_sink_input *i);
 pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i);
 void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute);
+void pa_sink_input_add_volume_factor(pa_sink_input *i, const char *key, const pa_cvolume *volume_factor);
+void pa_sink_input_remove_volume_factor(pa_sink_input *i, const char *key);
 pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute);
 
 void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save);

commit bf962a2600207bb9a338aa38c72ddbd2d3cd13b8
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Nov 19 12:59:31 2012 +0200

    card-restore: Log the restored profile name.

diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
index 7d101c5..643e074 100644
--- a/src/modules/module-card-restore.c
+++ b/src/modules/module-card-restore.c
@@ -377,8 +377,8 @@ static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new
 
     if (e->profile[0]) {
         if (!new_data->active_profile) {
-            pa_log_info("Restoring profile for card %s.", new_data->name);
             pa_card_new_data_set_profile(new_data, e->profile);
+            pa_log_info("Restored profile '%s' for card %s.", new_data->active_profile, new_data->name);
             new_data->save_profile = TRUE;
 
         } else

commit 0f44b1e820c1744bfb46d37e87b71d64109c4374
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Nov 16 18:24:34 2012 +0200

    Log the reason for every suspend/resume.
    
    I was looking at a log that showed that a suspend happened (at
    a strange time), but the log didn't tell me why the suspend was done.
    This patch tries to make sure that that won't happen again.

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index ed41b22..82c07c7 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -173,6 +173,8 @@ static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct u
     pa_assert(r);
     pa_assert(u);
 
+    pa_log_debug("Suspending sink %s, because another application requested us to release the device.", u->sink->name);
+
     if (pa_sink_suspend(u->sink, TRUE, PA_SUSPEND_APPLICATION) < 0)
         return PA_HOOK_CANCEL;
 
@@ -235,14 +237,17 @@ static int reserve_init(struct userdata *u, const char *dname) {
 }
 
 static pa_hook_result_t monitor_cb(pa_reserve_monitor_wrapper *w, void* busy, struct userdata *u) {
-    pa_bool_t b;
-
     pa_assert(w);
     pa_assert(u);
 
-    b = PA_PTR_TO_UINT(busy) && !u->reserve;
+    if (PA_PTR_TO_UINT(busy) && !u->reserve) {
+        pa_log_debug("Suspending sink %s, because another application is blocking the access to the device.", u->sink->name);
+        pa_sink_suspend(u->sink, true, PA_SUSPEND_APPLICATION);
+    } else {
+        pa_log_debug("Resuming sink %s, because other applications aren't blocking access to the device any more.", u->sink->name);
+        pa_sink_suspend(u->sink, false, PA_SUSPEND_APPLICATION);
+    }
 
-    pa_sink_suspend(u->sink, b, PA_SUSPEND_APPLICATION);
     return PA_HOOK_OK;
 }
 
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 9f3170d..58f00f7 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -148,6 +148,8 @@ static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct u
     pa_assert(r);
     pa_assert(u);
 
+    pa_log_debug("Suspending source %s, because another application requested us to release the device.", u->source->name);
+
     if (pa_source_suspend(u->source, TRUE, PA_SUSPEND_APPLICATION) < 0)
         return PA_HOOK_CANCEL;
 
@@ -210,14 +212,17 @@ static int reserve_init(struct userdata *u, const char *dname) {
 }
 
 static pa_hook_result_t monitor_cb(pa_reserve_monitor_wrapper *w, void* busy, struct userdata *u) {
-    pa_bool_t b;
-
     pa_assert(w);
     pa_assert(u);
 
-    b = PA_PTR_TO_UINT(busy) && !u->reserve;
+    if (PA_PTR_TO_UINT(busy) && !u->reserve) {
+        pa_log_debug("Suspending source %s, because another application is blocking the access to the device.", u->source->name);
+        pa_source_suspend(u->source, true, PA_SUSPEND_APPLICATION);
+    } else {
+        pa_log_debug("Resuming source %s, because other applications aren't blocking access to the device any more.", u->source->name);
+        pa_source_suspend(u->source, false, PA_SUSPEND_APPLICATION);
+    }
 
-    pa_source_suspend(u->source, b, PA_SUSPEND_APPLICATION);
     return PA_HOOK_OK;
 }
 
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 1236cb5..fdc621f 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1422,11 +1422,15 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
 
     if (acquire)
         if (bt_transport_acquire(u, FALSE) >= 0) {
-            if (u->source)
+            if (u->source) {
+                pa_log_debug("Resuming source %s, because the bluetooth audio state changed to 'playing'.", u->source->name);
                 pa_source_suspend(u->source, FALSE, PA_SUSPEND_IDLE|PA_SUSPEND_USER);
+            }
 
-            if (u->sink)
+            if (u->sink) {
+                pa_log_debug("Resuming sink %s, because the bluetooth audio state changed to 'playing'.", u->sink->name);
                 pa_sink_suspend(u->sink, FALSE, PA_SUSPEND_IDLE|PA_SUSPEND_USER);
+            }
         }
 
     if (release && bt_transport_is_acquired(u)) {
@@ -1436,11 +1440,15 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
            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)
+        if (u->source) {
+            pa_log_debug("Suspending source %s, because the remote end closed the stream.", u->source->name);
             pa_source_suspend(u->source, TRUE, PA_SUSPEND_USER);
+        }
 
-        if (u->sink)
+        if (u->sink) {
+            pa_log_debug("Suspending sink %s, because the remote end closed the stream.", u->sink->name);
             pa_sink_suspend(u->sink, TRUE, PA_SUSPEND_USER);
+        }
     }
 
 fail:
diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
index 316aba5..151938d 100644
--- a/src/modules/dbus/iface-device.c
+++ b/src/modules/dbus/iface-device.c
@@ -923,19 +923,30 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
 static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     pa_dbusiface_device *d = userdata;
     dbus_bool_t suspend = FALSE;
+    pa_client *client;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(d);
 
     pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &suspend, DBUS_TYPE_INVALID));
+    pa_assert_se(client = pa_dbus_protocol_get_client(d->dbus_protocol, conn));
 
-    if ((d->type == PA_DEVICE_TYPE_SINK) && (pa_sink_suspend(d->sink, suspend, PA_SUSPEND_USER) < 0)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_sink_suspend() failed.");
-        return;
-    } else if ((d->type == PA_DEVICE_TYPE_SOURCE) && (pa_source_suspend(d->source, suspend, PA_SUSPEND_USER) < 0)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_source_suspend() failed.");
-        return;
+    if (d->type == PA_DEVICE_TYPE_SINK) {
+        pa_log_debug("%s sink %s requested by client %" PRIu32 ".", suspend ? "Suspending" : "Resuming", d->sink->name, client->index);
+
+        if (pa_sink_suspend(d->sink, suspend, PA_SUSPEND_USER) < 0) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_sink_suspend() failed.");
+            return;
+        }
+
+    } else {
+        pa_log_debug("%s source %s requested by client %" PRIu32 ".", suspend ? "Suspending" : "Resuming", d->source->name, client->index);
+
+        if (pa_source_suspend(d->source, suspend, PA_SUSPEND_USER) < 0) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_source_suspend() failed.");
+            return;
+        }
     }
 
     pa_dbus_send_empty_reply(conn, msg);
diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
index e1f6043..f27dafd 100644
--- a/src/modules/module-suspend-on-idle.c
+++ b/src/modules/module-suspend-on-idle.c
@@ -128,15 +128,13 @@ static void resume(struct device_info *d) {
     d->userdata->core->mainloop->time_restart(d->time_event, NULL);
 
     if (d->sink) {
+        pa_log_debug("Sink %s becomes busy, resuming.", d->sink->name);
         pa_sink_suspend(d->sink, FALSE, PA_SUSPEND_IDLE);
-
-        pa_log_debug("Sink %s becomes busy.", d->sink->name);
     }
 
     if (d->source) {
+        pa_log_debug("Source %s becomes busy, resuming.", d->source->name);
         pa_source_suspend(d->source, FALSE, PA_SUSPEND_IDLE);
-
-        pa_log_debug("Source %s becomes busy.", d->source->name);
     }
 }
 
diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c
index 31416bd..ea4893e 100644
--- a/src/modules/module-udev-detect.c
+++ b/src/modules/module-udev-detect.c
@@ -350,8 +350,10 @@ static void verify_access(struct userdata *u, struct device *d) {
         /* If we are already loaded update suspend status with
          * accessible boolean */
 
-        if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD)))
+        if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) {
+            pa_log_debug("%s all sinks and sources of card %s.", accessible ? "Resuming" : "Suspending", d->card_name);
             pa_card_suspend(card, !accessible, PA_SUSPEND_SESSION);
+        }
     }
 }
 
diff --git a/src/modules/module-virtual-source.c b/src/modules/module-virtual-source.c
index b2a8359..4bdcded 100644
--- a/src/modules/module-virtual-source.c
+++ b/src/modules/module-virtual-source.c
@@ -126,6 +126,7 @@ static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) {
 
     if (state == PA_SINK_RUNNING) {
         /* need to wake-up source if it was suspended */
+        pa_log_debug("Resuming source %s, because its uplink sink became active.", u->source->name);
         pa_source_suspend(u->source, FALSE, PA_SUSPEND_ALL);
 
         /* FIXME: if there's no client connected, the source will suspend
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 130185a..8ddd3d5 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -1423,6 +1423,8 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b
         return -1;
     }
 
+    pa_log_debug("%s of sink %s requested via CLI.", suspend ? "Suspending" : "Resuming", sink->name);
+
     if ((r = pa_sink_suspend(sink, suspend, PA_SUSPEND_USER)) < 0)
         pa_strbuf_printf(buf, "Failed to resume/suspend sink: %s\n", pa_strerror(r));
 
@@ -1459,6 +1461,8 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf
         return -1;
     }
 
+    pa_log_debug("%s of source %s requested via CLI.", suspend ? "Suspending" : "Resuming", source->name);
+
     if ((r = pa_source_suspend(source, suspend, PA_SUSPEND_USER)) < 0)
         pa_strbuf_printf(buf, "Failed to resume/suspend source: %s\n", pa_strerror(r));
 
@@ -1484,6 +1488,8 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p
         return -1;
     }
 
+    pa_log_debug("%s of all sinks and sources requested via CLI.", suspend ? "Suspending" : "Resuming");
+
     if ((r = pa_sink_suspend_all(c, suspend, PA_SUSPEND_USER)) < 0)
         pa_strbuf_printf(buf, "Failed to resume/suspend all sinks: %s\n", pa_strerror(r));
 
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 15e857a..ce50084 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -949,6 +949,9 @@ static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const
     connection_write_prepare(c, sizeof(int32_t) * 2);
     connection_write(c, &ok, sizeof(int32_t));
 
+    pa_log_debug("%s of all sinks and sources requested by client %" PRIu32 ".",
+                 request == ESD_PROTO_STANDBY ? "Suspending" : "Resuming", c->client->index);
+
     if (request == ESD_PROTO_STANDBY) {
         ok = pa_sink_suspend_all(c->protocol->core, true, PA_SUSPEND_USER) >= 0;
         ok &= pa_source_suspend_all(c->protocol->core, true, PA_SUSPEND_USER) >= 0;
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index c39efc6..88808bf 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -4552,6 +4552,9 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
 
             CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
 
+            pa_log_debug("%s of sink %s requested by client %" PRIu32 ".",
+                         b ? "Suspending" : "Resuming", sink->name, c->client->index);
+
             if (pa_sink_suspend(sink, b, PA_SUSPEND_USER) < 0) {
                 pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
                 return;
@@ -4580,6 +4583,9 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
 
             CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
 
+            pa_log_debug("%s of source %s requested by client %" PRIu32 ".",
+                         b ? "Suspending" : "Resuming", source->name, c->client->index);
+
             if (pa_source_suspend(source, b, PA_SUSPEND_USER) < 0) {
                 pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
                 return;
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 4512470..a8a91d6 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1395,6 +1395,7 @@ pa_bool_t pa_sink_update_rate(pa_sink *s, uint32_t rate, pa_bool_t passthrough)
         if (!passthrough && pa_sink_used_by(s) > 0)
             return FALSE;
 
+        pa_log_debug("Suspending sink %s due to changing the sample rate.", s->name);
         pa_sink_suspend(s, TRUE, PA_SUSPEND_IDLE); /* needed before rate update, will be resumed automatically */
 
         if (s->update_rate(s, desired_rate) == TRUE) {
@@ -1531,8 +1532,10 @@ void pa_sink_enter_passthrough(pa_sink *s) {
     pa_cvolume volume;
 
     /* disable the monitor in passthrough mode */
-    if (s->monitor_source)
+    if (s->monitor_source) {
+        pa_log_debug("Suspending monitor source %s, because the sink is entering the passthrough mode.", s->monitor_source->name);
         pa_source_suspend(s->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH);
+    }
 
     /* set the volume to NORM */
     s->saved_volume = *pa_sink_get_volume(s, TRUE);
@@ -1545,8 +1548,10 @@ void pa_sink_enter_passthrough(pa_sink *s) {
 /* Called from main context */
 void pa_sink_leave_passthrough(pa_sink *s) {
     /* Unsuspend monitor */
-    if (s->monitor_source)
+    if (s->monitor_source) {
+        pa_log_debug("Resuming monitor source %s, because the sink is leaving the passthrough mode.", s->monitor_source->name);
         pa_source_suspend(s->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH);
+    }
 
     /* Restore sink volume to what it was before we entered passthrough mode */
     pa_sink_set_volume(s, &s->saved_volume, TRUE, s->saved_save_volume);
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index e9c3669..8c16606 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -1006,6 +1006,7 @@ pa_bool_t pa_source_update_rate(pa_source *s, uint32_t rate, pa_bool_t passthrou
         if (!passthrough && pa_source_used_by(s) > 0)
             return FALSE;
 
+        pa_log_debug("Suspending source %s due to changing the sample rate.", s->name);
         pa_source_suspend(s, TRUE, PA_SUSPEND_IDLE); /* needed before rate update, will be resumed automatically */
 
         if (s->update_rate(s, desired_rate) == TRUE) {

commit 28c49a12fc83b7487f367b4186de10ff29a23a2e
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Nov 16 18:03:55 2012 +0200

    esound: Suspend/resume also sources on STANDBY/RESUME commands.

diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 00ea000..15e857a 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -949,11 +949,13 @@ static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const
     connection_write_prepare(c, sizeof(int32_t) * 2);
     connection_write(c, &ok, sizeof(int32_t));
 
-    if (request == ESD_PROTO_STANDBY)
-        ok = pa_sink_suspend_all(c->protocol->core, TRUE, PA_SUSPEND_USER) >= 0;
-    else {
+    if (request == ESD_PROTO_STANDBY) {
+        ok = pa_sink_suspend_all(c->protocol->core, true, PA_SUSPEND_USER) >= 0;
+        ok &= pa_source_suspend_all(c->protocol->core, true, PA_SUSPEND_USER) >= 0;
+    } else {
         pa_assert(request == ESD_PROTO_RESUME);
-        ok = pa_sink_suspend_all(c->protocol->core, FALSE, PA_SUSPEND_USER) >= 0;
+        ok = pa_sink_suspend_all(c->protocol->core, false, PA_SUSPEND_USER) >= 0;
+        ok &= pa_source_suspend_all(c->protocol->core, false, PA_SUSPEND_USER) >= 0;
     }
 
     connection_write(c, &ok, sizeof(int32_t));

commit 3ee3b0f49b3252785330a26d5dd624d8ae43e09c
Author: Tanu Kaskinen <tanu.kaskinen at digia.com>
Date:   Mon Apr 2 15:01:06 2012 +0300

    mainloop: Check pa_write() return value.

diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c
index f13c0f8..a3d7715 100644
--- a/src/pulse/mainloop.c
+++ b/src/pulse/mainloop.c
@@ -770,7 +770,10 @@ void pa_mainloop_wakeup(pa_mainloop *m) {
     char c = 'W';
     pa_assert(m);
 
-    pa_write(m->wakeup_pipe[1], &c, sizeof(c), &m->wakeup_pipe_type);
+    if (pa_write(m->wakeup_pipe[1], &c, sizeof(c), &m->wakeup_pipe_type) < 0)
+        /* Not much options for recovering from the error. Let's at least log something. */
+        pa_log("pa_write() failed while trying to wake up the mainloop: %s", pa_cstrerror(errno));
+
     pa_atomic_store(&m->wakeup_requested, TRUE);
 }
 

commit 7e94a03e0c575726a21740b0f0cfdd6196eb40cb
Author: Tanu Kaskinen <tanu.kaskinen at digia.com>
Date:   Mon Apr 2 15:01:04 2012 +0300

    mainloop: Remove redundant wakeup_pipe validity checks.
    
    The pipe is opened when creating the mainloop and closed
    when freeing the mainloop. The pipe fds can never be less
    than zero.

diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c
index aec082c..f13c0f8 100644
--- a/src/pulse/mainloop.c
+++ b/src/pulse/mainloop.c
@@ -611,13 +611,11 @@ static void rebuild_pollfds(pa_mainloop *m) {
     m->n_pollfds = 0;
     p = m->pollfds;
 
-    if (m->wakeup_pipe[0] >= 0) {
-        m->pollfds[0].fd = m->wakeup_pipe[0];
-        m->pollfds[0].events = POLLIN;
-        m->pollfds[0].revents = 0;
-        p++;
-        m->n_pollfds++;
-    }
+    m->pollfds[0].fd = m->wakeup_pipe[0];
+    m->pollfds[0].events = POLLIN;
+    m->pollfds[0].revents = 0;
+    p++;
+    m->n_pollfds++;
 
     PA_LLIST_FOREACH(e, m->io_events) {
         if (e->dead) {
@@ -772,10 +770,8 @@ void pa_mainloop_wakeup(pa_mainloop *m) {
     char c = 'W';
     pa_assert(m);
 
-    if (m->wakeup_pipe[1] >= 0) {
-        pa_write(m->wakeup_pipe[1], &c, sizeof(c), &m->wakeup_pipe_type);
-        pa_atomic_store(&m->wakeup_requested, TRUE);
-    }
+    pa_write(m->wakeup_pipe[1], &c, sizeof(c), &m->wakeup_pipe_type);
+    pa_atomic_store(&m->wakeup_requested, TRUE);
 }
 
 static void clear_wakeup(pa_mainloop *m) {
@@ -783,9 +779,6 @@ static void clear_wakeup(pa_mainloop *m) {
 
     pa_assert(m);
 
-    if (m->wakeup_pipe[0] < 0)
-        return;
-
     if (pa_atomic_cmpxchg(&m->wakeup_requested, TRUE, FALSE)) {
         while (pa_read(m->wakeup_pipe[0], &c, sizeof(c), &m->wakeup_pipe_type) == sizeof(c))
             ;

commit 31d905aaae738969e9d63b8fb46ef92259fffbf0
Author: Luiz Augusto von Dentz <luiz.von.dentz at intel.com>
Date:   Mon Nov 12 12:26:02 2012 +0200

    bluetooth: Add support for transport created by external profile
    
    With BlueZ 5 it is possible to have profile registered by a third party
    process which does not share the same bus id as bluetoothd so it is
    necessary to store the sender of the transport to be able to talk to it.
    
    Note that this is backward compatible.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 14368da..e09e170 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -153,6 +153,7 @@ static void transport_free(pa_bluetooth_transport *t) {
     for (i = 0; i < PA_BLUETOOTH_TRANSPORT_HOOK_MAX; i++)
         pa_hook_done(&t->hooks[i]);
 
+    pa_xfree(t->owner);
     pa_xfree(t->path);
     pa_xfree(t->config);
     pa_xfree(t);
@@ -1042,7 +1043,7 @@ int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, const char *access
 
     dbus_error_init(&err);
 
-    pa_assert_se(m = dbus_message_new_method_call("org.bluez", t->path, "org.bluez.MediaTransport", "Acquire"));
+    pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport", "Acquire"));
     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID));
     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->y->connection), m, -1, &err);
 
@@ -1078,7 +1079,7 @@ void pa_bluetooth_transport_release(pa_bluetooth_transport *t, const char *acces
 
     dbus_error_init(&err);
 
-    pa_assert_se(m = dbus_message_new_method_call("org.bluez", t->path, "org.bluez.MediaTransport", "Release"));
+    pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport", "Release"));
     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID));
     dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->y->connection), m, -1, &err);
 
@@ -1105,12 +1106,13 @@ static int setup_dbus(pa_bluetooth_discovery *y) {
     return 0;
 }
 
-static pa_bluetooth_transport *transport_new(pa_bluetooth_discovery *y, const char *path, enum profile p, const uint8_t *config, int size) {
+static pa_bluetooth_transport *transport_new(pa_bluetooth_discovery *y, const char *owner, const char *path, enum profile p, const uint8_t *config, int size) {
     pa_bluetooth_transport *t;
     unsigned i;
 
     t = pa_xnew0(pa_bluetooth_transport, 1);
     t->y = y;
+    t->owner = pa_xstrdup(owner);
     t->path = pa_xstrdup(path);
     t->profile = p;
     t->config_size = size;
@@ -1130,7 +1132,7 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
     pa_bluetooth_discovery *y = userdata;
     pa_bluetooth_device *d;
     pa_bluetooth_transport *t;
-    const char *path, *dev_path = NULL, *uuid = NULL;
+    const char *sender, *path, *dev_path = NULL, *uuid = NULL;
     uint8_t *config = NULL;
     int size = 0;
     pa_bool_t nrec = FALSE;
@@ -1199,7 +1201,9 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
     else
         p = PROFILE_A2DP_SOURCE;
 
-    t = transport_new(y, path, p, config, size);
+    sender = dbus_message_get_sender(m);
+
+    t = transport_new(y, sender, path, p, config, size);
     if (nrec)
         t->nrec = nrec;
     pa_hashmap_put(d->transports, t->path, t);
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 874baa0..95dd5c6 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -72,6 +72,7 @@ typedef enum pa_bluetooth_transport_hook {
 
 struct pa_bluetooth_transport {
     pa_bluetooth_discovery *y;
+    char *owner;
     char *path;
     enum profile profile;
     uint8_t codec;

commit d4368aa608b79f58a279018eb74abd5a6bff30ac
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Oct 26 08:23:51 2012 +0200

    bluetooth: Handle UUIDs announced later
    
    In some cases (typically during pairing) UUIDs might be reported by
    BlueZ incrementally, that is, as soon as they have been discovered. At
    this point module-bluetooth-device might already be loaded, so the late
    UUID announcements need to be handled and additional card profiles
    might need to be created accordingly.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 6c0c746..1236cb5 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -140,6 +140,7 @@ struct userdata {
     pa_module *module;
 
     pa_bluetooth_device *device;
+    pa_hook_slot *uuid_added_slot;
     char *address;
     char *path;
     pa_bluetooth_transport *transport;
@@ -2536,6 +2537,38 @@ static pa_bluetooth_device* find_device(struct userdata *u, const char *address,
 }
 
 /* Run from main thread */
+static pa_hook_result_t uuid_added_cb(pa_bluetooth_device *d, const char *uuid, struct userdata *u) {
+    pa_card_profile *p;
+    pa_hashmap *new_ports;
+
+    pa_assert(d);
+    pa_assert(uuid);
+    pa_assert(u);
+
+    p = create_card_profile(u, uuid);
+
+    if (!p)
+        return PA_HOOK_OK;
+
+    if (pa_hashmap_get(u->card->profiles, p->name)) {
+        pa_card_profile_free(p);
+        return PA_HOOK_OK;
+    }
+
+    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_device_port_hashmap_free(new_ports);
+
+    return PA_HOOK_OK;
+}
+
+/* Run from main thread */
 static int setup_dbus(struct userdata *u) {
     DBusError err;
 
@@ -2658,6 +2691,9 @@ int pa__init(pa_module* m) {
 
     u->device = device;
 
+    u->uuid_added_slot = pa_hook_connect(&device->hooks[PA_BLUETOOTH_DEVICE_HOOK_UUID_ADDED], PA_HOOK_NORMAL,
+                                         (pa_hook_cb_t) uuid_added_cb, u);
+
     /* Add the card structure. This will also initialize the default profile */
     if (add_card(u) < 0)
         goto fail;
@@ -2776,6 +2812,9 @@ void pa__done(pa_module *m) {
         pa_dbus_connection_unref(u->connection);
     }
 
+    if (u->uuid_added_slot)
+        pa_hook_slot_free(u->uuid_added_slot);
+
     if (u->msg)
         pa_xfree(u->msg);
 

commit ebfd656fc99748e8beabf21885129dfbbf19a030
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Oct 26 08:23:50 2012 +0200

    dbus: Add signal org.PulseAudio.Core1.Card.NewProfile
    
    Add a new D-Bus signal to report profiles that have been dynamically
    created for already existing cards.

diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
index 6c3e367..1313e71 100644
--- a/src/modules/dbus/iface-card.c
+++ b/src/modules/dbus/iface-card.c
@@ -108,15 +108,18 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
 
 enum signal_index {
     SIGNAL_ACTIVE_PROFILE_UPDATED,
+    SIGNAL_NEW_PROFILE,
     SIGNAL_PROPERTY_LIST_UPDATED,
     SIGNAL_MAX
 };
 
 static pa_dbus_arg_info active_profile_updated_args[] = { { "profile",       "o",      NULL } };
+static pa_dbus_arg_info new_profile_args[] =            { { "profile",       "o",      NULL } };
 static pa_dbus_arg_info property_list_updated_args[] =  { { "property_list", "a{say}", NULL } };
 
 static pa_dbus_signal_info signals[SIGNAL_MAX] = {
     [SIGNAL_ACTIVE_PROFILE_UPDATED] = { .name = "ActiveProfileUpdated", .arguments = active_profile_updated_args, .n_arguments = 1 },
+    [SIGNAL_NEW_PROFILE]            = { .name = "NewProfile",           .arguments = new_profile_args,            .n_arguments = 1 },
     [SIGNAL_PROPERTY_LIST_UPDATED]  = { .name = "PropertyListUpdated",  .arguments = property_list_updated_args,  .n_arguments = 1 }
 };
 
@@ -485,6 +488,8 @@ static pa_hook_result_t card_profile_added_cb(void *hook_data, void *call_data,
     pa_dbusiface_card *c = slot_data;
     pa_card_profile *profile = call_data;
     pa_dbusiface_card_profile *p;
+    const char *object_path;
+    DBusMessage *signal_msg;
 
     if (profile->card != c->card)
         return PA_HOOK_OK;
@@ -492,6 +497,17 @@ static pa_hook_result_t card_profile_added_cb(void *hook_data, void *call_data,
     p = pa_dbusiface_card_profile_new(c, core, profile, c->next_profile_index++);
     pa_assert_se(pa_hashmap_put(c->profiles, pa_dbusiface_card_profile_get_name(p), p) >= 0);
 
+    /* Send D-Bus signal */
+    object_path = pa_dbusiface_card_profile_get_path(p);
+
+    pa_assert_se(signal_msg = dbus_message_new_signal(c->path,
+                                                      PA_DBUSIFACE_CARD_INTERFACE,
+                                                      signals[SIGNAL_NEW_PROFILE].name));
+    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg);
+    dbus_message_unref(signal_msg);
+
     return PA_HOOK_OK;
 }
 

commit 9c703f4e2d8de91cf92c35431088b506652c9ce6
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri Oct 26 08:23:49 2012 +0200

    dbus: Support dynamically created card profiles
    
    Use the recently added core hook to detect when new profiles are added
    to a card, and update the D-Bus object accordingly.

diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
index 89e3988..6c3e367 100644
--- a/src/modules/dbus/iface-card.c
+++ b/src/modules/dbus/iface-card.c
@@ -60,6 +60,8 @@ struct pa_dbusiface_card {
     pa_card_profile *active_profile;
     pa_proplist *proplist;
 
+    pa_hook_slot *card_profile_added_slot;
+
     pa_dbus_protocol *dbus_protocol;
     pa_subscription *subscription;
 };
@@ -478,6 +480,21 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
     }
 }
 
+static pa_hook_result_t card_profile_added_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_core *core = hook_data;
+    pa_dbusiface_card *c = slot_data;
+    pa_card_profile *profile = call_data;
+    pa_dbusiface_card_profile *p;
+
+    if (profile->card != c->card)
+        return PA_HOOK_OK;
+
+    p = pa_dbusiface_card_profile_new(c, core, profile, c->next_profile_index++);
+    pa_assert_se(pa_hashmap_put(c->profiles, pa_dbusiface_card_profile_get_name(p), p) >= 0);
+
+    return PA_HOOK_OK;
+}
+
 pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) {
     pa_dbusiface_card *c = NULL;
     pa_card_profile *profile;
@@ -504,6 +521,9 @@ pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card)
 
     pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &card_interface_info, c) >= 0);
 
+    c->card_profile_added_slot = pa_hook_connect(&card->core->hooks[PA_CORE_HOOK_CARD_PROFILE_ADDED], PA_HOOK_NORMAL,
+                                                 card_profile_added_cb, c);
+
     return c;
 }
 
@@ -520,6 +540,8 @@ void pa_dbusiface_card_free(pa_dbusiface_card *c) {
 
     pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, c->path, card_interface_info.name) >= 0);
 
+    pa_hook_slot_free(c->card_profile_added_slot);
+
     pa_hashmap_free(c->profiles, profile_free_cb, NULL);
     pa_proplist_free(c->proplist);
     pa_dbus_protocol_unref(c->dbus_protocol);



More information about the pulseaudio-commits mailing list