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;
#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"
"api",
#ifdef HAVE_ALSA
"tsched",
+#endif
+#ifdef HAVE_OSS_OUTPUT
+ "subdevices",
#endif
NULL
};
enum alsa_type {
ALSA_TYPE_PLAYBACK,
ALSA_TYPE_CAPTURE,
+ ALSA_TYPE_CONTROL,
ALSA_TYPE_OTHER
};
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);
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);
}
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;
/* 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 */
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);
#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;
/* 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;
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 */
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
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]);
}
}
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);
}
}
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 */
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 */
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 */
}
- 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
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);
}
}
/* 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:
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;
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
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;
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,
"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);
}