]> code.delx.au - pulseaudio/blobdiff - src/modules/module-hal-detect.c
module-equalizer-sink: Use = in initialising variables
[pulseaudio] / src / modules / module-hal-detect.c
index 832bc73e683f2055509178573248c4c4f34647bf..5ca683a35990be71aea64f66c2922d26aa267be6 100644 (file)
@@ -1,5 +1,3 @@
-/* $Id$ */
-
 /***
     This file is part of PulseAudio.
 
 /***
     This file is part of PulseAudio.
 
@@ -8,7 +6,7 @@
 
     PulseAudio is free software; you can redistribute it and/or modify
     it under the terms of the GNU Lesser General Public License as published
 
     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 of the License,
+    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
     or (at your option) any later version.
 
     PulseAudio is distributed in the hope that it will be useful, but
 #include <errno.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <sys/types.h>
-#include <sys/stat.h>
 
 #include <pulse/xmalloc.h>
 
 #include <pulse/xmalloc.h>
-#include <pulse/timeval.h>
 
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/module.h>
 #include <pulsecore/log.h>
 #include <pulsecore/hashmap.h>
 #include <pulsecore/idxset.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/module.h>
 #include <pulsecore/log.h>
 #include <pulsecore/hashmap.h>
 #include <pulsecore/idxset.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/namereg.h>
-#include <pulsecore/core-scache.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/modargs.h>
+#include <pulsecore/dbus-shared.h>
 
 #include <hal/libhal.h>
 
 
 #include <hal/libhal.h>
 
-#include "dbus-util.h"
 #include "module-hal-detect-symdef.h"
 
 PA_MODULE_AUTHOR("Shahms King");
 PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(TRUE);
 #include "module-hal-detect-symdef.h"
 
 PA_MODULE_AUTHOR("Shahms King");
 PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(TRUE);
-#if defined(HAVE_ALSA) && defined(HAVE_OSS)
-PA_MODULE_USAGE("api=<alsa or oss>");
+#if defined(HAVE_ALSA) && defined(HAVE_OSS_OUTPUT)
+PA_MODULE_USAGE("api=<alsa or oss> "
+                "tsched=<enable system timer based scheduling mode?> "
+                "subdevices=<init all subdevices>");
 #elif defined(HAVE_ALSA)
 #elif defined(HAVE_ALSA)
-PA_MODULE_USAGE("api=<alsa>");
-#elif defined(HAVE_OSS)
-PA_MODULE_USAGE("api=<oss>");
+PA_MODULE_USAGE("api=<alsa> "
+                "tsched=<enable system timer based scheduling mode?>");
+#elif defined(HAVE_OSS_OUTPUT)
+PA_MODULE_USAGE("api=<oss> "
+                "subdevices=<init all subdevices>");
 #endif
 #endif
+PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-hal-detect!");
 
 struct device {
 
 struct device {
-    uint32_t index;
-    char *udi;
-    char *sink_name, *source_name;
-    int acl_race_fix;
+    char *udi, *originating_udi;
+    char *card_name, *sink_name, *source_name;
+    uint32_t module;
+    pa_bool_t acl_race_fix;
 };
 
 struct userdata {
     pa_core *core;
     LibHalContext *context;
     pa_dbus_connection *connection;
 };
 
 struct userdata {
     pa_core *core;
     LibHalContext *context;
     pa_dbus_connection *connection;
-    pa_hashmap *devices;
+    pa_hashmap *devices; /* Every entry is indexed twice in this table: by the udi we found the device with and by the originating device's udi */
     const char *capability;
     const char *capability;
-};
-
-struct timerdata {
-    struct userdata *u;
-    char *udi;
+#ifdef HAVE_ALSA
+    pa_bool_t use_tsched;
+#endif
+#ifdef HAVE_OSS_OUTPUT
+    pa_bool_t init_subdevs;
+#endif
+    pa_bool_t filter_added:1;
 };
 
 #define CAPABILITY_ALSA "alsa"
 };
 
 #define CAPABILITY_ALSA "alsa"
