]> code.delx.au - pulseaudio/blobdiff - src/modules/module-hal-detect.c
echo-cancel: Increase threshold for resyncing, make it configurable
[pulseaudio] / src / modules / module-hal-detect.c
index 8b1f0b2d2a0654489c47dc633dacf7ea381fd651..5ca683a35990be71aea64f66c2922d26aa267be6 100644 (file)
@@ -6,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
-    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
 #include <errno.h>
 #include <stdlib.h>
 #include <sys/types.h>
-#include <sys/stat.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/core-scache.h>
 #include <pulsecore/modargs.h>
+#include <pulsecore/dbus-shared.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);
-#if defined(HAVE_ALSA) && defined(HAVE_OSS)
+#if defined(HAVE_ALSA) && defined(HAVE_OSS_OUTPUT)
 PA_MODULE_USAGE("api=<alsa or oss> "
-                "tsched=<enable system timer based scheduling mode?>");
+                "tsched=<enable system timer based scheduling mode?> "
+                "subdevices=<init all subdevices>");
 #elif defined(HAVE_ALSA)
 PA_MODULE_USAGE("api=<alsa> "
                 "tsched=<enable system timer based scheduling mode?>");
-#elif defined(HAVE_OSS)
-PA_MODULE_USAGE("api=<oss>");
+#elif defined(HAVE_OSS_OUTPUT)
+PA_MODULE_USAGE("api=<oss> "
+                "subdevices=<init all subdevices>");
 #endif
+PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-hal-detect!");
 
 struct device {
     char *udi, *originating_udi;
@@ -81,6 +80,10 @@ struct userdata {
 #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"
@@ -90,6 +93,9 @@ static const char* const valid_modargs[] = {
     "api",
 #ifdef HAVE_ALSA
     "tsched",
+#endif
+#ifdef HAVE_OSS_OUTPUT
+    "subdevices",
 #endif
     NULL
 };
@@ -121,6 +127,7 @@ static const char *strip_udi(const char *udi) {
 enum alsa_type {
     ALSA_TYPE_PLAYBACK,
     ALSA_TYPE_CAPTURE,
+    ALSA_TYPE_CONTROL,
     ALSA_TYPE_OTHER
 };
 
@@ -141,6 +148,8 @@ static enum alsa_type hal_alsa_device_get_type(LibHalContext *context, const cha
         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);
 
@@ -171,7 +180,8 @@ static pa_bool_t hal_alsa_device_is_modem(LibHalContext *context, const char *ud
 
 finish:
     if (dbus_error_is_set(&error)) {
-        pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
+        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);
     }
 
@@ -180,7 +190,7 @@ finish:
 
 static int hal_device_load_alsa(struct userdata *u, const char *udi, struct device *d) {
     enum alsa_type type;
-    int device, card;
+    int card;
     DBusError error;
     pa_module *m;
     char *args, *originating_udi = NULL, *card_name = NULL;
@@ -193,16 +203,24 @@ static int hal_device_load_alsa(struct userdata *u, const char *udi, struct devi
 
     /* We only care for PCM devices */
     type = hal_alsa_device_get_type(u->context, udi);
-    if (type == ALSA_TYPE_OTHER)
-        goto fail;
 
-    /* We don't care for modems */
-    if (hal_alsa_device_is_modem(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;
 
-    /* We only care for the main device */
-    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;
 
     /* We store only one entry per card, hence we look for the originating device */
@@ -220,7 +238,7 @@ static int hal_device_load_alsa(struct userdata *u, const char *udi, struct devi
         goto fail;
 
     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, strip_udi(originating_udi), card_name, (int) u->use_tsched);
+    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);
@@ -249,9 +267,9 @@ fail:
 
 #endif
 
-#ifdef HAVE_OSS
+#ifdef HAVE_OSS_OUTPUT
 
-static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi) {
+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;
     pa_bool_t r = FALSE;
@@ -281,7 +299,7 @@ static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi)
 
     /* 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)
+    if (dbus_error_is_set(&error) || (device != 0 && init_subdevices == FALSE))
         goto finish;
 
     r = TRUE;
@@ -311,7 +329,7 @@ static int hal_device_load_oss(struct userdata *u, const char *udi, struct devic
     pa_assert(d);
 
     /* We only care for OSS PCM devices */
-    if (!hal_oss_device_is_pcm(u->context, udi))
+    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 */
@@ -375,12 +393,13 @@ static struct device* hal_device_add(struct userdata *u, const char *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
     if (pa_streq(u->capability, CAPABILITY_ALSA))
         r = hal_device_load_alsa(u, udi,  d);
 #endif
-#ifdef HAVE_OSS
+#ifdef HAVE_OSS_OUTPUT
     if (pa_streq(u->capability, CAPABILITY_OSS))
         r = hal_device_load_oss(u, udi, d);
 #endif
@@ -413,11 +432,10 @@ static int hal_device_add_all(struct userdata *u) {
         int i;
 
         for (i = 0; i < n; i++) {
-            struct device *d;
-
-            if ((d = hal_device_add(u, udis[i])))
+            if (hal_device_add(u, udis[i])) {
                 count++;
-            else
+                pa_log_debug("Loaded device %s", udis[i]);
+            } else
                 pa_log_debug("Not loaded device %s", udis[i]);
         }
     }
@@ -453,10 +471,13 @@ static void device_added_cb(LibHalContext *context, const char *udi) {
 
     if (!hal_device_add(u, udi))
         pa_log_debug("Not loaded device %s", udi);
+    else
+        pa_log_debug("Loaded device %s", udi);
 
 finish:
     if (dbus_error_is_set(&error)) {
-        pa_log_error("D-Bus error while parsing HAL data: %s: %s", error.name, error.message);
+        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);
     }
 }
@@ -550,7 +571,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
                     pa_sink *sink;
 
                     if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK))) {
-                        pa_bool_t success = pa_sink_suspend(sink, suspend) >= 0;
+                        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 */
@@ -563,7 +584,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
                     pa_source *source;
 
                     if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE))) {
-                        pa_bool_t success = pa_source_suspend(source, suspend) >= 0;
+                        pa_bool_t success = pa_source_suspend(source, suspend, PA_SUSPEND_SESSION) >= 0;
 
                         if (!success && !suspend)
                             d->acl_race_fix = TRUE; /* resume failed, let's try again */
@@ -576,7 +597,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
                     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) >= 0;
