]> 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
 
     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)
+#if defined(HAVE_ALSA) && defined(HAVE_OSS_OUTPUT)
 PA_MODULE_USAGE("api=<alsa or oss> "
 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_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
 #endif
+PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-hal-detect!");
 
 struct device {
     char *udi, *originating_udi;
 
 struct device {
     char *udi, *originating_udi;
@@ -81,6 +80,10 @@ struct userdata {
 #ifdef HAVE_ALSA
     pa_bool_t use_tsched;
 #endif
 #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,6 +93,9 @@ static const char* const valid_modargs[] = {
     "api",
 #ifdef HAVE_ALSA
     "tsched",
     "api",
 #ifdef HAVE_ALSA
     "tsched",
+#endif
+#ifdef HAVE_OSS_OUTPUT
+    "subdevices",
 #endif
     NULL
 };
 #endif
     NULL
 };
@@ -121,6 +127,7 @@ static const char *strip_udi(const char *udi) {
 enum alsa_type {
     ALSA_TYPE_PLAYBACK,
     ALSA_TYPE_CAPTURE,
 enum alsa_type {
     ALSA_TYPE_PLAYBACK,
     ALSA_TYPE_CAPTURE,
+    ALSA_TYPE_CONTROL,
     ALSA_TYPE_OTHER
 };
 
     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;
         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);
 
@@ -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)) {
 
 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);
     }
 
         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;
 
 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;
     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);
 
     /* 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;
 
         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 */
         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));
         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);
 
     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
 
 
 #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;
     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);
 
     /* 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;
         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 */
     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 */
         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;
     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_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
     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++) {
         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++;
                 count++;
-            else
+                pa_log_debug("Loaded device %s", udis[i]);
+            } else
                 pa_log_debug("Not loaded device %s", udis[i]);
         }
     }
                 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);
 
     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)) {
 
 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);
     }
 }
         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_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 */
 
                         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_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 */
 
                         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_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 */
 
                         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
     } 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 *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)))
                 }
 
                 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)))
                 }
 
                 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);
 
             /* 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:
     }
 
 finish:
@@ -712,12 +730,9 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
         goto fail;
     }
 
-    m->userdata = u = pa_xnew(struct userdata, 1);
+    m->userdata = u = pa_xnew0(struct userdata, 1);
     u->core = m->core;
     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->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-    u->capability = NULL;
 
 #ifdef HAVE_ALSA
     u->use_tsched = TRUE;
 
 #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
 
     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
     if (pa_streq(api, "oss"))
         u->capability = CAPABILITY_OSS;
 #endif
@@ -745,6 +760,13 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
         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;
     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;
     }
         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,
 
     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);
 
                 "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);
     }
 
         pa_dbus_connection_unref(u->connection);
     }