@@ -90,25 +91,31 @@ struct timerdata {
 
 static const char* const valid_modargs[] = {
     "api",
 
 static const char* const valid_modargs[] = {
     "api",
+#ifdef HAVE_ALSA
+    "tsched",
+#endif
+#ifdef HAVE_OSS_OUTPUT
+    "subdevices",
+#endif
     NULL
 };
 
     NULL
 };
 
-static void hal_device_free(struct device* d) {
+static void device_free(struct device* d) {
     pa_assert(d);
 
     pa_xfree(d->udi);
     pa_assert(d);
 
     pa_xfree(d->udi);
+    pa_xfree(d->originating_udi);
     pa_xfree(d->sink_name);
     pa_xfree(d->source_name);
     pa_xfree(d->sink_name);
     pa_xfree(d->source_name);
+    pa_xfree(d->card_name);
     pa_xfree(d);
 }
 
     pa_xfree(d);
 }
 
-static void hal_device_free_cb(void *d, PA_GCC_UNUSED void *data) {
-    hal_device_free(d);
-}
-
 static const char *strip_udi(const char *udi) {
     const char *slash;
 
 static const char *strip_udi(const char *udi) {
     const char *slash;
 
+    pa_assert(udi);
+
     if ((slash = strrchr(udi, '/')))
         return slash+1;
 
     if ((slash = strrchr(udi, '/')))
         return slash+1;
 
@@ -117,405 +124,395 @@ static const char *strip_udi(const char *udi) {
 
 #ifdef HAVE_ALSA
 
 
 #ifdef HAVE_ALSA
 
-typedef enum {
-    ALSA_TYPE_SINK,
-    ALSA_TYPE_SOURCE,
-    ALSA_TYPE_OTHER,
-    ALSA_TYPE_MAX
-} alsa_type_t;
+enum alsa_type {
+    ALSA_TYPE_PLAYBACK,
+    ALSA_TYPE_CAPTURE,
+    ALSA_TYPE_CONTROL,
+    ALSA_TYPE_OTHER
+};
 
 
-static alsa_type_t hal_alsa_device_get_type(LibHalContext *context, const char *udi, DBusError *error) {
+static enum alsa_type hal_alsa_device_get_type(LibHalContext *context, const char *udi) {
     char *type;
     char *type;
-    alsa_type_t t;
+    enum alsa_type t = ALSA_TYPE_OTHER;
+    DBusError error;
 
 
-    if (!(type = libhal_device_get_property_string(context, udi, "alsa.type", error)))
-        return ALSA_TYPE_OTHER;
+    dbus_error_init(&error);
 
 
-    if (!strcmp(type, "playback"))
-        t = ALSA_TYPE_SINK;
-    else if (!strcmp(type, "capture"))
-        t = ALSA_TYPE_SOURCE;
-    else
-        t = ALSA_TYPE_OTHER;
+    pa_assert(context);
+    pa_assert(udi);
+
+    if (!(type = libhal_device_get_property_string(context, udi, "alsa.type", &error)))
+        goto finish;
+
+    if (pa_streq(type, "playback"))
+        t = ALSA_TYPE_PLAYBACK;
+    else if (pa_streq(type, "capture"))
+        t = ALSA_TYPE_CAPTURE;
+    else if (pa_streq(type, "control"))
+        t = ALSA_TYPE_CONTROL;
 
     libhal_free_string(type);
 
 
     libhal_free_string(type);
 
+finish:
+    if (dbus_error_is_set(&error)) {
+        pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
+        dbus_error_free(&error);
+    }
+
     return t;
 }
 
     return t;
 }
 
-static int hal_alsa_device_is_modem(LibHalContext *context, const char *udi, DBusError *error) {
+static pa_bool_t hal_alsa_device_is_modem(LibHalContext *context, const char *udi) {
     char *class;
     char *class;
-    int r;
+    pa_bool_t r = FALSE;
+    DBusError error;
 
 
-    if (!(class = libhal_device_get_property_string(context, udi, "alsa.pcm_class", error)))
-        return 0;
+    dbus_error_init(&error);
+
+    pa_assert(context);
+    pa_assert(udi);
+
+    if (!(class = libhal_device_get_property_string(context, udi, "alsa.pcm_class", &error)))
+        goto finish;
 
 
-    r = strcmp(class, "modem") == 0;
-    pa_xfree(class);
+    r = pa_streq(class, "modem");
+    libhal_free_string(class);
+
+finish:
+    if (dbus_error_is_set(&error)) {
+        if (!dbus_error_has_name(&error, "org.freedesktop.Hal.NoSuchProperty"))
+            pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
+        dbus_error_free(&error);
+    }
 
     return r;
 }
 
 
     return r;
 }
 
-static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi, char **sink_name, char **source_name) {
-    char *args;
-    alsa_type_t type;
-    int device, card;
-    const char *module_name;
+static int hal_device_load_alsa(struct userdata *u, const char *udi, struct device *d) {
+    enum alsa_type type;
+    int card;
     DBusError error;
     pa_module *m;
     DBusError error;
     pa_module *m;
+    char *args, *originating_udi = NULL, *card_name = NULL;
 
     dbus_error_init(&error);
 
     pa_assert(u);
 
     dbus_error_init(&error);
 
     pa_assert(u);
-    pa_assert(sink_name);
-    pa_assert(source_name);
-
-    *sink_name = *source_name = NULL;
+    pa_assert(udi);
+    pa_assert(d);
 
 
-    type = hal_alsa_device_get_type(u->context, udi, &error);
-    if (dbus_error_is_set(&error) || type == ALSA_TYPE_OTHER)
+    /* We only care for PCM devices */
+    type = hal_alsa_device_get_type(u->context, udi);
+
+    /* For each ALSA card that appears the control device will be the
+     * last one to be created, this is considered part of the ALSA
+     * userspace API. We rely on this and load our modules only when
+     * the control device is available assuming that *all* device
+     * nodes have been properly created and assigned the right ACLs at
+     * that time. Also see:
+     *
+     * http://mailman.alsa-project.org/pipermail/alsa-devel/2009-April/015958.html
+     *
+     * and the associated thread.*/
+
+    if (type != ALSA_TYPE_CONTROL)
         goto fail;
 
         goto fail;
 
-    device = libhal_device_get_property_int(u->context, udi, "alsa.device", &error);
-    if (dbus_error_is_set(&error) || device != 0)
+    /* We don't care for modems -- this is most likely not set for
+     * control devices, so kind of pointless here. */
+    if (hal_alsa_device_is_modem(u->context, udi))
         goto fail;
 
         goto fail;
 
-    card = libhal_device_get_property_int(u->context, udi, "alsa.card", &error);
-    if (dbus_error_is_set(&error))
+    /* We store only one entry per card, hence we look for the originating device */
+    originating_udi = libhal_device_get_property_string(u->context, udi, "alsa.originating_device", &error);
+    if (dbus_error_is_set(&error) || !originating_udi)
         goto fail;
 
         goto fail;
 
-    if (hal_alsa_device_is_modem(u->context, udi, &error))
+    /* Make sure we only load one module per card */
+    if (pa_hashmap_get(u->devices, originating_udi))
         goto fail;
 
         goto fail;
 
-    if (type == ALSA_TYPE_SINK) {
-        *sink_name = pa_sprintf_malloc("alsa_output.%s", strip_udi(udi));
-
-        module_name = "module-alsa-sink";
-        args = pa_sprintf_malloc("device_id=%u sink_name=%s", card, *sink_name);
-    } else {
-        *source_name = pa_sprintf_malloc("alsa_input.%s", strip_udi(udi));
-
-        module_name = "module-alsa-source";
-        args = pa_sprintf_malloc("device_id=%u source_name=%s", card, *source_name);
-    }
-
-    pa_log_debug("Loading %s with arguments '%s'", module_name, args);
+    /* We need the identifier */
+    card = libhal_device_get_property_int(u->context, udi, "alsa.card", &error);
+    if (dbus_error_is_set(&error))
+        goto fail;
 
 
-    m = pa_module_load(u->core, module_name, args);
+    card_name = pa_sprintf_malloc("alsa_card.%s", strip_udi(originating_udi));
+    args = pa_sprintf_malloc("device_id=%u name=\"%s\" card_name=\"%s\" tsched=%i card_properties=\"module-hal-detect.discovered=1\"", card, strip_udi(originating_udi), card_name, (int) u->use_tsched);
 
 
+    pa_log_debug("Loading module-alsa-card with arguments '%s'", args);
+    m = pa_module_load(u->core, "module-alsa-card", args);
     pa_xfree(args);
 
     pa_xfree(args);
 
-    if (!m) {
-        pa_xfree(*sink_name);
-        pa_xfree(*source_name);
-        *sink_name = *source_name = NULL;
-    }
+    if (!m)
+        goto fail;
 
 
-    return m;
+    d->originating_udi = originating_udi;
+    d->module = m->index;
+    d->card_name = card_name;
+
+    return 0;
 
 fail:
     if (dbus_error_is_set(&error)) {
 
 fail:
     if (dbus_error_is_set(&error)) {
-        pa_log_error("D-Bus error while parsing ALSA data: %s: %s", error.name, error.message);
+        pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
         dbus_error_free(&error);
     }
 
         dbus_error_free(&error);
     }
 
-    return NULL;
+    pa_xfree(originating_udi);
+    pa_xfree(card_name);
+
+    return -1;
 }
 
 #endif
 
 }
 
 #endif
 
-#ifdef HAVE_OSS
+#ifdef HAVE_OSS_OUTPUT
 
 
-static int hal_oss_device_is_pcm(LibHalContext *context, const char *udi, DBusError *error) {
+static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi, pa_bool_t init_subdevices) {
     char *class = NULL, *dev = NULL, *e;
     int device;
     char *class = NULL, *dev = NULL, *e;
     int device;
-    int r = 0;
+    pa_bool_t r = FALSE;
+    DBusError error;
 
 
-    class = libhal_device_get_property_string(context, udi, "oss.type", error);
-    if (dbus_error_is_set(error) || !class)
+    dbus_error_init(&error);
+
+    pa_assert(context);
+    pa_assert(udi);
+
+    /* We only care for PCM devices */
+    class = libhal_device_get_property_string(context, udi, "oss.type", &error);
+    if (dbus_error_is_set(&error) || !class)
         goto finish;
 
         goto finish;
 
-    if (strcmp(class, "pcm"))
+    if (!pa_streq(class, "pcm"))
         goto finish;
 
         goto finish;
 
-    dev = libhal_device_get_property_string(context, udi, "oss.device_file", error);
-    if (dbus_error_is_set(error) || !dev)
+    /* We don't like /dev/audio */
+    dev = libhal_device_get_property_string(context, udi, "oss.device_file", &error);
+    if (dbus_error_is_set(&error) || !dev)
         goto finish;
 
     if ((e = strrchr(dev, '/')))
         if (pa_startswith(e + 1, "audio"))
             goto finish;
 
         goto finish;
 
     if ((e = strrchr(dev, '/')))
         if (pa_startswith(e + 1, "audio"))
             goto finish;
 
-    device = libhal_device_get_property_int(context, udi, "oss.device", error);
-    if (dbus_error_is_set(error) || device != 0)
+    /* We only care for the main device */
+    device = libhal_device_get_property_int(context, udi, "oss.device", &error);
+    if (dbus_error_is_set(&error) || (device != 0 && init_subdevices == FALSE))
         goto finish;
 
         goto finish;
 
-    r = 1;
+    r = TRUE;
 
 finish:
 
 
 finish:
 
+    if (dbus_error_is_set(&error)) {
+        pa_log_error("D-Bus error while parsing HAL OSS data: %s: %s", error.name, error.message);
+        dbus_error_free(&error);
+    }
+
     libhal_free_string(class);
     libhal_free_string(dev);
 
     return r;
 }
 
     libhal_free_string(class);
     libhal_free_string(dev);
 
     return r;
 }
 
-static pa_module* hal_device_load_oss(struct userdata *u, const char *udi, char **sink_name, char **source_name) {
-    char* args;
-    char* device;
+static int hal_device_load_oss(struct userdata *u, const char *udi, struct device *d) {
     DBusError error;
     pa_module *m;
     DBusError error;
     pa_module *m;
+    char *args, *originating_udi = NULL, *device, *sink_name = NULL, *source_name = NULL;
 
     dbus_error_init(&error);
 
     pa_assert(u);
 
     dbus_error_init(&error);
 
     pa_assert(u);
-    pa_assert(sink_name);
-    pa_assert(source_name);
+    pa_assert(udi);
+    pa_assert(d);
 
 
-    *sink_name = *source_name = NULL;
+    /* We only care for OSS PCM devices */
+    if (!hal_oss_device_is_pcm(u->context, udi, u->init_subdevs))
+        goto fail;
+
+    /* We store only one entry per card, hence we look for the originating device */
+    originating_udi = libhal_device_get_property_string(u->context, udi, "oss.originating_device", &error);
+    if (dbus_error_is_set(&error) || !originating_udi)
+        goto fail;
 
 
-    if (!hal_oss_device_is_pcm(u->context, udi, &error) || dbus_error_is_set(&error))
+    /* Make sure we only load one module per card */
+    if (pa_hashmap_get(u->devices, originating_udi))
         goto fail;
 
         goto fail;
 
+    /* We need the device file */
     device = libhal_device_get_property_string(u->context, udi, "oss.device_file", &error);
     if (!device || dbus_error_is_set(&error))
         goto fail;
 
     device = libhal_device_get_property_string(u->context, udi, "oss.device_file", &error);
     if (!device || dbus_error_is_set(&error))
         goto fail;
 
-    *sink_name = pa_sprintf_malloc("oss_output.%s", strip_udi(udi));
-    *source_name = pa_sprintf_malloc("oss_input.%s", strip_udi(udi));
+    sink_name = pa_sprintf_malloc("oss_output.%s", strip_udi(udi));
+    source_name = pa_sprintf_malloc("oss_input.%s", strip_udi(udi));
+    args = pa_sprintf_malloc("device=%s sink_name=%s source_name=%s", device, sink_name, source_name);
 
 
-    args = pa_sprintf_malloc("device=%s sink_name=%s source_name=%s", device, *sink_name, *source_name);
     libhal_free_string(device);
 
     pa_log_debug("Loading module-oss with arguments '%s'", args);
     m = pa_module_load(u->core, "module-oss", args);
     pa_xfree(args);
 
     libhal_free_string(device);
 
     pa_log_debug("Loading module-oss with arguments '%s'", args);
     m = pa_module_load(u->core, "module-oss", args);
     pa_xfree(args);
 
-    if (!m) {
-        pa_xfree(*sink_name);
-        pa_xfree(*source_name);
-        *sink_name = *source_name = NULL;
-    }
+    if (!m)
+        goto fail;
 
 
-    return m;
+    d->originating_udi = originating_udi;
+    d->module = m->index;
+    d->sink_name = sink_name;
+    d->source_name = source_name;
+
+    return 0;
 
 fail:
     if (dbus_error_is_set(&error)) {
 
 fail:
     if (dbus_error_is_set(&error)) {
-        pa_log_error("D-Bus error while parsing OSS data: %s: %s", error.name, error.message);
+        pa_log_error("D-Bus error while parsing OSS HAL data: %s: %s", error.name, error.message);
         dbus_error_free(&error);
     }
 
         dbus_error_free(&error);
     }
 
-    return NULL;
+    pa_xfree(originating_udi);
+    pa_xfree(source_name);
+    pa_xfree(sink_name);
+
+    return -1;
 }
 #endif
 
 static struct device* hal_device_add(struct userdata *u, const char *udi) {
 }
 #endif
 
 static struct device* hal_device_add(struct userdata *u, const char *udi) {
-    pa_module* m = NULL;
     struct device *d;
     struct device *d;
-    char *sink_name = NULL, *source_name = NULL;
+    int r;
 
     pa_assert(u);
     pa_assert(u->capability);
 
     pa_assert(u);
     pa_assert(u->capability);
-    pa_assert(!pa_hashmap_get(u->devices, udi));
+
+    d = pa_xnew(struct device, 1);
+    d->acl_race_fix = FALSE;
+    d->udi = pa_xstrdup(udi);
+    d->originating_udi = NULL;
+    d->module = PA_INVALID_INDEX;
+    d->sink_name = d->source_name = d->card_name = NULL;
+    r = -1;
 
 #ifdef HAVE_ALSA
 
 #ifdef HAVE_ALSA
-    if (strcmp(u->capability, CAPABILITY_ALSA) == 0)
-        m = hal_device_load_alsa(u, udi, &sink_name, &source_name);
+    if (pa_streq(u->capability, CAPABILITY_ALSA))
+        r = hal_device_load_alsa(u, udi,  d);
 #endif
 #endif
-#ifdef HAVE_OSS
-    if (strcmp(u->capability, CAPABILITY_OSS) == 0)
-        m = hal_device_load_oss(u, udi, &sink_name, &source_name);
+#ifdef HAVE_OSS_OUTPUT
+    if (pa_streq(u->capability, CAPABILITY_OSS))
+        r = hal_device_load_oss(u, udi, d);
 #endif
 
 #endif
 
-    if (!m)
+    if (r < 0) {
+        device_free(d);
         return NULL;
         return NULL;
+    }
 
 
-    d = pa_xnew(struct device, 1);
-    d->acl_race_fix = 0;
-    d->udi = pa_xstrdup(udi);
-    d->index = m->index;
-    d->sink_name = sink_name;
-    d->source_name = source_name;
     pa_hashmap_put(u->devices, d->udi, d);
     pa_hashmap_put(u->devices, d->udi, d);
+    pa_hashmap_put(u->devices, d->originating_udi, d);
 
     return d;
 }
 
 
     return d;
 }
 
-static int hal_device_add_all(struct userdata *u, const char *capability) {
-    DBusError error;
-    int i, n, count = 0;
+static int hal_device_add_all(struct userdata *u) {
+    int n, count = 0;
     char** udis;
     char** udis;
-
-    pa_assert(u);
+    DBusError error;
 
     dbus_error_init(&error);
 
 
     dbus_error_init(&error);
 
-    if (u->capability && strcmp(u->capability, capability) != 0)
-        return 0;
-
-    pa_log_info("Trying capability %s", capability);
+    pa_assert(u);
 
 
-    udis = libhal_find_device_by_capability(u->context, capability, &n, &error);
-    if (dbus_error_is_set(&error)) {
-        pa_log_error("Error finding devices: %s: %s", error.name, error.message);
-        dbus_error_free(&error);
-        return -1;
-    }
+    udis = libhal_find_device_by_capability(u->context, u->capability, &n, &error);
+    if (dbus_error_is_set(&error) || !udis)
+        goto fail;
 
     if (n > 0) {
 
     if (n > 0) {
-        u->capability = capability;
+        int i;
 
         for (i = 0; i < n; i++) {
 
         for (i = 0; i < n; i++) {
-            struct device *d;
-
-            if (!(d = hal_device_add(u, udis[i])))
-                pa_log_debug("Not loaded device %s", udis[i]);
-            else {
-                if (d->sink_name)
-                    pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, PA_VOLUME_NORM, 0);
+            if (hal_device_add(u, udis[i])) {
                 count++;
                 count++;
-            }
+                pa_log_debug("Loaded device %s", udis[i]);
+            } else
+                pa_log_debug("Not loaded device %s", udis[i]);
         }
     }
 
     libhal_free_string_array(udis);
         }
     }
 
     libhal_free_string_array(udis);
-    return count;
-}
 
 
-static dbus_bool_t device_has_capability(LibHalContext *context, const char *udi, const char* cap, DBusError *error){
-    dbus_bool_t has_prop;
-
-    has_prop = libhal_device_property_exists(context, udi, "info.capabilities", error);
-    if (!has_prop || dbus_error_is_set(error))
-        return FALSE;
-
-    return libhal_device_query_capability(context, udi, cap, error);
-}
-
-static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev, const struct timeval *tv, void *userdata) {
-    DBusError error;
-    struct timerdata *td = userdata;
-
-    dbus_error_init(&error);
-
-    if (!pa_hashmap_get(td->u->devices, td->udi)) {
-        int b;
-        struct device *d;
+    return count;
 
 
-        b = libhal_device_exists(td->u->context, td->udi, &error);
-
-        if (dbus_error_is_set(&error)) {
-            pa_log_error("Error adding device: %s: %s", error.name, error.message);
-            dbus_error_free(&error);
-        } else if (b) {
-            if (!(d = hal_device_add(td->u, td->udi)))
-                pa_log_debug("Not loaded device %s", td->udi);
-            else {
-                if (d->sink_name)
-                    pa_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, PA_VOLUME_NORM, 0);
-            }
-        }
+fail:
+    if (dbus_error_is_set(&error)) {
+        pa_log_error("D-Bus error while parsing HAL data: %s: %s", error.name, error.message);
+        dbus_error_free(&error);
     }
 
     }
 
-    pa_xfree(td->udi);
-    pa_xfree(td);
-    ea->time_free(ev);
+    return -1;
 }
 
 static void device_added_cb(LibHalContext *context, const char *udi) {
     DBusError error;
 }
 
 static void device_added_cb(LibHalContext *context, const char *udi) {
     DBusError error;
-    struct timeval tv;
-    struct timerdata *t;
     struct userdata *u;
     struct userdata *u;
-    int good = 0;
-
-    pa_assert_se(u = libhal_ctx_get_user_data(context));
-
-    if (pa_hashmap_get(u->devices, udi))
-        return;
-
-    pa_log_debug("HAL Device added: %s", udi);
+    pa_bool_t good = FALSE;
 
     dbus_error_init(&error);
 
 
     dbus_error_init(&error);
 
-    if (u->capability) {
-
-        good = device_has_capability(context, udi, u->capability, &error);
-
-        if (dbus_error_is_set(&error)) {
-            pa_log_error("Error getting capability: %s: %s", error.name, error.message);
-            dbus_error_free(&error);
-            return;
-        }
+    pa_assert(context);
+    pa_assert(udi);
 
 
-    } else {
-
-#ifdef HAVE_ALSA
-        good = device_has_capability(context, udi, CAPABILITY_ALSA, &error);
-
-        if (dbus_error_is_set(&error)) {
-            pa_log_error("Error getting capability: %s: %s", error.name, error.message);
-            dbus_error_free(&error);
-            return;
-        }
-
-        if (good)
-            u->capability = CAPABILITY_ALSA;
-#endif
-#if defined(HAVE_OSS) && defined(HAVE_ALSA)
-        if (!good) {
-#endif
-#ifdef HAS_OSS
-            good = device_has_capability(context, udi, CAPABILITY_OSS, &error);
+    pa_assert_se(u = libhal_ctx_get_user_data(context));
 
 
-            if (dbus_error_is_set(&error)) {
-                pa_log_error("Error getting capability: %s: %s", error.name, error.message);
-                dbus_error_free(&error);
-                return;
-            }
+    good = libhal_device_query_capability(context, udi, u->capability, &error);
+    if (dbus_error_is_set(&error) || !good)
+        goto finish;
 
 
-            if (good)
-                u->capability = CAPABILITY_OSS;
+    if (!hal_device_add(u, udi))
+        pa_log_debug("Not loaded device %s", udi);
+    else
+        pa_log_debug("Loaded device %s", udi);
 
 
-#endif
-#if defined(HAVE_OSS) && defined(HAVE_ALSA)
-        }
-#endif
+finish:
+    if (dbus_error_is_set(&error)) {
+        if (!dbus_error_has_name(&error, "org.freedesktop.Hal.NoSuchProperty"))
+            pa_log_error("D-Bus error while parsing HAL data: %s: %s", error.name, error.message);
+        dbus_error_free(&error);
     }
     }
-
-    if (!good)
-        return;
-
-    /* actually add the device 1/2 second later */
-    t = pa_xnew(struct timerdata, 1);
-    t->u = u;
-    t->udi = pa_xstrdup(udi);
-
-    pa_gettimeofday(&tv);
-    pa_timeval_add(&tv, 500000);
-    u->core->mainloop->time_new(u->core->mainloop, &tv, device_added_time_cb, t);
 }
 
 static void device_removed_cb(LibHalContext* context, const char *udi) {
     struct device *d;
     struct userdata *u;
 
 }
 
 static void device_removed_cb(LibHalContext* context, const char *udi) {
     struct device *d;
     struct userdata *u;
 
+    pa_assert(context);
+    pa_assert(udi);
+
     pa_assert_se(u = libhal_ctx_get_user_data(context));
 
     pa_assert_se(u = libhal_ctx_get_user_data(context));
 
-    pa_log_debug("Device removed: %s", udi);
+    if (!(d = pa_hashmap_get(u->devices, udi)))
+        return;
 
 
-    if ((d = pa_hashmap_remove(u->devices, udi))) {
-        pa_module_unload_by_index(u->core, d->index);
-        hal_device_free(d);
-    }
+    pa_hashmap_remove(u->devices, d->originating_udi);
+    pa_hashmap_remove(u->devices, d->udi);
+
+    pa_log_debug("Removing HAL device: %s", d->originating_udi);
+
+    pa_module_unload_request_by_index(u->core, d->module, TRUE);
+    device_free(d);
 }
 
 static void new_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
     struct userdata *u;
 
 }
 
 static void new_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
     struct userdata *u;
 