+                        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 */
@@ -597,8 +618,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
 
         }
 
-        return DBUS_HANDLER_RESULT_HANDLED;
-
     } else if (dbus_message_is_signal(message, "org.pulseaudio.Server", "DirtyGiveUpMessage")) {
         /* We use this message to avoid a dirty race condition when we
            get an ACLAdded message before the previously owning PA
@@ -620,21 +639,21 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
                     pa_sink *sink;
 
                     if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK)))
-                        pa_sink_suspend(sink, FALSE);
+                        pa_sink_suspend(sink, FALSE, PA_SUSPEND_SESSION);
                 }
 
                 if (d->source_name) {
                     pa_source *source;
 
                     if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE)))
-                        pa_source_suspend(source, FALSE);
+                        pa_source_suspend(source, FALSE, PA_SUSPEND_SESSION);
                 }
 
                 if (d->card_name) {
                     pa_card *card;
 
                     if ((card = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_CARD)))
-                        pa_card_suspend(card, FALSE);
+                        pa_card_suspend(card, FALSE, PA_SUSPEND_SESSION);
                 }
             }
 
@@ -642,7 +661,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
             /* Yes, we don't check the UDI for validity, but hopefully HAL will */
             device_added_cb(u->context, udi);
 
-        return DBUS_HANDLER_RESULT_HANDLED;
     }
 
 finish:
@@ -712,12 +730,9 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    m->userdata = u = pa_xnew(struct userdata, 1);
+    m->userdata = u = pa_xnew0(struct userdata, 1);
     u->core = m->core;
-    u->context = NULL;
-    u->connection = NULL;
     u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-    u->capability = NULL;
 
 #ifdef HAVE_ALSA
     u->use_tsched = TRUE;
@@ -735,7 +750,7 @@ int pa__init(pa_module*m) {
     api = pa_modargs_get_value(ma, "api", "oss");
 #endif
 
-#ifdef HAVE_OSS
+#ifdef HAVE_OSS_OUTPUT
     if (pa_streq(api, "oss"))
         u->capability = CAPABILITY_OSS;
 #endif
@@ -745,6 +760,13 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
+#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;
@@ -772,6 +794,7 @@ int pa__init(pa_module*m) {
         pa_log_error("Failed to add filter function");
         goto fail;
     }
+    u->filter_added = TRUE;
 
     if (pa_dbus_add_matches(
                 pa_dbus_connection_get(u->connection), &error,
@@ -828,7 +851,8 @@ void pa__done(pa_module *m) {
                 "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLRemoved'",
                 "type='signal',interface='org.pulseaudio.Server',member='DirtyGiveUpMessage'", NULL);
 
-        dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), filter_cb, u);
+        if (u->filter_added)
+            dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), filter_cb, u);
         pa_dbus_connection_unref(u->connection);
     }