[pulseaudio-commits] 52 commits - Makefile.am bootstrap.sh configure.ac m4/.gitignore shell-completion/pulseaudio-bash-completion.sh shell-completion/pulseaudio-zsh-completion.zsh src/.gitignore src/Makefile.am src/map-file src/modules src/pulse src/pulsecore src/tests src/utils

Arun Raghavan arun at kemper.freedesktop.org
Mon Jun 3 12:13:44 PDT 2013


 Makefile.am                                                        |    1 
 bootstrap.sh                                                       |    3 
 configure.ac                                                       |   11 
 m4/.gitignore                                                      |   33 
 shell-completion/pulseaudio-bash-completion.sh                     |    2 
 shell-completion/pulseaudio-zsh-completion.zsh                     |    1 
 src/.gitignore                                                     |    1 
 src/Makefile.am                                                    |   15 
 src/map-file                                                       |    1 
 src/modules/alsa/alsa-mixer.c                                      |  140 +-
 src/modules/alsa/alsa-mixer.h                                      |    1 
 src/modules/alsa/alsa-ucm.c                                        |   14 
 src/modules/alsa/mixer/paths/analog-input-aux.conf                 |    2 
 src/modules/alsa/mixer/paths/analog-input-dock-mic.conf            |    2 
 src/modules/alsa/mixer/paths/analog-input-fm.conf                  |    2 
 src/modules/alsa/mixer/paths/analog-input-front-mic.conf           |    2 
 src/modules/alsa/mixer/paths/analog-input-headphone-mic.conf       |    2 
 src/modules/alsa/mixer/paths/analog-input-headset-mic.conf         |    2 
 src/modules/alsa/mixer/paths/analog-input-internal-mic-always.conf |    2 
 src/modules/alsa/mixer/paths/analog-input-internal-mic.conf        |    2 
 src/modules/alsa/mixer/paths/analog-input-mic-line.conf            |    2 
 src/modules/alsa/mixer/paths/analog-input-mic.conf                 |    2 
 src/modules/alsa/mixer/paths/analog-input-rear-mic.conf            |    2 
 src/modules/alsa/mixer/paths/analog-input-tvtuner.conf             |    2 
 src/modules/alsa/mixer/paths/analog-output-desktop-speaker.conf    |    2 
 src/modules/alsa/mixer/paths/analog-output-headphones-2.conf       |    2 
 src/modules/alsa/mixer/paths/analog-output-headphones.conf         |    2 
 src/modules/alsa/mixer/paths/analog-output-speaker-always.conf     |    2 
 src/modules/alsa/mixer/paths/analog-output-speaker.conf            |    2 
 src/modules/alsa/mixer/paths/analog-output.conf.common             |    8 
 src/modules/bluetooth/bluetooth-util.c                             |  483 ++++++++--
 src/modules/bluetooth/module-bluetooth-device.c                    |   38 
 src/modules/module-intended-roles.c                                |    2 
 src/modules/module-rescue-streams.c                                |    2 
 src/modules/module-switch-on-port-available.c                      |  108 +-
 src/modules/module-zeroconf-publish.c                              |  300 ++++--
 src/pulse/thread-mainloop.c                                        |   16 
 src/pulse/thread-mainloop.h                                        |    7 
 src/pulsecore/cli-text.c                                           |    2 
 src/pulsecore/core-util.c                                          |    6 
 src/pulsecore/core.h                                               |    1 
 src/pulsecore/device-port.c                                        |   92 +
 src/pulsecore/device-port.h                                        |   19 
 src/pulsecore/fdsem.c                                              |   12 
 src/pulsecore/module.c                                             |   10 
 src/pulsecore/msgobject.c                                          |    2 
 src/pulsecore/protocol-native.c                                    |    2 
 src/pulsecore/sink.c                                               |   11 
 src/pulsecore/source.c                                             |   11 
 src/tests/lo-latency-test.c                                        |  190 +++
 src/tests/lo-test-util.c                                           |  328 ++++++
 src/tests/lo-test-util.h                                           |   57 +
 src/utils/pactl.c                                                  |    3 
 src/utils/pasuspender.c                                            |   82 +
 54 files changed, 1684 insertions(+), 365 deletions(-)

New commits:
commit c6df40a5397fea49c1a8edb2ac8d5f3e74792606
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Mon May 27 14:27:42 2013 +0530

    tests: Make loopback latency test more accurate
    
    This makes sure that we always take the timing at the point when we
    write out the pulse, making the overall latency measurement more
    accurate.