+    pa_assert(context);
+    pa_assert(udi);
+    pa_assert(capability);
+
     pa_assert_se(u = libhal_ctx_get_user_data(context));
 
     pa_assert_se(u = libhal_ctx_get_user_data(context));
 
-    if (!u->capability || strcmp(u->capability, capability) == 0)
+    if (pa_streq(u->capability, capability))
         /* capability we care about, pretend it's a new device */
         device_added_cb(context, udi);
 }
         /* capability we care about, pretend it's a new device */
         device_added_cb(context, udi);
 }
@@ -523,20 +520,24 @@ static void new_capability_cb(LibHalContext *context, const char *udi, const cha
 static void lost_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
     struct userdata *u;
 
 static void lost_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
     struct userdata *u;
 
+    pa_assert(context);
+    pa_assert(udi);
+    pa_assert(capability);
+
     pa_assert_se(u = libhal_ctx_get_user_data(context));
 
     pa_assert_se(u = libhal_ctx_get_user_data(context));
 
-    if (u->capability && strcmp(u->capability, capability) == 0)
+    if (pa_streq(u->capability, capability))
         /* capability we care about, pretend it was removed */
         device_removed_cb(context, udi);
 }
 
 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
         /* capability we care about, pretend it was removed */
         device_removed_cb(context, udi);
 }
 
 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
