Attached is the current patch for HAL/DBus integration. It takes care of most
of the memory management issues in the last one as well as delaying the actual
loading of the modules so that it always actually succeeds.
Currently missing is OSS support, although it shouldn't be too difficult to add
as much of the infrastructure is there, just unimplemented.
There is also a rather serious issue with hot-remove at the moment, due to the
ALSA modules continuously trying to read/write to the now-non-existant device
which blocks further processing to respond to the device removed events and
actually unload the modules. I don't have enough experience with said modules
(or ALSA in general) to properly debug and fix them at the moment. My limited
attempts to do so have, so far, resulted in no success ;-P
Anyway, enjoy the extant patch, please give feedback so I can make it acceptable
for merging.
The patch should apply cleanly against revision 1186 with patch -p1.
Thanks,
--Shahms
diff -urN pa-upstream/configure.ac pa-hotplug/configure.ac
--- pa-upstream/configure.ac 2006-08-01 14:04:43.000000000 -0700
+++ pa-hotplug/configure.ac 2006-08-03 15:44:21.000000000 -0700
@@ -637,6 +637,37 @@
AC_SUBST(LIRC_LIBS)
AM_CONDITIONAL([HAVE_LIRC], [test "x$HAVE_LIRC" = x1])
+#### HAL support (optional) ####
+
+AC_ARG_ENABLE([hal],
+ AC_HELP_STRING([--disable-hal], [Disable optional HAL support]),
+ [
+ case "${enableval}" in
+ yes) hal=yes ;;
+ no) hal=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-hal) ;;
+ esac
+ ],
+ [hal=auto])
+
+if test "x${hal}" != xno ; then
+ PKG_CHECK_MODULES(HAL, [ hal >= 0.5.7 ],
+ HAVE_HAL=1,
+ [
+ HAVE_HAL=0
+ if test "x$hal" = xyes ; then
+ AC_MSG_ERROR([*** HAL support not found])
+ fi
+ ])
+else
+ HAVE_HAL=0
+fi
+
+AC_SUBST(HAL_CFLAGS)
+AC_SUBST(HAL_LIBS)
+AC_SUBST(HAVE_HAL)
+AM_CONDITIONAL([HAVE_HAL], [test "x$HAVE_HAL" = x1])
+
#### PulseAudio system group & user #####
AC_ARG_WITH(system_user, AS_HELP_STRING([--with-system-user=<user>],[User for running the PulseAudio daemon as a system-wide instance (pulse)]))
@@ -780,6 +811,11 @@
ENABLE_LIRC=yes
fi
+ENABLE_HAL=no
+if test "x$HAVE_HAL" = "x1" ; then
+ ENABLE_HAL=yes
+fi
+
ENABLE_TCPWRAP=no
if test "x${LIBWRAP_LIBS}" != x ; then
ENABLE_TCPWRAP=yes
@@ -804,6 +840,7 @@
Enable Jack: ${ENABLE_JACK}
Enable Async DNS: ${ENABLE_LIBASYNCNS}
Enable LIRC: ${ENABLE_LIRC}
+ Enable HAL: ${ENABLE_HAL}
Enable TCP Wrappers: ${ENABLE_TCPWRAP}
System User: ${PA_SYSTEM_USER}
System Group: ${PA_SYSTEM_GROUP}
diff -urN pa-upstream/src/Makefile.am pa-hotplug/src/Makefile.am
--- pa-upstream/src/Makefile.am 2006-07-28 15:52:28.000000000 -0700
+++ pa-hotplug/src/Makefile.am 2006-08-03 15:44:21.000000000 -0700
@@ -899,6 +899,12 @@
module-waveout.la
endif
+if HAVE_HAL
+modlibexec_LTLIBRARIES += \
+ libdbus-util.la \
+ module-hal-detect.la
+endif
+
# These are generated by a M4 script
SYMDEF_FILES = \
@@ -942,6 +948,7 @@
modules/module-jack-sink-symdef.h \
modules/module-jack-source-symdef.h \
modules/module-volume-restore-symdef.h \
+ modules/module-hal-detect-symdef.h \
modules/gconf/module-gconf-symdef.h
EXTRA_DIST += $(SYMDEF_FILES)
@@ -1182,6 +1189,17 @@
module_jack_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(JACK_LIBS)
module_jack_source_la_CFLAGS = $(AM_LIBADD) $(JACK_CFLAGS)
+# HAL
+libdbus_util_la_SOURCES = modules/dbus-util.c modules/dbus-util.h
+libdbus_util_la_LDFLAGS = -avoid-version
+libdbus_util_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore.la
+libdbus_util_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)
+
+module_hal_detect_la_SOURCES = modules/module-hal-detect.c
+module_hal_detect_la_LDFLAGS = -module -avoid-version
+module_hal_detect_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore.la libdbus-util.la
+module_hal_detect_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)
+
# GConf support
module_gconf_la_SOURCES = modules/gconf/module-gconf.c
module_gconf_la_LDFLAGS = -module -avoid-version
diff -urN pa-upstream/src/modules/dbus-util.c pa-hotplug/src/modules/dbus-util.c
--- pa-upstream/src/modules/dbus-util.c 1969-12-31 16:00:00.000000000 -0800
+++ pa-hotplug/src/modules/dbus-util.c 2006-08-04 11:35:57.000000000 -0700
@@ -0,0 +1,354 @@
+/***
+ This file is part of PulseAudio.
+
+ 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,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <pulsecore/log.h>
+#include <pulsecore/props.h>
+#include <pulse/xmalloc.h>
+
+#include "dbus-util.h"
+
+struct pa_dbus_connection {
+ int refcount;
+ pa_core *core;
+ DBusConnection *connection;
+ const char *property_name;
+ pa_defer_event* dispatch_event;
+};
+
+static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata)
+{
+ DBusConnection *conn = (DBusConnection *) userdata;
+ if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE) {
+ /* no more data to process, disable the deferred */
+ ea->defer_enable(ev, 0);
+ }
+}
+
+/* DBusDispatchStatusFunction callback for the pa mainloop */
+static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status,
+ void *userdata)
+{
+ pa_dbus_connection *c = (pa_dbus_connection*) userdata;
+ switch(status) {
+ case DBUS_DISPATCH_COMPLETE:
+ c->core->mainloop->defer_enable(c->dispatch_event, 0);
+ break;
+ case DBUS_DISPATCH_DATA_REMAINS:
+ case DBUS_DISPATCH_NEED_MEMORY:
+ default:
+ c->core->mainloop->defer_enable(c->dispatch_event, 1);
+ break;
+ }
+}
+
+static pa_io_event_flags_t
+get_watch_flags(DBusWatch *watch)
+{
+ unsigned int flags = dbus_watch_get_flags(watch);
+ pa_io_event_flags_t events = PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
+
+ /* no watch flags for disabled watches */
+ if (!dbus_watch_get_enabled(watch))
+ return PA_IO_EVENT_NULL;
+
+ if (flags & DBUS_WATCH_READABLE)
+ events |= PA_IO_EVENT_INPUT;
+ if (flags & DBUS_WATCH_WRITABLE)
+ events |= PA_IO_EVENT_OUTPUT;
+
+ return events;
+}
+
+static void timeval_next(struct timeval *tv, int millint)
+{
+ /* number of seconds in the milli-second interval */
+ tv->tv_sec += (millint / 1000);
+ /* milliseconds minus the seconds portion, converted to microseconds */
+ tv->tv_usec += (millint - tv->tv_sec * 1000) * 1000;
+}
+
+/* pa_io_event_cb_t IO event handler */
+static void handle_io_event(PA_GCC_UNUSED pa_mainloop_api *ea, pa_io_event *e,
+ int fd, pa_io_event_flags_t events, void *userdata)
+{
+ unsigned int flags = 0;
+ DBusWatch *watch = (DBusWatch*) userdata;
+
+ assert(fd == dbus_watch_get_fd(watch));
+
+ if (!dbus_watch_get_enabled(watch)) {
+ pa_log_warn(__FILE__": Asked to handle disabled watch: %p %i",
+ (void *) watch, fd);
+ return;
+ }
+
+ if (events & PA_IO_EVENT_INPUT)
+ flags |= DBUS_WATCH_READABLE;
+ if (events & PA_IO_EVENT_OUTPUT)
+ flags |= DBUS_WATCH_WRITABLE;
+ if (events & PA_IO_EVENT_HANGUP)
+ flags |= DBUS_WATCH_HANGUP;
+ if (events & PA_IO_EVENT_ERROR)
+ flags |= DBUS_WATCH_ERROR;
+
+ dbus_watch_handle(watch, flags);
+}
+
+/* pa_time_event_cb_t timer event handler */
+static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e,
+ const struct timeval *tv, void *userdata)
+{
+ DBusTimeout *timeout = (DBusTimeout*) userdata;
+
+ if (dbus_timeout_get_enabled(timeout)) {
+ struct timeval next = *tv;
+ dbus_timeout_handle(timeout);
+
+ /* restart it for the next scheduled time */
+ timeval_next(&next, dbus_timeout_get_interval(timeout));
+ ea->time_restart(e, &next);
+ }
+}
+
+/* DBusAddWatchFunction callback for pa mainloop */
+static dbus_bool_t add_watch(DBusWatch *watch, void *data)
+{
+ pa_io_event *ev;
+ pa_core *c = (pa_core*) data;
+
+ ev = c->mainloop->io_new(c->mainloop, dbus_watch_get_fd(watch),
+ get_watch_flags(watch),
+ handle_io_event, (void*) watch);
+ if (NULL == ev)
+ return FALSE;
+
+ /* dbus_watch_set_data(watch, (void*) ev, c->mainloop->io_free); */
+ dbus_watch_set_data(watch, (void*) ev, NULL);
+
+ return TRUE;
+}
+
+/* DBusRemoveWatchFunction callback for pa mainloop */
+static void remove_watch(DBusWatch *watch, void *data)
+{
+ pa_core *c = (pa_core*) data;
+ pa_io_event *ev = (pa_io_event*) dbus_watch_get_data(watch);
+
+ /* free the event */
+ if (NULL != ev)
+ c->mainloop->io_free(ev);
+}
+
+/* DBusWatchToggledFunction callback for pa mainloop */
+static void toggle_watch(DBusWatch *watch, void *data)
+{
+ pa_core *c = (pa_core*) data;
+ pa_io_event *ev = (pa_io_event*) dbus_watch_get_data(watch);
+
+ /* get_watch_flags() checks if the watch is enabled */
+ c->mainloop->io_enable(ev, get_watch_flags(watch));
+}
+
+/* DBusAddTimeoutFunction callback for pa mainloop */
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
+{
+ struct timeval tv;
+ pa_time_event *ev;
+ pa_core *c = (pa_core*) data;
+
+ if (!dbus_timeout_get_enabled(timeout))
+ return FALSE;
+
+ if (gettimeofday(&tv, NULL) < 0)
+ return -1;
+
+ timeval_next(&tv, dbus_timeout_get_interval(timeout));
+
+ ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event,
+ (void*) timeout);
+ if (NULL == ev)
+ return FALSE;
+
+ /* dbus_timeout_set_data(timeout, (void*) ev, c->mainloop->time_free); */
+ dbus_timeout_set_data(timeout, (void*) ev, NULL);
+
+ return TRUE;
+}
+
+/* DBusRemoveTimeoutFunction callback for pa mainloop */
+static void remove_timeout(DBusTimeout *timeout, void *data)
+{
+ pa_core *c = (pa_core*) data;
+ pa_time_event *ev = (pa_time_event*) dbus_timeout_get_data(timeout);
+
+ /* free the event */
+ if (NULL != ev)
+ c->mainloop->time_free(ev);
+}
+
+/* DBusTimeoutToggledFunction callback for pa mainloop */
+static void toggle_timeout(DBusTimeout *timeout, void *data)
+{
+ struct timeval tv;
+ pa_core *c = (pa_core*) data;
+ pa_time_event *ev = (pa_time_event*) dbus_timeout_get_data(timeout);
+
+ gettimeofday(&tv, NULL);
+ if (dbus_timeout_get_enabled(timeout)) {
+ timeval_next(&tv, dbus_timeout_get_interval(timeout));
+ c->mainloop->time_restart(ev, &tv);
+ } else {
+ /* set it to expire one second ago */
+ tv.tv_sec -= 1;
+ c->mainloop->time_restart(ev, &tv);
+ }
+}
+
+static void
+pa_dbus_connection_free(pa_dbus_connection *c)
+{
+ assert(c);
+ assert(!dbus_connection_get_is_connected(c->connection));
+
+ /* already disconnected, just free */
+ pa_property_remove(c->core, c->property_name);
+ c->core->mainloop->defer_free(c->dispatch_event);
+ dbus_connection_unref(c->connection);
+ pa_xfree(c);
+}
+
+static void
+wakeup_main(void *userdata)
+{
+ pa_dbus_connection *c = (pa_dbus_connection*) userdata;
+ /* this will wakeup the mainloop and dispatch events, although
+ * it may not be the cleanest way of accomplishing it */
+ c->core->mainloop->defer_enable(c->dispatch_event, 1);
+}
+
+static pa_dbus_connection* pa_dbus_connection_new(pa_core* c, DBusConnection *conn, const char* name)
+{
+ pa_dbus_connection *pconn = pa_xmalloc(sizeof(pa_dbus_connection));
+
+ pconn->refcount = 1;
+ pconn->core = c;
+ pconn->property_name = name;
+ pconn->connection = conn;
+ pconn->dispatch_event = c->mainloop->defer_new(c->mainloop, dispatch_cb,
+ (void*) conn);
+
+ pa_property_set(c, name, pconn);
+
+ return pconn;
+}
+
+DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c)
+{
+ assert(c && c->connection);
+ return c->connection;
+}
+
+void pa_dbus_connection_unref(pa_dbus_connection *c)
+{
+ assert(c);
+
+ /* non-zero refcount, still outstanding refs */
+ if (--(c->refcount))
+ return;
+
+ /* refcount is zero */
+ if (dbus_connection_get_is_connected(c->connection)) {
+ /* disconnect as we have no more internal references */
+ dbus_connection_close(c->connection);
+ /* must process remaining messages, bit of a kludge to
+ * handle both unload and shutdown */
+ while(dbus_connection_read_write_dispatch(c->connection, -1));
+ }
+ pa_dbus_connection_free(c);
+}
+
+pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c)
+{
+ assert(c);
+
+ ++(c->refcount);
+
+ return c;
+}
+
+pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type,
+ DBusError *error)
+{
+ const char* name;
+ DBusConnection *conn;
+ pa_dbus_connection *pconn;
+ static const char sysname[] = "dbus-connection-system";
+ static const char sessname[] = "dbus-connection-session";
+ static const char startname[] = "dbus-connection-starter";
+
+ switch (type) {
+ case DBUS_BUS_SYSTEM:
+ name = sysname;
+ break;
+ case DBUS_BUS_SESSION:
+ name = sessname;
+ break;
+ case DBUS_BUS_STARTER:
+ name = startname;
+ break;
+ default:
+ assert(0); /* never reached */
+ break;
+ }
+
+ if ((pconn = pa_property_get(c, name)))
+ return pa_dbus_connection_ref(pconn);
+
+ /* else */
+ conn = dbus_bus_get_private(type, error);
+ if (conn == NULL || dbus_error_is_set(error)) {
+ return NULL;
+ }
+
+ pconn = pa_dbus_connection_new(c, conn, name);
+
+ /* don't exit on disconnect */
+ dbus_connection_set_exit_on_disconnect(conn, FALSE);
+ /* set up the DBUS call backs */
+ dbus_connection_set_dispatch_status_function(conn, dispatch_status,
+ (void*) pconn, NULL);
+ dbus_connection_set_watch_functions(conn,
+ add_watch,
+ remove_watch,
+ toggle_watch,
+ (void*) c, NULL);
+ dbus_connection_set_timeout_functions(conn,
+ add_timeout,
+ remove_timeout,
+ toggle_timeout,
+ (void*) c, NULL);
+ dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
+
+ return pconn;
+}
diff -urN pa-upstream/src/modules/dbus-util.h pa-hotplug/src/modules/dbus-util.h
--- pa-upstream/src/modules/dbus-util.h 1969-12-31 16:00:00.000000000 -0800
+++ pa-hotplug/src/modules/dbus-util.h 2006-08-04 11:35:57.000000000 -0700
@@ -0,0 +1,36 @@
+#ifndef foodbusutilhfoo
+#define foodbusutilhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ 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,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <dbus/dbus.h>
+
+typedef struct pa_dbus_connection pa_dbus_connection;
+
+/* return the DBusConnection of the specified type for the given core,
+ * like dbus_bus_get(), but integrates the connection with the pa_core */
+pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error);
+
+DBusConnection* pa_dbus_connection_get(pa_dbus_connection *conn);
+
+pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *conn);
+void pa_dbus_connection_unref(pa_dbus_connection *conn);
+
+#endif
diff -urN pa-upstream/src/modules/module-hal-detect.c pa-hotplug/src/modules/module-hal-detect.c
--- pa-upstream/src/modules/module-hal-detect.c 1969-12-31 16:00:00.000000000 -0800
+++ pa-hotplug/src/modules/module-hal-detect.c 2006-08-04 13:42:45.000000000 -0700
@@ -0,0 +1,495 @@
+/***
+ This file is part of PulseAudio.
+
+ 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,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/core-util.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)
+
+static const char* capabilities[] = { "alsa", "oss" };
+
+typedef enum {
+ CAP_ALSA,
+ CAP_OSS,
+ CAP_MAX
+} capability_t;
+
+typedef enum {
+ ALSA_TYPE_SINK,
+ ALSA_TYPE_SOURCE,
+ ALSA_TYPE_OTHER,
+ ALSA_TYPE_MAX
+} alsa_type_t;
+
+struct device {
+ char *udi;
+ pa_module *module;
+};
+
+struct userdata {
+ pa_core *core;
+ pa_subscription *sub;
+ LibHalContext *ctx;
+ capability_t capability;
+ pa_dbus_connection *conn;
+ pa_hashmap *by_udi;
+ pa_hashmap *by_module;
+};
+
+struct timerdata {
+ struct userdata *u;
+ char *udi;
+};
+
+static alsa_type_t hal_device_get_alsa_type(LibHalContext *ctx, const char *udi,
+ DBusError *error)
+{
+ char *type;
+ alsa_type_t t;
+
+ type = libhal_device_get_property_string(ctx, udi, "alsa.type", error);
+ if (!type || dbus_error_is_set(error))
+ return FALSE;
+
+ if (!strcmp(type, "playback")) {
+ t = ALSA_TYPE_SINK;
+ } else if (!strcmp(type, "capture")) {
+ t = ALSA_TYPE_SOURCE;
+ } else {
+ t = ALSA_TYPE_OTHER;
+ }
+ libhal_free_string(type);
+
+ return t;
+}
+
+static int hal_device_get_alsa_card(LibHalContext *ctx, const char *udi,
+ DBusError *error)
+{
+ return libhal_device_get_property_int(ctx, udi, "alsa.card", error);
+}
+
+static int hal_device_get_alsa_device(LibHalContext *ctx, const char *udi,
+ DBusError *error)
+{
+ return libhal_device_get_property_int(ctx, udi, "alsa.device", error);
+}
+
+static void hal_device_free(struct device* d) {
+ pa_xfree(d->udi);
+ pa_xfree(d);
+}
+
+static void hal_device_free_cb(void *d, PA_GCC_UNUSED void *data) {
+ hal_device_free((struct device*) d);
+}
+
+static dbus_bool_t hal_device_add_alsa(struct userdata *u, const char *udi,
+ DBusError *error)
+{
+ char args[64];
+ alsa_type_t type;
+ int device, card;
+ pa_module *m;
+ struct device *d;
+ const char *module_name;
+
+ type = hal_device_get_alsa_type(u->ctx, udi, error);
+ if (dbus_error_is_set(error) || type == ALSA_TYPE_OTHER) {
+ return FALSE;
+ }
+
+ device = hal_device_get_alsa_device(u->ctx, udi, error);
+ if (dbus_error_is_set(error) || device != 0)
+ return FALSE;
+
+ card = hal_device_get_alsa_card(u->ctx, udi, error);
+ if (dbus_error_is_set(error))
+ return FALSE;
+
+ module_name = (type == ALSA_TYPE_SINK) ? "module-alsa-sink"
+ : "module-alsa-source";
+ snprintf(args, sizeof(args), "device=hw:%u", card);
+ if (!(m = pa_module_load(u->core, module_name, args)))
+ return FALSE;
+
+ d = pa_xmalloc(sizeof(struct device));
+ d->udi = pa_xstrdup(udi);
+ d->module = m;
+
+ pa_hashmap_put(u->by_module, m, d);
+ pa_hashmap_put(u->by_udi, udi, d);
+
+ return TRUE;
+}
+
+static int hal_device_add_all(struct userdata *u, capability_t capability)
+{
+ DBusError error;
+ int i,n,count;
+ dbus_bool_t r;
+ char** udis;
+ const char* cap = capabilities[capability];
+
+ assert(capability < CAP_MAX);
+
+ pa_log_info(__FILE__": Trying capability %u (%s)", capability, cap);
+ dbus_error_init(&error);
+ udis = libhal_find_device_by_capability(u->ctx, cap, &n, &error);
+ if (dbus_error_is_set(&error)) {
+ pa_log_error(__FILE__": Error finding devices: %s: %s", error.name,
+ error.message);
+ dbus_error_free(&error);
+ return -1;
+ }
+ count = 0;
+ for (i = 0; i < n; ++i) {
+ switch(capability) {
+ case CAP_ALSA:
+ r = hal_device_add_alsa(u, udis[i], &error);
+ break;
+ case CAP_OSS:
+ /* r = hal_device_add_oss(u, udis[i], &error)
+ * break; */
+ case CAP_MAX:
+ default:
+ assert(FALSE);
+ break;
+ }
+
+ if (dbus_error_is_set(&error)) {
+ pa_log_error(__FILE__": Error adding device: %s: %s", error.name,
+ error.message);
+ dbus_error_free(&error);
+ count = -1;
+ break;
+ }
+ if (r)
+ ++count;
+ }
+
+ libhal_free_string_array(udis);
+ u->capability = capability;
+ return count;
+}
+
+static dbus_bool_t device_has_capability(LibHalContext *ctx, const char *udi,
+ const char* cap, DBusError *error)
+{
+ dbus_bool_t has_prop;
+ has_prop = libhal_device_property_exists(ctx, udi, "info.capabilities",
+ error);
+ if (!has_prop || dbus_error_is_set(error))
+ return FALSE;
+
+ return libhal_device_query_capability(ctx, 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 = (struct timerdata*) userdata;
+
+ dbus_error_init(&error);
+ if (!libhal_device_exists(td->u->ctx, td->udi, &error))
+ goto exit;
+
+ switch(td->u->capability) {
+ case CAP_ALSA:
+ hal_device_add_alsa(td->u, td->udi, &error);
+ break;
+ case CAP_OSS:
+ /* hal_device_add_oss(td->u, td->udi, &error);
+ * break; */
+ case CAP_MAX:
+ default:
+ /* not reached */
+ assert(FALSE);
+ break;
+ }
+
+exit:
+ if (dbus_error_is_set(&error)) {
+ pa_log_error(__FILE__": Error adding device: %s: %s", error.name,
+ error.message);
+ dbus_error_free(&error);
+ }
+
+ pa_xfree(td->udi);
+ pa_xfree(td);
+ ea->time_free(ev);
+}
+
+static void device_added_cb(LibHalContext *ctx, const char *udi)
+{
+ DBusError error;
+ struct timeval tv;
+ dbus_bool_t has_cap;
+ struct timerdata *t;
+ struct userdata *u = (struct userdata*) libhal_ctx_get_user_data(ctx);
+ const char* cap = capabilities[u->capability];
+
+ pa_log_debug(__FILE__": HAL Device added: %s", udi);
+
+ dbus_error_init(&error);
+ has_cap = device_has_capability(ctx, udi, cap, &error);
+ if (dbus_error_is_set(&error)) {
+ pa_log_error(__FILE__": Error getting capability: %s: %s", error.name,
+ error.message);
+ dbus_error_free(&error);
+ return;
+ }
+
+ /* skip it */
+ if (!has_cap)
+ return;
+
+ /* actually add the device one second later */
+ t = pa_xmalloc(sizeof(struct timerdata));
+ t->u = u;
+ t->udi = pa_xstrdup(udi);
+
+ gettimeofday(&tv, NULL);
+ tv.tv_sec += 1;
+ u->core->mainloop->time_new(u->core->mainloop, &tv,
+ device_added_time_cb, t);
+}
+
+static void device_removed_cb(LibHalContext* ctx, const char *udi)
+{
+ struct device *d;
+ struct userdata *u = (struct userdata*) libhal_ctx_get_user_data(ctx);
+
+ pa_log_debug(__FILE__": Device removed: %s", udi);
+ if ((d = pa_hashmap_remove(u->by_udi, udi))) {
+ d = pa_hashmap_remove(u->by_module, d->module);
+ pa_log_debug(__FILE__": Unloading: %s <%s>", d->module->name, d->module->argument);
+ pa_module_unload_request(d->module);
+ hal_device_free(d);
+ }
+}
+
+#if 0
+static void new_capability_cb(LibHalContext *ctx, const char *udi,
+ const char* capability)
+{
+}
+
+static void lost_capability_cb(LibHalContext *ctx, const char *udi,
+ const char* capability)
+{
+}
+
+static void property_modified_cb(LibHalContext *ctx, const char *udi,
+ const char* key,
+ dbus_bool_t is_removed,
+ dbus_bool_t is_added)
+{
+}
+#endif
+
+static void subscribe_notify_cb(pa_core *c, pa_subscription_event_type_t type,
+ uint32_t idx, void *userdata)
+{
+ pa_module *m;
+ struct device *d;
+ struct userdata *u = (struct userdata*) userdata;
+
+ /* only listen for module remove events */
+ if (type != (PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE))
+ return;
+
+ if (!(m = pa_idxset_get_by_index(c->modules, idx)))
+ return;
+
+ /* we found the module, see if it's one we care about */
+ if ((d = pa_hashmap_remove(u->by_module, m))) {
+ pa_log_debug(__FILE__": Removing module #%u %s: %s",
+ m->index, m->name, d->udi);
+ d = pa_hashmap_remove(u->by_udi, d->udi);
+ hal_device_free(d);
+ }
+}
+
+
+static void pa_hal_context_free(LibHalContext* hal_ctx)
+{
+ DBusError error;
+
+ dbus_error_init(&error);
+ libhal_ctx_shutdown(hal_ctx, &error);
+ libhal_ctx_free(hal_ctx);
+
+ if (dbus_error_is_set(&error)) {
+ dbus_error_free(&error);
+ }
+}
+
+static void userdata_free(struct userdata *u) {
+ pa_hal_context_free(u->ctx);
+ pa_subscription_free(u->sub);
+ /* free the hashmap */
+ pa_hashmap_free(u->by_module, NULL, NULL);
+ /* free the devices with the hashmap */
+ pa_hashmap_free(u->by_udi, hal_device_free_cb, NULL);
+ pa_dbus_connection_unref(u->conn);
+ pa_xfree(u);
+}
+
+static LibHalContext* pa_hal_context_new(pa_core* c, DBusConnection *conn)
+{
+ DBusError error;
+ LibHalContext *hal_ctx = NULL;
+
+ dbus_error_init(&error);
+ if (!(hal_ctx = libhal_ctx_new())) {
+ pa_log_error(__FILE__": libhal_ctx_new() failed");
+ goto fail;
+ }
+
+ if (!libhal_ctx_set_dbus_connection(hal_ctx, conn)) {
+ pa_log_error(__FILE__": Error establishing DBUS connection: %s: %s",
+ error.name, error.message);
+ goto fail;
+ }
+
+ if (!libhal_ctx_init(hal_ctx, &error)) {
+ pa_log_error(__FILE__": Couldn't connect to hald: %s: %s",
+ error.name, error.message);
+ goto fail;
+ }
+
+ return hal_ctx;
+
+fail:
+ if (hal_ctx)
+ pa_hal_context_free(hal_ctx);
+
+ if (dbus_error_is_set(&error))
+ dbus_error_free(&error);
+
+ return NULL;
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ int n;
+ DBusError error;
+ pa_dbus_connection *conn;
+ struct userdata *u = NULL;
+ LibHalContext *hal_ctx = NULL;
+
+ assert(c);
+ assert(m);
+
+ dbus_error_init(&error);
+ if (!(conn = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &error))) {
+ pa_log_error(__FILE__": Unable to contact DBUS system bus: %s: %s",
+ error.name, error.message);
+ dbus_error_free(&error);
+ return -1;
+ }
+
+ if (!(hal_ctx = pa_hal_context_new(c, pa_dbus_connection_get(conn)))) {
+ /* pa_hal_context_new() logs appropriate errors */
+ return -1;
+ }
+
+ u = pa_xmalloc(sizeof(struct userdata));
+ u->core = c;
+ u->ctx = hal_ctx;
+ u->conn = conn;
+ u->by_module = pa_hashmap_new(NULL, NULL);
+ u->by_udi = pa_hashmap_new(pa_idxset_string_hash_func,
+ pa_idxset_string_compare_func);
+ u->sub = pa_subscription_new(c, PA_SUBSCRIPTION_MASK_MODULE,
+ subscribe_notify_cb, (void*) u);
+ m->userdata = (void*) u;
+
+#if HAVE_ALSA
+ if ((n = hal_device_add_all(u, CAP_ALSA)) <= 0)
+#endif
+#if HAVE_OSS
+ if ((n = hal_device_add_all(u, CAP_OSS)) <= 0)
+#endif
+ {
+ pa_log_warn(__FILE__": failed to detect any sound hardware.");
+ userdata_free(u);
+ return -1;
+ }
+
+ libhal_ctx_set_user_data(hal_ctx, (void*) u);
+ libhal_ctx_set_device_added(hal_ctx, device_added_cb);
+ libhal_ctx_set_device_removed(hal_ctx, device_removed_cb);
+
+ dbus_error_init(&error);
+ if (!libhal_device_property_watch_all(hal_ctx, &error)) {
+ pa_log_error(__FILE__": error monitoring device list: %s: %s",
+ error.name, error.message);
+ dbus_error_free(&error);
+ userdata_free(u);
+ return -1;
+ }
+
+ /*
+ libhal_ctx_set_device_new_capability(hal_ctx, new_capability_cb);
+ libhal_ctx_set_device_lost_capability(hal_ctx, lost_capability_cb);
+ libhal_ctx_set_device_property_modified(hal_ctx, property_modified_cb);
+ */
+
+ pa_log_info(__FILE__": loaded %i modules.", n);
+
+ return 0;
+}
+
+
+void pa__done(PA_GCC_UNUSED pa_core *c, pa_module *m) {
+ assert (c && m);
+
+ /* free the user data */
+ userdata_free(m->userdata);
+}
diff -urN pa-upstream/src/pulse/introspect.c pa-hotplug/src/pulse/introspect.c
--- pa-upstream/src/pulse/introspect.c 2006-08-03 15:33:11.000000000 -0700
+++ pa-hotplug/src/pulse/introspect.c 2006-08-04 09:44:49.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: introspect.c 1184 2006-08-03 22:33:11Z lennart $ */
+/* $Id: introspect.c 1179 2006-08-01 21:04:43Z lennart $ */
/***
This file is part of PulseAudio.
diff -urN pa-upstream/src/pulse/introspect.h pa-hotplug/src/pulse/introspect.h
--- pa-upstream/src/pulse/introspect.h 2006-08-03 15:33:11.000000000 -0700
+++ pa-hotplug/src/pulse/introspect.h 2006-08-04 09:44:49.000000000 -0700
@@ -1,7 +1,7 @@
#ifndef foointrospecthfoo
#define foointrospecthfoo
-/* $Id: introspect.h 1184 2006-08-03 22:33:11Z lennart $ */
+/* $Id: introspect.h 1177 2006-07-31 21:55:09Z lennart $ */
/***
This file is part of PulseAudio.
diff -urN pa-upstream/src/pulsecore/cli-command.c pa-hotplug/src/pulsecore/cli-command.c
--- pa-upstream/src/pulsecore/cli-command.c 2006-08-03 15:31:35.000000000 -0700
+++ pa-hotplug/src/pulsecore/cli-command.c 2006-08-04 09:44:49.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: cli-command.c 1182 2006-08-03 22:31:35Z lennart $ */
+/* $Id: cli-command.c 1169 2006-07-29 15:07:15Z lennart $ */
/***
This file is part of PulseAudio.
diff -urN pa-upstream/src/pulsecore/protocol-native.c pa-hotplug/src/pulsecore/protocol-native.c
--- pa-upstream/src/pulsecore/protocol-native.c 2006-08-03 15:32:23.000000000 -0700
+++ pa-hotplug/src/pulsecore/protocol-native.c 2006-08-04 09:44:49.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: protocol-native.c 1183 2006-08-03 22:32:23Z lennart $ */
+/* $Id: protocol-native.c 1176 2006-07-31 21:54:46Z lennart $ */
/***
This file is part of PulseAudio.
diff -urN pa-upstream/src/pulsecore/sink-input.c pa-hotplug/src/pulsecore/sink-input.c
--- pa-upstream/src/pulsecore/sink-input.c 2006-08-03 15:29:55.000000000 -0700
+++ pa-hotplug/src/pulsecore/sink-input.c 2006-08-04 09:44:49.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: sink-input.c 1180 2006-08-03 22:29:55Z lennart $ */
+/* $Id: sink-input.c 1168 2006-07-29 15:06:49Z lennart $ */
/***
This file is part of PulseAudio.
diff -urN pa-upstream/src/pulsecore/source-output.c pa-hotplug/src/pulsecore/source-output.c
--- pa-upstream/src/pulsecore/source-output.c 2006-08-03 15:30:45.000000000 -0700
+++ pa-hotplug/src/pulsecore/source-output.c 2006-08-04 09:44:49.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: source-output.c 1181 2006-08-03 22:30:45Z lennart $ */
+/* $Id: source-output.c 1033 2006-06-19 21:53:48Z lennart $ */
/***
This file is part of PulseAudio.
diff -urN pa-upstream/src/pulsecore/source-output.h pa-hotplug/src/pulsecore/source-output.h
--- pa-upstream/src/pulsecore/source-output.h 2006-08-03 15:30:45.000000000 -0700
+++ pa-hotplug/src/pulsecore/source-output.h 2006-08-04 09:44:49.000000000 -0700
@@ -1,7 +1,7 @@
#ifndef foosourceoutputhfoo
#define foosourceoutputhfoo
-/* $Id: source-output.h 1181 2006-08-03 22:30:45Z lennart $ */
+/* $Id: source-output.h 1033 2006-06-19 21:53:48Z lennart $ */
/***
This file is part of PulseAudio.
diff -urN pa-upstream/src/utils/pactl.c pa-hotplug/src/utils/pactl.c
--- pa-upstream/src/utils/pactl.c 2006-08-03 15:33:54.000000000 -0700
+++ pa-hotplug/src/utils/pactl.c 2006-08-04 09:44:49.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: pactl.c 1185 2006-08-03 22:33:54Z lennart $ */
+/* $Id: pactl.c 1179 2006-08-01 21:04:43Z lennart $ */
/***
This file is part of PulseAudio.
diff -urN pa-upstream/todo pa-hotplug/todo
--- pa-upstream/todo 2006-08-03 15:34:22.000000000 -0700
+++ pa-hotplug/todo 2006-08-04 09:44:49.000000000 -0700
@@ -1,4 +1,4 @@
-*** $Id: todo 1186 2006-08-03 22:34:22Z lennart $ ***
+*** $Id: todo 1179 2006-08-01 21:04:43Z lennart $ ***
Post 0.9.0:
- alsa mmap driver
_______________________________________________
pulseaudio-discuss mailing list
[email protected]
https://tango.0pointer.de/mailman/listinfo/pulseaudio-discuss