diff --git a/src/tests/lo-latency-test.c b/src/tests/lo-latency-test.c
index 124693d..ad5e7a5 100644
--- a/src/tests/lo-latency-test.c
+++ b/src/tests/lo-latency-test.c
@@ -54,12 +54,17 @@ static void nop_free_cb(void *p) {
 static void write_cb(pa_stream *s, size_t nbytes, void *userdata) {
     pa_lo_test_context *ctx = (pa_lo_test_context *) userdata;
     static int ppos = 0;
-    int r, nsamp = nbytes / ctx->fs;
+    int r, nsamp;
+
+    /* Get the real requested bytes since the last write might have been
+     * incomplete if it caused a wrap around */
+    nbytes = pa_stream_writable_size(s);
+    nsamp = nbytes / ctx->fs;
 
     if (ppos + nsamp > N_OUT) {
-        r = pa_stream_write(s, &out[ppos][0], (N_OUT - ppos) * ctx->fs, nop_free_cb, 0, PA_SEEK_RELATIVE);
-        nbytes -= (N_OUT - ppos) * ctx->fs;
-        ppos = 0;
+        /* Wrap-around, write to end and exit. Next iteration will fill up the
+         * rest */
+        nbytes = (N_OUT - ppos) * ctx->fs;
     }
 
     if (ppos == 0)

commit e001cc1424d4f24303168bc5994529c61e2ec042
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Thu May 23 15:27:40 2013 +0530

    tests: Factor out loopback setup code
    
    This moves over setup code for the loopback latency test into a private
    library so that we can easily write more tests using the same framework.

diff --git a/src/Makefile.am b/src/Makefile.am
index 163976e..44c191d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -223,6 +223,7 @@ pax11publish_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
 ###################################
 #         Test programs           #
 ###################################
+noinst_LTLIBRARIES =
 
 TESTS_default = \
 		mainloop-test \
@@ -575,8 +576,13 @@ echo_cancel_test_CXXFLAGS = $(module_echo_cancel_la_CXXFLAGS) -DECHO_CANCEL_TEST
 endif
 echo_cancel_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
 
+liblo_test_util_la_SOURCES = tests/lo-test-util.h tests/lo-test-util.c
+liblo_test_util_la_LIBADD = libpulsecore- at PA_MAJORMINOR@.la
+liblo_test_util_la_LDFLAGS = -avoid-version
+noinst_LTLIBRARIES += liblo-test-util.la
+
 lo_latency_test_SOURCES = tests/lo-latency-test.c
-lo_latency_test_LDADD = $(AM_LDADD) libpulse.la
+lo_latency_test_LDADD = $(AM_LDADD) libpulse.la liblo-test-util.la
 lo_latency_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
 lo_latency_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
 
@@ -855,7 +861,6 @@ libpulsedsp_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version -disable-static
 ###################################
 
 lib_LTLIBRARIES += libpulsecore- at PA_MAJORMINOR@.la
-noinst_LTLIBRARIES =
 
 # Pure core stuff
 libpulsecore_ at PA_MAJORMINOR@_la_SOURCES = \
diff --git a/src/tests/lo-latency-test.c b/src/tests/lo-latency-test.c
index 8f3b04d..124693d 100644
--- a/src/tests/lo-latency-test.c
+++ b/src/tests/lo-latency-test.c
@@ -32,62 +32,33 @@
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <math.h>
 
 #include <check.h>
 
-#include <pulse/pulseaudio.h>
-#include <pulse/mainloop.h>
-
-/* for pa_make_realtime */
-#include <pulsecore/core-util.h>
+#include "lo-test-util.h"
 
 #define SAMPLE_HZ 44100
 #define CHANNELS 2
 #define N_OUT (SAMPLE_HZ * 1)
 
-#define TONE_HZ (SAMPLE_HZ / 100)
-#define PLAYBACK_LATENCY 25 /* ms */
-#define CAPTURE_LATENCY 5 /* ms */
-
-static pa_context *context = NULL;
-static pa_stream *pstream, *rstream;
-static pa_mainloop_api *mainloop_api = NULL;
-static const char *context_name = NULL;
-
 static float out[N_OUT][CHANNELS];
-static int ppos = 0;
 
-static int n_underflow = 0;
-static int n_overflow = 0;
+pa_lo_test_context test_ctx;
+static const char *context_name = NULL;
 
 static struct timeval tv_out, tv_in;
 
-static const pa_sample_spec sample_spec = {
-    .format = PA_SAMPLE_FLOAT32,
-    .rate = SAMPLE_HZ,
-    .channels = CHANNELS,
-};
-static int ss, fs;
-
-static void nop_free_cb(void *p) {}
-
-static void underflow_cb(struct pa_stream *s, void *userdata) {
-    fprintf(stderr, "Underflow\n");
-    n_underflow++;
-}
-
-static void overflow_cb(struct pa_stream *s, void *userdata) {
-    fprintf(stderr, "Overlow\n");
-    n_overflow++;
+static void nop_free_cb(void *p) {
 }
 
 static void write_cb(pa_stream *s, size_t nbytes, void *userdata) {
-    int r, nsamp = nbytes / fs;
+    pa_lo_test_context *ctx = (pa_lo_test_context *) userdata;
+    static int ppos = 0;
+    int r, nsamp = nbytes / ctx->fs;
 
     if (ppos + nsamp > N_OUT) {
-        r = pa_stream_write(s, &out[ppos][0], (N_OUT - ppos) * fs, nop_free_cb, 0, PA_SEEK_RELATIVE);
-        nbytes -= (N_OUT - ppos) * fs;
+        r = pa_stream_write(s, &out[ppos][0], (N_OUT - ppos) * ctx->fs, nop_free_cb, 0, PA_SEEK_RELATIVE);
+        nbytes -= (N_OUT - ppos) * ctx->fs;
         ppos = 0;
     }
 
@@ -97,22 +68,13 @@ static void write_cb(pa_stream *s, size_t nbytes, void *userdata) {
     r = pa_stream_write(s, &out[ppos][0], nbytes, nop_free_cb, 0, PA_SEEK_RELATIVE);
     fail_unless(r == 0);
 
-    ppos = (ppos + nbytes / fs) % N_OUT;
-}
-
-static inline float rms(const float *s, int n) {
-    float sq = 0;
-    int i;
-
-    for (i = 0; i < n; i++)
-        sq += s[i] * s[i];
-
-    return sqrtf(sq / n);
+    ppos = (ppos + nbytes / ctx->fs) % N_OUT;
 }
 
 #define WINDOW (2 * CHANNELS)
 
 static void read_cb(pa_stream *s, size_t nbytes, void *userdata) {
+    pa_lo_test_context *ctx = (pa_lo_test_context *) userdata;
     static float last = 0.0f;
     const float *in;
     float cur;
@@ -143,16 +105,16 @@ static void read_cb(pa_stream *s, size_t nbytes, void *userdata) {
 #if 0
         {
             int j;
-            fprintf(stderr, "%g (", rms(in, WINDOW));
+            fprintf(stderr, "%g (", pa_rms(in, WINDOW));
             for (j = 0; j < WINDOW; j++)
                 fprintf(stderr, "%g ", in[j]);
             fprintf(stderr, ")\n");
         }
 #endif
-        if (i + (ss * WINDOW) < l)
-            cur = rms(in, WINDOW);
+        if (i + (ctx->ss * WINDOW) < l)
+            cur = pa_rms(in, WINDOW);
         else
-            cur = rms(in, (l - i)/ss);
+            cur = pa_rms(in, (l - i) / ctx->ss);
 
         /* We leave the definition of 0 generous since the window might
          * straddle the 0->1 transition, raising the average power. We keep the
@@ -165,223 +127,26 @@ static void read_cb(pa_stream *s, size_t nbytes, void *userdata) {
 
         last = cur;
         in += WINDOW;
-        i += ss * WINDOW;
-    } while (i + (ss * WINDOW) <= l);
-
-    pa_stream_drop(s);
-}
-
-/*
- * We run a simple volume calibration so that we know we can detect the signal
- * being played back. We start with the playback stream at 100% volume, and
- * capture at 0.
- *
- * First, we then play a sine wave and increase the capture volume till the
- * signal is clearly received.
- *
- * Next, we play back silence and make sure that the level is low enough to
- * distinguish from when playback is happening.
- *
- * Finally, we hand off to the real read/write callbacks to run the actual
- * test.
- */
-
-enum {
-    CALIBRATION_ONE,
-    CALIBRATION_ZERO,
-    CALIBRATION_DONE,
-};
-
-static int cal_state = CALIBRATION_ONE;
-
-static void calibrate_write_cb(pa_stream *s, size_t nbytes, void *userdata) {
-    int i, r, nsamp = nbytes / fs;
-    float tmp[nsamp][2];
-    static int count = 0;
-
-    /* Write out a sine tone */
-    for (i = 0; i < nsamp; i++)
-        tmp[i][0] = tmp[i][1] = cal_state == CALIBRATION_ONE ? sinf(count++ * TONE_HZ * 2 * M_PI / SAMPLE_HZ) : 0.0f;
-
-    r = pa_stream_write(s, &tmp, nbytes, nop_free_cb, 0, PA_SEEK_RELATIVE);
-    fail_unless(r == 0);
-
-    if (cal_state == CALIBRATION_DONE)
-        pa_stream_set_write_callback(s, write_cb, NULL);
-}
-
-static void calibrate_read_cb(pa_stream *s, size_t nbytes, void *userdata) {
-    static double v = 0;
-    static int skip = 0, confirm;
-
-    pa_cvolume vol;
-    pa_operation *o;
-    int r, nsamp;
-    float *in;
-    size_t l;
-
-    r = pa_stream_peek(s, (const void **)&in, &l);
-    fail_unless(r == 0);
-
-    nsamp = l / fs;
-
-    /* For each state or volume step change, throw out a few samples so we know
-     * we're seeing the changed samples. */
-    if (skip++ < 100)
-        goto out;
-    else
-        skip = 0;
-
-    switch (cal_state) {
-        case CALIBRATION_ONE:
-            /* Try to detect the sine wave. RMS is 0.5, */
-            if (rms(in, nsamp) < 0.40f) {
-                confirm = 0;
-                v += 0.02f;
-
-                if (v > 1.0) {
-                    fprintf(stderr, "Capture signal too weak at 100%% volume (%g). Giving up.\n", rms(in, nsamp));
-                    fail();
-                }
-
-                pa_cvolume_set(&vol, CHANNELS, v * PA_VOLUME_NORM);
-                o = pa_context_set_source_output_volume(context, pa_stream_get_index(s), &vol, NULL, NULL);
-                fail_if(o == NULL);
-                pa_operation_unref(o);
-            } else {
-                /* Make sure the signal strength is steadily above our threshold */
-                if (++confirm > 5) {
-#if 0
-                    fprintf(stderr, "Capture volume = %g (%g)\n", v, rms(in, nsamp));
-#endif
-                    cal_state = CALIBRATION_ZERO;
-                }
-            }
-
-            break;
-
-        case CALIBRATION_ZERO:
-            /* Now make sure silence doesn't trigger a false positive because
-             * of noise. */
-            if (rms(in, nsamp) > 0.1f) {
-                fprintf(stderr, "Too much noise on capture (%g). Giving up.\n", rms(in, nsamp));
-                fail();
-            }
-
-            cal_state = CALIBRATION_DONE;
-            pa_stream_set_read_callback(s, read_cb, NULL);
-
-            break;
-
-        default:
-            break;
-    }
+        i += ctx->ss * WINDOW;
+    } while (i + (ctx->ss * WINDOW) <= l);
 
-out:
     pa_stream_drop(s);
 }
 
-/* This routine is called whenever the stream state changes */
-static void stream_state_callback(pa_stream *s, void *userdata) {
-    switch (pa_stream_get_state(s)) {
-        case PA_STREAM_UNCONNECTED:
-        case PA_STREAM_CREATING:
-        case PA_STREAM_TERMINATED:
-            break;
-
-        case PA_STREAM_READY: {
-            pa_cvolume vol;
-            pa_operation *o;
-
-            /* Set volumes for calibration */
-            if (!userdata) {
-                pa_cvolume_set(&vol, CHANNELS, PA_VOLUME_NORM);
-                o = pa_context_set_sink_input_volume(context, pa_stream_get_index(s), &vol, NULL, NULL);
-            } else {
-                pa_cvolume_set(&vol, CHANNELS, pa_sw_volume_from_linear(0.0));
-                o = pa_context_set_source_output_volume(context, pa_stream_get_index(s), &vol, NULL, NULL);
-            }
-
-            if (!o) {
-                fprintf(stderr, "Could not set stream volume: %s\n", pa_strerror(pa_context_errno(context)));
-                fail();
-            } else
-                pa_operation_unref(o);
-
-            break;
-        }
-
-        case PA_STREAM_FAILED:
-        default:
-            fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
-            fail();
-    }
-}
+START_TEST (loopback_test) {
+    int i, pulse_hz = SAMPLE_HZ / 1000;
 
-/* This is called whenever the context status changes */
-static void context_state_callback(pa_context *c, void *userdata) {
-    fail_unless(c != NULL);
-
-    switch (pa_context_get_state(c)) {
-        case PA_CONTEXT_CONNECTING:
-        case PA_CONTEXT_AUTHORIZING:
-        case PA_CONTEXT_SETTING_NAME:
-            break;
-
-        case PA_CONTEXT_READY: {
-            pa_buffer_attr buffer_attr;
-
-            pa_make_realtime(4);
-
-            /* Create playback stream */
-            buffer_attr.maxlength = -1;
-            buffer_attr.tlength = SAMPLE_HZ * fs * PLAYBACK_LATENCY / 1000;
-            buffer_attr.prebuf = 0; /* Setting prebuf to 0 guarantees us the stream will run synchronously, no matter what */
-            buffer_attr.minreq = -1;
-            buffer_attr.fragsize = -1;
-
-            pstream = pa_stream_new(c, "loopback: play", &sample_spec, NULL);
-            fail_unless(pstream != NULL);
-            pa_stream_set_state_callback(pstream, stream_state_callback, (void *) 0);
-            pa_stream_set_write_callback(pstream, calibrate_write_cb, NULL);
-            pa_stream_set_underflow_callback(pstream, underflow_cb, userdata);
-
-            pa_stream_connect_playback(pstream, getenv("TEST_SINK"), &buffer_attr,
-                    PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
-
-            /* Create capture stream */
-            buffer_attr.maxlength = -1;
-            buffer_attr.tlength = (uint32_t) -1;
-            buffer_attr.prebuf = 0;
-            buffer_attr.minreq = (uint32_t) -1;
-            buffer_attr.fragsize = SAMPLE_HZ * fs * CAPTURE_LATENCY / 1000;
-
-            rstream = pa_stream_new(c, "loopback: rec", &sample_spec, NULL);
-            fail_unless(rstream != NULL);
-            pa_stream_set_state_callback(rstream, stream_state_callback, (void *) 1);
-            pa_stream_set_read_callback(rstream, calibrate_read_cb, NULL);
-            pa_stream_set_overflow_callback(rstream, overflow_cb, userdata);
-
-            pa_stream_connect_record(rstream, getenv("TEST_SOURCE"), &buffer_attr,
-                    PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);
-
-            break;
-        }
+    test_ctx.context_name = context_name;
 
-        case PA_CONTEXT_TERMINATED:
-            mainloop_api->quit(mainloop_api, 0);
-            break;
+    test_ctx.sample_spec.format = PA_SAMPLE_FLOAT32,
+    test_ctx.sample_spec.rate = SAMPLE_HZ,
+    test_ctx.sample_spec.channels = CHANNELS,
 
-        case PA_CONTEXT_FAILED:
-        default:
-            fprintf(stderr, "Context error: %s\n", pa_strerror(pa_context_errno(c)));
-            fail();
-    }
-}
+    test_ctx.play_latency = 25;
+    test_ctx.rec_latency = 5;
 
-START_TEST (loopback_test) {
-    pa_mainloop* m = NULL;
-    int i, ret = 0, pulse_hz = SAMPLE_HZ / 1000;
+    test_ctx.read_cb = read_cb;
+    test_ctx.write_cb = write_cb;
 
     /* Generate a square pulse */
     for (i = 0; i < N_OUT; i++)
@@ -390,40 +155,9 @@ START_TEST (loopback_test) {
         else
             out[i][0] = out[i][1] = 0.0f;
 
-    ss = pa_sample_size(&sample_spec);
-    fs = pa_frame_size(&sample_spec);
-
-    pstream = NULL;
-
-    /* Set up a new main loop */
-    m = pa_mainloop_new();
-    fail_unless(m != NULL);
-
-    mainloop_api = pa_mainloop_get_api(m);
-
-    context = pa_context_new(mainloop_api, context_name);
-    fail_unless(context != NULL);
-
-    pa_context_set_state_callback(context, context_state_callback, NULL);
-
-    /* Connect the context */
-    if (pa_context_connect(context, NULL, 0, NULL) < 0) {
-        fprintf(stderr, "pa_context_connect() failed.\n");
-        goto quit;
-    }
-
-    if (pa_mainloop_run(m, &ret) < 0)
-        fprintf(stderr, "pa_mainloop_run() failed.\n");
-
-quit:
-    pa_context_unref(context);
-
-    if (pstream)
-        pa_stream_unref(pstream);
-
-    pa_mainloop_free(m);
-
-    fail_unless(ret == 0);
+    fail_unless(pa_lo_test_init(&test_ctx) == 0);
+    fail_unless(pa_lo_test_run(&test_ctx) == 0);
+    pa_lo_test_deinit(&test_ctx);
 }
 END_TEST
 
@@ -435,8 +169,8 @@ int main(int argc, char *argv[]) {
 
     context_name = argv[0];
 
-    s = suite_create("Loopback");
-    tc = tcase_create("loopback");
+    s = suite_create("Loopback latency");
+    tc = tcase_create("loopback latency");
     tcase_add_test(tc, loopback_test);
     tcase_set_timeout(tc, 5 * 60);
     suite_add_tcase(s, tc);
diff --git a/src/tests/lo-test-util.c b/src/tests/lo-test-util.c
new file mode 100644
index 0000000..01eb295
--- /dev/null
+++ b/src/tests/lo-test-util.c
@@ -0,0 +1,328 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Collabora Ltd.
+  Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
+
+  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 <math.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "lo-test-util.h"
+
+/* Keep the frequency high so RMS over ranges of a few ms remains relatively
+ * high as well */
+#define TONE_HZ 4410
+
+static void nop_free_cb(void *p) {
+}
+
+static void underflow_cb(struct pa_stream *s, void *userdata) {
+    pa_log_warn("Underflow\n");
+}
+
+static void overflow_cb(struct pa_stream *s, void *userdata) {
+    pa_log_warn("Overlow\n");
+}
+
+/*
+ * We run a simple volume calibration so that we know we can detect the signal
+ * being played back. We start with the playback stream at 100% volume, and
+ * capture at 0.
+ *
+ * First, we then play a sine wave and increase the capture volume till the
+ * signal is clearly received.
+ *
+ * Next, we play back silence and make sure that the level is low enough to
+ * distinguish from when playback is happening.
+ *
+ * Finally, we hand off to the real read/write callbacks to run the actual
+ * test.
+ */
+
+enum {
+    CALIBRATION_ONE,
+    CALIBRATION_ZERO,
+    CALIBRATION_DONE,
+};
+
+static int cal_state = CALIBRATION_ONE;
+
+static void calibrate_write_cb(pa_stream *s, size_t nbytes, void *userdata) {
+    pa_lo_test_context *ctx = (pa_lo_test_context *) userdata;
+    int i, r, nsamp = nbytes / ctx->fs;
+    float tmp[nsamp][2];
+    static int count = 0;
+
+    /* Write out a sine tone */
+    for (i = 0; i < nsamp; i++)
+        tmp[i][0] = tmp[i][1] = cal_state == CALIBRATION_ONE ? sinf(count++ * TONE_HZ * 2 * M_PI / ctx->sample_spec.rate) : 0.0f;
+
+    r = pa_stream_write(s, &tmp, nbytes, nop_free_cb, 0, PA_SEEK_RELATIVE);
+    pa_assert(r == 0);
+
+    if (cal_state == CALIBRATION_DONE)
+        pa_stream_set_write_callback(s, ctx->write_cb, ctx);
+}
+
+static void calibrate_read_cb(pa_stream *s, size_t nbytes, void *userdata) {
+    pa_lo_test_context *ctx = (pa_lo_test_context *) userdata;
+    static double v = 0;
+    static int skip = 0, confirm;
+
+    pa_cvolume vol;
+    pa_operation *o;
+    int r, nsamp;
+    float *in;
+    size_t l;
+
+    r = pa_stream_peek(s, (const void **)&in, &l);
+    pa_assert(r == 0);
+
+    nsamp = l / ctx->fs;
+
+    /* For each state or volume step change, throw out a few samples so we know
+     * we're seeing the changed samples. */
+    if (skip++ < 100)
+        goto out;
+    else
+        skip = 0;
+
+    switch (cal_state) {
+        case CALIBRATION_ONE:
+            /* Try to detect the sine wave. RMS is 0.5, */
+            if (pa_rms(in, nsamp) < 0.40f) {
+                confirm = 0;
+                v += 0.02f;
+
+                if (v > 1.0) {
+                    pa_log_error("Capture signal too weak at 100%% volume (%g). Giving up.\n", pa_rms(in, nsamp));
+                    pa_assert_not_reached();
+                }
+
+                pa_cvolume_set(&vol, ctx->sample_spec.channels, v * PA_VOLUME_NORM);
+                o = pa_context_set_source_output_volume(ctx->context, pa_stream_get_index(s), &vol, NULL, NULL);
+                pa_assert(o != NULL);
+                pa_operation_unref(o);
+            } else {
+                /* Make sure the signal strength is steadily above our threshold */
+                if (++confirm > 5) {
+#if 0
+                    pa_log_debug(stderr, "Capture volume = %g (%g)\n", v, pa_rms(in, nsamp));
+#endif
+                    cal_state = CALIBRATION_ZERO;
+                }
+            }
+
+            break;
+
+        case CALIBRATION_ZERO:
+            /* Now make sure silence doesn't trigger a false positive because
+             * of noise. */
+            if (pa_rms(in, nsamp) > 0.1f) {
+                fprintf(stderr, "Too much noise on capture (%g). Giving up.\n", pa_rms(in, nsamp));
+                pa_assert_not_reached();
+            }
+
+            cal_state = CALIBRATION_DONE;
+            pa_stream_set_read_callback(s, ctx->read_cb, ctx);
+
+            break;
+
+        default:
+            break;
+    }
+
+out:
+    pa_stream_drop(s);
+}
+
+/* This routine is called whenever the stream state changes */
+static void stream_state_callback(pa_stream *s, void *userdata) {
+    pa_lo_test_context *ctx = (pa_lo_test_context *) userdata;
+
+    switch (pa_stream_get_state(s)) {
+        case PA_STREAM_UNCONNECTED:
+        case PA_STREAM_CREATING:
+        case PA_STREAM_TERMINATED:
+            break;
+
+        case PA_STREAM_READY: {
+            pa_cvolume vol;
+            pa_operation *o;
+
+            /* Set volumes for calibration */
+            if (s == ctx->play_stream) {
+                pa_cvolume_set(&vol, ctx->sample_spec.channels, PA_VOLUME_NORM);
+                o = pa_context_set_sink_input_volume(ctx->context, pa_stream_get_index(s), &vol, NULL, NULL);
+            } else {
+                pa_cvolume_set(&vol, ctx->sample_spec.channels, pa_sw_volume_from_linear(0.0));
+                o = pa_context_set_source_output_volume(ctx->context, pa_stream_get_index(s), &vol, NULL, NULL);
+            }
+
+            if (!o) {
+                pa_log_error("Could not set stream volume: %s\n", pa_strerror(pa_context_errno(ctx->context)));
+                pa_assert_not_reached();
+            } else
+                pa_operation_unref(o);
+
+            break;
+        }
+
+        case PA_STREAM_FAILED:
+        default:
+            pa_log_error("Stream error: %s\n", pa_strerror(pa_context_errno(ctx->context)));
+            pa_assert_not_reached();
+    }
+}
+
+/* This is called whenever the context status changes */
+static void context_state_callback(pa_context *c, void *userdata) {
+    pa_lo_test_context *ctx = (pa_lo_test_context *) userdata;
+    pa_mainloop_api *api;
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY: {
+            pa_buffer_attr buffer_attr;
+
+            pa_make_realtime(4);
+
+            /* Create playback stream */
+            buffer_attr.maxlength = -1;
+            buffer_attr.tlength = ctx->sample_spec.rate * ctx->fs * ctx->play_latency / 1000;
+            buffer_attr.prebuf = 0; /* Setting prebuf to 0 guarantees us the stream will run synchronously, no matter what */
+            buffer_attr.minreq = -1;
+            buffer_attr.fragsize = -1;
+
+            ctx->play_stream = pa_stream_new(c, "loopback: play", &ctx->sample_spec, NULL);
+            pa_assert(ctx->play_stream != NULL);
+            pa_stream_set_state_callback(ctx->play_stream, stream_state_callback, ctx);
+            pa_stream_set_write_callback(ctx->play_stream, calibrate_write_cb, ctx);
+            pa_stream_set_underflow_callback(ctx->play_stream, underflow_cb, userdata);
+
+            pa_stream_connect_playback(ctx->play_stream, getenv("TEST_SINK"), &buffer_attr,
+                    PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
+
+            /* Create capture stream */
+            buffer_attr.maxlength = -1;
+            buffer_attr.tlength = (uint32_t) -1;
+            buffer_attr.prebuf = 0;
+            buffer_attr.minreq = (uint32_t) -1;
+            buffer_attr.fragsize = ctx->sample_spec.rate * ctx->fs * ctx->rec_latency / 1000;
+
+            ctx->rec_stream = pa_stream_new(c, "loopback: rec", &ctx->sample_spec, NULL);
+            pa_assert(ctx->rec_stream != NULL);
+            pa_stream_set_state_callback(ctx->rec_stream, stream_state_callback, ctx);
+            pa_stream_set_read_callback(ctx->rec_stream, calibrate_read_cb, ctx);
+            pa_stream_set_overflow_callback(ctx->rec_stream, overflow_cb, userdata);
+
+            pa_stream_connect_record(ctx->rec_stream, getenv("TEST_SOURCE"), &buffer_attr,
+                    PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);
+
+            break;
+        }
+
+        case PA_CONTEXT_TERMINATED:
+            api = pa_mainloop_get_api(ctx->mainloop);
+            api->quit(api, 0);
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            pa_log_error("Context error: %s\n", pa_strerror(pa_context_errno(c)));
+            pa_assert_not_reached();
+    }
+}
+
+int pa_lo_test_init(pa_lo_test_context *ctx) {
+    /* FIXME: need to deal with non-float samples at some point */
+    pa_assert(ctx->sample_spec.format == PA_SAMPLE_FLOAT32);
+
+    ctx->ss = pa_sample_size(&ctx->sample_spec);
+    ctx->fs = pa_frame_size(&ctx->sample_spec);
+
+    ctx->mainloop = pa_mainloop_new();
+    ctx->context = pa_context_new(pa_mainloop_get_api(ctx->mainloop), ctx->context_name);
+
+    pa_context_set_state_callback(ctx->context, context_state_callback, ctx);
+
+    /* Connect the context */
+    if (pa_context_connect(ctx->context, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) {
+        pa_log_error("pa_context_connect() failed.\n");
+        goto quit;
+    }
+
+    return 0;
+
+quit:
+    pa_context_unref(ctx->context);
+    pa_mainloop_free(ctx->mainloop);
+
+    return -1;
+}
+
+int pa_lo_test_run(pa_lo_test_context *ctx) {
+    int ret;
+
+    if (pa_mainloop_run(ctx->mainloop, &ret) < 0) {
+        pa_log_error("pa_mainloop_run() failed.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+void pa_lo_test_deinit(pa_lo_test_context *ctx) {
+    if (ctx->play_stream) {
+        pa_stream_disconnect(ctx->play_stream);
+        pa_stream_unref(ctx->play_stream);
+    }
+
+    if (ctx->rec_stream) {
+        pa_stream_disconnect(ctx->rec_stream);
+        pa_stream_unref(ctx->rec_stream);
+    }
+
+    if (ctx->context)
+        pa_context_unref(ctx->context);
+
+    if (ctx->mainloop)
+        pa_mainloop_free(ctx->mainloop);
+}
+
+float pa_rms(const float *s, int n) {
+    float sq = 0;
+    int i;
+
+    for (i = 0; i < n; i++)
+        sq += s[i] * s[i];
+
+    return sqrtf(sq / n);
+}
diff --git a/src/tests/lo-test-util.h b/src/tests/lo-test-util.h
new file mode 100644
index 0000000..d0de609
--- /dev/null
+++ b/src/tests/lo-test-util.h
@@ -0,0 +1,57 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Collabora Ltd.
+  Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
+
+  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/pulseaudio.h>
+
+typedef struct pa_lo_test_context {
+    /* Tests need to set these */
+    const char *context_name;
+
+    pa_sample_spec sample_spec;
+    int play_latency; /* ms */
+    int rec_latency; /* ms */
+
+    pa_stream_request_cb_t write_cb, read_cb;
+
+    /* These are set by lo_test_init() */
+    pa_mainloop *mainloop;
+    pa_context *context;
+
+    pa_stream *play_stream, *rec_stream;
+
+    int ss, fs; /* sample size, frame size for convenience */
+} pa_lo_test_context;
+
+/* Initialise the test parameters, connect */
+int pa_lo_test_init(pa_lo_test_context *ctx);
+/* Start running the test */
+int pa_lo_test_run(pa_lo_test_context *ctx);
+/* Clean up */
+void pa_lo_test_deinit(pa_lo_test_context *ctx);
+
+/* Return RMS for the given signal. Assumes the data is a single channel for
+ * simplicity */
+float pa_rms(const float *s, int n);

commit 36bdd720aa3ff11bede9efc5b630e4f8a7f5ea02
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed May 22 15:59:24 2013 +0530

    tests: Adjust latency test calibration
    
    This makes the test more robust by:
    
    1. Decreasing the '1' threshold during calibration - the RMS value for
       the sine wave will be 0.5, so the previous code was making us take
       the ALSA mixer past 0dB.
    
    2. Using the difference rather than absolute value for 0->1 transitions,
       so that we're somewhat independent noise in our calculations.

diff --git a/src/tests/lo-latency-test.c b/src/tests/lo-latency-test.c
index c22affc..8f3b04d 100644
--- a/src/tests/lo-latency-test.c
+++ b/src/tests/lo-latency-test.c
@@ -158,7 +158,7 @@ static void read_cb(pa_stream *s, size_t nbytes, void *userdata) {
          * straddle the 0->1 transition, raising the average power. We keep the
          * definition of 1 tight in this case and detect the transition in the
          * next round. */
-        if (last < 0.5f && cur > 0.8f) {
+        if (cur - last > 0.4f) {
             pa_gettimeofday(&tv_in);
             fprintf(stderr, "Latency %llu\n", (unsigned long long) pa_timeval_diff(&tv_in, &tv_out));
         }
@@ -234,8 +234,8 @@ static void calibrate_read_cb(pa_stream *s, size_t nbytes, void *userdata) {
 
     switch (cal_state) {
         case CALIBRATION_ONE:
-            /* Try to detect the sine wave */
-            if (rms(in, nsamp) < 0.8f) {
+            /* Try to detect the sine wave. RMS is 0.5, */
+            if (rms(in, nsamp) < 0.40f) {
                 confirm = 0;
                 v += 0.02f;
 

commit 3f16c7100be0b3f9aa5069350bc27c1f9d2aa47f
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed May 22 12:03:47 2013 +0530

    tests: Make loopback latency test run with rt priority

diff --git a/src/tests/lo-latency-test.c b/src/tests/lo-latency-test.c
index 2bb6678..c22affc 100644
--- a/src/tests/lo-latency-test.c
+++ b/src/tests/lo-latency-test.c
@@ -39,6 +39,9 @@
 #include <pulse/pulseaudio.h>
 #include <pulse/mainloop.h>
 
+/* for pa_make_realtime */
+#include <pulsecore/core-util.h>
+
 #define SAMPLE_HZ 44100
 #define CHANNELS 2
 #define N_OUT (SAMPLE_HZ * 1)
@@ -328,6 +331,8 @@ static void context_state_callback(pa_context *c, void *userdata) {
         case PA_CONTEXT_READY: {
             pa_buffer_attr buffer_attr;
 
+            pa_make_realtime(4);
+
             /* Create playback stream */
             buffer_attr.maxlength = -1;
             buffer_attr.tlength = SAMPLE_HZ * fs * PLAYBACK_LATENCY / 1000;

commit 0862a281aafc016e85070de1fa7a25879edb8113
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Tue May 21 18:39:30 2013 +0530

    tests: Add a latency measurement test
    
    This test is intended to measure real latency by playing a sample to a
    sink and capturing that over a loopback interface. The loopback can
    either be physical (cable running from headphone out to line in) or
    virtual (monitor source or module loopback).
    
    Also included in this is calibration code to make sure that volumes are
    sufficiently adjusted to be able to detect the played back signal (and
    that there aren't false positives due to line noise).
    
    One of the objectives of all this is to later factor out the setup code
    to allow us to easily write more loopback tests for various
    functionality (volumes, resampling, mixing, etc.).

diff --git a/src/Makefile.am b/src/Makefile.am
index a621a30..163976e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -261,7 +261,8 @@ TESTS_norun = \
 		rtstutter \
 		sig2str-test \
 		stripnul \
-		echo-cancel-test
+		echo-cancel-test \
+		lo-latency-test
 
 # These tests need a running pulseaudio daemon
 TESTS_daemon = \
@@ -574,6 +575,11 @@ echo_cancel_test_CXXFLAGS = $(module_echo_cancel_la_CXXFLAGS) -DECHO_CANCEL_TEST
 endif
 echo_cancel_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
 
+lo_latency_test_SOURCES = tests/lo-latency-test.c
+lo_latency_test_LDADD = $(AM_LDADD) libpulse.la
+lo_latency_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+lo_latency_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
 ###################################
 #         Common library          #
 ###################################
diff --git a/src/tests/lo-latency-test.c b/src/tests/lo-latency-test.c
new file mode 100644
index 0000000..2bb6678
--- /dev/null
+++ b/src/tests/lo-latency-test.c
@@ -0,0 +1,446 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Collabora Ltd.
+  Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
+
+  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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <check.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/mainloop.h>
+
+#define SAMPLE_HZ 44100
+#define CHANNELS 2
+#define N_OUT (SAMPLE_HZ * 1)
+
+#define TONE_HZ (SAMPLE_HZ / 100)
+#define PLAYBACK_LATENCY 25 /* ms */
+#define CAPTURE_LATENCY 5 /* ms */
+
+static pa_context *context = NULL;
+static pa_stream *pstream, *rstream;
+static pa_mainloop_api *mainloop_api = NULL;
+static const char *context_name = NULL;
+
+static float out[N_OUT][CHANNELS];
+static int ppos = 0;
+
+static int n_underflow = 0;
+static int n_overflow = 0;
+
+static struct timeval tv_out, tv_in;
+
+static const pa_sample_spec sample_spec = {
+    .format = PA_SAMPLE_FLOAT32,
+    .rate = SAMPLE_HZ,
+    .channels = CHANNELS,
+};
+static int ss, fs;
+
+static void nop_free_cb(void *p) {}
+
+static void underflow_cb(struct pa_stream *s, void *userdata) {
+    fprintf(stderr, "Underflow\n");
+    n_underflow++;
+}
+
+static void overflow_cb(struct pa_stream *s, void *userdata) {
+    fprintf(stderr, "Overlow\n");
+    n_overflow++;
+}
+
+static void write_cb(pa_stream *s, size_t nbytes, void *userdata) {
+    int r, nsamp = nbytes / fs;
+
+    if (ppos + nsamp > N_OUT) {
+        r = pa_stream_write(s, &out[ppos][0], (N_OUT - ppos) * fs, nop_free_cb, 0, PA_SEEK_RELATIVE);
+        nbytes -= (N_OUT - ppos) * fs;
+        ppos = 0;
+    }
+
+    if (ppos == 0)
+        pa_gettimeofday(&tv_out);
+
+    r = pa_stream_write(s, &out[ppos][0], nbytes, nop_free_cb, 0, PA_SEEK_RELATIVE);
+    fail_unless(r == 0);
+
+    ppos = (ppos + nbytes / fs) % N_OUT;
+}
+
+static inline float rms(const float *s, int n) {
+    float sq = 0;
+    int i;
+
+    for (i = 0; i < n; i++)
+        sq += s[i] * s[i];
+
+    return sqrtf(sq / n);
+}
+
+#define WINDOW (2 * CHANNELS)
+
+static void read_cb(pa_stream *s, size_t nbytes, void *userdata) {
+    static float last = 0.0f;
+    const float *in;
+    float cur;
+    int r;
+    unsigned int i = 0;
+    size_t l;
+
+    r = pa_stream_peek(s, (const void **)&in, &l);
+    fail_unless(r == 0);
+
+    if (l == 0)
+        return;
+
+#if 0
+    {
+        static int fd = -1;
+
+        if (fd == -1) {
+            fd = open("loopback.raw", O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
+            fail_if(fd < 0);
+        }
+
+        r = write(fd, in, l);
+    }
+#endif
+
+    do {
+#if 0
+        {
+            int j;
+            fprintf(stderr, "%g (", rms(in, WINDOW));
+            for (j = 0; j < WINDOW; j++)
+                fprintf(stderr, "%g ", in[j]);
+            fprintf(stderr, ")\n");
+        }
+#endif
+        if (i + (ss * WINDOW) < l)
+            cur = rms(in, WINDOW);
+        else
+            cur = rms(in, (l - i)/ss);
+
+        /* We leave the definition of 0 generous since the window might
+         * straddle the 0->1 transition, raising the average power. We keep the
+         * definition of 1 tight in this case and detect the transition in the
+         * next round. */
+        if (last < 0.5f && cur > 0.8f) {
+            pa_gettimeofday(&tv_in);
+            fprintf(stderr, "Latency %llu\n", (unsigned long long) pa_timeval_diff(&tv_in, &tv_out));
+        }
+
+        last = cur;
+        in += WINDOW;
+        i += ss * WINDOW;
+    } while (i + (ss * WINDOW) <= l);
+
+    pa_stream_drop(s);
+}
+
+/*
+ * We run a simple volume calibration so that we know we can detect the signal
+ * being played back. We start with the playback stream at 100% volume, and
+ * capture at 0.
+ *
+ * First, we then play a sine wave and increase the capture volume till the
+ * signal is clearly received.
+ *
+ * Next, we play back silence and make sure that the level is low enough to
+ * distinguish from when playback is happening.
+ *
+ * Finally, we hand off to the real read/write callbacks to run the actual
+ * test.
+ */
+
+enum {
+    CALIBRATION_ONE,
+    CALIBRATION_ZERO,
+    CALIBRATION_DONE,
+};
+
+static int cal_state = CALIBRATION_ONE;
+
+static void calibrate_write_cb(pa_stream *s, size_t nbytes, void *userdata) {
+    int i, r, nsamp = nbytes / fs;
+    float tmp[nsamp][2];
+    static int count = 0;
+
+    /* Write out a sine tone */
+    for (i = 0; i < nsamp; i++)
+        tmp[i][0] = tmp[i][1] = cal_state == CALIBRATION_ONE ? sinf(count++ * TONE_HZ * 2 * M_PI / SAMPLE_HZ) : 0.0f;
+
+    r = pa_stream_write(s, &tmp, nbytes, nop_free_cb, 0, PA_SEEK_RELATIVE);
+    fail_unless(r == 0);
+
+    if (cal_state == CALIBRATION_DONE)
+        pa_stream_set_write_callback(s, write_cb, NULL);
+}
+
+static void calibrate_read_cb(pa_stream *s, size_t nbytes, void *userdata) {
+    static double v = 0;
+    static int skip = 0, confirm;
+
+    pa_cvolume vol;
+    pa_operation *o;
+    int r, nsamp;
+    float *in;
+    size_t l;
+
+    r = pa_stream_peek(s, (const void **)&in, &l);
+    fail_unless(r == 0);
+
+    nsamp = l / fs;
+
+    /* For each state or volume step change, throw out a few samples so we know
+     * we're seeing the changed samples. */
+    if (skip++ < 100)
+        goto out;
+    else
+        skip = 0;
+
+    switch (cal_state) {
+        case CALIBRATION_ONE:
+            /* Try to detect the sine wave */
+            if (rms(in, nsamp) < 0.8f) {
+                confirm = 0;
+                v += 0.02f;
+
+                if (v > 1.0) {
+                    fprintf(stderr, "Capture signal too weak at 100%% volume (%g). Giving up.\n", rms(in, nsamp));
+                    fail();
+                }
+
+                pa_cvolume_set(&vol, CHANNELS, v * PA_VOLUME_NORM);
+                o = pa_context_set_source_output_volume(context, pa_stream_get_index(s), &vol, NULL, NULL);
+                fail_if(o == NULL);
+                pa_operation_unref(o);
+            } else {
+                /* Make sure the signal strength is steadily above our threshold */
+                if (++confirm > 5) {
+#if 0
+                    fprintf(stderr, "Capture volume = %g (%g)\n", v, rms(in, nsamp));
+#endif
+                    cal_state = CALIBRATION_ZERO;
+                }
+            }
+
+            break;
+
+        case CALIBRATION_ZERO:
+            /* Now make sure silence doesn't trigger a false positive because
+             * of noise. */
+            if (rms(in, nsamp) > 0.1f) {
+                fprintf(stderr, "Too much noise on capture (%g). Giving up.\n", rms(in, nsamp));
+                fail();
+            }
+
+            cal_state = CALIBRATION_DONE;
+            pa_stream_set_read_callback(s, read_cb, NULL);
+
+            break;
+
+        default:
+            break;
+    }
+
+out:
+    pa_stream_drop(s);
+}
+
+/* This routine is called whenever the stream state changes */
+static void stream_state_callback(pa_stream *s, void *userdata) {
+    switch (pa_stream_get_state(s)) {
+        case PA_STREAM_UNCONNECTED:
+        case PA_STREAM_CREATING:
+        case PA_STREAM_TERMINATED:
+            break;
+
+        case PA_STREAM_READY: {
+            pa_cvolume vol;
+            pa_operation *o;
+
+            /* Set volumes for calibration */
+            if (!userdata) {
+                pa_cvolume_set(&vol, CHANNELS, PA_VOLUME_NORM);
+                o = pa_context_set_sink_input_volume(context, pa_stream_get_index(s), &vol, NULL, NULL);
+            } else {
+                pa_cvolume_set(&vol, CHANNELS, pa_sw_volume_from_linear(0.0));
+                o = pa_context_set_source_output_volume(context, pa_stream_get_index(s), &vol, NULL, NULL);
+            }
+
+            if (!o) {
+                fprintf(stderr, "Could not set stream volume: %s\n", pa_strerror(pa_context_errno(context)));
+                fail();
+            } else
+                pa_operation_unref(o);
+
+            break;
+        }
+
+        case PA_STREAM_FAILED:
+        default:
+            fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+            fail();
+    }
+}
+
+/* This is called whenever the context status changes */
+static void context_state_callback(pa_context *c, void *userdata) {
+    fail_unless(c != NULL);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY: {
+            pa_buffer_attr buffer_attr;
+
+            /* Create playback stream */
+            buffer_attr.maxlength = -1;
+            buffer_attr.tlength = SAMPLE_HZ * fs * PLAYBACK_LATENCY / 1000;
+            buffer_attr.prebuf = 0; /* Setting prebuf to 0 guarantees us the stream will run synchronously, no matter what */
+            buffer_attr.minreq = -1;
+            buffer_attr.fragsize = -1;
+
+            pstream = pa_stream_new(c, "loopback: play", &sample_spec, NULL);
+            fail_unless(pstream != NULL);
+            pa_stream_set_state_callback(pstream, stream_state_callback, (void *) 0);
+            pa_stream_set_write_callback(pstream, calibrate_write_cb, NULL);
+            pa_stream_set_underflow_callback(pstream, underflow_cb, userdata);
+
+            pa_stream_connect_playback(pstream, getenv("TEST_SINK"), &buffer_attr,
+                    PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
+
+            /* Create capture stream */
+            buffer_attr.maxlength = -1;
+            buffer_attr.tlength = (uint32_t) -1;
+            buffer_attr.prebuf = 0;
+            buffer_attr.minreq = (uint32_t) -1;
+            buffer_attr.fragsize = SAMPLE_HZ * fs * CAPTURE_LATENCY / 1000;
+
+            rstream = pa_stream_new(c, "loopback: rec", &sample_spec, NULL);
+            fail_unless(rstream != NULL);
+            pa_stream_set_state_callback(rstream, stream_state_callback, (void *) 1);
+            pa_stream_set_read_callback(rstream, calibrate_read_cb, NULL);
+            pa_stream_set_overflow_callback(rstream, overflow_cb, userdata);
+
+            pa_stream_connect_record(rstream, getenv("TEST_SOURCE"), &buffer_attr,
+                    PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);
+
+            break;
+        }
+
+        case PA_CONTEXT_TERMINATED:
+            mainloop_api->quit(mainloop_api, 0);
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            fprintf(stderr, "Context error: %s\n", pa_strerror(pa_context_errno(c)));
+            fail();
+    }
+}
+
+START_TEST (loopback_test) {
+    pa_mainloop* m = NULL;
+    int i, ret = 0, pulse_hz = SAMPLE_HZ / 1000;
+
+    /* Generate a square pulse */
+    for (i = 0; i < N_OUT; i++)
+        if (i < pulse_hz)
+            out[i][0] = out[i][1] = 1.0f;
+        else
+            out[i][0] = out[i][1] = 0.0f;
+
+    ss = pa_sample_size(&sample_spec);
+    fs = pa_frame_size(&sample_spec);
+
+    pstream = NULL;
+
+    /* Set up a new main loop */
+    m = pa_mainloop_new();
+    fail_unless(m != NULL);
+
+    mainloop_api = pa_mainloop_get_api(m);
+
+    context = pa_context_new(mainloop_api, context_name);
+    fail_unless(context != NULL);
+
+    pa_context_set_state_callback(context, context_state_callback, NULL);
+
+    /* Connect the context */
+    if (pa_context_connect(context, NULL, 0, NULL) < 0) {
+        fprintf(stderr, "pa_context_connect() failed.\n");
+        goto quit;
+    }
+
+    if (pa_mainloop_run(m, &ret) < 0)
+        fprintf(stderr, "pa_mainloop_run() failed.\n");
+
+quit:
+    pa_context_unref(context);
+
+    if (pstream)
+        pa_stream_unref(pstream);
+
+    pa_mainloop_free(m);
+
+    fail_unless(ret == 0);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    context_name = argv[0];
+
+    s = suite_create("Loopback");
+    tc = tcase_create("loopback");
+    tcase_add_test(tc, loopback_test);
+    tcase_set_timeout(tc, 5 * 60);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}

commit 50af14bd5bdc3906e13155ca5f7f7b1e088a1885
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Fri May 24 15:17:13 2013 +0300

    bluetooth: Fix input port description

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index c82cb53..f82033a 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -2147,7 +2147,7 @@ static void create_card_ports(struct userdata *u, pa_hashmap *ports) {
 
     pa_device_port_new_data_init(&port_data);
     pa_device_port_new_data_set_name(&port_data, u->input_port_name);
-    pa_device_port_new_data_set_description(&port_data, output_description);
+    pa_device_port_new_data_set_description(&port_data, input_description);
     pa_device_port_new_data_set_direction(&port_data, PA_DIRECTION_INPUT);
     pa_device_port_new_data_set_available(&port_data, get_port_availability(u, PA_DIRECTION_INPUT));
     pa_assert_se(port = pa_device_port_new(u->core, &port_data, 0));

commit 5ae5dee75eb70ff81414bd64b4ea41e6b8db7599
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed May 15 09:41:19 2013 +0530

    zeroconf: Make Avahi usage in m-z-publish async
    
    This pushes all avahi-client code to a threaded mainloop from the PA
    mainloop context. We need to do this because avahi-client makes blocking
    D-Bus calls, and we don't want to block the mainloop for that long.
    
    The only exception to this now that I don't see a workaround for is
    during module unload time. However, this shouldn't be a huge problem
    since in most cases, this will only happen at server shutdown time.
    
    The bulk of the change is partitioning the data so that PA core objects
    only (well, mostly) get accessed in the PA mainloop and Avahi calls
    happen only in the Avahi threaded mainloop.
    
    Bug: https://bugs.freedesktop.org/show_bug.cgi?id=58758

diff --git a/src/modules/module-zeroconf-publish.c b/src/modules/module-zeroconf-publish.c
index c21ff3e..f96d4ce 100644
--- a/src/modules/module-zeroconf-publish.c
+++ b/src/modules/module-zeroconf-publish.c
@@ -35,6 +35,7 @@
 
 #include <pulse/xmalloc.h>
 #include <pulse/util.h>
+#include <pulse/thread-mainloop.h>
 
 #include <pulsecore/parseaddr.h>
 #include <pulsecore/sink.h>
@@ -64,10 +65,39 @@ PA_MODULE_LOAD_ONCE(TRUE);
 #define SERVICE_SUBTYPE_SOURCE_MONITOR "_monitor._sub."SERVICE_TYPE_SOURCE
 #define SERVICE_SUBTYPE_SOURCE_NON_MONITOR "_non-monitor._sub."SERVICE_TYPE_SOURCE
 
+/*
+ * Note: Because the core avahi-client calls result in synchronous D-Bus
+ * communication, calling any of those functions in the PA mainloop context
+ * could lead to the mainloop being blocked for long periods.
+ *
+ * To avoid this, we create a threaded-mainloop for Avahi calls, and push all
+ * D-Bus communication into that thread. The thumb-rule for the split is:
+ *
+ * 1. If access to PA data structures is needed, use the PA mainloop context
+ *
+ * 2. If a (blocking) avahi-client call is needed, use the Avahi mainloop
+ *
+ * We do have message queue to pass messages from the Avahi mainloop to the PA
+ * mainloop.
+ */
+
 static const char* const valid_modargs[] = {
     NULL
 };
 
+struct avahi_msg {
+    pa_msgobject parent;
+};
+
+typedef struct avahi_msg avahi_msg;
+PA_DEFINE_PRIVATE_CLASS(avahi_msg, pa_msgobject);
+
+enum {
+    AVAHI_MESSAGE_PUBLISH_ALL,
+    AVAHI_MESSAGE_SHUTDOWN_START,
+    AVAHI_MESSAGE_SHUTDOWN_COMPLETE,
+};
+
 enum service_subtype {
     SUBTYPE_HARDWARE,
     SUBTYPE_VIRTUAL,
@@ -75,21 +105,35 @@ enum service_subtype {
 };
 
 struct service {
+    void *key;
+
     struct userdata *userdata;
     AvahiEntryGroup *entry_group;
     char *service_name;
-    pa_object *device;
+    const char *service_type;
     enum service_subtype subtype;
+
+    char *name;
+    bool is_sink;
+    pa_sample_spec ss;
+    pa_channel_map cm;
+    pa_proplist *proplist;
 };
 
 struct userdata {
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+    avahi_msg *msg;
+
     pa_core *core;
     pa_module *module;
+    pa_mainloop_api *api;
+    pa_threaded_mainloop *mainloop;
 
     AvahiPoll *avahi_poll;
     AvahiClient *client;
 
-    pa_hashmap *services;
+    pa_hashmap *services; /* protect with mainloop lock */
     char *service_name;
 
     AvahiEntryGroup *main_entry_group;
@@ -99,34 +143,38 @@ struct userdata {
     pa_native_protocol *native;
 };
 
-static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_channel_map *ret_map, const char **ret_name, pa_proplist **ret_proplist, enum service_subtype *ret_subtype) {
+/* Runs in PA mainloop context */
+static void get_service_data(struct service *s, pa_object *device) {
     pa_assert(s);
-    pa_assert(ret_ss);
-    pa_assert(ret_proplist);
-    pa_assert(ret_subtype);
-
-    if (pa_sink_isinstance(s->device)) {
-        pa_sink *sink = PA_SINK(s->device);
 
-        *ret_ss = sink->sample_spec;
-        *ret_map = sink->channel_map;
-        *ret_name = sink->name;
-        *ret_proplist = sink->proplist;
-        *ret_subtype = sink->flags & PA_SINK_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL;
-
-    } else if (pa_source_isinstance(s->device)) {
-        pa_source *source = PA_SOURCE(s->device);
-
-        *ret_ss = source->sample_spec;
-        *ret_map = source->channel_map;
-        *ret_name = source->name;
-        *ret_proplist = source->proplist;
-        *ret_subtype = source->monitor_of ? SUBTYPE_MONITOR : (source->flags & PA_SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL);
+    if (pa_sink_isinstance(device)) {
+        pa_sink *sink = PA_SINK(device);
+
+        s->is_sink = TRUE;
+        s->service_type = SERVICE_TYPE_SINK;
+        s->ss = sink->sample_spec;
+        s->cm = sink->channel_map;
+        s->name = pa_xstrdup(sink->name);
+        s->proplist = pa_proplist_copy(sink->proplist);
+        s->subtype = sink->flags & PA_SINK_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL;
+
+    } else if (pa_source_isinstance(device)) {
+        pa_source *source = PA_SOURCE(device);
+
+        s->is_sink = FALSE;
+        s->service_type = SERVICE_TYPE_SOURCE;
+        s->ss = source->sample_spec;
+        s->cm = source->channel_map;
+        s->name = pa_xstrdup(source->name);
+        s->proplist = pa_proplist_copy(source->proplist);
+        s->subtype = source->monitor_of ? SUBTYPE_MONITOR : (source->flags & PA_SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL);
 
     } else
         pa_assert_not_reached();
 }
 
+/* Can be used in either PA or Avahi mainloop context since the bits of u->core
+ * that we access don't change after startup. */
 static AvahiStringList* txt_record_server_data(pa_core *c, AvahiStringList *l) {
     char s[128];
     char *t;
@@ -153,8 +201,9 @@ static AvahiStringList* txt_record_server_data(pa_core *c, AvahiStringList *l) {
     return l;
 }
 
-static int publish_service(struct service *s);
+static void publish_service(pa_mainloop_api *api, void *service);
 
+/* Runs in Avahi mainloop context */
 static void service_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
     struct service *s = userdata;
 
@@ -174,7 +223,7 @@ static void service_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupStat
             pa_xfree(s->service_name);
             s->service_name = t;
 
-            publish_service(s);
+            publish_service(NULL, s);
             break;
         }
 
@@ -195,6 +244,7 @@ static void service_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupStat
 
 static void service_free(struct service *s);
 
+/* Can run in either context */
 static uint16_t compute_port(struct userdata *u) {
     pa_strlist *i;
 
@@ -219,15 +269,13 @@ static uint16_t compute_port(struct userdata *u) {
     return PA_NATIVE_DEFAULT_PORT;
 }
 
-static int publish_service(struct service *s) {
+/* Runs in Avahi mainloop context */
+static void publish_service(pa_mainloop_api *api PA_GCC_UNUSED, void *service) {
+    struct service *s = (struct service *) service;
     int r = -1;
     AvahiStringList *txt = NULL;
-    const char *name = NULL, *t;
-    pa_proplist *proplist = NULL;
-    pa_sample_spec ss;
-    pa_channel_map map;
     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
-    enum service_subtype subtype;
+    const char *t;
 
     const char * const subtype_text[] = {
         [SUBTYPE_HARDWARE] = "hardware",
@@ -238,7 +286,7 @@ static int publish_service(struct service *s) {
     pa_assert(s);
 
     if (!s->userdata->client || avahi_client_get_state(s->userdata->client) != AVAHI_CLIENT_S_RUNNING)
-        return 0;
+        return;
 
     if (!s->entry_group) {
         if (!(s->entry_group = avahi_entry_group_new(s->userdata->client, service_entry_group_callback, s))) {
@@ -250,25 +298,24 @@ static int publish_service(struct service *s) {
 
     txt = txt_record_server_data(s->userdata->core, txt);
 
-    get_service_data(s, &ss, &map, &name, &proplist, &subtype);
-    txt = avahi_string_list_add_pair(txt, "device", name);
-    txt = avahi_string_list_add_printf(txt, "rate=%u", ss.rate);
-    txt = avahi_string_list_add_printf(txt, "channels=%u", ss.channels);
-    txt = avahi_string_list_add_pair(txt, "format", pa_sample_format_to_string(ss.format));
-    txt = avahi_string_list_add_pair(txt, "channel_map", pa_channel_map_snprint(cm, sizeof(cm), &map));
-    txt = avahi_string_list_add_pair(txt, "subtype", subtype_text[subtype]);
+    txt = avahi_string_list_add_pair(txt, "device", s->name);
+    txt = avahi_string_list_add_printf(txt, "rate=%u", s->ss.rate);
+    txt = avahi_string_list_add_printf(txt, "channels=%u", s->ss.channels);
+    txt = avahi_string_list_add_pair(txt, "format", pa_sample_format_to_string(s->ss.format));
+    txt = avahi_string_list_add_pair(txt, "channel_map", pa_channel_map_snprint(cm, sizeof(cm), &s->cm));
+    txt = avahi_string_list_add_pair(txt, "subtype", subtype_text[s->subtype]);
 
-    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_DESCRIPTION)))
+    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)))
         txt = avahi_string_list_add_pair(txt, "description", t);
-    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_ICON_NAME)))
+    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_ICON_NAME)))
         txt = avahi_string_list_add_pair(txt, "icon-name", t);
-    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_VENDOR_NAME)))
+    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_VENDOR_NAME)))
         txt = avahi_string_list_add_pair(txt, "vendor-name", t);
-    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_PRODUCT_NAME)))
+    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_PRODUCT_NAME)))
         txt = avahi_string_list_add_pair(txt, "product-name", t);
-    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_CLASS)))
+    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_CLASS)))
         txt = avahi_string_list_add_pair(txt, "class", t);
-    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_FORM_FACTOR)))
+    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_FORM_FACTOR)))
         txt = avahi_string_list_add_pair(txt, "form-factor", t);
 
     if (avahi_entry_group_add_service_strlst(
@@ -276,7 +323,7 @@ static int publish_service(struct service *s) {
                 AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
                 0,
                 s->service_name,
-                pa_sink_isinstance(s->device) ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE,
+                s->service_type,
                 NULL,
                 NULL,
                 compute_port(s->userdata),
@@ -291,16 +338,16 @@ static int publish_service(struct service *s) {
                 AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
                 0,
                 s->service_name,
-                pa_sink_isinstance(s->device) ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE,
+                s->service_type,
                 NULL,
-                pa_sink_isinstance(s->device) ? (subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SINK_HARDWARE : SERVICE_SUBTYPE_SINK_VIRTUAL) :
-                (subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SOURCE_HARDWARE : (subtype == SUBTYPE_VIRTUAL ? SERVICE_SUBTYPE_SOURCE_VIRTUAL : SERVICE_SUBTYPE_SOURCE_MONITOR))) < 0) {
+                s->is_sink ? (s->subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SINK_HARDWARE : SERVICE_SUBTYPE_SINK_VIRTUAL) :
+                (s->subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SOURCE_HARDWARE : (s->subtype == SUBTYPE_VIRTUAL ? SERVICE_SUBTYPE_SOURCE_VIRTUAL : SERVICE_SUBTYPE_SOURCE_MONITOR))) < 0) {
 
         pa_log("avahi_entry_group_add_service_subtype(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
         goto finish;
     }
 
-    if (pa_source_isinstance(s->device) && subtype != SUBTYPE_MONITOR) {
+    if (!s->is_sink && s->subtype != SUBTYPE_MONITOR) {
         if (avahi_entry_group_add_service_subtype(
                     s->entry_group,
                     AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
@@ -327,15 +374,14 @@ finish:
 
     /* Remove this service */
     if (r < 0) {
-        pa_hashmap_remove(s->userdata->services, s->device);
+        pa_hashmap_remove(s->userdata->services, s->key);
         service_free(s);
     }
 
     avahi_string_list_free(txt);
-
-    return r;
 }
 
+/* Runs in PA mainloop context */
 static struct service *get_service(struct userdata *u, pa_object *device) {
     struct service *s;
     char *hn, *un;
@@ -344,21 +390,20 @@ static struct service *get_service(struct userdata *u, pa_object *device) {
     pa_assert(u);
     pa_object_assert_ref(device);
 
+    pa_threaded_mainloop_lock(u->mainloop);
+
     if ((s = pa_hashmap_get(u->services, device)))
-        return s;
+        goto out;
 
     s = pa_xnew(struct service, 1);
+    s->key = device;
     s->userdata = u;
     s->entry_group = NULL;
-    s->device = device;
 
-    if (pa_sink_isinstance(device)) {
-        if (!(n = pa_proplist_gets(PA_SINK(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
-            n = PA_SINK(device)->name;
-    } else {
-        if (!(n = pa_proplist_gets(PA_SOURCE(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
-            n = PA_SOURCE(device)->name;
-    }
+    get_service_data(s, device);
+
+    if (!(n = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+        n = s->name;
 
     hn = pa_get_host_name_malloc();
     un = pa_get_user_name_malloc();
@@ -368,11 +413,15 @@ static struct service *get_service(struct userdata *u, pa_object *device) {
     pa_xfree(un);
     pa_xfree(hn);
 
-    pa_hashmap_put(u->services, s->device, s);
+    pa_hashmap_put(u->services, device, s);
+
+out:
+    pa_threaded_mainloop_unlock(u->mainloop);
 
     return s;
 }
 
+/* Run from Avahi mainloop context */
 static void service_free(struct service *s) {
     pa_assert(s);
 
@@ -382,9 +431,14 @@ static void service_free(struct service *s) {
     }
 
     pa_xfree(s->service_name);
+
+    pa_xfree(s->name);
+    pa_proplist_free(s->proplist);
+
     pa_xfree(s);
 }
 
+/* Runs in PA mainloop context */
 static pa_bool_t shall_ignore(pa_object *o) {
     pa_object_assert_ref(o);
 
@@ -397,30 +451,37 @@ static pa_bool_t shall_ignore(pa_object *o) {
     pa_assert_not_reached();
 }
 
+/* Runs in PA mainloop context */
 static pa_hook_result_t device_new_or_changed_cb(pa_core *c, pa_object *o, struct userdata *u) {
     pa_assert(c);
     pa_object_assert_ref(o);
 
     if (!shall_ignore(o))
-        publish_service(get_service(u, o));
+        pa_mainloop_api_once(u->api, publish_service, get_service(u, o));
 
     return PA_HOOK_OK;
 }
 
+/* Runs in PA mainloop context */
 static pa_hook_result_t device_unlink_cb(pa_core *c, pa_object *o, struct userdata *u) {
     struct service *s;
 
     pa_assert(c);
     pa_object_assert_ref(o);
 
+    pa_threaded_mainloop_lock(u->mainloop);
+
     if ((s = pa_hashmap_remove(u->services, o)))
         service_free(s);
 
+    pa_threaded_mainloop_unlock(u->mainloop);
+
     return PA_HOOK_OK;
 }
 
 static int publish_main_service(struct userdata *u);
 
+/* Runs in Avahi mainloop context */
 static void main_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
     struct userdata *u = userdata;
     pa_assert(u);
@@ -457,6 +518,7 @@ static void main_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState s
     }
 }
 
+/* Runs in Avahi mainloop context */
 static int publish_main_service(struct userdata *u) {
     AvahiStringList *txt = NULL;
     int r = -1;
@@ -501,6 +563,7 @@ fail:
     return r;
 }
 
+/* Runs in PA mainloop context */
 static int publish_all_services(struct userdata *u) {
     pa_sink *sink;
     pa_source *source;
@@ -513,11 +576,11 @@ static int publish_all_services(struct userdata *u) {
 
     for (sink = PA_SINK(pa_idxset_first(u->core->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(u->core->sinks, &idx)))
         if (!shall_ignore(PA_OBJECT(sink)))
-            publish_service(get_service(u, PA_OBJECT(sink)));
+            pa_mainloop_api_once(u->api, publish_service, get_service(u, PA_OBJECT(sink)));
 
     for (source = PA_SOURCE(pa_idxset_first(u->core->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(u->core->sources, &idx)))
         if (!shall_ignore(PA_OBJECT(source)))
-            publish_service(get_service(u, PA_OBJECT(source)));
+            pa_mainloop_api_once(u->api, publish_service, get_service(u, PA_OBJECT(source)));
 
     if (publish_main_service(u) < 0)
         goto fail;
@@ -528,6 +591,7 @@ fail:
     return r;
 }
 
+/* Runs in Avahi mainloop context */
 static void unpublish_all_services(struct userdata *u, pa_bool_t rem) {
     void *state = NULL;
     struct service *s;
@@ -561,6 +625,31 @@ static void unpublish_all_services(struct userdata *u, pa_bool_t rem) {
     }
 }
 
+/* Runs in PA mainloop context */
+static int avahi_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = (struct userdata *) data;
+
+    switch (code) {
+        case AVAHI_MESSAGE_PUBLISH_ALL:
+            publish_all_services(u);
+            break;
+
+        case AVAHI_MESSAGE_SHUTDOWN_START:
+            pa_module_unload(u->core, u->module, true);
+            break;
+
+        case AVAHI_MESSAGE_SHUTDOWN_COMPLETE:
+            /* pa__done() is waiting for this */
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    return 0;
+}
+
+/* Runs in Avahi mainloop context */
 static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
     struct userdata *u = userdata;
 
@@ -571,7 +660,8 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
 
     switch (state) {
         case AVAHI_CLIENT_S_RUNNING:
-            publish_all_services(u);
+            /* Collect all sinks/sources, and publish them */
+            pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->msg), AVAHI_MESSAGE_PUBLISH_ALL, u, 0, NULL, NULL);
             break;
 
         case AVAHI_CLIENT_S_COLLISION:
@@ -600,12 +690,31 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
     }
 }
 
+/* Runs in Avahi mainloop context */
+static void create_client(pa_mainloop_api *api PA_GCC_UNUSED, void *userdata) {
+    struct userdata *u = (struct userdata *) userdata;
+    int error;
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+        pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
+        goto fail;
+    }
+
+    pa_log_debug("Started Avahi threaded mainloop");
+
+    return;
+
+fail:
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->msg), AVAHI_MESSAGE_SHUTDOWN_START, u, 0, NULL, NULL);
+}
+
 int pa__init(pa_module*m) {
 
     struct userdata *u;
     pa_modargs *ma = NULL;
     char *hn, *un;
-    int error;
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
         pa_log("Failed to parse module arguments.");
@@ -617,7 +726,15 @@ int pa__init(pa_module*m) {
     u->module = m;
     u->native = pa_native_protocol_get(u->core);
 
-    u->avahi_poll = pa_avahi_poll_new(m->core->mainloop);
+    u->rtpoll = pa_rtpoll_new();
+    u->mainloop = pa_threaded_mainloop_new();
+    u->api = pa_threaded_mainloop_get_api(u->mainloop);
+
+    pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll);
+    u->msg = pa_msgobject_new(avahi_msg);
+    u->msg->parent.process_msg = avahi_process_msg;
+
+    u->avahi_poll = pa_avahi_poll_new(u->api);
 
     u->services = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
@@ -636,10 +753,9 @@ int pa__init(pa_module*m) {
     pa_xfree(un);
     pa_xfree(hn);
 
-    if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
-        pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
-        goto fail;
-    }
+    pa_threaded_mainloop_set_name(u->mainloop, "avahi-ml");
+    pa_threaded_mainloop_start(u->mainloop);
+    pa_mainloop_api_once(u->api, create_client, u);
 
     pa_modargs_free(ma);
 
@@ -654,6 +770,24 @@ fail:
     return -1;
 }
 
+/* Runs in Avahi mainloop context */
+static void client_free(pa_mainloop_api *api PA_GCC_UNUSED, void *userdata) {
+    struct userdata *u = (struct userdata *) userdata;
+
+    pa_hashmap_free(u->services, (pa_free_cb_t) service_free);
+
+    if (u->main_entry_group)
+        avahi_entry_group_free(u->main_entry_group);
+
+    if (u->client)
+        avahi_client_free(u->client);
+
+    if (u->avahi_poll)
+        pa_avahi_poll_free(u->avahi_poll);
+
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->msg), AVAHI_MESSAGE_SHUTDOWN_COMPLETE, NULL, 0, NULL, NULL);
+}
+
 void pa__done(pa_module*m) {
     struct userdata*u;
     pa_assert(m);
@@ -661,8 +795,14 @@ void pa__done(pa_module*m) {
     if (!(u = m->userdata))
         return;
 
-    if (u->services)
-        pa_hashmap_free(u->services, (pa_free_cb_t) service_free);
+    pa_mainloop_api_once(u->api, client_free, u);
+    pa_asyncmsgq_wait_for(u->thread_mq.outq, AVAHI_MESSAGE_SHUTDOWN_COMPLETE);
+
+    pa_threaded_mainloop_stop(u->mainloop);
+    pa_threaded_mainloop_free(u->mainloop);
+
+    pa_thread_mq_done(&u->thread_mq);
+    pa_rtpoll_free(u->rtpoll);
 
     if (u->sink_new_slot)
         pa_hook_slot_free(u->sink_new_slot);
@@ -677,18 +817,10 @@ void pa__done(pa_module*m) {
     if (u->source_unlink_slot)
         pa_hook_slot_free(u->source_unlink_slot);
 
-    if (u->main_entry_group)
-        avahi_entry_group_free(u->main_entry_group);
-
-    if (u->client)
-        avahi_client_free(u->client);
-
-    if (u->avahi_poll)
-        pa_avahi_poll_free(u->avahi_poll);
-
     if (u->native)
         pa_native_protocol_unref(u->native);
 
+    pa_xfree(u->msg);
     pa_xfree(u->service_name);
     pa_xfree(u);
 }

commit 8d129977ca8d77eeceee1c1a467ccc12f6215063
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed May 15 09:37:45 2013 +0530

    mainloop: Add API to set thread name for threaded mainloop

diff --git a/src/map-file b/src/map-file
index 955b295..90c8ea2 100644
--- a/src/map-file
+++ b/src/map-file
@@ -343,6 +343,7 @@ pa_threaded_mainloop_get_retval;
 pa_threaded_mainloop_in_thread;
 pa_threaded_mainloop_lock;
 pa_threaded_mainloop_new;
+pa_threaded_mainloop_set_name;
 pa_threaded_mainloop_signal;
 pa_threaded_mainloop_start;
 pa_threaded_mainloop_stop;
diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c
index aa56a92..a05d959 100644
--- a/src/pulse/thread-mainloop.c
+++ b/src/pulse/thread-mainloop.c
@@ -50,6 +50,8 @@ struct pa_threaded_mainloop {
     pa_thread* thread;
     pa_mutex* mutex;
     pa_cond* cond, *accept_cond;
+
+    char *name;
 };
 
 static inline int in_worker(pa_threaded_mainloop *m) {
@@ -106,6 +108,7 @@ pa_threaded_mainloop *pa_threaded_mainloop_new(void) {
     m->cond = pa_cond_new();
     m->accept_cond = pa_cond_new();
     m->thread = NULL;
+    m->name = NULL;
 
     pa_mainloop_set_poll_func(m->real_mainloop, poll_func, m->mutex);
 
@@ -132,6 +135,7 @@ void pa_threaded_mainloop_free(pa_threaded_mainloop* m) {
     pa_cond_free(m->cond);
     pa_cond_free(m->accept_cond);
 
+    pa_xfree(m->name);
     pa_xfree(m);
 }
 
@@ -140,7 +144,7 @@ int pa_threaded_mainloop_start(pa_threaded_mainloop *m) {
 
     pa_assert(!m->thread || !pa_thread_is_running(m->thread));
 
-    if (!(m->thread = pa_thread_new("threaded-ml", thread, m)))
+    if (!(m->thread = pa_thread_new(m->name ? m->name : "threaded-ml", thread, m)))
         return -1;
 
     return 0;
@@ -239,3 +243,13 @@ int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m) {
 
     return m->thread && pa_thread_self() == m->thread;
 }
+
+void pa_threaded_mainloop_set_name(pa_threaded_mainloop *m, const char *name) {
+    pa_assert(m);
+    pa_assert(name);
+
+    m->name = pa_xstrdup(name);
+
+    if (m->thread)
+        pa_thread_set_name(m->thread, m->name);
+}
diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h
index 3437001..689336d 100644
--- a/src/pulse/thread-mainloop.h
+++ b/src/pulse/thread-mainloop.h
@@ -311,6 +311,9 @@ pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m);
 /** Returns non-zero when called from within the event loop thread. \since 0.9.7 */
 int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m);
 
+/** Sets the name of the thread. \since 4.0 */
+void pa_threaded_mainloop_set_name(pa_threaded_mainloop *m, const char *name);
+
 PA_C_DECL_END
 
 #endif

commit 1b0136be44ebcf5a5d55709de2907db4e36f3a82
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed May 15 09:36:17 2013 +0530

    pulsecore: Fix assert in pa_msgobject creation
    
    Allows for creation of derived types that don't have any other fields.

diff --git a/src/pulsecore/msgobject.c b/src/pulsecore/msgobject.c
index 075a28c..b55ba8b 100644
--- a/src/pulsecore/msgobject.c
+++ b/src/pulsecore/msgobject.c
@@ -31,7 +31,7 @@ PA_DEFINE_PUBLIC_CLASS(pa_msgobject, pa_object);
 pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_id, pa_bool_t (*check_type)(const char *type_name)) {
     pa_msgobject *o;
 
-    pa_assert(size > sizeof(pa_msgobject));
+    pa_assert(size >= sizeof(pa_msgobject));
     pa_assert(type_id);
 
     if (!check_type)

commit 126a80f3b9f06932238a8d72b75bec743ebeb067
Author: poljar (Damir Jelić) <poljarinho at gmail.com>
Date:   Wed May 22 14:58:10 2013 +0200

    shell-completion: Add list-cards to the pacmd completion.
    
    The list-cards command was missing for both of the shell completions.
    This fixes it.

diff --git a/shell-completion/pulseaudio-bash-completion.sh b/shell-completion/pulseaudio-bash-completion.sh
index a82b10e..5f60092 100644
--- a/shell-completion/pulseaudio-bash-completion.sh
+++ b/shell-completion/pulseaudio-bash-completion.sh
@@ -259,7 +259,7 @@ _pacmd() {
     local cur prev words cword preprev command
     local comps
     local flags='-h --help --version'
-    local commands=(exit help list-modules list-sinks list-sources list-clients
+    local commands=(exit help list-modules list-cards list-sinks list-sources list-clients
                     list-samples list-sink-inputs list-source-outputs stat info
                     load-module unload-module describe-module set-sink-volume
                     set-source-volume set-sink-input-volume set-source-output-volume
diff --git a/shell-completion/pulseaudio-zsh-completion.zsh b/shell-completion/pulseaudio-zsh-completion.zsh
index d73b74d..142d031 100644
--- a/shell-completion/pulseaudio-zsh-completion.zsh
+++ b/shell-completion/pulseaudio-zsh-completion.zsh
@@ -329,6 +329,7 @@ _pacmd_completion() {
         _pacmd_commands=(
             'help: show help and exit'
             'list-modules: list modules'
+            'list-cards: list cards'
             'list-sinks: list sinks'
             'list-sources: list sources'
             'list-clients: list clients'

commit b0ee51e19e485c57d08f44785911c60b8faa9bcc
Author: Peter Meerwald <p.meerwald at bct-electronic.com>
Date:   Wed May 15 09:57:56 2013 +0200

    rescue-streams: Fix wording of module description
    
    Signed-off-by: Peter Meerwald <p.meerwald at bct-electronic.com>

diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c
index 8b35809..f3e7424 100644
--- a/src/modules/module-rescue-streams.c
+++ b/src/modules/module-rescue-streams.c
@@ -36,7 +36,7 @@
 #include "module-rescue-streams-symdef.h"
 
 PA_MODULE_AUTHOR("Lennart Poettering");
-PA_MODULE_DESCRIPTION("When a sink/source is removed, try to move their streams to the default sink/source");
+PA_MODULE_DESCRIPTION("When a sink/source is removed, try to move its streams to the default sink/source");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(TRUE);
 

commit 73b816d6ac2d91503e217d695eacf715d3abb722
Author: Peter Meerwald <p.meerwald at bct-electronic.com>
Date:   Wed May 15 09:53:30 2013 +0200

    intended-roles: Fix typo in module description
    
    Signed-off-by: Peter Meerwald <p.meerwald at bct-electronic.com>

diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c
index d1e9b81..5fa0355 100644
--- a/src/modules/module-intended-roles.c
+++ b/src/modules/module-intended-roles.c
@@ -36,7 +36,7 @@
 #include "module-intended-roles-symdef.h"
 
 PA_MODULE_AUTHOR("Lennart Poettering");
-PA_MODULE_DESCRIPTION("Automatically set device of streams based of intended roles of devices");
+PA_MODULE_DESCRIPTION("Automatically set device of streams based on intended roles of devices");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(TRUE);
 PA_MODULE_USAGE(

commit 83c3cf0a65fb05900f81bd2dbb38e6956eb23935
Author: Peter Meerwald <p.meerwald at bct-electronic.com>
Date:   Wed May 15 09:39:26 2013 +0200

    pactl: Use colon to separate priority in profiles output
    
    for example:
      Profiles:
    	input:analog-stereo: Analog Stereo Input (sinks: 0, sources: 1, priority. 60)
    	output:analog-stereo: Analog Stereo Output (sinks: 1, sources: 0, priority. 6000)
    
    it should be "priority: xxx", not "priority. xxx"
    
    Signed-off-by: Peter Meerwald <p.meerwald at bct-electronic.com>

diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 3b6770f..8e6ec76 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -588,7 +588,7 @@ static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_
 
         printf(_("\tProfiles:\n"));
         for (p = i->profiles; p->name; p++)
-            printf("\t\t%s: %s (sinks: %u, sources: %u, priority. %u)\n", p->name, p->description, p->n_sinks, p->n_sources, p->priority);
+            printf("\t\t%s: %s (sinks: %u, sources: %u, priority: %u)\n", p->name, p->description, p->n_sinks, p->n_sources, p->priority);
     }
 
     if (i->active_profile)

commit 2dcf052e13cfdd5e51e010ea855a84877a06db65
Author: Mitchell Fang <mitchell.fang at gmail.com>
Date:   Wed May 22 11:23:27 2013 +0300

    thread-mainloop: Fix bug in example code
    
    Checking the operation state caused a deadlock, because the state
    won't change before my_drain_callback() returns, and it doesn't
    return before my_drain_stream_func() calls
    pa_threaded_mainloop_accept().

diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h
index ff166f8..3437001 100644
--- a/src/pulse/thread-mainloop.h
+++ b/src/pulse/thread-mainloop.h
@@ -166,7 +166,7 @@ PA_C_DECL_BEGIN
  * access this data safely, we must extend our example a bit:
  *
  * \code
- * static int *drain_result;
+ * static volatile int *drain_result = NULL;
  *
  * static void my_drain_callback(pa_stream*s, int success, void *userdata) {
  *     pa_threaded_mainloop *m;
@@ -187,7 +187,7 @@ PA_C_DECL_BEGIN
  *     o = pa_stream_drain(s, my_drain_callback, m);
  *     assert(o);
  *
- *     while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
+ *     while (drain_result == NULL)
  *         pa_threaded_mainloop_wait(m);
  *
  *     pa_operation_unref(o);

commit 9c1174925879ca0b5dcfea06cca3c23a98f98c01
Author: Peter Meerwald <p.meerwald at bct-electronic.com>
Date:   Wed May 15 17:49:42 2013 +0200

    cli: Fix plural in caption of source-outputs list
    
    $ pacmd list-source-outputs
    >>> 4 source outputs(s) available.
    
    outputs(s) makes no sense
    
    Signed-off-by: Peter Meerwald <p.meerwald at bct-electronic.com>

diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 963f130..85c97b3 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -486,7 +486,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
 
     s = pa_strbuf_new();
 
-    pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs));
+    pa_strbuf_printf(s, "%u source output(s) available.\n", pa_idxset_size(c->source_outputs));
 
     PA_IDXSET_FOREACH(o, c->source_outputs, idx) {
         char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];

commit 0e4c16e120f95b122c7139deb046192927d1b6c7
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri May 10 10:30:47 2013 +0200

    bluetooth: Support transport auto-release
    
    With BlueZ 5, if the remote device suspends the audio, the transport
    state will change to "idle" and the endpoint is not required to release
    the transport, since this could introduce race conditions. Therefore,
    ignore the call to pa_bluetooth_transport_release() if the transport is
    not acquired any more.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 2e794ac..5924736 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1538,6 +1538,12 @@ void pa_bluetooth_transport_release(pa_bluetooth_transport *t) {
         pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID));
     } else {
         pa_assert(t->device->discovery->version == BLUEZ_VERSION_5);
+
+        if (t->state <= PA_BLUETOOTH_TRANSPORT_STATE_IDLE) {
+            pa_log_info("Transport %s auto-released by BlueZ or already released", t->path);
+            return;
+        }
+
         pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport1", "Release"));
     }
 

commit 9615def4b96f0bab365ddc03f6c003f382a54752
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri May 10 10:30:46 2013 +0200

    bluetooth: Update to new BlueZ 5 transport acquire/release API
    
    The new D-Bus API doesn't support access rights, which weren't used by
    PulseAudio anyway, but it does solve a race condition: now optional
    acquires can be implemented by bluetooth-util atomically using the D-Bus
    TryAcquire() method.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index ff8afaa..2e794ac 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1452,40 +1452,52 @@ bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d) {
 }
 
 int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
-    const char *accesstype = "rw";
-    const char *interface;
     DBusMessage *m, *r;
     DBusError err;
     int ret;
     uint16_t i, o;
+    const char *method;
 
     pa_assert(t);
     pa_assert(t->device);
     pa_assert(t->device->discovery);
 
-    interface = t->device->discovery->version == BLUEZ_VERSION_4 ? "org.bluez.MediaTransport" : "org.bluez.MediaTransport1";
-
-    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);
+
+    if (t->device->discovery->version == BLUEZ_VERSION_4) {
+        const char *accesstype = "rw";
+
+        if (optional) {
+            /* 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 with the API in BlueZ 4.x */
+            if (t->state < PA_BLUETOOTH_TRANSPORT_STATE_PLAYING) {
+                pa_log_info("Failed optional acquire of unavailable transport %s", t->path);
+                return -1;
+            }
         }
-    }
 
-    dbus_error_init(&err);
+        method = "Acquire";
+        pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport", method));
+        pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID));
+    } else {
+        pa_assert(t->device->discovery->version == BLUEZ_VERSION_5);
+
+        method = optional ? "TryAcquire" : "Acquire";
+        pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport1", method));
+    }
 
-    pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, interface, "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->device->discovery->connection), m, -1, &err);
 
     if (!r) {
+        if (optional && pa_streq(err.name, "org.bluez.Error.NotAvailable"))
+            pa_log_info("Failed optional acquire of unavailable transport %s", t->path);
+        else
+            pa_log("Transport %s() failed for transport %s (%s)", method, t->path, err.message);
+
         dbus_error_free(&err);
         return -1;
     }
@@ -1510,8 +1522,6 @@ fail:
 }
 
 void pa_bluetooth_transport_release(pa_bluetooth_transport *t) {
-    const char *accesstype = "rw";
-    const char *interface;
     DBusMessage *m;
     DBusError err;
 
@@ -1519,12 +1529,18 @@ void pa_bluetooth_transport_release(pa_bluetooth_transport *t) {
     pa_assert(t->device);
     pa_assert(t->device->discovery);
 
-    interface = t->device->discovery->version == BLUEZ_VERSION_4 ? "org.bluez.MediaTransport" : "org.bluez.MediaTransport1";
-
     dbus_error_init(&err);
 
-    pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, interface, "Release"));
-    pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID));
+    if (t->device->discovery->version == BLUEZ_VERSION_4) {
+        const char *accesstype = "rw";
+
+        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));
+    } else {
+        pa_assert(t->device->discovery->version == BLUEZ_VERSION_5);
+        pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport1", "Release"));
+    }
+
     dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
 
     if (dbus_error_is_set(&err)) {
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 6ae3570..c82cb53 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -368,14 +368,8 @@ static int bt_transport_acquire(struct userdata *u, bool optional) {
     pa_log_debug("Acquiring transport %s", u->transport->path);
 
     u->stream_fd = pa_bluetooth_transport_acquire(u->transport, optional, &u->read_link_mtu, &u->write_link_mtu);
-    if (u->stream_fd < 0) {
-        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);
-
+    if (u->stream_fd < 0)
         return -1;
-    }
 
     u->transport_acquired = true;
     pa_log_info("Transport %s acquired: fd %d", u->transport->path, u->stream_fd);

commit 6fdf2b05b8867d4597c497243b469ecd0d8c2426
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri May 10 10:30:45 2013 +0200

    bluetooth: Support media transport's State property
    
    BlueZ 5 exposes a 'State' property in the media transport interface.
    With regard to PA, this replaces the profile-specific interfaces, since
    they were being used to know if the audio was streaming or not.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index a0e6811..ff8afaa 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -126,6 +126,20 @@ static pa_bt_audio_state_t audio_state_from_string(const char* value) {
     return PA_BT_AUDIO_STATE_INVALID;
 }
 
+static int transport_state_from_string(const char* value, pa_bluetooth_transport_state_t *state) {
+    pa_assert(value);
+    pa_assert(state);
+
+    if (pa_streq(value, "idle"))
+        *state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
+    else if (pa_streq(value, "pending") || pa_streq(value, "active")) /* We don't need such a separation */
+        *state = PA_BLUETOOTH_TRANSPORT_STATE_PLAYING;
+    else
+        return -1;
+
+    return 0;
+}
+
 const char *pa_bt_profile_to_string(enum profile profile) {
     switch(profile) {
         case PROFILE_A2DP:
@@ -1089,6 +1103,29 @@ static int transport_parse_property(pa_bluetooth_transport *t, DBusMessageIter *
 
             break;
          }
+
+        case DBUS_TYPE_STRING: {
+
+            const char *value;
+            dbus_message_iter_get_basic(&variant_i, &value);
+
+            if (pa_streq(key, "State")) { /* Added in BlueZ 5.0 */
+                bool old_any_connected = pa_bluetooth_device_any_audio_connected(t->device);
+
+                if (transport_state_from_string(value, &t->state) < 0) {
+                    pa_log("Transport %s has an invalid state: '%s'", t->path, value);
+                    return -1;
+                }
+
+                pa_log_debug("dbus: transport %s set to state '%s'", t->path, value);
+                pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
+
+                if (old_any_connected != pa_bluetooth_device_any_audio_connected(t->device))
+                    run_callback(t->device, old_any_connected);
+            }
+
+            break;
+        }
     }
 
     return 0;
@@ -1567,7 +1604,10 @@ static pa_bluetooth_transport *transport_new(pa_bluetooth_device *d, const char
         memcpy(t->config, config, size);
     }
 
-    t->state = audio_state_to_transport_state(d->profile_state[p]);
+    if (d->discovery->version == BLUEZ_VERSION_4)
+        t->state = audio_state_to_transport_state(d->profile_state[p]);
+    else
+        t->state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
 
     return t;
 }

commit 2f79fb580ad583b2492b567d32630e7e03883855
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri May 10 10:30:44 2013 +0200

    bluetooth: Parse media transport's properties
    
    Add the code to parse the properties of the media transport object when
    a PropertiesChanged signal is received.
    
    Note that the transport might have an owner other than BlueZ, and thus
    the property changes would be emitted from arbitrary senders. For
    performance reasons, the installed match considers the interface name
    where the property has changed.
    
    It could be possible to install and remove the D-Bus matches dynamically
    when a new owner is registered/unregistered, but filtering based on the
    interface name seems good enough already.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index a32227a..a0e6811 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1094,6 +1094,24 @@ static int transport_parse_property(pa_bluetooth_transport *t, DBusMessageIter *
     return 0;
 }
 
+static int parse_transport_properties(pa_bluetooth_transport *t, DBusMessageIter *i) {
+    DBusMessageIter element_i;
+
+    dbus_message_iter_recurse(i, &element_i);
+
+    while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+        DBusMessageIter dict_i;
+
+        dbus_message_iter_recurse(&element_i, &dict_i);
+
+        transport_parse_property(t, &dict_i);
+
+        dbus_message_iter_next(&element_i);
+    }
+
+    return 0;
+}
+
 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
     DBusError err;
     pa_bluetooth_discovery *y;
@@ -1315,6 +1333,13 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
             }
 
             parse_device_properties(d, &arg_i, true);
+        } else if (pa_streq(interface, "org.bluez.MediaTransport1")) {
+            pa_bluetooth_transport *t;
+
+            if (!(t = pa_hashmap_get(y->transports, dbus_message_get_path(m))))
+                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+            parse_transport_properties(t, &arg_i);
         }
 
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -1979,6 +2004,8 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
                 "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'",
                 "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
                 ",arg0='org.bluez.Device1'",
+                "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
+                ",arg0='org.bluez.MediaTransport1'",
                 NULL) < 0) {
         pa_log("Failed to add D-Bus matches: %s", err.message);
         goto fail;
@@ -2056,6 +2083,8 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
             "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'",
             "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
             ",arg0='org.bluez.Device1'",
+            "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
+            ",arg0='org.bluez.MediaTransport1'",
             NULL);
 
         if (y->filter_added)

commit 235611a7d13ab251689f5b480183d7f39e043a2e
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri May 10 10:30:43 2013 +0200

    bluetooth: Support Properties.PropertiesChanged signal
    
    Install matches for signal Properties.PropertiesChanged and process the
    properties corresponding to the tracked devices.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index cb7d3c4..a32227a 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1289,6 +1289,35 @@ 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.freedesktop.DBus.Properties", "PropertiesChanged")) {
+        DBusMessageIter arg_i;
+        const char *interface;
+
+        if (y->version != BLUEZ_VERSION_5)
+            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
+
+        if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "sa{sv}as")) {
+            pa_log("Invalid signature found in PropertiesChanged");
+            goto fail;
+        }
+
+        dbus_message_iter_get_basic(&arg_i, &interface);
+
+        pa_assert_se(dbus_message_iter_next(&arg_i));
+        pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
+
+        if (pa_streq(interface, "org.bluez.Device1")) {
+            pa_bluetooth_device *d;
+
+            if (!(d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
+                pa_log_warn("Property change in unknown device %s", dbus_message_get_path(m));
+                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            }
+
+            parse_device_properties(d, &arg_i, true);
+        }
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     }
 
 fail:
@@ -1948,6 +1977,8 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
                 "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
                 "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'",
                 "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'",
+                "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
+                ",arg0='org.bluez.Device1'",
                 NULL) < 0) {
         pa_log("Failed to add D-Bus matches: %s", err.message);
         goto fail;
@@ -2023,6 +2054,8 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
             "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
             "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'",
             "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'",
+            "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
+            ",arg0='org.bluez.Device1'",
             NULL);
 
         if (y->filter_added)

commit 114edb0696ce979ad10f5616067afda1b76ae4e2
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Fri May 10 10:30:42 2013 +0200

    bluetooth: Support ObjectManager interface add/remove
    
    Install matches for signals ObjectManager.InterfacesAdded and
    ObjectManager.InterfacesRemoved, and process the devices that are
    registered and unregistered dynamically.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index fb8f753..cb7d3c4 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1233,6 +1233,62 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
             goto fail;
 
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")) {
+        DBusMessageIter arg_i;
+
+        if (y->version != BLUEZ_VERSION_5)
+            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
+
+        if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sa{sv}}")) {
+            pa_log("Invalid signature found in InterfacesAdded");
+            goto fail;
+        }
+
+        if (parse_interfaces_and_properties(y, &arg_i) < 0)
+            goto fail;
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved")) {
+        const char *path;
+        DBusMessageIter arg_i;
+        DBusMessageIter element_i;
+
+        if (y->version != BLUEZ_VERSION_5)
+            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
+
+        if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oas")) {
+            pa_log("Invalid signature found in InterfacesRemoved");
+            goto fail;
+        }
+
+        dbus_message_iter_get_basic(&arg_i, &path);
+
+        pa_assert_se(dbus_message_iter_next(&arg_i));
+        pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
+
+        dbus_message_iter_recurse(&arg_i, &element_i);
+
+        while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_STRING) {
+            const char *interface;
+
+            dbus_message_iter_get_basic(&element_i, &interface);
+
+            if (pa_streq(interface, "org.bluez.Device1")) {
+                pa_bluetooth_device *d;
+
+                if (!(d = pa_hashmap_remove(y->devices, path)))
+                    pa_log_warn("Unknown device removed %s", path);
+                else {
+                    pa_log_debug("Device %s removed", path);
+                    run_callback(d, true);
+                    device_free(d);
+                }
+            }
+
+            dbus_message_iter_next(&element_i);
+        }
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     }
 
 fail:
@@ -1890,6 +1946,8 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
                 "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'",
                 "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'",
                 "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
+                "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'",
+                "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'",
                 NULL) < 0) {
         pa_log("Failed to add D-Bus matches: %s", err.message);
         goto fail;
@@ -1963,6 +2021,8 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
             "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'",
             "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'",
             "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
+            "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'",
+            "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'",
             NULL);
 
         if (y->filter_added)

commit ab37be46f6406ce2af4daa92da2d2f0a4ea12bcf
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Mon Jul 23 14:20:05 2012 +0530

    core: Add an "internal" suspend cause
    
    This lets us suspend devices from within the core for short periods
    without having to overload one of the existing suspend causes.
    
    https://bugs.freedesktop.org/show_bug.cgi?id=64118

diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index a8cff5c..261cc99 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -36,6 +36,7 @@ typedef enum pa_suspend_cause {
     PA_SUSPEND_IDLE = 4,         /* Used by module-suspend-on-idle */
     PA_SUSPEND_SESSION = 8,      /* Used by module-hal for mark inactive sessions */
     PA_SUSPEND_PASSTHROUGH = 16, /* Used to suspend monitor sources when the sink is in passthrough mode */
+    PA_SUSPEND_INTERNAL = 32,    /* This is used for short period server-internal suspends, such as for sample rate updates */
     PA_SUSPEND_ALL = 0xFFFF      /* Magic cause that can be used to resume forcibly */
 } pa_suspend_cause_t;
 
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 9b4b066..a473552 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1380,6 +1380,8 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
 /* Called from main thread */
 pa_bool_t pa_sink_update_rate(pa_sink *s, uint32_t rate, pa_bool_t passthrough)
 {
+    pa_bool_t ret = FALSE;
+
     if (s->update_rate) {
         uint32_t desired_rate = rate;
         uint32_t default_rate = s->default_sample_rate;
@@ -1439,7 +1441,7 @@ pa_bool_t pa_sink_update_rate(pa_sink *s, uint32_t rate, pa_bool_t passthrough)
             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 */
+        pa_sink_suspend(s, TRUE, PA_SUSPEND_INTERNAL);
 
         if (s->update_rate(s, desired_rate) == TRUE) {
             /* update monitor source as well */
@@ -1452,10 +1454,13 @@ pa_bool_t pa_sink_update_rate(pa_sink *s, uint32_t rate, pa_bool_t passthrough)
                     pa_sink_input_update_rate(i);
             }
 
-            return TRUE;
+            ret = TRUE;
         }
+
+        pa_sink_suspend(s, FALSE, PA_SUSPEND_INTERNAL);
     }