-    struct userdata*u = userdata;
+    struct userdata*u;
     DBusError error;
 
     pa_assert(bus);
     pa_assert(message);
     DBusError error;
 
     pa_assert(bus);
     pa_assert(message);
-    pa_assert(u);
+    pa_assert_se(u = userdata);
 
     dbus_error_init(&error);
 
 
     dbus_error_init(&error);
 
@@ -548,13 +549,14 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
     if (dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLAdded") ||
         dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLRemoved")) {
         uint32_t uid;
     if (dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLAdded") ||
         dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLRemoved")) {
         uint32_t uid;
-        int suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0;
+        pa_bool_t suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0;
 
         if (!dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
             pa_log_error("Failed to parse ACL message: %s: %s", error.name, error.message);
             goto finish;
         }
 
 
         if (!dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
             pa_log_error("Failed to parse ACL message: %s: %s", error.name, error.message);
             goto finish;
         }
 
+        /* Check if this is about us? */
         if (uid == getuid() || uid == geteuid()) {
             struct device *d;
             const char *udi;
         if (uid == getuid() || uid == geteuid()) {
             struct device *d;
             const char *udi;
@@ -562,47 +564,45 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
             udi = dbus_message_get_path(message);
 
             if ((d = pa_hashmap_get(u->devices, udi))) {
             udi = dbus_message_get_path(message);
 
             if ((d = pa_hashmap_get(u->devices, udi))) {
-                int send_acl_race_fix_message = 0;
-
-                d->acl_race_fix = 0;
+                pa_bool_t send_acl_race_fix_message = FALSE;
+                d->acl_race_fix = FALSE;
 
                 if (d->sink_name) {
                     pa_sink *sink;
 
 
                 if (d->sink_name) {
                     pa_sink *sink;
 
-                    if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) {
-                        int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
-
-                        if (prev_suspended && !suspend) {
-                            /* resume */
-                            if (pa_sink_suspend(sink, 0) >= 0)
-                                pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, 0);
-                            else
-                                d->acl_race_fix = 1;
-
-                        } else if (!prev_suspended && suspend) {
-                            /* suspend */
-                            if (pa_sink_suspend(sink, 1) >= 0)
-                                send_acl_race_fix_message = 1;
-                        }
+                    if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK))) {
+                        pa_bool_t success = pa_sink_suspend(sink, suspend, PA_SUSPEND_SESSION) >= 0;
+
+                        if (!success && !suspend)
+                            d->acl_race_fix = TRUE; /* resume failed, let's try again */
+                        else if (suspend)
+                            send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
                     }
                 }
 
                 if (d->source_name) {
                     pa_source *source;
 
                     }
                 }
 
                 if (d->source_name) {
                     pa_source *source;
 
-                    if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0))) {
-                        int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED;
+                    if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE))) {
+                        pa_bool_t success = pa_source_suspend(source, suspend, PA_SUSPEND_SESSION) >= 0;
 
 
-                        if (prev_suspended && !suspend) {
-                            /* resume */
-                            if (pa_source_suspend(source, 0) < 0)
-                                d->acl_race_fix = 1;
+                        if (!success && !suspend)
+                            d->acl_race_fix = TRUE; /* resume failed, let's try again */
+                        else if (suspend)
+                            send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
+                    }
+                }
 
 
-                        } else if (!prev_suspended && suspend) {
-                            /* suspend */
-                            if (pa_source_suspend(source, 0) >= 0)
-                                send_acl_race_fix_message = 1;
-                        }
+                if (d->card_name) {
+                    pa_card *card;
+
+                    if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) {
+                        pa_bool_t success = pa_card_suspend(card, suspend, PA_SUSPEND_SESSION) >= 0;
+
+                        if (!success && !suspend)
+                            d->acl_race_fix = TRUE; /* resume failed, let's try again */
+                        else if (suspend)
+                            send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
                     }
                 }
 
                     }
                 }
 
