core: split of FAIL_ON_SUSPEND into KILL_ON_SUSPEND and NO_CREATE_ON_SUSPEND
[pulseaudio-mirror.git] / src / modules / module-hal-detect.c
blobec370d613af3ac7796addd27b1bf5ba30bba2b21
1 /***
2 This file is part of PulseAudio.
4 Copyright 2006 Lennart Poettering
5 Copyright 2006 Shams E. King
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
36 #include <pulse/xmalloc.h>
37 #include <pulse/timeval.h>
39 #include <pulsecore/core-error.h>
40 #include <pulsecore/module.h>
41 #include <pulsecore/log.h>
42 #include <pulsecore/hashmap.h>
43 #include <pulsecore/idxset.h>
44 #include <pulsecore/core-util.h>
45 #include <pulsecore/namereg.h>
46 #include <pulsecore/core-scache.h>
47 #include <pulsecore/modargs.h>
48 #include <pulsecore/dbus-shared.h>
50 #include <hal/libhal.h>
52 #include "module-hal-detect-symdef.h"
54 PA_MODULE_AUTHOR("Shahms King");
55 PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
56 PA_MODULE_VERSION(PACKAGE_VERSION);
57 PA_MODULE_LOAD_ONCE(TRUE);
58 #if defined(HAVE_ALSA) && defined(HAVE_OSS_OUTPUT)
59 PA_MODULE_USAGE("api=<alsa or oss> "
60 "tsched=<enable system timer based scheduling mode?>"
61 "subdevices=<init all subdevices>");
62 #elif defined(HAVE_ALSA)
63 PA_MODULE_USAGE("api=<alsa> "
64 "tsched=<enable system timer based scheduling mode?>");
65 #elif defined(HAVE_OSS_OUTPUT)
66 PA_MODULE_USAGE("api=<oss>"
67 "subdevices=<init all subdevices>");
68 #endif
69 PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-hal-detect!");
71 struct device {
72 char *udi, *originating_udi;
73 char *card_name, *sink_name, *source_name;
74 uint32_t module;
75 pa_bool_t acl_race_fix;
78 struct userdata {
79 pa_core *core;
80 LibHalContext *context;
81 pa_dbus_connection *connection;
82 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 */
83 const char *capability;
84 #ifdef HAVE_ALSA
85 pa_bool_t use_tsched;
86 #endif
87 #ifdef HAVE_OSS_OUTPUT
88 pa_bool_t init_subdevs;
89 #endif
92 #define CAPABILITY_ALSA "alsa"
93 #define CAPABILITY_OSS "oss"
95 static const char* const valid_modargs[] = {
96 "api",
97 #ifdef HAVE_ALSA
98 "tsched",
99 #endif
100 #ifdef HAVE_OSS_OUTPUT
101 "subdevices",
102 #endif
103 NULL
106 static void device_free(struct device* d) {
107 pa_assert(d);
109 pa_xfree(d->udi);
110 pa_xfree(d->originating_udi);
111 pa_xfree(d->sink_name);
112 pa_xfree(d->source_name);
113 pa_xfree(d->card_name);
114 pa_xfree(d);
117 static const char *strip_udi(const char *udi) {
118 const char *slash;
120 pa_assert(udi);
122 if ((slash = strrchr(udi, '/')))
123 return slash+1;
125 return udi;
128 #ifdef HAVE_ALSA
130 enum alsa_type {
131 ALSA_TYPE_PLAYBACK,
132 ALSA_TYPE_CAPTURE,
133 ALSA_TYPE_CONTROL,
134 ALSA_TYPE_OTHER
137 static enum alsa_type hal_alsa_device_get_type(LibHalContext *context, const char *udi) {
138 char *type;
139 enum alsa_type t = ALSA_TYPE_OTHER;
140 DBusError error;
142 dbus_error_init(&error);
144 pa_assert(context);
145 pa_assert(udi);
147 if (!(type = libhal_device_get_property_string(context, udi, "alsa.type", &error)))
148 goto finish;
150 if (pa_streq(type, "playback"))
151 t = ALSA_TYPE_PLAYBACK;
152 else if (pa_streq(type, "capture"))
153 t = ALSA_TYPE_CAPTURE;
154 else if (pa_streq(type, "control"))
155 t = ALSA_TYPE_CONTROL;
157 libhal_free_string(type);
159 finish:
160 if (dbus_error_is_set(&error)) {
161 pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
162 dbus_error_free(&error);
165 return t;
168 static pa_bool_t hal_alsa_device_is_modem(LibHalContext *context, const char *udi) {
169 char *class;
170 pa_bool_t r = FALSE;
171 DBusError error;
173 dbus_error_init(&error);
175 pa_assert(context);
176 pa_assert(udi);
178 if (!(class = libhal_device_get_property_string(context, udi, "alsa.pcm_class", &error)))
179 goto finish;
181 r = pa_streq(class, "modem");
182 libhal_free_string(class);
184 finish:
185 if (dbus_error_is_set(&error)) {
186 if (!dbus_error_has_name(&error, "org.freedesktop.Hal.NoSuchProperty"))
187 pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
188 dbus_error_free(&error);
191 return r;
194 static int hal_device_load_alsa(struct userdata *u, const char *udi, struct device *d) {
195 enum alsa_type type;
196 int card;
197 DBusError error;
198 pa_module *m;
199 char *args, *originating_udi = NULL, *card_name = NULL;
201 dbus_error_init(&error);
203 pa_assert(u);
204 pa_assert(udi);
205 pa_assert(d);
207 /* We only care for PCM devices */
208 type = hal_alsa_device_get_type(u->context, udi);
210 /* For each ALSA card that appears the control device will be the
211 * last one to be created, this is considered part of the ALSA
212 * usperspace API. We rely on this and load our modules only when
213 * the control device is available assuming that *all* device
214 * nodes have been properly created and assigned the right ACLs at
215 * that time. Also see:
217 * http://mailman.alsa-project.org/pipermail/alsa-devel/2009-April/015958.html
219 * and the associated thread.*/
221 if (type != ALSA_TYPE_CONTROL)
222 goto fail;
224 /* We don't care for modems -- this is most likely not set for
225 * control devices, so kind of pointless here. */
226 if (hal_alsa_device_is_modem(u->context, udi))
227 goto fail;
229 /* We store only one entry per card, hence we look for the originating device */
230 originating_udi = libhal_device_get_property_string(u->context, udi, "alsa.originating_device", &error);
231 if (dbus_error_is_set(&error) || !originating_udi)
232 goto fail;
234 /* Make sure we only load one module per card */
235 if (pa_hashmap_get(u->devices, originating_udi))
236 goto fail;
238 /* We need the identifier */
239 card = libhal_device_get_property_int(u->context, udi, "alsa.card", &error);
240 if (dbus_error_is_set(&error))
241 goto fail;
243 card_name = pa_sprintf_malloc("alsa_card.%s", strip_udi(originating_udi));
244 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);
246 pa_log_debug("Loading module-alsa-card with arguments '%s'", args);
247 m = pa_module_load(u->core, "module-alsa-card", args);
248 pa_xfree(args);
250 if (!m)
251 goto fail;
253 d->originating_udi = originating_udi;
254 d->module = m->index;
255 d->card_name = card_name;
257 return 0;
259 fail:
260 if (dbus_error_is_set(&error)) {
261 pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
262 dbus_error_free(&error);
265 pa_xfree(originating_udi);
266 pa_xfree(card_name);
268 return -1;
271 #endif
273 #ifdef HAVE_OSS_OUTPUT
275 static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi, pa_bool_t init_subdevices) {
276 char *class = NULL, *dev = NULL, *e;
277 int device;
278 pa_bool_t r = FALSE;
279 DBusError error;
281 dbus_error_init(&error);
283 pa_assert(context);
284 pa_assert(udi);
286 /* We only care for PCM devices */
287 class = libhal_device_get_property_string(context, udi, "oss.type", &error);
288 if (dbus_error_is_set(&error) || !class)
289 goto finish;
291 if (!pa_streq(class, "pcm"))
292 goto finish;
294 /* We don't like /dev/audio */
295 dev = libhal_device_get_property_string(context, udi, "oss.device_file", &error);
296 if (dbus_error_is_set(&error) || !dev)
297 goto finish;
299 if ((e = strrchr(dev, '/')))
300 if (pa_startswith(e + 1, "audio"))
301 goto finish;
303 /* We only care for the main device */
304 device = libhal_device_get_property_int(context, udi, "oss.device", &error);
305 if (dbus_error_is_set(&error) || (device != 0 && init_subdevices == FALSE))
306 goto finish;
308 r = TRUE;
310 finish:
312 if (dbus_error_is_set(&error)) {
313 pa_log_error("D-Bus error while parsing HAL OSS data: %s: %s", error.name, error.message);
314 dbus_error_free(&error);
317 libhal_free_string(class);
318 libhal_free_string(dev);
320 return r;
323 static int hal_device_load_oss(struct userdata *u, const char *udi, struct device *d) {
324 DBusError error;
325 pa_module *m;
326 char *args, *originating_udi = NULL, *device, *sink_name = NULL, *source_name = NULL;
328 dbus_error_init(&error);
330 pa_assert(u);
331 pa_assert(udi);
332 pa_assert(d);
334 /* We only care for OSS PCM devices */
335 if (!hal_oss_device_is_pcm(u->context, udi, u->init_subdevs))
336 goto fail;
338 /* We store only one entry per card, hence we look for the originating device */
339 originating_udi = libhal_device_get_property_string(u->context, udi, "oss.originating_device", &error);
340 if (dbus_error_is_set(&error) || !originating_udi)
341 goto fail;
343 /* Make sure we only load one module per card */
344 if (pa_hashmap_get(u->devices, originating_udi))
345 goto fail;
347 /* We need the device file */
348 device = libhal_device_get_property_string(u->context, udi, "oss.device_file", &error);
349 if (!device || dbus_error_is_set(&error))
350 goto fail;
352 sink_name = pa_sprintf_malloc("oss_output.%s", strip_udi(udi));
353 source_name = pa_sprintf_malloc("oss_input.%s", strip_udi(udi));
354 args = pa_sprintf_malloc("device=%s sink_name=%s source_name=%s", device, sink_name, source_name);
356 libhal_free_string(device);
358 pa_log_debug("Loading module-oss with arguments '%s'", args);
359 m = pa_module_load(u->core, "module-oss", args);
360 pa_xfree(args);
362 if (!m)
363 goto fail;
365 d->originating_udi = originating_udi;
366 d->module = m->index;
367 d->sink_name = sink_name;
368 d->source_name = source_name;
370 return 0;
372 fail:
373 if (dbus_error_is_set(&error)) {
374 pa_log_error("D-Bus error while parsing OSS HAL data: %s: %s", error.name, error.message);
375 dbus_error_free(&error);
378 pa_xfree(originating_udi);
379 pa_xfree(source_name);
380 pa_xfree(sink_name);
382 return -1;
384 #endif
386 static struct device* hal_device_add(struct userdata *u, const char *udi) {
387 struct device *d;
388 int r;
390 pa_assert(u);
391 pa_assert(u->capability);
393 d = pa_xnew(struct device, 1);
394 d->acl_race_fix = FALSE;
395 d->udi = pa_xstrdup(udi);
396 d->originating_udi = NULL;
397 d->module = PA_INVALID_INDEX;
398 d->sink_name = d->source_name = d->card_name = NULL;
399 r = -1;
401 #ifdef HAVE_ALSA
402 if (pa_streq(u->capability, CAPABILITY_ALSA))
403 r = hal_device_load_alsa(u, udi, d);
404 #endif
405 #ifdef HAVE_OSS_OUTPUT
406 if (pa_streq(u->capability, CAPABILITY_OSS))
407 r = hal_device_load_oss(u, udi, d);
408 #endif
410 if (r < 0) {
411 device_free(d);
412 return NULL;
415 pa_hashmap_put(u->devices, d->udi, d);
416 pa_hashmap_put(u->devices, d->originating_udi, d);
418 return d;
421 static int hal_device_add_all(struct userdata *u) {
422 int n, count = 0;
423 char** udis;
424 DBusError error;
426 dbus_error_init(&error);
428 pa_assert(u);
430 udis = libhal_find_device_by_capability(u->context, u->capability, &n, &error);
431 if (dbus_error_is_set(&error) || !udis)
432 goto fail;
434 if (n > 0) {
435 int i;
437 for (i = 0; i < n; i++) {
438 struct device *d;
440 if ((d = hal_device_add(u, udis[i]))) {
441 count++;
442 pa_log_debug("Loaded device %s", udis[i]);
443 } else
444 pa_log_debug("Not loaded device %s", udis[i]);
448 libhal_free_string_array(udis);
450 return count;
452 fail:
453 if (dbus_error_is_set(&error)) {
454 pa_log_error("D-Bus error while parsing HAL data: %s: %s", error.name, error.message);
455 dbus_error_free(&error);
458 return -1;
461 static void device_added_cb(LibHalContext *context, const char *udi) {
462 DBusError error;
463 struct userdata *u;
464 pa_bool_t good = FALSE;
466 dbus_error_init(&error);
468 pa_assert(context);
469 pa_assert(udi);
471 pa_assert_se(u = libhal_ctx_get_user_data(context));
473 good = libhal_device_query_capability(context, udi, u->capability, &error);
474 if (dbus_error_is_set(&error) || !good)
475 goto finish;
477 if (!hal_device_add(u, udi))
478 pa_log_debug("Not loaded device %s", udi);
479 else
480 pa_log_debug("Loaded device %s", udi);
482 finish:
483 if (dbus_error_is_set(&error)) {
484 if (!dbus_error_has_name(&error, "org.freedesktop.Hal.NoSuchProperty"))
485 pa_log_error("D-Bus error while parsing HAL data: %s: %s", error.name, error.message);
486 dbus_error_free(&error);
490 static void device_removed_cb(LibHalContext* context, const char *udi) {
491 struct device *d;
492 struct userdata *u;
494 pa_assert(context);
495 pa_assert(udi);
497 pa_assert_se(u = libhal_ctx_get_user_data(context));
499 if (!(d = pa_hashmap_get(u->devices, udi)))
500 return;
502 pa_hashmap_remove(u->devices, d->originating_udi);
503 pa_hashmap_remove(u->devices, d->udi);
505 pa_log_debug("Removing HAL device: %s", d->originating_udi);
507 pa_module_unload_request_by_index(u->core, d->module, TRUE);
508 device_free(d);
511 static void new_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
512 struct userdata *u;
514 pa_assert(context);
515 pa_assert(udi);
516 pa_assert(capability);
518 pa_assert_se(u = libhal_ctx_get_user_data(context));
520 if (pa_streq(u->capability, capability))
521 /* capability we care about, pretend it's a new device */
522 device_added_cb(context, udi);
525 static void lost_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
526 struct userdata *u;
528 pa_assert(context);
529 pa_assert(udi);
530 pa_assert(capability);
532 pa_assert_se(u = libhal_ctx_get_user_data(context));
534 if (pa_streq(u->capability, capability))
535 /* capability we care about, pretend it was removed */
536 device_removed_cb(context, udi);
539 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
540 struct userdata*u;
541 DBusError error;
543 pa_assert(bus);
544 pa_assert(message);
545 pa_assert_se(u = userdata);
547 dbus_error_init(&error);
549 pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
550 dbus_message_get_interface(message),
551 dbus_message_get_path(message),
552 dbus_message_get_member(message));
554 if (dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLAdded") ||
555 dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLRemoved")) {
556 uint32_t uid;
557 pa_bool_t suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0;
559 if (!dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
560 pa_log_error("Failed to parse ACL message: %s: %s", error.name, error.message);
561 goto finish;
564 /* Check if this is about us? */
565 if (uid == getuid() || uid == geteuid()) {
566 struct device *d;
567 const char *udi;
569 udi = dbus_message_get_path(message);
571 if ((d = pa_hashmap_get(u->devices, udi))) {
572 pa_bool_t send_acl_race_fix_message = FALSE;
573 d->acl_race_fix = FALSE;
575 if (d->sink_name) {
576 pa_sink *sink;
578 if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK))) {
579 pa_bool_t success = pa_sink_suspend(sink, suspend, PA_SUSPEND_SESSION) >= 0;
581 if (!success && !suspend)
582 d->acl_race_fix = TRUE; /* resume failed, let's try again */
583 else if (suspend)
584 send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
588 if (d->source_name) {
589 pa_source *source;
591 if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE))) {
592 pa_bool_t success = pa_source_suspend(source, suspend, PA_SUSPEND_SESSION) >= 0;
594 if (!success && !suspend)
595 d->acl_race_fix = TRUE; /* resume failed, let's try again */
596 else if (suspend)
597 send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
601 if (d->card_name) {
602 pa_card *card;
604 if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) {
605 pa_bool_t success = pa_card_suspend(card, suspend, PA_SUSPEND_SESSION) >= 0;
607 if (!success && !suspend)
608 d->acl_race_fix = TRUE; /* resume failed, let's try again */
609 else if (suspend)
610 send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
614 if (send_acl_race_fix_message) {
615 DBusMessage *msg;
616 msg = dbus_message_new_signal(udi, "org.pulseaudio.Server", "DirtyGiveUpMessage");
617 dbus_connection_send(pa_dbus_connection_get(u->connection), msg, NULL);
618 dbus_message_unref(msg);
621 } else if (!suspend)
622 device_added_cb(u->context, udi);
626 return DBUS_HANDLER_RESULT_HANDLED;
628 } else if (dbus_message_is_signal(message, "org.pulseaudio.Server", "DirtyGiveUpMessage")) {
629 /* We use this message to avoid a dirty race condition when we
630 get an ACLAdded message before the previously owning PA
631 sever has closed the device. We can remove this as
632 soon as HAL learns frevoke() */
634 struct device *d;
635 const char *udi;
637 udi = dbus_message_get_path(message);
639 if ((d = pa_hashmap_get(u->devices, udi))) {
641 if (d->acl_race_fix) {
642 d->acl_race_fix = FALSE;
643 pa_log_debug("Got dirty give up message for '%s', trying resume ...", udi);
645 if (d->sink_name) {
646 pa_sink *sink;
648 if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK)))
649 pa_sink_suspend(sink, FALSE, PA_SUSPEND_SESSION);
652 if (d->source_name) {
653 pa_source *source;
655 if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE)))
656 pa_source_suspend(source, FALSE, PA_SUSPEND_SESSION);
659 if (d->card_name) {
660 pa_card *card;
662 if ((card = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_CARD)))
663 pa_card_suspend(card, FALSE, PA_SUSPEND_SESSION);
667 } else
668 /* Yes, we don't check the UDI for validity, but hopefully HAL will */
669 device_added_cb(u->context, udi);
671 return DBUS_HANDLER_RESULT_HANDLED;
674 finish:
675 dbus_error_free(&error);
677 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
680 static void hal_context_free(LibHalContext* hal_context) {
681 DBusError error;
683 dbus_error_init(&error);
685 libhal_ctx_shutdown(hal_context, &error);
686 libhal_ctx_free(hal_context);
688 dbus_error_free(&error);
691 static LibHalContext* hal_context_new(DBusConnection *connection) {
692 DBusError error;
693 LibHalContext *hal_context = NULL;
695 dbus_error_init(&error);
697 pa_assert(connection);
699 if (!(hal_context = libhal_ctx_new())) {
700 pa_log_error("libhal_ctx_new() failed");
701 goto fail;
704 if (!libhal_ctx_set_dbus_connection(hal_context, connection)) {
705 pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);
706 goto fail;
709 if (!libhal_ctx_init(hal_context, &error)) {
710 pa_log_error("Couldn't connect to hald: %s: %s", error.name, error.message);
711 goto fail;
714 return hal_context;
716 fail:
717 if (hal_context)
718 hal_context_free(hal_context);
720 dbus_error_free(&error);
722 return NULL;
725 int pa__init(pa_module*m) {
726 DBusError error;
727 struct userdata *u = NULL;
728 int n = 0;
729 pa_modargs *ma;
730 const char *api;
732 pa_assert(m);
734 dbus_error_init(&error);
736 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
737 pa_log("Failed to parse module arguments");
738 goto fail;
741 m->userdata = u = pa_xnew(struct userdata, 1);
742 u->core = m->core;
743 u->context = NULL;
744 u->connection = NULL;
745 u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
746 u->capability = NULL;
748 #ifdef HAVE_ALSA
749 u->use_tsched = TRUE;
751 if (pa_modargs_get_value_boolean(ma, "tsched", &u->use_tsched) < 0) {
752 pa_log("Failed to parse tsched argument.");
753 goto fail;
756 api = pa_modargs_get_value(ma, "api", "alsa");
758 if (pa_streq(api, "alsa"))
759 u->capability = CAPABILITY_ALSA;
760 #else
761 api = pa_modargs_get_value(ma, "api", "oss");
762 #endif
764 #ifdef HAVE_OSS_OUTPUT
765 if (pa_streq(api, "oss"))
766 u->capability = CAPABILITY_OSS;
767 #endif
769 if (!u->capability) {
770 pa_log_error("Invalid API specification.");
771 goto fail;
774 #ifdef HAVE_OSS_OUTPUT
775 if (pa_modargs_get_value_boolean(ma, "subdevices", &u->init_subdevs) < 0) {
776 pa_log("Failed to parse subdevices= argument.");
777 goto fail;
779 #endif
781 if (!(u->connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
782 pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message);
783 goto fail;
786 if (!(u->context = hal_context_new(pa_dbus_connection_get(u->connection)))) {
787 /* pa_hal_context_new() logs appropriate errors */
788 goto fail;
791 n = hal_device_add_all(u);
793 libhal_ctx_set_user_data(u->context, u);
794 libhal_ctx_set_device_added(u->context, device_added_cb);
795 libhal_ctx_set_device_removed(u->context, device_removed_cb);
796 libhal_ctx_set_device_new_capability(u->context, new_capability_cb);
797 libhal_ctx_set_device_lost_capability(u->context, lost_capability_cb);
799 if (!libhal_device_property_watch_all(u->context, &error)) {
800 pa_log_error("Error monitoring device list: %s: %s", error.name, error.message);
801 goto fail;
804 if (!dbus_connection_add_filter(pa_dbus_connection_get(u->connection), filter_cb, u, NULL)) {
805 pa_log_error("Failed to add filter function");
806 goto fail;
809 if (pa_dbus_add_matches(
810 pa_dbus_connection_get(u->connection), &error,
811 "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLAdded'",
812 "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLRemoved'",
813 "type='signal',interface='org.pulseaudio.Server',member='DirtyGiveUpMessage'", NULL) < 0) {
814 pa_log_error("Unable to subscribe to HAL ACL signals: %s: %s", error.name, error.message);
815 goto fail;
818 pa_log_info("Loaded %i modules.", n);
820 pa_modargs_free(ma);
822 return 0;
824 fail:
825 if (ma)
826 pa_modargs_free(ma);
828 dbus_error_free(&error);
829 pa__done(m);
831 return -1;
834 void pa__done(pa_module *m) {
835 struct userdata *u;
837 pa_assert(m);
839 if (!(u = m->userdata))
840 return;
842 if (u->context)
843 hal_context_free(u->context);
845 if (u->devices) {
846 struct device *d;
848 while ((d = pa_hashmap_first(u->devices))) {
849 pa_hashmap_remove(u->devices, d->udi);
850 pa_hashmap_remove(u->devices, d->originating_udi);
851 device_free(d);
854 pa_hashmap_free(u->devices, NULL, NULL);
857 if (u->connection) {
858 pa_dbus_remove_matches(
859 pa_dbus_connection_get(u->connection),
860 "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLAdded'",
861 "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLRemoved'",
862 "type='signal',interface='org.pulseaudio.Server',member='DirtyGiveUpMessage'", NULL);
864 dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), filter_cb, u);
865 pa_dbus_connection_unref(u->connection);
868 pa_xfree(u);