-    return FALSE;
+
+    return ret ;
 }
 
 /* Called from main thread */
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index c6aa5e3..911684b 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -970,6 +970,8 @@ void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *
 /* Called from main thread */
 pa_bool_t pa_source_update_rate(pa_source *s, uint32_t rate, pa_bool_t passthrough)
 {
+    pa_bool_t ret = FALSE;
+
     if (s->update_rate) {
         uint32_t desired_rate = rate;
         uint32_t default_rate = s->default_sample_rate;
@@ -1022,7 +1024,7 @@ pa_bool_t pa_source_update_rate(pa_source *s, uint32_t rate, pa_bool_t passthrou
             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 */
+        pa_source_suspend(s, TRUE, PA_SUSPEND_INTERNAL);
 
         if (s->update_rate(s, desired_rate) == TRUE) {
             pa_log_info("Changed sampling rate successfully ");
@@ -1031,10 +1033,13 @@ pa_bool_t pa_source_update_rate(pa_source *s, uint32_t rate, pa_bool_t passthrou
                 if (o->state == PA_SOURCE_OUTPUT_CORKED)
                     pa_source_output_update_rate(o);
             }
-            return TRUE;
+            ret = TRUE;
         }
+
+        pa_source_suspend(s, FALSE, PA_SUSPEND_INTERNAL);
     }
-    return FALSE;
+
+    return ret;
 }
 
 /* Called from main thread */

commit 8cc4e7786f04929c55f12d480b38bb484506eee4
Author: Nikolay Amiantov <nikoamia at gmail.com>
Date:   Sun May 5 15:22:53 2013 +0400

    pactl: Flush stdout buffer when printing subscribe events.
    
    "pactl subscribe" is running continuously, and without flushing its output is
    not usable for "process-on-arrival" per-line tasks, such as grepping. This
    patch should fix this. For example, now:
    pactl subscribe | grep 'server'
    should print only server events as they arrive.

diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 0fb62cb..3b6770f 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -1169,6 +1169,7 @@ static void context_subscribe_callback(pa_context *c, pa_subscription_event_type
            subscription_event_type_to_string(t),
            subscription_event_facility_to_string(t),
            idx);
+    fflush(stdout);
 }
 
 static void context_state_callback(pa_context *c, void *userdata) {

commit cfb96b2530690df113e1aceb5b82fc166f9d2cfd
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Mon Apr 29 18:28:04 2013 +0200

    bluetooth: BlueZ 5 interface rename to org.bluez.MediaTransport1
    
    Use the new interface name if BlueZ 5 has been detected.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index ff83f53..fb8f753 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1306,6 +1306,7 @@ bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d) {
 
 int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
     const char *accesstype = "rw";
+    const char *interface;
     DBusMessage *m, *r;
     DBusError err;
     int ret;
@@ -1315,6 +1316,8 @@ int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, bool optional, siz
     pa_assert(t->device);
     pa_assert(t->device->discovery);
 
+    interface = t->device->discovery->version == BLUEZ_VERSION_4 ? "org.bluez.MediaTransport" : "org.bluez.MediaTransport1";
+
     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
@@ -1331,7 +1334,7 @@ int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, bool optional, siz
 
     dbus_error_init(&err);
 
-    pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport", "Acquire"));
+    pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, interface, "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->device->discovery->connection), m, -1, &err);
 
@@ -1342,7 +1345,7 @@ int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, bool optional, siz
 
     if (!dbus_message_get_args(r, &err, DBUS_TYPE_UNIX_FD, &ret, DBUS_TYPE_UINT16, &i, DBUS_TYPE_UINT16, &o,
                                DBUS_TYPE_INVALID)) {
-        pa_log("Failed to parse org.bluez.MediaTransport.Acquire(): %s", err.message);
+        pa_log("Failed to parse the media transport Acquire() reply: %s", err.message);
         ret = -1;
         dbus_error_free(&err);
         goto fail;
@@ -1361,6 +1364,7 @@ fail:
 
 void pa_bluetooth_transport_release(pa_bluetooth_transport *t) {
     const char *accesstype = "rw";
+    const char *interface;
     DBusMessage *m;
     DBusError err;
 
@@ -1368,9 +1372,11 @@ void pa_bluetooth_transport_release(pa_bluetooth_transport *t) {
     pa_assert(t->device);
     pa_assert(t->device->discovery);
 
+    interface = t->device->discovery->version == BLUEZ_VERSION_4 ? "org.bluez.MediaTransport" : "org.bluez.MediaTransport1";
+
     dbus_error_init(&err);
 
-    pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport", "Release"));
+    pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, interface, "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->device->discovery->connection), m, -1, &err);
 

commit 61e8fd8854308cb2d41e6313f25ae7131c912ea8
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Mon Apr 29 18:28:03 2013 +0200

    bluetooth: BlueZ 5 interface rename to org.bluez.Media1
    
    Use the new interface name if BlueZ 5 has been detected.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 5d5c7ab..ff83f53 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -834,8 +834,7 @@ static void register_endpoint_reply(DBusPendingCall *pending, void *userdata) {
     }
 
     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
-        pa_log("org.bluez.Media.RegisterEndpoint() failed: %s: %s", dbus_message_get_error_name(r),
-               pa_dbus_get_error_message(r));
+        pa_log("RegisterEndpoint() failed: %s: %s", dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
         goto finish;
     }
 
@@ -852,10 +851,11 @@ static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const
     DBusMessage *m;
     DBusMessageIter i, d;
     uint8_t codec = 0;
+    const char *interface = y->version == BLUEZ_VERSION_4 ? "org.bluez.Media" : "org.bluez.Media1";
 
     pa_log_debug("Registering %s on adapter %s.", endpoint, path);
 
-    pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Media", "RegisterEndpoint"));
+    pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, interface, "RegisterEndpoint"));
 
     dbus_message_iter_init_append(m, &i);
 

commit d22ea7ff7630bef83a3bf8c26cc56c196dec33a1
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Mon Apr 29 18:28:02 2013 +0200

    bluetooth: BlueZ 5 interface rename to org.bluez.MediaEndpoint1
    
    Use the new interface name if BlueZ 5 has been detected.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 8b9c1ba..5d5c7ab 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -61,6 +61,30 @@
     " </interface>"                                                     \
     "</node>"
 
+#define MEDIA_ENDPOINT_1_INTROSPECT_XML                                 \
+    DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
+    "<node>"                                                            \
+    " <interface name=\"org.bluez.MediaEndpoint1\">"                    \
+    "  <method name=\"SetConfiguration\">"                              \
+    "   <arg name=\"transport\" direction=\"in\" type=\"o\"/>"          \
+    "   <arg name=\"configuration\" direction=\"in\" type=\"ay\"/>"     \
+    "  </method>"                                                       \
+    "  <method name=\"SelectConfiguration\">"                           \
+    "   <arg name=\"capabilities\" direction=\"in\" type=\"ay\"/>"      \
+    "   <arg name=\"configuration\" direction=\"out\" type=\"ay\"/>"    \
+    "  </method>"                                                       \
+    "  <method name=\"ClearConfiguration\">"                            \
+    "  </method>"                                                       \
+    "  <method name=\"Release\">"                                       \
+    "  </method>"                                                       \
+    " </interface>"                                                     \
+    " <interface name=\"org.freedesktop.DBus.Introspectable\">"         \
+    "  <method name=\"Introspect\">"                                    \
+    "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
+    "  </method>"                                                       \
+    " </interface>"                                                     \
+    "</node>"
+
 typedef enum pa_bluez_version {
     BLUEZ_VERSION_UNKNOWN,
     BLUEZ_VERSION_4,
@@ -1453,7 +1477,7 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
     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);
+        pa_log("Endpoint SetConfiguration: Transport %s is already configured.", path);
         goto fail2;
     }
 
@@ -1547,11 +1571,10 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
     return NULL;
 
 fail:
-    pa_log("org.bluez.MediaEndpoint.SetConfiguration: invalid arguments");
+    pa_log("Endpoint SetConfiguration: invalid arguments");
 
 fail2:
-    pa_assert_se(r = dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments",
-                                            "Unable to set configuration"));
+    pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to set configuration"));
     return r;
 }
 
@@ -1565,7 +1588,7 @@ static DBusMessage *endpoint_clear_configuration(DBusConnection *c, DBusMessage
     dbus_error_init(&e);
 
     if (!dbus_message_get_args(m, &e, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
-        pa_log("org.bluez.MediaEndpoint.ClearConfiguration: %s", e.message);
+        pa_log("Endpoint ClearConfiguration: %s", e.message);
         dbus_error_free(&e);
         goto fail;
     }
@@ -1590,8 +1613,7 @@ static DBusMessage *endpoint_clear_configuration(DBusConnection *c, DBusMessage
     return r;
 
 fail:
-    pa_assert_se(r = dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments",
-                                            "Unable to clear configuration"));
+    pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to clear configuration"));
     return r;
 }
 
@@ -1661,7 +1683,7 @@ static DBusMessage *endpoint_select_configuration(DBusConnection *c, DBusMessage
     dbus_error_init(&e);
 
     if (!dbus_message_get_args(m, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
-        pa_log("org.bluez.MediaEndpoint.SelectConfiguration: %s", e.message);
+        pa_log("Endpoint SelectConfiguration: %s", e.message);
         dbus_error_free(&e);
         goto fail;
     }
@@ -1758,8 +1780,7 @@ done:
     return r;
 
 fail:
-    pa_assert_se(r = dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments",
-                                            "Unable to select configuration"));
+    pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to select configuration"));
     return r;
 }
 
@@ -1783,17 +1804,19 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi
         !pa_streq(path, HFP_HS_ENDPOINT))
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
+    interface = y->version == BLUEZ_VERSION_4 ? "org.bluez.MediaEndpoint" : "org.bluez.MediaEndpoint1";
+
     if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
-        const char *xml = ENDPOINT_INTROSPECT_XML;
+        const char *xml = y->version == BLUEZ_VERSION_4 ? ENDPOINT_INTROSPECT_XML : MEDIA_ENDPOINT_1_INTROSPECT_XML;
 
         pa_assert_se(r = dbus_message_new_method_return(m));
         pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
 
-    } else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "SetConfiguration"))
+    } else if (dbus_message_is_method_call(m, interface, "SetConfiguration"))
         r = endpoint_set_configuration(c, m, userdata);
-    else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "SelectConfiguration"))
+    else if (dbus_message_is_method_call(m, interface, "SelectConfiguration"))
         r = endpoint_select_configuration(c, m, userdata);
-    else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "ClearConfiguration"))
+    else if (dbus_message_is_method_call(m, interface, "ClearConfiguration"))
         r = endpoint_clear_configuration(c, m, userdata);
     else
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;

commit d9ed42c40f744b273c8d466e95dd4a06e41364b9
Author: Tanu Kaskinen <tanu.kaskinen at intel.com>
Date:   Wed May 1 13:39:36 2013 +0300

    bluetooth: Fix error checking style

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index cee283e..8b9c1ba 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -910,7 +910,7 @@ static int parse_device_properties(pa_bluetooth_device *d, DBusMessageIter *i, b
 
         dbus_message_iter_recurse(&element_i, &dict_i);
 
-        if (parse_device_property(d, &dict_i, is_property_change))
+        if (parse_device_property(d, &dict_i, is_property_change) < 0)
             ret = -1;
 
         dbus_message_iter_next(&element_i);

commit c4bd51a34547ed0991ff3c7219c2d5de49669ca8
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Mon Apr 29 18:27:59 2013 +0200

    bluetooth: Parse the tree returned by ObjectManager
    
    Parse the result of ObjectManager.GetManagedObjects(), which includes
    all objects registered, their interfaces and the corresponding
    properties per interface.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 1883c8a..cee283e 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -362,8 +362,6 @@ static int parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i, boo
 
     dbus_message_iter_recurse(i, &variant_i);
 
-/*     pa_log_debug("Parsing property org.bluez.Device.%s", key); */
-
     switch (dbus_message_iter_get_arg_type(&variant_i)) {
 
         case DBUS_TYPE_STRING: {
@@ -452,6 +450,11 @@ static int parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i, boo
                     uuiddata.uuid = value;
                     pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_UUID_ADDED], &uuiddata);
 
+                    if (d->discovery->version >= BLUEZ_VERSION_5) {
+                        dbus_message_iter_next(&ai);
+                        continue;
+                    }
+
                     /* Vudentz said the interfaces are here when the UUIDs are announced */
                     if (strcasecmp(HSP_AG_UUID, value) == 0 || strcasecmp(HFP_AG_UUID, value) == 0) {
                         pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.HandsfreeGateway",
@@ -867,16 +870,25 @@ static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const
     send_and_add_to_pending(y, m, register_endpoint_reply, pa_xstrdup(endpoint));
 }
 
+static void register_adapter_endpoints(pa_bluetooth_discovery *y, const char *path) {
+    register_endpoint(y, path, A2DP_SOURCE_ENDPOINT, A2DP_SOURCE_UUID);
+    register_endpoint(y, path, A2DP_SINK_ENDPOINT, A2DP_SINK_UUID);
+
+    /* For BlueZ 5, only A2DP is registered in the Media API */
+    if (y->version >= BLUEZ_VERSION_5)
+        return;
+
+    register_endpoint(y, path, HFP_AG_ENDPOINT, HFP_AG_UUID);
+    register_endpoint(y, path, HFP_HS_ENDPOINT, HFP_HS_UUID);
+}
+
 static void found_adapter(pa_bluetooth_discovery *y, const char *path) {
     DBusMessage *m;
 
     pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "GetProperties"));
     send_and_add_to_pending(y, m, get_properties_reply, NULL);
 
-    register_endpoint(y, path, HFP_AG_ENDPOINT, HFP_AG_UUID);
-    register_endpoint(y, path, HFP_HS_ENDPOINT, HFP_HS_UUID);
-    register_endpoint(y, path, A2DP_SOURCE_ENDPOINT, A2DP_SOURCE_UUID);
-    register_endpoint(y, path, A2DP_SINK_ENDPOINT, A2DP_SINK_UUID);
+    register_adapter_endpoints(y, path);
 }
 
 static void list_adapters(pa_bluetooth_discovery *y) {
@@ -887,10 +899,94 @@ static void list_adapters(pa_bluetooth_discovery *y) {
     send_and_add_to_pending(y, m, get_properties_reply, NULL);
 }
 
+static int parse_device_properties(pa_bluetooth_device *d, DBusMessageIter *i, bool is_property_change) {
+    DBusMessageIter element_i;
+    int ret = 0;
+
+    dbus_message_iter_recurse(i, &element_i);
+
+    while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+        DBusMessageIter dict_i;
+
+        dbus_message_iter_recurse(&element_i, &dict_i);
+
+        if (parse_device_property(d, &dict_i, is_property_change))
+            ret = -1;
+
+        dbus_message_iter_next(&element_i);
+    }
+
+    if (!d->address || !d->alias || d->paired < 0 || d->trusted < 0) {
+        pa_log_error("Non-optional information missing for device %s", d->path);
+        d->device_info_valid = -1;
+        return -1;
+    }
+
+    d->device_info_valid = 1;
+    return ret;
+}
+
+static int parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessageIter *dict_i) {
+    DBusMessageIter element_i;
+    const char *path;
+
+    pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_OBJECT_PATH);
+    dbus_message_iter_get_basic(dict_i, &path);
+
+    pa_assert_se(dbus_message_iter_next(dict_i));
+    pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_ARRAY);
+
+    dbus_message_iter_recurse(dict_i, &element_i);
+
+    while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+        DBusMessageIter iface_i;
+        const char *interface;
+
+        dbus_message_iter_recurse(&element_i, &iface_i);
+
+        pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_STRING);
+        dbus_message_iter_get_basic(&iface_i, &interface);
+
+        pa_assert_se(dbus_message_iter_next(&iface_i));
+        pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_ARRAY);
+
+        if (pa_streq(interface, "org.bluez.Adapter1")) {
+            pa_log_debug("Adapter %s found", path);
+            register_adapter_endpoints(y, path);
+        } else if (pa_streq(interface, "org.bluez.Device1")) {
+            pa_bluetooth_device *d;
+
+            if (pa_hashmap_get(y->devices, path)) {
+                pa_log("Found duplicated D-Bus path for device %s", path);
+                return -1;
+            }
+
+            pa_log_debug("Device %s found", path);
+
+            d = device_new(y, path);
+            pa_hashmap_put(y->devices, d->path, d);
+
+            /* FIXME: BlueZ 5 doesn't support the old Audio interface, and thus
+               it's not possible to know if any audio profile is about to be
+               connected. This can introduce regressions with modules such as
+               module-card-restore */
+            d->audio_state = PA_BT_AUDIO_STATE_DISCONNECTED;
+
+            if (parse_device_properties(d, &iface_i, false) < 0)
+                return -1;
+        }
+
+        dbus_message_iter_next(&element_i);
+    }
+
+    return 0;
+}
+
 static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata) {
     DBusMessage *r;
     pa_dbus_pending *p;
     pa_bluetooth_discovery *y;
+    DBusMessageIter arg_i, element_i;
 
     pa_assert_se(p = userdata);
     pa_assert_se(y = p->context_data);
@@ -911,6 +1007,23 @@ static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata)
     pa_log_info("D-Bus ObjectManager detected so assuming BlueZ version 5.");
     y->version = BLUEZ_VERSION_5;
 
+    if (!dbus_message_iter_init(r, &arg_i) || !pa_streq(dbus_message_get_signature(r), "a{oa{sa{sv}}}")) {
+        pa_log("Invalid reply signature for GetManagedObjects().");
+        goto finish;
+    }
+
+    dbus_message_iter_recurse(&arg_i, &element_i);
+    while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+        DBusMessageIter dict_i;
+
+        dbus_message_iter_recurse(&element_i, &dict_i);
+
+        /* Ignore errors here and proceed with next object */
+        parse_interfaces_and_properties(y, &dict_i);
+
+        dbus_message_iter_next(&element_i);
+    }
+
 finish:
     dbus_message_unref(r);
 

commit 2247b187393ef68879162faee8b422da9f5e556f
Author: Mikel Astiz <mikel.astiz at bmw-carit.de>
Date:   Mon Apr 29 18:27:58 2013 +0200

    bluetooth: Detect BlueZ 5
    
    Check the existence of ObjectManager to detect the version of the
    running daemon. If the interface exists, it should be BlueZ 5.

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index c15ecd1..1883c8a 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -61,12 +61,19 @@
     " </interface>"                                                     \
     "</node>"
 
+typedef enum pa_bluez_version {
+    BLUEZ_VERSION_UNKNOWN,
+    BLUEZ_VERSION_4,
+    BLUEZ_VERSION_5,
+} pa_bluez_version_t;
+
 struct pa_bluetooth_discovery {
     PA_REFCNT_DECLARE;
 
     pa_core *core;
     pa_dbus_connection *connection;
     PA_LLIST_HEAD(pa_dbus_pending, pending);
+    pa_bluez_version_t version;
     bool adapters_listed;
     pa_hashmap *devices;
     pa_hashmap *transports;
@@ -880,6 +887,46 @@ static void list_adapters(pa_bluetooth_discovery *y) {
     send_and_add_to_pending(y, m, get_properties_reply, NULL);
 }
 
+static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata) {
+    DBusMessage *r;
+    pa_dbus_pending *p;
+    pa_bluetooth_discovery *y;
+
+    pa_assert_se(p = userdata);
+    pa_assert_se(y = p->context_data);
+    pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+    if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) {
+        pa_log_info("D-Bus ObjectManager not detected so falling back to BlueZ version 4 API.");
+        y->version = BLUEZ_VERSION_4;
+        list_adapters(y);
+        goto finish;
+    }
+
+    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+        pa_log("GetManagedObjects() failed: %s: %s", dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
+        goto finish;
+    }
+
+    pa_log_info("D-Bus ObjectManager detected so assuming BlueZ version 5.");
+    y->version = BLUEZ_VERSION_5;
+
+finish:
+    dbus_message_unref(r);
+
+    PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
+    pa_dbus_pending_free(p);
+}
+
+static void init_bluez(pa_bluetooth_discovery *y) {
+    DBusMessage *m;
+    pa_assert(y);
+
+    pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.freedesktop.DBus.ObjectManager",
+                                                  "GetManagedObjects"));
+    send_and_add_to_pending(y, m, get_managed_objects_reply, NULL);
+}
+
 static int transport_parse_property(pa_bluetooth_transport *t, DBusMessageIter *i) {
     const char *key;
     DBusMessageIter variant_i;
@@ -1023,11 +1070,12 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
                 pa_log_debug("Bluetooth daemon disappeared.");
                 remove_all_devices(y);
                 y->adapters_listed = false;
+                y->version = BLUEZ_VERSION_UNKNOWN;
             }
 
             if (new_owner && *new_owner) {
                 pa_log_debug("Bluetooth daemon appeared.");
-                list_adapters(y);
+                init_bluez(y);
             }
         }
 