@@ -615,6 +615,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
 
             } else if (!suspend)
                 device_added_cb(u->context, udi);
 
             } else if (!suspend)
                 device_added_cb(u->context, udi);
+
         }
 
     } else if (dbus_message_is_signal(message, "org.pulseaudio.Server", "DirtyGiveUpMessage")) {
         }
 
     } else if (dbus_message_is_signal(message, "org.pulseaudio.Server", "DirtyGiveUpMessage")) {
@@ -623,52 +624,49 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
            sever has closed the device. We can remove this as
            soon as HAL learns frevoke() */
 
            sever has closed the device. We can remove this as
            soon as HAL learns frevoke() */
 
-        const char *udi;
         struct device *d;
         struct device *d;
+        const char *udi;
 
         udi = dbus_message_get_path(message);
 
 
         udi = dbus_message_get_path(message);
 
-        if ((d = pa_hashmap_get(u->devices, udi)) && d->acl_race_fix) {
-            pa_log_debug("Got dirty give up message for '%s', trying resume ...", udi);
-
-            d->acl_race_fix = 0;
-
-            if (d->sink_name) {
-                pa_sink *sink;
+        if ((d = pa_hashmap_get(u->devices, udi))) {
 
 
-                if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) {
+            if (d->acl_race_fix) {
+                d->acl_race_fix = FALSE;
+                pa_log_debug("Got dirty give up message for '%s', trying resume ...", udi);
 
 
-                    int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
+                if (d->sink_name) {
+                    pa_sink *sink;
 
 
-                    if (prev_suspended) {
-                        /* resume */
-                        if (pa_sink_suspend(sink, 0) >= 0)
-                            pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, 0);
-                    }
+                    if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK)))
+                        pa_sink_suspend(sink, FALSE, PA_SUSPEND_SESSION);
                 }
                 }
-            }
 
 
-            if (d->source_name) {
-                pa_source *source;
+                if (d->source_name) {
+                    pa_source *source;
 
 
-                if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0))) {
+                    if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE)))
+                        pa_source_suspend(source, FALSE, PA_SUSPEND_SESSION);
+                }
 
 
-                    int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED;
+                if (d->card_name) {
+                    pa_card *card;
 
 
-                    if (prev_suspended)
-                        pa_source_suspend(source, 0);
+                    if ((card = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_CARD)))
+                        pa_card_suspend(card, FALSE, PA_SUSPEND_SESSION);
                 }
             }
 
         } else
             /* Yes, we don't check the UDI for validity, but hopefully HAL will */
             device_added_cb(u->context, udi);
                 }
             }
 
         } else
             /* Yes, we don't check the UDI for validity, but hopefully HAL will */
             device_added_cb(u->context, udi);
+
     }
 
 finish:
     dbus_error_free(&error);
 
     }
 
 finish:
     dbus_error_free(&error);
 
-    return DBUS_HANDLER_RESULT_HANDLED;
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
 static void hal_context_free(LibHalContext* hal_context) {
 }
 
 static void hal_context_free(LibHalContext* hal_context) {
@@ -682,18 +680,20 @@ static void hal_context_free(LibHalContext* hal_context) {
     dbus_error_free(&error);
 }
 
     dbus_error_free(&error);
 }
 