@@ -1710,7 +1758,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
     pa_assert_se(dbus_connection_register_object_path(conn, A2DP_SOURCE_ENDPOINT, &vtable_endpoint, y));
     pa_assert_se(dbus_connection_register_object_path(conn, A2DP_SINK_ENDPOINT, &vtable_endpoint, y));
 
-    list_adapters(y);
+    init_bluez(y);
 
     return y;
 

commit aacac20e765c8ce9176e1fd9b25a07c1bf6b6838
Author: Tanu Kaskinen <tanu.kaskinen at intel.com>
Date:   Mon Apr 29 16:46:13 2013 +0300

    module: Assign the index before calling init()
    
    Any code that runs inside the init() callback sees an invalid module
    index. Sometimes init() does things that cause hooks to be fired. This
    means that any code that uses hooks may see an invalid module index.
    Fix this by assigning the module index before init() is called.
    
    There are no known issues in the upstream code base where an invalid
    module index would be used, but an out-of-tree module
    (module-murphy-ivi) had a problem with this.
    
    BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=63923

diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index 6f276bb..f30a3ce 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -113,14 +113,14 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
     m->core = c;
     m->unload_requested = FALSE;
 
+    pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0);
+    pa_assert(m->index != PA_IDXSET_INVALID);
+
     if (m->init(m) < 0) {
         pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : "");
         goto fail;
     }
 
-    pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0);
-    pa_assert(m->index != PA_IDXSET_INVALID);
-
     pa_log_info("Loaded \"%s\" (index: #%u; argument: \"%s\").", m->name, m->index, m->argument ? m->argument : "");
 
     pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index);
@@ -144,6 +144,9 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
 fail:
 
     if (m) {
+        if (m->index != PA_IDXSET_INVALID)
+            pa_idxset_remove_by_index(c->modules, m->index);
+
         if (m->proplist)
             pa_proplist_free(m->proplist);
 

commit 6cc974844e6efde880d431cb7c4bfb7259a5bd4b
Author: João Paulo Rechi Vita <jprvita at openbossa.org>
Date:   Fri Apr 26 21:24:52 2013 -0300

    bluetooth: Remove the 'bluez.name' property
    
    The 'Name' property of the Device interface became optional in BlueZ 5
    and may not be present anymore (that happens when testing against the
    PTS 4.7.0), so it's better not to expose it to clients so they don't
    rely on its existence.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index edc2ab1..6ae3570 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -2262,7 +2262,6 @@ static int add_card(struct userdata *u) {
 
     pa_proplist_sets(data.proplist, "bluez.path", device->path);
     pa_proplist_setf(data.proplist, "bluez.class", "0x%06x", (unsigned) device->class);
-    pa_proplist_sets(data.proplist, "bluez.name", device->name);
     pa_proplist_sets(data.proplist, "bluez.alias", device->alias);
     data.name = get_name("card", u->modargs, device->address, &b);
     data.namereg_fail = b;

commit 5b48062f943ea7cd17835141f9fa0af5cf9e27af
Author: João Paulo Rechi Vita <jprvita at openbossa.org>
Date:   Fri Apr 26 21:26:36 2013 -0300

    bluetooth: Use 'Alias' value as the device description
    
    The 'Alias' property should be preffered over the 'Name' property,
    according to the BlueZ API documentation.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index c4a1fb0..edc2ab1 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -2249,7 +2249,7 @@ static int add_card(struct userdata *u) {
     data.driver = __FILE__;
     data.module = u->module;
 
-    n = pa_bluetooth_cleanup_name(device->name);
+    n = pa_bluetooth_cleanup_name(device->alias);
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, n);
     pa_xfree(n);
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, device->address);

commit 6e73c05cbcc877f9c02bf54ea367a79dff4acca0
Author: João Paulo Rechi Vita <jprvita at openbossa.org>
Date:   Fri Apr 26 12:30:24 2013 -0300

    bluetooth: Add 'bluez.alias' property

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 260c3d8..c4a1fb0 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -2263,6 +2263,7 @@ static int add_card(struct userdata *u) {
     pa_proplist_sets(data.proplist, "bluez.path", device->path);
     pa_proplist_setf(data.proplist, "bluez.class", "0x%06x", (unsigned) device->class);
     pa_proplist_sets(data.proplist, "bluez.name", device->name);
+    pa_proplist_sets(data.proplist, "bluez.alias", device->alias);
     data.name = get_name("card", u->modargs, device->address, &b);
     data.namereg_fail = b;
 

commit fee5a29e330ba89ed991e5d9f15d1a7df27c7f70
Author: Peter Meerwald <p.meerwald at bct-electronic.com>
Date:   Wed Apr 17 09:49:03 2013 +0200

    .gitignore: Ignore src/*.gcno files created when building with --enable-gcov
    
    Signed-off-by: Peter Meerwald <p.meerwald at bct-electronic.com>

diff --git a/src/.gitignore b/src/.gitignore
index cd9c51a..24046ff 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -2,6 +2,7 @@ TAGS
 *.lo
 *.o
 *.la
+*.gcno
 .deps
 .libs
 /Makefile

commit f8101279bb48a9b5cd2f9aa9b2d5020984312433
Author: poljar (Damir Jelić) <poljarinho at gmail.com>
Date:   Fri Apr 19 16:35:14 2013 +0200

    switch-on-port-available: Silence gcc warning.
    
    This silences this gcc warning:
    module-switch-on-port-available.c:111:12: warning:
        'good' may be used uninitialized in this function

diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c
index 819835d..35cecea 100644
--- a/src/modules/module-switch-on-port-available.c
+++ b/src/modules/module-switch-on-port-available.c
@@ -94,7 +94,7 @@ static int try_to_switch_profile(pa_device_port *port) {
     pa_log_debug("Finding best profile");
 
     PA_HASHMAP_FOREACH(profile, port->profiles, state) {
-        bool good;
+        bool good = false;
 
         if (best_profile && best_profile->priority >= profile->priority)
             continue;

commit 55571f89998aca68058803b080c594e227b6cc29
Author: Ismo Puustinen <ismo.puustinen at intel.com>
Date:   Fri Apr 12 17:38:12 2013 +0300

    module: initialize module index to invalid value.
    
    m->init() was called while m->index was uninitialized, which was bad
    style.

diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index 268d85d..6f276bb 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -64,6 +64,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
     m->argument = pa_xstrdup(argument);
     m->load_once = FALSE;
     m->proplist = pa_proplist_new();
+    m->index = PA_IDXSET_INVALID;
 
     if (!(m->dl = lt_dlopenext(name))) {
         /* We used to print the error that is returned by lt_dlerror(), but

commit 8336078afac0375f14a3ef91e8a3d31655ea5ff4
Author: Peter Meerwald <p.meerwald at bct-electronic.com>
Date:   Tue Apr 9 16:10:16 2013 +0200

    fdsem: Remember pa_write() type in pa_fdsem_post()
    
    pa_write() knows two types of operation:
    calling send() and calling write()
    
    there is a flag (a pointer to an int) passed to pa_write()
    which can remember which write type was successful
    
    if the pointer is NULL or the int is 0, send() is tried first,
    with a fallback to write() if send() resulted in ENOTSOCK
    
    pa_fdsem_post() calls pa_write() with a NULL pointer;
    unfortunately (at least with HAVE_SYS_EVENTFD_H #define'd) send()
    always fails here and write() is called -- causing an extra syscall
    quite frequently
    
    strace:
    send(17, "\1\0\0\0\0\0\0\0", 8, MSG_NOSIGNAL) = -1 ENOTSOCK (Socket operation on non-socket)
    write(17, "\1\0\0\0\0\0\0\0", 8) = 8
    
    the patch adds a write_type field to pa_fdsem to the successful
    pa_write() type can be remembered and unnecessary send() calls are
    avoided
    
    Signed-off-by: Peter Meerwald <p.meerwald at bct-electronic.com>

diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c
index 14fcbd6..ec32461 100644
--- a/src/pulsecore/fdsem.c
+++ b/src/pulsecore/fdsem.c
@@ -52,14 +52,14 @@ struct pa_fdsem {
 #ifdef HAVE_SYS_EVENTFD_H
     int efd;
 #endif
-
+    int write_type;
     pa_fdsem_data *data;
 };
 
 pa_fdsem *pa_fdsem_new(void) {
     pa_fdsem *f;
 
-    f = pa_xmalloc(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data)));
+    f = pa_xmalloc0(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data)));
 
 #ifdef HAVE_SYS_EVENTFD_H
     if ((f->efd = eventfd(0, EFD_CLOEXEC)) >= 0)
@@ -89,7 +89,7 @@ pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd) {
     pa_assert(event_fd >= 0);
 
 #ifdef HAVE_SYS_EVENTFD_H
-    f = pa_xnew(pa_fdsem, 1);
+    f = pa_xnew0(pa_fdsem, 1);
 
     f->efd = event_fd;
     pa_make_fd_cloexec(f->efd);
@@ -108,7 +108,7 @@ pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd) {
 
 #ifdef HAVE_SYS_EVENTFD_H
 
-    f = pa_xnew(pa_fdsem, 1);
+    f = pa_xnew0(pa_fdsem, 1);
 
     if ((f->efd = eventfd(0, EFD_CLOEXEC)) < 0) {
         pa_xfree(f);
@@ -196,7 +196,7 @@ void pa_fdsem_post(pa_fdsem *f) {
                 if (f->efd >= 0) {
                     uint64_t u = 1;
 
-                    if ((r = pa_write(f->efd, &u, sizeof(u), NULL)) != sizeof(u)) {
+                    if ((r = pa_write(f->efd, &u, sizeof(u), &f->write_type)) != sizeof(u)) {
                         if (r >= 0 || errno != EINTR) {
                             pa_log_error("Invalid write to eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
                             pa_assert_not_reached();
@@ -207,7 +207,7 @@ void pa_fdsem_post(pa_fdsem *f) {
                 } else
 #endif
 
-                if ((r = pa_write(f->fds[1], &x, 1, NULL)) != 1) {
+                if ((r = pa_write(f->fds[1], &x, 1, &f->write_type)) != 1) {
                     if (r >= 0 || errno != EINTR) {
                         pa_log_error("Invalid write to pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
                         pa_assert_not_reached();

commit 5237bab36e22341dd25a5aa61f906748779c750e
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Jun 13 12:45:16 2012 +0300

    pasuspender: Resume before exiting in case of SIGINT or fork() failure.
    
    Pressing Ctrl-C in a terminal while pasuspender is running
    causes the sinks and sources to stay suspended after
    pasuspender has exited, which is very annoying. This patch
    fixes that problem, and also a similar problem with fork()
    failures.

diff --git a/src/utils/pasuspender.c b/src/utils/pasuspender.c
index 5825191..0da570e 100644
--- a/src/utils/pasuspender.c
+++ b/src/utils/pasuspender.c
@@ -52,6 +52,7 @@ static int child_argc = 0;
 static pid_t child_pid = (pid_t) -1;
 static int child_ret = 0;
 static int dead = 1;
+static int fork_failed = 0;
 
 static void quit(int ret) {
     pa_assert(mainloop_api);
@@ -66,18 +67,22 @@ static void context_drain_complete(pa_context *c, void *userdata) {
 static void drain(void) {
     pa_operation *o;
 
-    if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
-        pa_context_disconnect(context);
-    else
-        pa_operation_unref(o);
+    if (context) {
+        if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
+            pa_context_disconnect(context);
+        else
+            pa_operation_unref(o);
+    } else
+        quit(0);
 }
 
-static void start_child(void) {
+static int start_child(void) {
 
     if ((child_pid = fork()) < 0) {
-
         fprintf(stderr, _("fork(): %s\n"), strerror(errno));
-        quit(1);
+        fork_failed = 1;
+
+        return -1;
 
     } else if (child_pid == 0) {
         /* Child */
@@ -96,36 +101,59 @@ static void start_child(void) {
         /* parent */
         dead = 0;
     }
+
+    return 0;
 }
 
-static void suspend_complete(pa_context *c, int success, void *userdata) {
+static void resume_complete(pa_context *c, int success, void *userdata) {
     static int n = 0;
 
     n++;
 
     if (!success) {
-        fprintf(stderr, _("Failure to suspend: %s\n"), pa_strerror(pa_context_errno(c)));
+        fprintf(stderr, _("Failure to resume: %s\n"), pa_strerror(pa_context_errno(c)));
         quit(1);
         return;
     }
 
     if (n >= 2)
-        start_child();
+        drain(); /* drain and quit */
 }
 
-static void resume_complete(pa_context *c, int success, void *userdata) {
+static void resume(void) {
+    static int n = 0;
+
+    n++;
+
+    if (n > 1)
+        return;
+
+    if (context) {
+        if (pa_context_is_local(context)) {
+            pa_operation_unref(pa_context_suspend_sink_by_index(context, PA_INVALID_INDEX, 0, resume_complete, NULL));
+            pa_operation_unref(pa_context_suspend_source_by_index(context, PA_INVALID_INDEX, 0, resume_complete, NULL));
+        } else
+            drain();
+    } else {
+        quit(0);
+    }
+}
+
+static void suspend_complete(pa_context *c, int success, void *userdata) {
     static int n = 0;
 
     n++;
 
     if (!success) {
-        fprintf(stderr, _("Failure to resume: %s\n"), pa_strerror(pa_context_errno(c)));
+        fprintf(stderr, _("Failure to suspend: %s\n"), pa_strerror(pa_context_errno(c)));
         quit(1);
         return;
     }
 
-    if (n >= 2)
-        drain(); /* drain and quit */
+    if (n >= 2) {
+        if (start_child() < 0)
+            resume();
+    }
 }
 
 static void context_state_callback(pa_context *c, void *userdata) {
@@ -143,7 +171,8 @@ static void context_state_callback(pa_context *c, void *userdata) {
                 pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, 1, suspend_complete, NULL));
             } else {
                 fprintf(stderr, _("WARNING: Sound server is not local, not suspending.\n"));
-                start_child();
+                if (start_child() < 0)
+                    drain();
             }
 
             break;
@@ -159,10 +188,11 @@ static void context_state_callback(pa_context *c, void *userdata) {
             pa_context_unref(context);
             context = NULL;
 
-            if (child_pid == (pid_t) -1)
+            if (child_pid == (pid_t) -1) {
                 /* not started yet, then we do it now */
-                start_child();
-            else if (dead)
+                if (start_child() < 0)
+                    quit(1);
+            } else if (dead)
                 /* already started, and dead, so let's quit */
                 quit(1);
 
@@ -172,7 +202,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
 
 static void sigint_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
     fprintf(stderr, _("Got SIGINT, exiting.\n"));
-    quit(0);
+    resume();
 }
 
 static void sigchld_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
@@ -193,16 +223,7 @@ static void sigchld_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, vo
         child_ret = 1;
     }
 
-    if (context) {
-        if (pa_context_is_local(context)) {
-            /* A context is around, so let's resume */
-            pa_operation_unref(pa_context_suspend_sink_by_index(context, PA_INVALID_INDEX, 0, resume_complete, NULL));
-            pa_operation_unref(pa_context_suspend_source_by_index(context, PA_INVALID_INDEX, 0, resume_complete, NULL));
-        } else
-            drain();
-    } else
-        /* Hmm, no context here, so let's terminate right away */
-        quit(0);
+    resume();
 }
 
 static void help(const char *argv0) {
@@ -303,6 +324,9 @@ int main(int argc, char *argv[]) {
         goto quit;
     }
 
+    if (ret == 0 && fork_failed)
+        ret = 1;
+
 quit:
     if (context)
         pa_context_unref(context);

commit a7567b78e33a473df7d5a2ce21f8b1803faa57c5
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Mar 29 17:17:14 2013 +0200

    alsa: Don't use pa_strna() for port names
    
    The name variable is never NULL, so there's no point in using
    pa_strna().

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index d8ff621..3b55e82 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -691,7 +691,7 @@ static void ucm_add_port_combination(
         pa_device_port_new_data port_data;
 
         pa_device_port_new_data_init(&port_data);
-        pa_device_port_new_data_set_name(&port_data, pa_strna(name));
+        pa_device_port_new_data_set_name(&port_data, name);
         pa_device_port_new_data_set_description(&port_data, desc);
         pa_device_port_new_data_set_direction(&port_data, is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
 

commit 1fd79c443953892e3c7013d35d1f5ee7aeb413c1
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Jun 13 11:15:03 2012 +0300

    core-util: Don't accept random words in pa_parse_boolean()
    
    The old code accepted any word that started with "y", "Y",
    "n", "N", "t", "T", "f" or "F". Fix this by having
    a whitelist of full strings instead of checking just the
    first letter.

diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 96cf4e8..da8266b 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -937,9 +937,11 @@ int pa_parse_boolean(const char *v) {
     pa_assert(v);
 
     /* First we check language independent */
-    if (pa_streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
+    if (pa_streq(v, "1") || !strcasecmp(v, "y") || !strcasecmp(v, "t")
+            || !strcasecmp(v, "yes") || !strcasecmp(v, "true") || !strcasecmp(v, "on"))
         return 1;
-    else if (pa_streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
+    else if (pa_streq(v, "0") || !strcasecmp(v, "n") || !strcasecmp(v, "f")
+                 || !strcasecmp(v, "no") || !strcasecmp(v, "false") || !strcasecmp(v, "off"))
         return 0;
 
 #ifdef HAVE_LANGINFO_H

commit 0af05213be59e54934c79d7be77b5d2dfb6fe6e7
Author: Peter Meerwald <p.meerwald at bct-electronic.com>
Date:   Thu Mar 28 14:46:48 2013 +0100

    build: make ARM NEON check in configure.ac more strict
    
    the check for NEON so far only checked if -mfpu=neon is understood by the compiler,
    however, this is not enough:
    
    (i) #include <arm_neon.h> should be checked
    (ii) -mfpu=neon must be passed before CFLAGS because eventually the per-library CFLAGS
    for NEON code in src/Makefile.am are passed to the compiler before the global CFLAGS
    
    in case the build environment passes CFLAGS to configure and we try to set conflicting
    CFLAGS option, the former take precedence; CFLAGS cannot be overridden
    
    this does not fix
    http://lists.freedesktop.org/archives/pulseaudio-discuss/2012-December/015570.html
    but at least makes the build fail in configure (and not while compiling stuff)
    and gives better diagnostics
    
    Signed-off-by: Peter Meerwald <p.meerwald at bct-electronic.com>

diff --git a/configure.ac b/configure.ac
index 9e33bb9..5f2398d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -321,9 +321,9 @@ AC_ARG_ENABLE([neon-opt],
     AS_HELP_STRING([--enable-neon-opt], [Enable NEON optimisations on ARM CPUs that support it]))
 
 AS_IF([test "x$enable_neon_opt" != "xno"],
-    [save_CFLAGS="$CFLAGS"; CFLAGS="$CFLAGS -mfpu=neon"
+    [save_CFLAGS="$CFLAGS"; CFLAGS="-mfpu=neon $CFLAGS"
      AC_COMPILE_IFELSE(
-        AC_LANG_PROGRAM([], []),
+        AC_LANG_PROGRAM([[#include <arm_neon.h>]], []),
         [
          HAVE_NEON=1
          NEON_CFLAGS="-mfpu=neon"
@@ -337,7 +337,7 @@ AS_IF([test "x$enable_neon_opt" != "xno"],
     [HAVE_NEON=0])
 
 AS_IF([test "x$enable_neon_opt" = "xyes" && test "x$HAVE_NEON" = "x0"],
-      [AC_MSG_ERROR([*** Compiler does not support -mfpu=neon])])
+      [AC_MSG_ERROR([*** Compiler does not support -mfpu=neon or CFLAGS override -mfpu])])
 
 AC_SUBST(HAVE_NEON)
 AC_SUBST(NEON_CFLAGS)

commit 80b0e285a5a2651f1cf43af32db3b0c583f99fda
Author: poljar (Damir Jelić) <poljarinho at gmail.com>
Date:   Thu Mar 28 12:05:11 2013 +0100

    device-port: Introduce pa_device_port_new_data
    
    Port creation is now slightly different. It is now similar to how
    other objects are created (e.g. sinks/sources/cards).
    
    This should become more useful in the future when we move more stuff to
    the ports.
    
    Functionally nothing has changed.

diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 252858b..be9ee4e 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -4496,11 +4496,15 @@ static pa_device_port* device_port_alsa_init(pa_hashmap *ports, /* card ports */
 
     if (!p) {
         pa_alsa_port_data *data;
-        pa_direction_t direction;
+        pa_device_port_new_data port_data;
 
-        direction = path->direction == PA_ALSA_DIRECTION_OUTPUT ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
+        pa_device_port_new_data_init(&port_data);
+        pa_device_port_new_data_set_name(&port_data, name);
+        pa_device_port_new_data_set_description(&port_data, description);
+        pa_device_port_new_data_set_direction(&port_data, path->direction == PA_ALSA_DIRECTION_OUTPUT ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
 
-        p = pa_device_port_new(core, name, description, direction, sizeof(pa_alsa_port_data));
+        p = pa_device_port_new(core, &port_data, sizeof(pa_alsa_port_data));
+        pa_device_port_new_data_done(&port_data);
         pa_assert(p);
         pa_hashmap_put(ports, p->name, p);
         pa_proplist_update(p->proplist, PA_UPDATE_REPLACE, path->proplist);
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index a6de7aa..d8ff621 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -688,7 +688,15 @@ static void ucm_add_port_combination(
 
     port = pa_hashmap_get(ports, name);
     if (!port) {
-        port = pa_device_port_new(core, pa_strna(name), desc, is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT, 0);
+        pa_device_port_new_data port_data;
+
+        pa_device_port_new_data_init(&port_data);
+        pa_device_port_new_data_set_name(&port_data, pa_strna(name));
+        pa_device_port_new_data_set_description(&port_data, desc);
+        pa_device_port_new_data_set_direction(&port_data, is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
+
+        port = pa_device_port_new(core, &port_data, 0);
+        pa_device_port_new_data_done(&port_data);
         pa_assert(port);
 
         pa_hashmap_put(ports, port->name, port);
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 9fa923a..260c3d8 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -2070,6 +2070,8 @@ off:
 /* Run from main thread */
 static void create_card_ports(struct userdata *u, pa_hashmap *ports) {
     pa_device_port *port;
+    pa_device_port_new_data port_data;
+
     const char *name_prefix = NULL;
     const char *input_description = NULL;
     const char *output_description = NULL;
@@ -2140,13 +2142,23 @@ static void create_card_ports(struct userdata *u, pa_hashmap *ports) {
     u->output_port_name = pa_sprintf_malloc("%s-output", name_prefix);
     u->input_port_name = pa_sprintf_malloc("%s-input", name_prefix);
 
-    pa_assert_se(port = pa_device_port_new(u->core, u->output_port_name, output_description, PA_DIRECTION_OUTPUT, 0));
+    pa_device_port_new_data_init(&port_data);
+    pa_device_port_new_data_set_name(&port_data, u->output_port_name);
+    pa_device_port_new_data_set_description(&port_data, output_description);
+    pa_device_port_new_data_set_direction(&port_data, PA_DIRECTION_OUTPUT);
+    pa_device_port_new_data_set_available(&port_data, get_port_availability(u, PA_DIRECTION_OUTPUT));
+    pa_assert_se(port = pa_device_port_new(u->core, &port_data, 0));
     pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0);
-    port->available = get_port_availability(u, PA_DIRECTION_OUTPUT);
-
-    pa_assert_se(port = pa_device_port_new(u->core, u->input_port_name, input_description, PA_DIRECTION_OUTPUT, 0));
+    pa_device_port_new_data_done(&port_data);
+
+    pa_device_port_new_data_init(&port_data);
+    pa_device_port_new_data_set_name(&port_data, u->input_port_name);
+    pa_device_port_new_data_set_description(&port_data, output_description);
+    pa_device_port_new_data_set_direction(&port_data, PA_DIRECTION_INPUT);
+    pa_device_port_new_data_set_available(&port_data, get_port_availability(u, PA_DIRECTION_INPUT));
+    pa_assert_se(port = pa_device_port_new(u->core, &port_data, 0));
     pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0);
-    port->available = get_port_availability(u, PA_DIRECTION_INPUT);
+    pa_device_port_new_data_done(&port_data);
 }
 
 /* Run from main thread */
diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c
index acfc44d..9660702 100644
--- a/src/pulsecore/device-port.c
+++ b/src/pulsecore/device-port.c
@@ -26,6 +26,47 @@
 
 PA_DEFINE_PUBLIC_CLASS(pa_device_port, pa_object);
 
+pa_device_port_new_data *pa_device_port_new_data_init(pa_device_port_new_data *data) {
+    pa_assert(data);
+
+    pa_zero(*data);
+    data->available = PA_AVAILABLE_UNKNOWN;
+    return data;
+}
+
+void pa_device_port_new_data_set_name(pa_device_port_new_data *data, const char *name) {
+    pa_assert(data);
+
+    pa_xfree(data->name);
+    data->name = pa_xstrdup(name);
+}
+
+void pa_device_port_new_data_set_description(pa_device_port_new_data *data, const char *description) {
+    pa_assert(data);
+
+    pa_xfree(data->description);
+    data->description = pa_xstrdup(description);
+}
+
+void pa_device_port_new_data_set_available(pa_device_port_new_data *data, pa_available_t available) {
+    pa_assert(data);
+
+    data->available = available;
+}
+
+void pa_device_port_new_data_set_direction(pa_device_port_new_data *data, pa_direction_t direction) {
+    pa_assert(data);
+
+    data->direction = direction;
+}
+
+void pa_device_port_new_data_done(pa_device_port_new_data *data) {
+    pa_assert(data);
+
+    pa_xfree(data->name);
+    pa_xfree(data->description);
+}
+
 void pa_device_port_set_available(pa_device_port *p, pa_available_t status)
 {
     pa_core *core;
@@ -66,23 +107,27 @@ static void device_port_free(pa_object *o) {
 }
 
 
-pa_device_port *pa_device_port_new(pa_core *c, const char *name, const char *description, pa_direction_t direction, size_t extra) {
+pa_device_port *pa_device_port_new(pa_core *c, pa_device_port_new_data *data, size_t extra) {
     pa_device_port *p;
 
-    pa_assert(name);
+    pa_assert(data);
+    pa_assert(data->name);
+    pa_assert(data->direction == PA_DIRECTION_OUTPUT || data->direction == PA_DIRECTION_INPUT);
 
     p = PA_DEVICE_PORT(pa_object_new_internal(PA_ALIGN(sizeof(pa_device_port)) + extra, pa_device_port_type_id, pa_device_port_check_type));
     p->parent.free = device_port_free;
 
-    p->core = c;
-    p->name = pa_xstrdup(name);
-    p->description = pa_xstrdup(description);
+    p->name = data->name;
+    data->name = NULL;
+    p->description = data->description;
+    data->description = NULL;
     p->core = c;
     p->card = NULL;
     p->priority = 0;
-    p->available = PA_AVAILABLE_UNKNOWN;
+    p->available = data->available;
     p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-    p->direction = direction;
+    p->direction = data->direction;
+
     p->latency_offset = 0;
     p->proplist = pa_proplist_new();
 
diff --git a/src/pulsecore/device-port.h b/src/pulsecore/device-port.h
index 7f21072..b10d554 100644
--- a/src/pulsecore/device-port.h
+++ b/src/pulsecore/device-port.h
@@ -62,7 +62,21 @@ PA_DECLARE_PUBLIC_CLASS(pa_device_port);
 
 #define PA_DEVICE_PORT_DATA(d) ((void*) ((uint8_t*) d + PA_ALIGN(sizeof(pa_device_port))))
 
-pa_device_port *pa_device_port_new(pa_core *c, const char *name, const char *description, pa_direction_t direction, size_t extra);
+typedef struct pa_device_port_new_data {
+    char *name;
+    char *description;
+    pa_available_t available;
+    pa_direction_t direction;
+} pa_device_port_new_data;
+
+pa_device_port_new_data *pa_device_port_new_data_init(pa_device_port_new_data *data);
+void pa_device_port_new_data_set_name(pa_device_port_new_data *data, const char *name);
+void pa_device_port_new_data_set_description(pa_device_port_new_data *data, const char *description);
+void pa_device_port_new_data_set_available(pa_device_port_new_data *data, pa_available_t available);
+void pa_device_port_new_data_set_direction(pa_device_port_new_data *data, pa_direction_t direction);
+void pa_device_port_new_data_done(pa_device_port_new_data *data);
+
+pa_device_port *pa_device_port_new(pa_core *c, pa_device_port_new_data *data, size_t extra);
 
 /* The port's available status has changed */
 void pa_device_port_set_available(pa_device_port *p, pa_available_t available);

commit 23f88ecb84ef5ecccbdd45fbab55e52f602e4795
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Thu Mar 28 10:49:27 2013 +0200

    .gitignore: Update m4/ ignore list

diff --git a/m4/.gitignore b/m4/.gitignore
index f883d98..db94175 100644
--- a/m4/.gitignore
+++ b/m4/.gitignore
@@ -1,16 +1,37 @@
+codeset.m4
+fcntl-o.m4
 gettext.m4
+glibc2.m4
+glibc21.m4
 iconv.m4
+intdiv0.m4
+intl.m4
+intldir.m4
+intlmacosx.m4
+intltool.m4
+intmax.m4
+inttypes-pri.m4
+inttypes_h.m4
+lcmessage.m4
 lib-ld.m4
 lib-link.m4
 lib-prefix.m4
-nls.m4
-po.m4
-progtest.m4
-argz.m4
-intltool.m4
 libtool.m4
-ltdl.m4
+lock.m4
+longlong.m4
 ltoptions.m4
 ltsugar.m4
 ltversion.m4
 lt~obsolete.m4
+nls.m4
+po.m4
+printf-posix.m4
+progtest.m4
+size_max.m4
+stdint_h.m4
+threadlib.m4
+uintmax_t.m4
+visibility.m4
+wchar_t.m4
+wint_t.m4
+xsize.m4

commit b2a0a1575d51621158d914bfef433d893fcf476b
Author: Javier Jardón <jjardon at gnome.org>
Date:   Wed Mar 27 14:00:36 2013 +0000

    build-sys: Use upstream gettext instead glib one

diff --git a/Makefile.am b/Makefile.am
index b0b2553..2a1e9f6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -19,6 +19,7 @@ ACLOCAL_AMFLAGS = -I m4
 
 EXTRA_DIST = \
 	bootstrap.sh \
+	config.rpath \
 	git-version-gen \
 	LICENSE \
 	GPL \
diff --git a/bootstrap.sh b/bootstrap.sh
index d0baf95..0ad60ca 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -68,7 +68,6 @@ if ! pkg-config --version &>/dev/null; then
 fi
 
 # Other necessary programs
-glib-gettextize --version >/dev/null || DIE=1
 intltoolize --version >/dev/null || DIE=1
 $LIBTOOLIZE --version >/dev/null || DIE=1
 test "$DIE" = 1 && exit 1
@@ -85,7 +84,7 @@ else
     rm -f config.cache
 
     rm -f Makefile.am~ configure.ac~
-    glib-gettextize --copy --force
+    autopoint --force
     test -f Makefile.am~ && mv Makefile.am~ Makefile.am
     test -f configure.ac~ && mv configure.ac~ configure.ac
 
diff --git a/configure.ac b/configure.ac
index 388fae2..9e33bb9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -97,10 +97,13 @@ PKG_PROG_PKG_CONFIG
 
 if test "x$enable_nls" != "xno"; then
 IT_PROG_INTLTOOL([0.35.0])
+
+AM_GNU_GETTEXT_VERSION([0.18.1])
+AM_GNU_GETTEXT([external])
+
 GETTEXT_PACKAGE=pulseaudio
 AC_SUBST([GETTEXT_PACKAGE])
 AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE],["$GETTEXT_PACKAGE"],[Gettext package])
-AM_GLIB_GNU_GETTEXT
 
 pulselocaledir='${prefix}/${DATADIRNAME}/locale'
 AX_DEFINE_DIR(PULSE_LOCALEDIR, pulselocaledir, [Gettext locale dir])

commit 3086d01dd72009ec9170405a4285f3a3c66c95c1
Author: João Paulo Rechi Vita <jprvita at openbossa.org>
Date:   Wed Mar 27 10:45:33 2013 -0300

    bluetooth: Improve code and log readability
    
    This commit makes the code cleaner, avoiding unnecessary line breaks. It
    also changes the debug message elements order, to make it look more
    natural ("path, interface, member" instead of "interface, path,
    member").

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 81cbf9b..c15ecd1 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1606,16 +1606,16 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi
     struct pa_bluetooth_discovery *y = userdata;
     DBusMessage *r = NULL;
     DBusError e;
-    const char *path;
+    const char *path, *interface, *member;
 
     pa_assert(y);
 
-    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));
-
     path = dbus_message_get_path(m);
+    interface = dbus_message_get_interface(m);
+    member = dbus_message_get_member(m);
+
+    pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
+
     dbus_error_init(&e);
 
     if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT) && !pa_streq(path, HFP_AG_ENDPOINT) &&
@@ -1626,10 +1626,7 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi
         const char *xml = ENDPOINT_INTROSPECT_XML;
 
         pa_assert_se(r = dbus_message_new_method_return(m));
-        pa_assert_se(dbus_message_append_args(
-                                 r,
-                                 DBUS_TYPE_STRING, &xml,
-                                 DBUS_TYPE_INVALID));
+        pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
 
     } else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "SetConfiguration"))
         r = endpoint_set_configuration(c, m, userdata);

commit 3ef024831cdaf629186216cf2af89592f16f73d7
Author: João Paulo Rechi Vita <jprvita at openbossa.org>
Date:   Wed Mar 27 10:45:32 2013 -0300

    bluetooth: Remove unnecessary braces

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 5e4b77b..81cbf9b 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -1631,11 +1631,11 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi
                                  DBUS_TYPE_STRING, &xml,
                                  DBUS_TYPE_INVALID));
 
-    } else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "SetConfiguration")) {
+    } else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "SetConfiguration"))
         r = endpoint_set_configuration(c, m, userdata);
-    } else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "SelectConfiguration")) {
+    else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "SelectConfiguration"))
         r = endpoint_select_configuration(c, m, userdata);
-    } else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "ClearConfiguration"))
+    else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "ClearConfiguration"))
         r = endpoint_clear_configuration(c, m, userdata);
     else
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;