-static LibHalContext* hal_context_new(pa_core* c, DBusConnection *conn) {
+static LibHalContext* hal_context_new(DBusConnection *connection) {
     DBusError error;
     LibHalContext *hal_context = NULL;
 
     dbus_error_init(&error);
 
     DBusError error;
     LibHalContext *hal_context = NULL;
 
     dbus_error_init(&error);
 
+    pa_assert(connection);
+
     if (!(hal_context = libhal_ctx_new())) {
         pa_log_error("libhal_ctx_new() failed");
         goto fail;
     }
 
     if (!(hal_context = libhal_ctx_new())) {
         pa_log_error("libhal_ctx_new() failed");
         goto fail;
     }
 
-    if (!libhal_ctx_set_dbus_connection(hal_context, conn)) {
+    if (!libhal_ctx_set_dbus_connection(hal_context, connection)) {
         pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);
         goto fail;
     }
         pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);
         goto fail;
     }
@@ -716,9 +716,7 @@ fail:
 
 int pa__init(pa_module*m) {
     DBusError error;
 
 int pa__init(pa_module*m) {
     DBusError error;
-    pa_dbus_connection *conn;
     struct userdata *u = NULL;
     struct userdata *u = NULL;
-    LibHalContext *hal_context = NULL;
     int n = 0;
     pa_modargs *ma;
     const char *api;
     int n = 0;
     pa_modargs *ma;
     const char *api;
@@ -732,87 +730,81 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
         goto fail;
     }
 
-    if ((api = pa_modargs_get_value(ma, "api", NULL))) {
-        int good = 0;
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
 
 #ifdef HAVE_ALSA
 
 #ifdef HAVE_ALSA
-        if (strcmp(api, CAPABILITY_ALSA) == 0) {
-            good = 1;
-            api = CAPABILITY_ALSA;
-        }
+    u->use_tsched = TRUE;
+
+    if (pa_modargs_get_value_boolean(ma, "tsched", &u->use_tsched) < 0) {
+        pa_log("Failed to parse tsched argument.");
+        goto fail;
+    }
+
+    api = pa_modargs_get_value(ma, "api", "alsa");
+
+    if (pa_streq(api, "alsa"))
+        u->capability = CAPABILITY_ALSA;
+#else
+    api = pa_modargs_get_value(ma, "api", "oss");
 #endif
 #endif
-#ifdef HAVE_OSS
-        if (strcmp(api, CAPABILITY_OSS) == 0) {
-            good = 1;
-            api = CAPABILITY_OSS;
-        }
+
+#ifdef HAVE_OSS_OUTPUT
+    if (pa_streq(api, "oss"))
+        u->capability = CAPABILITY_OSS;
 #endif
 
 #endif
 
-        if (!good) {
-            pa_log_error("Invalid API specification.");
-            goto fail;
-        }
+    if (!u->capability) {
+        pa_log_error("Invalid API specification.");
+        goto fail;
     }
 
     }
 
-    if (!(conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
-        if (conn)
-            pa_dbus_connection_unref(conn);
+#ifdef HAVE_OSS_OUTPUT
+    if (pa_modargs_get_value_boolean(ma, "subdevices", &u->init_subdevs) < 0) {
+        pa_log("Failed to parse subdevices= argument.");
+        goto fail;
+    }
+#endif
+
+    if (!(u->connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
         pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message);
         goto fail;
     }
 
         pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message);
         goto fail;
     }
 
-    if (!(hal_context = hal_context_new(m->core, pa_dbus_connection_get(conn)))) {
+    if (!(u->context = hal_context_new(pa_dbus_connection_get(u->connection)))) {
         /* pa_hal_context_new() logs appropriate errors */
         /* pa_hal_context_new() logs appropriate errors */
-        pa_dbus_connection_unref(conn);
         goto fail;
     }
 
         goto fail;
     }
 
-    u = pa_xnew(struct userdata, 1);
-    u->core = m->core;
-    u->context = hal_context;
-    u->connection = conn;
-    u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-    u->capability = api;
-    m->userdata = u;
-
-#ifdef HAVE_ALSA
-    n = hal_device_add_all(u, CAPABILITY_ALSA);
-#endif
-#if defined(HAVE_ALSA) && defined(HAVE_OSS)
-    if (n <= 0)
-#endif
-#ifdef HAVE_OSS
-        n += hal_device_add_all(u, CAPABILITY_OSS);
-#endif
+    n = hal_device_add_all(u);
 
 
-    libhal_ctx_set_user_data(hal_context, u);
-    libhal_ctx_set_device_added(hal_context, device_added_cb);
-    libhal_ctx_set_device_removed(hal_context, device_removed_cb);
-    libhal_ctx_set_device_new_capability(hal_context, new_capability_cb);
-    libhal_ctx_set_device_lost_capability(hal_context, lost_capability_cb);
+    libhal_ctx_set_user_data(u->context, u);
+    libhal_ctx_set_device_added(u->context, device_added_cb);
+    libhal_ctx_set_device_removed(u->context, device_removed_cb);
+    libhal_ctx_set_device_new_capability(u->context, new_capability_cb);
+    libhal_ctx_set_device_lost_capability(u->context, lost_capability_cb);
 
 
-    if (!libhal_device_property_watch_all(hal_context, &error)) {
+    if (!libhal_device_property_watch_all(u->context, &error)) {
         pa_log_error("Error monitoring device list: %s: %s", error.name, error.message);
         goto fail;
     }
 
         pa_log_error("Error monitoring device list: %s: %s", error.name, error.message);
         goto fail;
     }
 
-    if (!dbus_connection_add_filter(pa_dbus_connection_get(conn), filter_cb, u, NULL)) {
+    if (!dbus_connection_add_filter(pa_dbus_connection_get(u->connection), filter_cb, u, NULL)) {
         pa_log_error("Failed to add filter function");
         goto fail;
     }
         pa_log_error("Failed to add filter function");
         goto fail;
     }
+    u->filter_added = TRUE;
 
 
-    dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',sender='org.freedesktop.Hal', interface='org.freedesktop.Hal.Device.AccessControl'", &error);
-    if (dbus_error_is_set(&error)) {
+    if (pa_dbus_add_matches(
+                pa_dbus_connection_get(u->connection), &error,
+                "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLAdded'",
+                "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLRemoved'",
+                "type='signal',interface='org.pulseaudio.Server',member='DirtyGiveUpMessage'", NULL) < 0) {
         pa_log_error("Unable to subscribe to HAL ACL signals: %s: %s", error.name, error.message);
         goto fail;
     }
 
         pa_log_error("Unable to subscribe to HAL ACL signals: %s: %s", error.name, error.message);
         goto fail;
     }
 
-    dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',interface='org.pulseaudio.Server'", &error);
-    if (dbus_error_is_set(&error)) {
-        pa_log_error("Unable to subscribe to PulseAudio signals: %s: %s", error.name, error.message);
-        goto fail;
-    }
-
     pa_log_info("Loaded %i modules.", n);
 
     pa_modargs_free(ma);
     pa_log_info("Loaded %i modules.", n);
 
     pa_modargs_free(ma);
@@ -829,7 +821,6 @@ fail:
     return -1;
 }
 
     return -1;
 }
 