commit 0bbef56976496084bf04cee703a6b7e62d457cb3
Author: Tanu Kaskinen <tanu.kaskinen at digia.com>
Date:   Fri Jun 29 18:04:59 2012 +0300

    device-port: Make it impossible to have dual-direction ports

diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index f93ddbb..252858b 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -2850,12 +2850,41 @@ void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mix
         pa_alsa_path_set_callback(p, m, cb, userdata);
 }
 
+static pa_alsa_path *profile_set_get_path(pa_alsa_profile_set *ps, const char *path_name) {
+    pa_alsa_path *path;
+
+    pa_assert(ps);
+    pa_assert(path_name);
+
+    if ((path = pa_hashmap_get(ps->output_paths, path_name)))
+        return path;
+
+    return pa_hashmap_get(ps->input_paths, path_name);
+}
+
+static void profile_set_add_path(pa_alsa_profile_set *ps, pa_alsa_path *path) {
+    pa_assert(ps);
+    pa_assert(path);
+
+    switch (path->direction) {
+        case PA_ALSA_DIRECTION_OUTPUT:
+            pa_assert_se(pa_hashmap_put(ps->output_paths, path->name, path) >= 0);
+            break;
+
+        case PA_ALSA_DIRECTION_INPUT:
+            pa_assert_se(pa_hashmap_put(ps->input_paths, path->name, path) >= 0);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+}
+
 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
     pa_alsa_path_set *ps;
     char **pn = NULL, **en = NULL, **ie;
     pa_alsa_decibel_fix *db_fix;
     void *state, *state2;
-    pa_hashmap *cache;
 
     pa_assert(m);
     pa_assert(m->profile_set);
@@ -2869,14 +2898,10 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d
     ps->direction = direction;
     ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
-    if (direction == PA_ALSA_DIRECTION_OUTPUT) {
+    if (direction == PA_ALSA_DIRECTION_OUTPUT)
         pn = m->output_path_names;
-        cache = m->profile_set->output_paths;
-    }
-    else if (direction == PA_ALSA_DIRECTION_INPUT) {
+    else
         pn = m->input_path_names;
-        cache = m->profile_set->input_paths;
-    }
 
     if (pn) {
         char **in;
@@ -2895,15 +2920,21 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d
             if (duplicate)
                 continue;
 
-            p = pa_hashmap_get(cache, *in);
+            p = profile_set_get_path(m->profile_set, *in);
+
+            if (p && p->direction != direction) {
+                pa_log("Configuration error: Path %s is used both as an input and as an output path.", p->name);
+                goto fail;
+            }
+
             if (!p) {
                 char *fn = pa_sprintf_malloc("%s.conf", *in);
                 p = pa_alsa_path_new(paths_dir, fn, direction);
                 pa_xfree(fn);
                 if (p)
-                    pa_hashmap_put(cache, *in, p);
+                    profile_set_add_path(m->profile_set, p);
             }
-            pa_assert(pa_hashmap_get(cache, *in) == p);
+
             if (p)
                 pa_hashmap_put(ps->paths, p, p);
 
@@ -2914,13 +2945,11 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d
 
     if (direction == PA_ALSA_DIRECTION_OUTPUT)
         en = m->output_element;
-    else if (direction == PA_ALSA_DIRECTION_INPUT)
+    else
         en = m->input_element;
 
-    if (!en) {
-        pa_alsa_path_set_free(ps);
-        return NULL;
-    }
+    if (!en)
+        goto fail;
 
     for (ie = en; *ie; ie++) {
         char **je;
@@ -2972,6 +3001,12 @@ finish:
     }
 
     return ps;
+
+fail:
+    if (ps)
+        pa_alsa_path_set_free(ps);
+
+    return NULL;
 }
 
 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
@@ -4444,13 +4479,13 @@ void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *ps) {
     }
 }
 