-
 void pa__done(pa_module *m) {
     struct userdata *u;
 
 void pa__done(pa_module *m) {
     struct userdata *u;
 
@@ -841,11 +832,29 @@ void pa__done(pa_module *m) {
     if (u->context)
         hal_context_free(u->context);
 
     if (u->context)
         hal_context_free(u->context);
 
-    if (u->devices)
-        pa_hashmap_free(u->devices, hal_device_free_cb, NULL);
+    if (u->devices) {
+        struct device *d;
 
 
-    if (u->connection)
+        while ((d = pa_hashmap_first(u->devices))) {
+            pa_hashmap_remove(u->devices, d->udi);
+            pa_hashmap_remove(u->devices, d->originating_udi);
+            device_free(d);
+        }
+
+        pa_hashmap_free(u->devices, NULL, NULL);
+    }
+
+    if (u->connection) {
+        pa_dbus_remove_matches(
+                pa_dbus_connection_get(u->connection),
+                "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLAdded'",
+                "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLRemoved'",
+                "type='signal',interface='org.pulseaudio.Server',member='DirtyGiveUpMessage'", NULL);
+
+        if (u->filter_added)
+            dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), filter_cb, u);
         pa_dbus_connection_unref(u->connection);
         pa_dbus_connection_unref(u->connection);
+    }
 
     pa_xfree(u);
 }
 
     pa_xfree(u);
 }