-static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
+static pa_device_port* device_port_alsa_init(pa_hashmap *ports, /* card ports */
     const char* name,
     const char* description,
     pa_alsa_path *path,
     pa_alsa_setting *setting,
     pa_card_profile *cp,
-    pa_hashmap *extra,
+    pa_hashmap *extra, /* sink/source ports */
     pa_core *core) {
 
     pa_device_port *p;
@@ -4461,8 +4496,11 @@ static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
 
     if (!p) {
         pa_alsa_port_data *data;
+        pa_direction_t direction;
 
-        p = pa_device_port_new(core, name, description, sizeof(pa_alsa_port_data));
+        direction = path->direction == PA_ALSA_DIRECTION_OUTPUT ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
+
+        p = pa_device_port_new(core, name, description, direction, sizeof(pa_alsa_port_data));
         pa_assert(p);
         pa_hashmap_put(ports, p->name, p);
         pa_proplist_update(p->proplist, PA_UPDATE_REPLACE, path->proplist);
@@ -4473,9 +4511,6 @@ static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
         path->port = p;
     }
 
-    p->is_input |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_INPUT;
-    p->is_output |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_OUTPUT;
-
     if (cp)
         pa_hashmap_put(p->profiles, cp->name, cp);
 
@@ -4490,8 +4525,8 @@ static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
 void pa_alsa_path_set_add_ports(
         pa_alsa_path_set *ps,
         pa_card_profile *cp,
-        pa_hashmap *ports,
-        pa_hashmap *extra,
+        pa_hashmap *ports, /* card ports */
+        pa_hashmap *extra, /* sink/source ports */
         pa_core *core) {
 
     pa_alsa_path *path;
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index f69ee89..a6de7aa 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -688,7 +688,7 @@ static void ucm_add_port_combination(
 
     port = pa_hashmap_get(ports, name);
     if (!port) {
-        port = pa_device_port_new(core, pa_strna(name), desc, 0);
+        port = pa_device_port_new(core, pa_strna(name), desc, is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT, 0);
         pa_assert(port);
 
         pa_hashmap_put(ports, port->name, port);
@@ -697,10 +697,6 @@ static void ucm_add_port_combination(
     }
 
     port->priority = priority;
-    if (is_sink)
-        port->is_output = TRUE;
-    else
-        port->is_input = TRUE;
 
     pa_xfree(name);
     pa_xfree(desc);
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index fd6739d..9fa923a 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -2140,16 +2140,12 @@ static void create_card_ports(struct userdata *u, pa_hashmap *ports) {
     u->output_port_name = pa_sprintf_malloc("%s-output", name_prefix);
     u->input_port_name = pa_sprintf_malloc("%s-input", name_prefix);
 
-    pa_assert_se(port = pa_device_port_new(u->core, u->output_port_name, output_description, 0));
+    pa_assert_se(port = pa_device_port_new(u->core, u->output_port_name, output_description, PA_DIRECTION_OUTPUT, 0));
     pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0);
-    port->is_output = 1;
-    port->is_input = 0;
     port->available = get_port_availability(u, PA_DIRECTION_OUTPUT);
 
-    pa_assert_se(port = pa_device_port_new(u->core, u->input_port_name, input_description, 0));
+    pa_assert_se(port = pa_device_port_new(u->core, u->input_port_name, input_description, PA_DIRECTION_OUTPUT, 0));
     pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0);
-    port->is_output = 0;
-    port->is_input = 1;
     port->available = get_port_availability(u, PA_DIRECTION_INPUT);
 }
 
diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c
index a487e04..819835d 100644
--- a/src/modules/module-switch-on-port-available.c
+++ b/src/modules/module-switch-on-port-available.c
@@ -94,14 +94,13 @@ static int try_to_switch_profile(pa_device_port *port) {
     pa_log_debug("Finding best profile");
 
     PA_HASHMAP_FOREACH(profile, port->profiles, state) {
-        pa_direction_t direction = port->is_output ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
         bool good;
 
         if (best_profile && best_profile->priority >= profile->priority)
             continue;
 
         /* We make a best effort to keep other direction unchanged */
-        switch (direction) {
+        switch (port->direction) {
             case PA_DIRECTION_OUTPUT:
                 good = profile_good_for_output(profile);
                 break;
@@ -136,15 +135,19 @@ static void find_sink_and_source(pa_card *card, pa_device_port *port, pa_sink **
     pa_source *source = NULL;
     uint32_t state;
 
-    if (port->is_output)
-        PA_IDXSET_FOREACH(sink, card->sinks, state)
-            if (port == pa_hashmap_get(sink->ports, port->name))
-                break;
+    switch (port->direction) {
+        case PA_DIRECTION_OUTPUT:
+            PA_IDXSET_FOREACH(sink, card->sinks, state)
+                if (port == pa_hashmap_get(sink->ports, port->name))
+                    break;
+            break;
 
-    if (port->is_input)
-        PA_IDXSET_FOREACH(source, card->sources, state)
-            if (port == pa_hashmap_get(source->ports, port->name))
-                break;
+        case PA_DIRECTION_INPUT:
+            PA_IDXSET_FOREACH(source, card->sources, state)
+                if (port == pa_hashmap_get(source->ports, port->name))
+                    break;
+            break;
+    }
 
     *si = sink;
     *so = source;
diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c
index f16de3a..acfc44d 100644
--- a/src/pulsecore/device-port.c
+++ b/src/pulsecore/device-port.c
@@ -66,7 +66,7 @@ static void device_port_free(pa_object *o) {
 }
 
 
-pa_device_port *pa_device_port_new(pa_core *c, const char *name, const char *description, size_t extra) {
+pa_device_port *pa_device_port_new(pa_core *c, const char *name, const char *description, pa_direction_t direction, size_t extra) {
     pa_device_port *p;
 
     pa_assert(name);
@@ -74,6 +74,7 @@ pa_device_port *pa_device_port_new(pa_core *c, const char *name, const char *des
     p = PA_DEVICE_PORT(pa_object_new_internal(PA_ALIGN(sizeof(pa_device_port)) + extra, pa_device_port_type_id, pa_device_port_check_type));
     p->parent.free = device_port_free;
 
+    p->core = c;
     p->name = pa_xstrdup(name);
     p->description = pa_xstrdup(description);
     p->core = c;
@@ -81,8 +82,7 @@ pa_device_port *pa_device_port_new(pa_core *c, const char *name, const char *des
     p->priority = 0;
     p->available = PA_AVAILABLE_UNKNOWN;
     p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-    p->is_input = FALSE;
-    p->is_output = FALSE;
+    p->direction = direction;
     p->latency_offset = 0;
     p->proplist = pa_proplist_new();
 
@@ -100,23 +100,32 @@ void pa_device_port_set_latency_offset(pa_device_port *p, int64_t offset) {
 
     p->latency_offset = offset;
 
-    if (p->is_output) {
-        pa_sink *sink;
+    switch (p->direction) {
+        case PA_DIRECTION_OUTPUT: {
+            pa_sink *sink;
 
-        PA_IDXSET_FOREACH(sink, p->core->sinks, state)
-            if (sink->active_port == p) {
-                pa_sink_set_latency_offset(sink, p->latency_offset);
-                break;
+            PA_IDXSET_FOREACH(sink, p->core->sinks, state) {
+                if (sink->active_port == p) {
+                    pa_sink_set_latency_offset(sink, p->latency_offset);
+                    break;
+                }
             }
 
-    } else {
-        pa_source *source;
+            break;
+        }
+
+        case PA_DIRECTION_INPUT: {
+            pa_source *source;
 
-        PA_IDXSET_FOREACH(source, p->core->sources, state)
-            if (source->active_port == p) {
-                pa_source_set_latency_offset(source, p->latency_offset);
-                break;
+            PA_IDXSET_FOREACH(source, p->core->sources, state) {
+                if (source->active_port == p) {
+                    pa_source_set_latency_offset(source, p->latency_offset);
+                    break;
+                }
             }
+
+            break;
+        }
     }
 
     pa_assert_se(core = p->core);
diff --git a/src/pulsecore/device-port.h b/src/pulsecore/device-port.h
index c0c00cf..7f21072 100644
--- a/src/pulsecore/device-port.h
+++ b/src/pulsecore/device-port.h
@@ -51,8 +51,7 @@ struct pa_device_port {
 
     pa_proplist *proplist;
     pa_hashmap *profiles; /* Does not own the profiles */
-    pa_bool_t is_input:1;
-    pa_bool_t is_output:1;
+    pa_direction_t direction;
     int64_t latency_offset;
 
     /* .. followed by some implementation specific data */
@@ -63,7 +62,7 @@ PA_DECLARE_PUBLIC_CLASS(pa_device_port);
 
 #define PA_DEVICE_PORT_DATA(d) ((void*) ((uint8_t*) d + PA_ALIGN(sizeof(pa_device_port))))
 
-pa_device_port *pa_device_port_new(pa_core *c, const char *name, const char *description, size_t extra);
+pa_device_port *pa_device_port_new(pa_core *c, const char *name, const char *description, pa_direction_t direction, size_t extra);
 
 /* The port's available status has changed */
 void pa_device_port_set_available(pa_device_port *p, pa_available_t available);
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 9523e7a..96b6850 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -3302,7 +3302,7 @@ static void card_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_car
         pa_tagstruct_puts(t, port->description);
         pa_tagstruct_putu32(t, port->priority);
         pa_tagstruct_putu32(t, port->available);
-        pa_tagstruct_putu8(t, /* FIXME: port->direction */ (port->is_input ? PA_DIRECTION_INPUT : 0) | (port->is_output ? PA_DIRECTION_OUTPUT : 0));
+        pa_tagstruct_putu8(t, port->direction);
         pa_tagstruct_put_proplist(t, port->proplist);
 
         pa_tagstruct_putu32(t, pa_hashmap_size(port->profiles));

commit 5ceb184e3e9151376d1a1e21c412bca747e20988
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Jun 29 18:04:58 2012 +0300

    alsa-mixer: Remove the "name" option from the "General" section of path configuration files
    
    This means that the path names will always correspond to the
    path configuration file names, so they will automatically be
    unique (in the scope of one card).

diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index d41b584..f93ddbb 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -2397,7 +2397,6 @@ pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa
         { "priority",            pa_config_parse_unsigned,          NULL, "General" },
         { "description-key",     pa_config_parse_string,            NULL, "General" },
         { "description",         pa_config_parse_string,            NULL, "General" },
-        { "name",                pa_config_parse_string,            NULL, "General" },
         { "mute-during-activation", pa_config_parse_bool,           NULL, "General" },
         { "eld-device",          pa_config_parse_int,               NULL, "General" },
 
@@ -2437,9 +2436,8 @@ pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa
     items[0].data = &p->priority;
     items[1].data = &p->description_key;
     items[2].data = &p->description;
-    items[3].data = &p->name;
-    items[4].data = &mute_during_activation;
-    items[5].data = &p->eld_device;
+    items[3].data = &mute_during_activation;
+    items[4].data = &p->eld_device;
 
     if (!paths_dir)
         paths_dir = get_default_paths_dir();
@@ -3216,52 +3214,48 @@ static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
     }
 }
 
-static pa_alsa_path* path_set_find_path_by_name(pa_alsa_path_set *ps, const char* name, pa_alsa_path *ignore)
-{
+static pa_alsa_path* path_set_find_path_by_description(pa_alsa_path_set *ps, const char* description, pa_alsa_path *ignore) {
     pa_alsa_path* p;
     void *state;
 
     PA_HASHMAP_FOREACH(p, ps->paths, state)
-        if (p != ignore && pa_streq(p->name, name))
+        if (p != ignore && pa_streq(p->description, description))
             return p;
+
     return NULL;
 }
 
-static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
+static void path_set_make_path_descriptions_unique(pa_alsa_path_set *ps) {
     pa_alsa_path *p, *q;
     void *state, *state2;
 
     PA_HASHMAP_FOREACH(p, ps->paths, state) {
         unsigned i;
-        char *m;
+        char *old_description;
 
-        q = path_set_find_path_by_name(ps, p->name, p);
+        q = path_set_find_path_by_description(ps, p->description, p);
 
         if (!q)
             continue;
 
-        m = pa_xstrdup(p->name);
+        old_description = pa_xstrdup(p->description);
 
-        /* OK, this name is not unique, hence let's rename */
+        /* OK, this description is not unique, hence let's rename */
         i = 1;
         PA_HASHMAP_FOREACH(q, ps->paths, state2) {
-            char *nn, *nd;
+            char *new_description;
 
-            if (!pa_streq(q->name, m))
+            if (!pa_streq(q->description, old_description))
                 continue;
 
-            nn = pa_sprintf_malloc("%s-%u", m, i);
-            pa_xfree(q->name);
-            q->name = nn;
-
-            nd = pa_sprintf_malloc("%s %u", q->description, i);
+            new_description = pa_sprintf_malloc("%s %u", q->description, i);
             pa_xfree(q->description);
-            q->description = nd;
+            q->description = new_description;
 
             i++;
         }
 
-        pa_xfree(m);
+        pa_xfree(old_description);
     }
 }
 
@@ -3764,7 +3758,7 @@ static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
     }
 
     path_set_condense(ps, mixer_handle);
-    path_set_make_paths_unique(ps);
+    path_set_make_path_descriptions_unique(ps);
 
     if (mixer_handle)
         snd_mixer_close(mixer_handle);

commit 3c1ca6d4b881863b41154b561591cf0a68230c16
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Jun 29 18:04:57 2012 +0300

    alsa-mixer: Introduce "description-key" option for paths
    
    Previously the path description was looked up based on the
    path name only. Since there can be multiple paths that use
    the same description, it had to be possible to have multiple
    paths with the same name.
    
    Having the same name with multiple paths makes identifying
    the paths more complex than necessary, so the plan is to
    make it impossible to have paths with the same name. This
    patch prepares for that by retaining the possibility to
    still have the same description with multiple paths. Instead
    of the path name, the path description is looked up by using
    the "path description key" if it is set (path name is still
    used as a fallback lookup key).

diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 21655d7..d41b584 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -53,15 +53,18 @@
 static int setting_select(pa_alsa_setting *s, snd_mixer_t *m);
 
 struct description_map {
-    const char *name;
+    const char *key;
     const char *description;
 };
 
-static const char *lookup_description(const char *name, const struct description_map dm[], unsigned n) {
+static const char *lookup_description(const char *key, const struct description_map dm[], unsigned n) {
     unsigned i;
 
+    if (!key)
+        return NULL;
+
     for (i = 0; i < n; i++)
-        if (pa_streq(dm[i].name, name))
+        if (pa_streq(dm[i].key, key))
             return _(dm[i].description);
 
     return NULL;
@@ -2361,12 +2364,16 @@ static int path_verify(pa_alsa_path *p) {
             return -1;
 
     if (!p->description)
-        p->description = pa_xstrdup(lookup_description(p->name,
+        p->description = pa_xstrdup(lookup_description(p->description_key ? p->description_key : p->name,
                                                        well_known_descriptions,
                                                        PA_ELEMENTSOF(well_known_descriptions)));
 
-    if (!p->description)
+    if (!p->description) {
+        if (p->description_key)
+            pa_log_warn("Path %s: Unrecognized description key: %s", p->name, p->description_key);
+
         p->description = pa_xstrdup(p->name);
+    }
 
     return 0;
 }
@@ -2388,6 +2395,7 @@ pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa
     pa_config_item items[] = {
         /* [General] */
         { "priority",            pa_config_parse_unsigned,          NULL, "General" },
+        { "description-key",     pa_config_parse_string,            NULL, "General" },
         { "description",         pa_config_parse_string,            NULL, "General" },
         { "name",                pa_config_parse_string,            NULL, "General" },
         { "mute-during-activation", pa_config_parse_bool,           NULL, "General" },
@@ -2427,10 +2435,11 @@ pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa
     p->eld_device = -1;
 
     items[0].data = &p->priority;
-    items[1].data = &p->description;
-    items[2].data = &p->name;
-    items[3].data = &mute_during_activation;
-    items[4].data = &p->eld_device;
+    items[1].data = &p->description_key;
+    items[2].data = &p->description;
+    items[3].data = &p->name;
+    items[4].data = &mute_during_activation;
+    items[5].data = &p->eld_device;
 
     if (!paths_dir)
         paths_dir = get_default_paths_dir();
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index b3f7455..3d50ebb 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -180,6 +180,7 @@ struct pa_alsa_path {
     pa_device_port* port;
 
     char *name;
+    char *description_key;
     char *description;
     unsigned priority;
     int eld_device;
diff --git a/src/modules/alsa/mixer/paths/analog-input-aux.conf b/src/modules/alsa/mixer/paths/analog-input-aux.conf
index e6aa064..a5a4baa 100644
--- a/src/modules/alsa/mixer/paths/analog-input-aux.conf
+++ b/src/modules/alsa/mixer/paths/analog-input-aux.conf
@@ -20,7 +20,7 @@
 
 [General]
 priority = 80
-name = analog-input
+description-key = analog-input
 
 [Element Capture]
 switch = mute
diff --git a/src/modules/alsa/mixer/paths/analog-input-dock-mic.conf b/src/modules/alsa/mixer/paths/analog-input-dock-mic.conf
index f6ea94b..9334d45 100644
--- a/src/modules/alsa/mixer/paths/analog-input-dock-mic.conf
+++ b/src/modules/alsa/mixer/paths/analog-input-dock-mic.conf
@@ -20,7 +20,7 @@
 
 [General]
 priority = 78
-name = analog-input-microphone-dock
+description-key = analog-input-microphone-dock
 
 [Jack Dock Mic]
 required-any = any
diff --git a/src/modules/alsa/mixer/paths/analog-input-fm.conf b/src/modules/alsa/mixer/paths/analog-input-fm.conf
index 7f150e3..dfadb00 100644
--- a/src/modules/alsa/mixer/paths/analog-input-fm.conf
+++ b/src/modules/alsa/mixer/paths/analog-input-fm.conf
@@ -20,7 +20,7 @@
 
 [General]
 priority = 70
-name = analog-input-radio
+description-key = analog-input-radio
 
 [Element Capture]
 switch = mute
diff --git a/src/modules/alsa/mixer/paths/analog-input-front-mic.conf b/src/modules/alsa/mixer/paths/analog-input-front-mic.conf
index bb2e806..3fb5f5e 100644
--- a/src/modules/alsa/mixer/paths/analog-input-front-mic.conf
+++ b/src/modules/alsa/mixer/paths/analog-input-front-mic.conf
@@ -20,7 +20,7 @@
 
 [General]
 priority = 85
-name = analog-input-microphone-front
+description-key = analog-input-microphone-front
 
 [Jack Front Mic]
 required-any = any
diff --git a/src/modules/alsa/mixer/paths/analog-input-headphone-mic.conf b/src/modules/alsa/mixer/paths/analog-input-headphone-mic.conf
index 9b9213a..688b8ac 100644
--- a/src/modules/alsa/mixer/paths/analog-input-headphone-mic.conf
+++ b/src/modules/alsa/mixer/paths/analog-input-headphone-mic.conf
@@ -21,7 +21,7 @@
 
 [General]
 priority = 87
-name = analog-input-microphone
+description-key = analog-input-microphone
 
 [Jack Headphone Mic]
 required-any = any
diff --git a/src/modules/alsa/mixer/paths/analog-input-headset-mic.conf b/src/modules/alsa/mixer/paths/analog-input-headset-mic.conf
index 9a9dc35..0a3d661 100644
--- a/src/modules/alsa/mixer/paths/analog-input-headset-mic.conf
+++ b/src/modules/alsa/mixer/paths/analog-input-headset-mic.conf
@@ -20,7 +20,7 @@
 
 [General]
 priority = 87
-name = analog-input-microphone-headset
+description-key = analog-input-microphone-headset
 
 [Jack Headset Mic]
 required-any = any
diff --git a/src/modules/alsa/mixer/paths/analog-input-internal-mic-always.conf b/src/modules/alsa/mixer/paths/analog-input-internal-mic-always.conf
index 37f9b17..cd08531 100644
--- a/src/modules/alsa/mixer/paths/analog-input-internal-mic-always.conf
+++ b/src/modules/alsa/mixer/paths/analog-input-internal-mic-always.conf
@@ -21,7 +21,7 @@
 
 [General]
 priority = 89
-name = analog-input-microphone-internal
+description-key = analog-input-microphone-internal
 
 [Jack Mic]
 state.plugged = no
diff --git a/src/modules/alsa/mixer/paths/analog-input-internal-mic.conf b/src/modules/alsa/mixer/paths/analog-input-internal-mic.conf
index 4718dd0..ae3e9a8 100644
--- a/src/modules/alsa/mixer/paths/analog-input-internal-mic.conf
+++ b/src/modules/alsa/mixer/paths/analog-input-internal-mic.conf
@@ -21,7 +21,7 @@
 
 [General]
 priority = 89
-name = analog-input-microphone-internal
+description-key = analog-input-microphone-internal
 
 [Jack Mic]
 state.plugged = no
diff --git a/src/modules/alsa/mixer/paths/analog-input-mic-line.conf b/src/modules/alsa/mixer/paths/analog-input-mic-line.conf
index fb80838..13db33b 100644
--- a/src/modules/alsa/mixer/paths/analog-input-mic-line.conf
+++ b/src/modules/alsa/mixer/paths/analog-input-mic-line.conf
@@ -20,7 +20,7 @@
 
 [General]
 priority = 85
-name = analog-input
+description-key = analog-input
 
 [Element Capture]
 switch = mute
diff --git a/src/modules/alsa/mixer/paths/analog-input-mic.conf b/src/modules/alsa/mixer/paths/analog-input-mic.conf
index 9e5f044..17f3988 100644
--- a/src/modules/alsa/mixer/paths/analog-input-mic.conf
+++ b/src/modules/alsa/mixer/paths/analog-input-mic.conf
@@ -20,7 +20,7 @@
 
 [General]
 priority = 87
-name = analog-input-microphone
+description-key = analog-input-microphone
 
 [Jack Mic]
 required-any = any
diff --git a/src/modules/alsa/mixer/paths/analog-input-rear-mic.conf b/src/modules/alsa/mixer/paths/analog-input-rear-mic.conf
index 397efba..ac78b5b 100644
--- a/src/modules/alsa/mixer/paths/analog-input-rear-mic.conf
+++ b/src/modules/alsa/mixer/paths/analog-input-rear-mic.conf
@@ -20,7 +20,7 @@
 
 [General]
 priority = 82
-name = analog-input-microphone-rear
+description-key = analog-input-microphone-rear
 
 [Jack Rear Mic]
 required-any = any
diff --git a/src/modules/alsa/mixer/paths/analog-input-tvtuner.conf b/src/modules/alsa/mixer/paths/analog-input-tvtuner.conf
index fae3ce8..446446a 100644
--- a/src/modules/alsa/mixer/paths/analog-input-tvtuner.conf
+++ b/src/modules/alsa/mixer/paths/analog-input-tvtuner.conf
@@ -20,7 +20,7 @@
 
 [General]
 priority = 70
-name = analog-input-video
+description-key = analog-input-video
 
 [Element Capture]
 switch = mute
diff --git a/src/modules/alsa/mixer/paths/analog-output-desktop-speaker.conf b/src/modules/alsa/mixer/paths/analog-output-desktop-speaker.conf
index 099823e..2b68117 100644
--- a/src/modules/alsa/mixer/paths/analog-output-desktop-speaker.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-desktop-speaker.conf
@@ -20,7 +20,7 @@
 
 [General]
 priority = 101
-name = analog-output-speaker
+description-key = analog-output-speaker
 
 [Properties]
 device.icon_name = audio-speakers
diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf b/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf
index 6161ff5..7568924 100644
--- a/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-headphones-2.conf
@@ -20,7 +20,7 @@
 
 [General]
 priority = 89
-name = analog-output-headphones
+description-key = analog-output-headphones
 
 [Properties]
 device.icon_name = audio-headphones
diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones.conf b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
index 76cd01e..e682033 100644
--- a/src/modules/alsa/mixer/paths/analog-output-headphones.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
@@ -20,7 +20,7 @@
 
 [General]
 priority = 90
-name = analog-output-headphones
+description-key = analog-output-headphones
 
 [Properties]
 device.icon_name = audio-headphones
diff --git a/src/modules/alsa/mixer/paths/analog-output-speaker-always.conf b/src/modules/alsa/mixer/paths/analog-output-speaker-always.conf
index 8a2b3df..c511813 100644
--- a/src/modules/alsa/mixer/paths/analog-output-speaker-always.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-speaker-always.conf
@@ -21,7 +21,7 @@
 
 [General]
 priority = 100
-name = analog-output-speaker
+description-key = analog-output-speaker
 
 [Properties]
 device.icon_name = audio-speakers
diff --git a/src/modules/alsa/mixer/paths/analog-output-speaker.conf b/src/modules/alsa/mixer/paths/analog-output-speaker.conf
index 7b47fc2..8416a25 100644
--- a/src/modules/alsa/mixer/paths/analog-output-speaker.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-speaker.conf
@@ -20,7 +20,7 @@
 
 [General]
 priority = 100
-name = analog-output-speaker
+description-key = analog-output-speaker
 
 [Properties]
 device.icon_name = audio-speakers
diff --git a/src/modules/alsa/mixer/paths/analog-output.conf.common b/src/modules/alsa/mixer/paths/analog-output.conf.common
index 73d09f8..727b776 100644
--- a/src/modules/alsa/mixer/paths/analog-output.conf.common
+++ b/src/modules/alsa/mixer/paths/analog-output.conf.common
@@ -55,7 +55,13 @@
 
 ; [General]
 ; priority = ...                         # Priority for this path
-; description = ...
+; description-key = ...                  # The path description is looked up from a table in path_verify() in
+;                                        # src/modules/alsa/alsa-mixer.c. By default the path name (i.e. the file name
+;                                        # minus the ".conf" suffix) is used as the lookup key, but if this option is
+;                                        # set, then the given string is used as the key instead. In any case the
+;                                        # "description" option can be used to override the path description.
+; description = ...                      # Description for this path. Overrides the normal description lookup logic, as
+;                                        # described in the "description-key" documentation above.
 ; mute-during-activation = yes | no      # If this path supports hardware mute, should the hw mute be used while activating this
 ;                                        # path? In some cases this can reduce extra noises during port switching, while in other
 ;                                        # cases this can increase such noises. Default: no.

commit 66aeea7f72c5d4b978f8b5ed59987f7eed2a884a
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Mar 27 16:06:04 2013 +0200

    switch-on-port-available: Fix error reporting style

diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c
index d04d4a8..a487e04 100644
--- a/src/modules/module-switch-on-port-available.c
+++ b/src/modules/module-switch-on-port-available.c
@@ -87,7 +87,7 @@ static bool profile_good_for_input(pa_card_profile *profile) {
     return true;
 }
 
-static pa_bool_t try_to_switch_profile(pa_device_port *port) {
+static int try_to_switch_profile(pa_device_port *port) {
     pa_card_profile *best_profile = NULL, *profile;
     void *state;
 
@@ -119,15 +119,15 @@ static pa_bool_t try_to_switch_profile(pa_device_port *port) {
 
     if (!best_profile) {
         pa_log_debug("No suitable profile found");
-        return FALSE;
+        return -1;
     }
 
     if (pa_card_set_profile(port->card, best_profile->name, FALSE) != 0) {
         pa_log_debug("Could not set profile %s", best_profile->name);
-        return FALSE;
+        return -1;
     }
 
-    return TRUE;
+    return 0;
 }
 
 static void find_sink_and_source(pa_card *card, pa_device_port *port, pa_sink **si, pa_source **so)
@@ -184,7 +184,7 @@ static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port
             return PA_HOOK_OK;
 
         if (!is_active_profile) {
-            if (!try_to_switch_profile(port))
+            if (try_to_switch_profile(port) < 0)
                 return PA_HOOK_OK;
 
             pa_assert(card->active_profile == pa_hashmap_get(port->profiles, card->active_profile->name));

commit d4d319820d941814d1e8bd151bf863709359d910
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Mar 27 16:05:26 2013 +0200

    switch-on-port-available: Remove a redundant function argument

diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c
index 293d3ec..d04d4a8 100644
--- a/src/modules/module-switch-on-port-available.c
+++ b/src/modules/module-switch-on-port-available.c
@@ -87,7 +87,7 @@ static bool profile_good_for_input(pa_card_profile *profile) {
     return true;
 }
 
-static pa_bool_t try_to_switch_profile(pa_card *card, pa_device_port *port) {
+static pa_bool_t try_to_switch_profile(pa_device_port *port) {
     pa_card_profile *best_profile = NULL, *profile;
     void *state;
 
@@ -122,7 +122,7 @@ static pa_bool_t try_to_switch_profile(pa_card *card, pa_device_port *port) {
         return FALSE;
     }
 
-    if (pa_card_set_profile(card, best_profile->name, FALSE) != 0) {
+    if (pa_card_set_profile(port->card, best_profile->name, FALSE) != 0) {
         pa_log_debug("Could not set profile %s", best_profile->name);
         return FALSE;
     }
@@ -184,7 +184,7 @@ static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port
             return PA_HOOK_OK;
 
         if (!is_active_profile) {
-            if (!try_to_switch_profile(card, port))
+            if (!try_to_switch_profile(port))
                 return PA_HOOK_OK;
 
             pa_assert(card->active_profile == pa_hashmap_get(port->profiles, card->active_profile->name));

commit 7560314f36e85e44834bfa6b90c4ea9d34c0f1e6
Author: Tanu Kaskinen <tanu.kaskinen at digia.com>
Date:   Fri Jun 29 18:04:56 2012 +0300

    switch-on-port-available: Prepare for dual-direction ports going away
    
    As an extra, I broke try_to_switch_profile() into smaller
    functions, because the two levels of loops with continue
    statements inside both were a bit hard to follow.

diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c
index abd8777..293d3ec 100644
--- a/src/modules/module-switch-on-port-available.c
+++ b/src/modules/module-switch-on-port-available.c
@@ -51,6 +51,42 @@ static pa_device_port* find_best_port(pa_hashmap *ports) {
     return result;
 }
 
+static bool profile_good_for_output(pa_card_profile *profile) {
+    pa_sink *sink;
+    uint32_t idx;
+
+    pa_assert(profile);
+
+    if (profile->card->active_profile->n_sources != profile->n_sources)
+        return false;
+
+    if (profile->card->active_profile->max_source_channels != profile->max_source_channels)
+        return false;
+
+    /* Try not to switch to HDMI sinks from analog when HDMI is becoming available */
+    PA_IDXSET_FOREACH(sink, profile->card->sinks, idx) {
+        if (!sink->active_port)
+            continue;
+
+        if (sink->active_port->available != PA_AVAILABLE_NO)
+            return false;
+    }
+
+    return true;
+}
+
+static bool profile_good_for_input(pa_card_profile *profile) {
+    pa_assert(profile);
+
+    if (profile->card->active_profile->n_sinks != profile->n_sinks)
+        return false;
+
+    if (profile->card->active_profile->max_sink_channels != profile->max_sink_channels)
+        return false;
+
+    return true;
+}
+
 static pa_bool_t try_to_switch_profile(pa_card *card, pa_device_port *port) {
     pa_card_profile *best_profile = NULL, *profile;
     void *state;
@@ -58,42 +94,25 @@ static pa_bool_t try_to_switch_profile(pa_card *card, pa_device_port *port) {
     pa_log_debug("Finding best profile");
 
     PA_HASHMAP_FOREACH(profile, port->profiles, state) {
+        pa_direction_t direction = port->is_output ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
+        bool good;
+
         if (best_profile && best_profile->priority >= profile->priority)
             continue;
 
         /* We make a best effort to keep other direction unchanged */
-        if (!port->is_input) {
-            if (card->active_profile->n_sources != profile->n_sources)
-                continue;
-
-            if (card->active_profile->max_source_channels != profile->max_source_channels)
-                continue;
-        }
-
-        if (!port->is_output) {
-            if (card->active_profile->n_sinks != profile->n_sinks)
-                continue;
+        switch (direction) {
+            case PA_DIRECTION_OUTPUT:
+                good = profile_good_for_output(profile);
+                break;
 
-            if (card->active_profile->max_sink_channels != profile->max_sink_channels)
-                continue;
+            case PA_DIRECTION_INPUT:
+                good = profile_good_for_input(profile);
+                break;
         }
 
-        if (port->is_output) {
-            /* Try not to switch to HDMI sinks from analog when HDMI is becoming available */
-            uint32_t state2;
-            pa_sink *sink;
-            pa_bool_t found_active_port = FALSE;
-
-            PA_IDXSET_FOREACH(sink, card->sinks, state2) {
-                if (!sink->active_port)
-                    continue;
-                if (sink->active_port->available != PA_AVAILABLE_NO)
-                    found_active_port = TRUE;
-            }
-
-            if (found_active_port)
-                continue;
-        }
+        if (!good)
+            continue;
 
         best_profile = profile;
     }



More information about the pulseaudio-commits mailing list